#include "legato.h"
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include "interfaces.h"
#define ID_RIFF 0x46464952
#define ID_WAVE 0x45564157
#define ID_FMT 0x20746d66
#define ID_DATA 0x61746164
#define FORMAT_PCM 1
struct
{
enum
{
STOP,
PLAY,
PAUSE,
RESUME,
RECORD,
DISCONNECT
} typeOption;
} OptionContext;
typedef struct {
uint32_t riffId;
uint32_t riffSize;
uint32_t riffFmt;
uint32_t fmtId;
uint32_t fmtSize;
uint16_t audioFormat;
uint16_t channelsCount;
uint32_t sampleRate;
uint32_t byteRate;
uint16_t blockAlign;
uint16_t bitsPerSample;
uint32_t dataId;
uint32_t dataSize;
} WavHeader_t;
typedef struct
{
WavHeader_t hd;
uint32_t wroteLen;
int pipefd[2];
le_thread_Ref_t mainThreadRef;
bool playDone;
}
PbRecSamplesThreadCtx_t;
static le_audio_StreamRef_t MdmRxAudioRef = NULL;
static le_audio_StreamRef_t MdmTxAudioRef = NULL;
static le_audio_StreamRef_t FeInRef = NULL;
static le_audio_StreamRef_t FeOutRef = NULL;
static le_audio_StreamRef_t FileAudioRef = NULL;
static le_audio_ConnectorRef_t AudioInputConnectorRef = NULL;
static le_audio_ConnectorRef_t AudioOutputConnectorRef = NULL;
static le_audio_MediaHandlerRef_t MediaHandlerRef = NULL;
static const char* AudioTestCase;
static const char* MainAudioSoundPath;
static const char* AudioFilePath;
static int AudioFileFd = -1;
static bool PlayInLoop = false;
static PbRecSamplesThreadCtx_t PbRecSamplesThreadCtx;
static le_thread_Ref_t RecPbThreadRef = NULL;
static uint32_t ChannelsCount;
static uint32_t SampleRate;
static uint32_t BitsPerSample;
static void DisconnectAllAudio(void);
static void PlaySamples(void* param1Ptr,void* param2Ptr);
static uint8_t NextOptionArg;
static le_audio_Format_t AudioFormat;
static bool DtxActivation;
static le_audio_AmrMode_t AmrMode;
static void GainTimerHandler
(
)
{
static int32_t vol = 0, getVol = 0;
static bool increase = true;
if ( increase )
{
vol+=1;
if (vol == 100)
{
increase = false;
}
}
else
{
vol-=1;
if (vol == 0)
{
increase = true;
}
}
LE_INFO(
"Playback volume: vol %d", vol);
{
LE_FATAL(
"le_audio_SetGain error : %d", result);
}
if ((result !=
LE_OK) || (vol != getVol))
{
LE_FATAL(
"le_audio_GetGain error : %d read volume: %d", result, getVol);
}
}
static void MuteTimerHandler
(
)
{
static bool mute = false;
if ( mute )
{
mute = false;
}
else
{
mute = true;
}
{
LE_FATAL(
"le_audio_Mute/le_audio_Unmute Failed %d", result);
}
}
static void DestroyRecThread
(
void *contextPtr
)
{
PbRecSamplesThreadCtx_t *threadCtxPtr = (PbRecSamplesThreadCtx_t*) contextPtr;
LE_INFO(
"wroteLen %d", threadCtxPtr->wroteLen);
}
static void* RecSamplesThread
(
void* contextPtr
)
{
int pipefd[2];
int32_t len = 1024;
char data[len];
uint32_t channelsCount;
uint32_t sampleRate;
uint32_t bitsPerSample;
PbRecSamplesThreadCtx_t *threadCtxPtr = (PbRecSamplesThreadCtx_t*) contextPtr;
lseek(AudioFileFd, 0, SEEK_SET);
if (pipe(pipefd) == -1)
{
return NULL;
}
LE_INFO(
"Start getting samples...");
int32_t readLen;
int32_t writeLen;
while ((readLen = read( pipefd[0], data, len )))
{
if (readLen < 0)
{
LE_ERROR(
"read error %d %d", len, readLen);
break;
}
writeLen = write( AudioFileFd, data, readLen );
if (writeLen < 0)
{
LE_ERROR(
"write error %d %d", readLen, writeLen);
break;
}
threadCtxPtr->wroteLen += writeLen;
}
return NULL;
}
static void RecSamples
(
void
)
{
memset(&PbRecSamplesThreadCtx,0,sizeof(PbRecSamplesThreadCtx_t));
RecPbThreadRef = le_thread_Create("RecSamples", RecSamplesThread, &PbRecSamplesThreadCtx);
le_thread_AddChildDestructor(RecPbThreadRef,
DestroyRecThread,
&PbRecSamplesThreadCtx);
le_thread_Start(RecPbThreadRef);
}
static void DestroyPlayThread
(
void *contextPtr
)
{
PbRecSamplesThreadCtx_t *threadCtxPtr = (PbRecSamplesThreadCtx_t*) contextPtr;
LE_INFO(
"DestroyPlayThread playDone %d PlayInLoop %d", threadCtxPtr->playDone, PlayInLoop);
if (RecPbThreadRef != NULL)
{
le_thread_Cancel(RecPbThreadRef);
RecPbThreadRef = NULL;
if (threadCtxPtr->playDone && PlayInLoop)
{
PlaySamples,
contextPtr,
NULL);
}
}
}
static void* PlaySamplesThread
(
void* contextPtr
)
{
char data[1024];
int32_t len = 1;
uint32_t channelsCount;
uint32_t sampleRate;
uint32_t bitsPerSample;
PbRecSamplesThreadCtx_t *threadCtxPtr = (PbRecSamplesThreadCtx_t*) contextPtr;
lseek(AudioFileFd, 0, SEEK_SET);
if ( ( threadCtxPtr->pipefd[0] == -1 ) && ( threadCtxPtr->pipefd[1] == -1 ) )
{
if (pipe(threadCtxPtr->pipefd) == -1)
{
return NULL;
}
LE_INFO(
"Start playing samples...");
}
while ((len = read(AudioFileFd, data, 1024)) > 0)
{
int32_t wroteLen = write( threadCtxPtr->pipefd[1], data, len );
if (wroteLen <= 0)
{
return NULL;
}
}
threadCtxPtr->playDone = true;
return NULL;
}
static void PlaySamples
(
void* param1Ptr,
void* param2Ptr
)
{
if (RecPbThreadRef == NULL)
{
RecPbThreadRef = le_thread_Create("PlaySamples", PlaySamplesThread, param1Ptr);
le_thread_AddChildDestructor(RecPbThreadRef,
DestroyPlayThread,
param1Ptr);
le_thread_Start(RecPbThreadRef);
}
}
static void ExecuteNextOption
(
void
)
{
bool nextOption = false;
{
if (NULL == nextOptionArgPtr)
{
exit(EXIT_FAILURE);
}
if ( strncmp(nextOptionArgPtr, "STOP", 4) == 0 )
{
const char* stop = nextOptionArgPtr;
if (strlen(stop) > 5)
{
LE_INFO(
"STOP will be done in %d seconds", atoi(stop+5));
interval.
sec = atoi(stop+5);
OptionContext.typeOption = STOP;
}
}
else if ( strncmp(nextOptionArgPtr, "PLAY", 4) == 0 )
{
const char* play = nextOptionArgPtr;
if (strlen(play) > 5)
{
LE_INFO(
"PLAY will be done in %d seconds", atoi(play+5));
interval.
sec = atoi(play+5);
OptionContext.typeOption = PLAY;
}
}
else if ( strncmp(nextOptionArgPtr, "RECORD", 6) == 0 )
{
const char* play = nextOptionArgPtr;
if (strlen(play) > 7)
{
LE_INFO(
"RECORD will be done in %d seconds", atoi(play+7));
interval.
sec = atoi(play+7);
OptionContext.typeOption = RECORD;
}
}
else if ( strncmp(nextOptionArgPtr, "PAUSE", 5) == 0 )
{
const char* pause = nextOptionArgPtr;
if (strlen(pause) > 6)
{
LE_INFO(
"PAUSE will be done in %d seconds", atoi(pause+6));
interval.
sec = atoi(pause+6);
OptionContext.typeOption = PAUSE;
}
}
else if ( strncmp(nextOptionArgPtr, "RESUME", 6) == 0 )
{
const char* resume = nextOptionArgPtr;
if (strlen(resume) > 7)
{
LE_INFO(
"RESUME will be done in %d seconds", atoi(resume+7));
interval.
sec = atoi(resume+7);
OptionContext.typeOption = RESUME;
}
}
else if ( strncmp(nextOptionArgPtr, "DISCONNECT", 10) == 0 )
{
const char* resume = nextOptionArgPtr;
if (strlen(resume) > 11)
{
LE_INFO(
"DISCONNECT will be done in %d seconds", atoi(resume+11));
interval.
sec = atoi(resume+11);
OptionContext.typeOption = DISCONNECT;
}
}
else if ( strncmp(nextOptionArgPtr, "LOOP", 4) == 0 )
{
PlayInLoop = true;
nextOption = true;
}
else if ( strncmp(nextOptionArgPtr, "MUTE", 4) == 0 )
{
}
else if ( strncmp(nextOptionArgPtr,"GAIN",4) == 0 )
{
LE_INFO(
"Test the playback volume");
}
NextOptionArg++;
}
if (nextOption)
{
ExecuteNextOption();
}
}
void OptionTimerHandler
(
)
{
LE_INFO(
"timeout for %d", OptionContext.typeOption);
switch (OptionContext.typeOption)
{
case STOP:
if (RecPbThreadRef)
{
le_thread_Cancel(RecPbThreadRef);
RecPbThreadRef = NULL;
}
if ( strncmp(AudioTestCase,"PB_SAMPLES",10)==0 )
{
PbRecSamplesThreadCtx.pipefd[0] = -1;
PbRecSamplesThreadCtx.pipefd[1] = -1;
PbRecSamplesThreadCtx.playDone = 0;
}
break;
case PLAY:
if ( strncmp(AudioTestCase,"PB_SAMPLES",10)==0 )
{
PlaySamples(&PbRecSamplesThreadCtx, NULL);
}
else
{
}
break;
case RECORD:
if ( strncmp(AudioTestCase,"REC_SAMPLES",11)==0 )
{
RecSamples();
}
else
{
LE_INFO(
"Record result %d", result);
}
break;
case PAUSE:
LE_INFO(
"Pause result %d", result);
break;
case RESUME:
LE_INFO(
"Resume result %d", result);
break;
case DISCONNECT:
DisconnectAllAudio();
break;
default:
break;
}
ExecuteNextOption();
}
static void MyMediaEventHandler
(
le_audio_StreamRef_t streamRef,
le_audio_MediaEvent_t event,
void* contextPtr
)
{
if (!streamRef)
{
return;
}
switch(event)
{
case LE_AUDIO_MEDIA_ENDED:
LE_INFO(
"File event is LE_AUDIO_MEDIA_ENDED.");
if (PlayInLoop)
{
}
break;
case LE_AUDIO_MEDIA_ERROR:
LE_INFO(
"File event is LE_AUDIO_MEDIA_ERROR.");
break;
case LE_AUDIO_MEDIA_NO_MORE_SAMPLES:
LE_INFO(
"File event is LE_AUDIO_MEDIA_NO_MORE_SAMPLES.");
break;
default:
LE_INFO(
"File event is %d.", event);
break;
}
if (GainTimerRef)
{
GainTimerRef = NULL;
}
if (MuteTimerRef)
{
MuteTimerRef = NULL;
}
}
static void ConnectAudioToUsb
(
void
)
{
LE_ERROR_IF((FeOutRef==NULL),
"OpenUsbTx returns NULL!");
LE_ERROR_IF((FeInRef==NULL),
"OpenUsbRx returns NULL!");
LE_ERROR_IF((AudioInputConnectorRef==NULL),
"AudioInputConnectorRef is NULL!");
LE_ERROR_IF((AudioOutputConnectorRef==NULL),
"AudioOutputConnectorRef is NULL!");
{
}
}
static void ConnectAudioToFileLocalPlay
(
void
)
{
if ((AudioFileFd=open(AudioFilePath, O_RDONLY)) == -1)
{
DisconnectAllAudio();
exit(0);
}
else
{
LE_INFO(
"Open file %s with AudioFileFd.%d", AudioFilePath, AudioFileFd);
}
LE_ERROR_IF((FileAudioRef==NULL),
"OpenFilePlayback returns NULL!");
if (FileAudioRef && AudioOutputConnectorRef)
{
{
LE_ERROR(
"Failed to connect FilePlayback on output connector (%s)!",
return;
}
if (strncmp(AudioTestCase,"PB_SAMPLES",10)==0)
{
memset(&PbRecSamplesThreadCtx,0,sizeof(PbRecSamplesThreadCtx_t));
PbRecSamplesThreadCtx.pipefd[0] = -1;
PbRecSamplesThreadCtx.pipefd[1] = -1;
PbRecSamplesThreadCtx.mainThreadRef = le_thread_GetCurrent();
PlaySamples(&PbRecSamplesThreadCtx, NULL);
}
else
{
LE_INFO(
"FilePlayback is now connected.");
{
return;
}
else
{
}
}
ExecuteNextOption();
}
}
static void ConnectAudioToFileLocalRec
(
void
)
{
if ((AudioFileFd=open(AudioFilePath, O_WRONLY | O_CREAT | O_TRUNC)) == -1)
{
DisconnectAllAudio();
exit(0);
}
else
{
LE_INFO(
"Open file %s with AudioFileFd.%d", AudioFilePath, AudioFileFd);
}
LE_ERROR_IF((FileAudioRef==NULL),
"OpenFileRecording returns NULL!");
if (FileAudioRef && AudioInputConnectorRef)
{
{
LE_ERROR(
"Failed to connect FileRecording on input connector (%s)!",
return;
}
LE_INFO(
"Recorder is now connected.");
if ( strncmp(AudioTestCase,"REC_SAMPLES",11)==0 )
{
memset(&PbRecSamplesThreadCtx,0,sizeof(PbRecSamplesThreadCtx_t));
RecSamples();
ExecuteNextOption();
}
else
{
{
return;
}
if (AudioFormat == LE_AUDIO_AMR)
{
LE_INFO(
"Set AMR mode %d", AmrMode);
{
return;
}
LE_INFO(
"Set AMR DTX %d", DtxActivation);
{
return;
}
}
{
return;
}
else
{
}
le_thread_Sleep(1);
ExecuteNextOption();
}
}
}
static void ConnectAudioToCodec
(
void
)
{
LE_ERROR_IF((FeOutRef==NULL),
"OpenSpeaker returns NULL!");
LE_ERROR_IF((AudioInputConnectorRef==NULL),
"AudioInputConnectorRef is NULL!");
LE_ERROR_IF((AudioOutputConnectorRef==NULL),
"AudioOutputConnectorRef is NULL!");
{
LE_ERROR_IF((res!=
LE_OK),
"Failed to connect Speaker on Output connector (res %s)!",
}
}
static void ConnectAudioToPcm
(
void
)
{
LE_ERROR_IF((FeOutRef==NULL),
"OpenPcmTx returns NULL!");
LE_ERROR_IF((FeInRef==NULL),
"OpenPcmRx returns NULL!");
LE_ERROR_IF((AudioInputConnectorRef==NULL),
"AudioInputConnectorRef is NULL!");
LE_ERROR_IF((AudioOutputConnectorRef==NULL),
"AudioOutputConnectorRef is NULL!");
{
}
}
static void ConnectAudioToI2s
(
void
)
{
LE_ERROR_IF((FeOutRef==NULL),
"OpenI2sTx returns NULL!");
LE_ERROR_IF((FeInRef==NULL),
"OpenI2sRx returns NULL!");
LE_INFO(
"Open I2s: FeInRef.%p FeOutRef.%p", FeInRef, FeOutRef);
LE_ERROR_IF((AudioInputConnectorRef==NULL),
"AudioInputConnectorRef is NULL!");
LE_ERROR_IF((AudioOutputConnectorRef==NULL),
"AudioOutputConnectorRef is NULL!");
{
}
LE_INFO(
"Open I2s: FeInRef.%p FeOutRef.%p", FeInRef, FeOutRef);
}
static void ConnectAudio
(
void
)
{
if ((strncmp(AudioTestCase,"PB",2)==0) || (strncmp(AudioTestCase,"REC",3)==0))
{
if (strcmp(MainAudioSoundPath,"MIC")==0)
{
LE_INFO(
"Connect MIC and SPEAKER ");
ConnectAudioToCodec();
}
else
if (strcmp(MainAudioSoundPath,"PCM")==0)
{
ConnectAudioToPcm();
}
else if (strcmp(MainAudioSoundPath,"I2S")==0)
{
ConnectAudioToI2s();
}
else if (strcmp(MainAudioSoundPath,"USB")==0)
{
ConnectAudioToUsb();
}
else
{
LE_INFO(
"Error in format could not connect audio");
}
if (strncmp(AudioTestCase,"PB",2)==0)
{
ConnectAudioToFileLocalPlay();
}
else if (strncmp(AudioTestCase,"REC",3)==0)
{
ConnectAudioToFileLocalRec();
}
else
{
LE_INFO(
"Error in format could not connect audio");
}
}
else
{
LE_INFO(
"Error in format could not connect audio");
}
}
static void DisconnectAllAudio
(
void
)
{
if (AudioInputConnectorRef)
{
if(FileAudioRef)
{
LE_INFO(
"Disconnect %p from connector.%p", FileAudioRef, AudioInputConnectorRef);
}
if (FeInRef)
{
LE_INFO(
"Disconnect %p from connector.%p", FeInRef, AudioInputConnectorRef);
}
if(MdmTxAudioRef)
{
LE_INFO(
"Disconnect %p from connector.%p", MdmTxAudioRef, AudioInputConnectorRef);
}
}
if(AudioOutputConnectorRef)
{
if(FileAudioRef)
{
LE_INFO(
"Disconnect %p from connector.%p", FileAudioRef, AudioOutputConnectorRef);
}
if(FeOutRef)
{
LE_INFO(
"Disconnect %p from connector.%p", FeOutRef, AudioOutputConnectorRef);
}
if(MdmRxAudioRef)
{
LE_INFO(
"Disconnect %p from connector.%p", MdmRxAudioRef, AudioOutputConnectorRef);
}
}
if(AudioInputConnectorRef)
{
AudioInputConnectorRef = NULL;
}
if(AudioOutputConnectorRef)
{
AudioOutputConnectorRef = NULL;
}
if(FileAudioRef)
{
FileAudioRef = NULL;
}
if(FeInRef)
{
FeInRef = NULL;
}
if(FeOutRef)
{
FeOutRef = NULL;
}
if(MdmRxAudioRef)
{
MdmRxAudioRef = NULL;
}
if(MdmTxAudioRef)
{
MdmTxAudioRef = NULL;
}
if(MediaHandlerRef)
{
MediaHandlerRef = NULL;
}
exit(0);
}
static void PrintUsage
(
void
)
{
int idx;
bool sandboxed = (getuid() != 0);
const char * usagePtr[] = {
"Usage of the audioPlaybackRec test is:",
" app runProc audioPlaybackRec --exe=audioPlaybackRecTest -- <test case>"
"[main audio path] [file's name] [option]",
"",
"Test cases are:",
" - PB (for Local playback)",
" - REC (for Local recording)",
" - PB_SAMPLES (for Local samples play)",
" - REC_SAMPLES (for Local samples recording) [option]",
"",
"Main audio paths are: (for file playback/recording only)",
" - MIC (for mic/speaker)",
" - PCM (not supported on mangOH board - for AR755x, AR8652 devkit's codec use, "
"execute 'wm8940_demo --pcm' command)",
" - I2S (not supported on mangOH board - for AR755x, AR8652 devkit's codec use, "
"execute 'wm8940_demo --i2s' command)",
" - USB (for USB)",
"",
"Options are:",
" - ChannelNmbr SampleRate BitsPerSample (for REC_SAMPLES)",
" - AMR AmrMode DTX (for REC in AMR Narrowband format)",
" - WAV (for REC in WAV format)",
" - GAIN (for playback gain testing)",
" - LOOP (to replay a file in loop) (optional)",
" - PLAY=<timer value> (to replay a file after a delay) (optional)",
" - RECORD=<timer value> (to record a file after a delay) (optional)",
" - STOP=<timer value> (to stop a file playback/capture after a delay) (optional)",
" - PAUSE=<timer value> (to pause a file playback/capture after a delay) (optional)",
" - RESUME=<timer value> (to resume a file playback/capture after a delay) (optional)",
" - DISCONNECT=<timer value> (to disconnect connectors and streams"
" after a delay) (optional)",
" - MUTE (for playback MUTE testing)",
"",
"File's name can be the complete file's path.",
};
{
if(sandboxed)
{
}
else
{
fprintf(stderr, "%s\n", usagePtr[idx]);
}
}
}
static void SigHandler
(
int sigNum
)
{
LE_INFO(
"Disconnect All Audio and end test");
if (RecPbThreadRef)
{
le_thread_Cancel(RecPbThreadRef);
RecPbThreadRef = NULL;
}
DisconnectAllAudio();
exit(EXIT_SUCCESS);
}
{
{
signal(SIGINT, SigHandler);
LE_INFO(
"======== Start Audio implementation Test (audioPlaybackRecTest) ========");
if (NULL == AudioTestCase)
{
exit(EXIT_FAILURE);
}
LE_INFO(
" Test case.%s", AudioTestCase);
{
LE_INFO(
" Main audio path.%s", MainAudioSoundPath);
LE_INFO(
" Audio file [%s]", AudioFilePath);
}
if ( (strncmp(AudioTestCase,"REC_SAMPLES",11)==0) ||
(strncmp(AudioTestCase,"PB_SAMPLES",10)==0) )
{
{
PrintUsage();
exit(EXIT_FAILURE);
}
if (NULL == channelsCountPtr)
{
exit(EXIT_FAILURE);
}
if (NULL == sampleRatePtr)
{
exit(EXIT_FAILURE);
}
if (NULL == bitsPerSamplePtr)
{
exit(EXIT_FAILURE);
}
ChannelsCount = atoi(channelsCountPtr);
SampleRate = atoi(sampleRatePtr);
BitsPerSample = atoi(bitsPerSamplePtr);
LE_INFO(
" Get/Play PCM samples with ChannelsCount.%d SampleRate.%d BitsPerSample.%d",
ChannelsCount, SampleRate, BitsPerSample);
NextOptionArg = 6;
}
else if (strncmp(AudioTestCase,"REC", 3)==0)
{
if (NULL == recFormat)
{
exit(EXIT_FAILURE);
}
if (strncmp(recFormat,"WAV", 3)==0)
{
AudioFormat = LE_AUDIO_WAVE;
}
else if (strncmp(recFormat,"AMR", 3)==0)
{
AudioFormat = LE_AUDIO_AMR;
}
else
{
PrintUsage();
exit(EXIT_FAILURE);
}
if (AudioFormat == LE_AUDIO_WAVE)
{
NextOptionArg = 4;
}
else
{
if (NULL == amrModePtr)
{
exit(EXIT_FAILURE);
}
if (NULL == dtxActivationPtr)
{
exit(EXIT_FAILURE);
}
AmrMode = atoi(amrModePtr);
DtxActivation = atoi(dtxActivationPtr);
NextOptionArg = 6;
}
}
else
{
NextOptionArg = 3;
}
ConnectAudio();
LE_INFO(
"======== Audio implementation Test (audioPlaybackRec) started successfully ========");
}
else
{
PrintUsage();
exit(EXIT_FAILURE);
}
}