/*---------------------------------------------------------------------------*
* audioin.c *
* *
* Copyright 2007, 2008 Nuance Communciations, Inc. *
* *
* Licensed under the Apache License, Version 2.0 (the 'License'); *
* you may not use this file except in compliance with the License. *
* *
* You may obtain a copy of the License at *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an 'AS IS' BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* *
*---------------------------------------------------------------------------*/
/* -------------------------------------------------------------------------+
| ScanSoft Inc. |
+ -------------------------------------------------------------------------*/
/* -------------------------------------------------------------------------+
| Project : ScanSoft AudioIn
| Module : audioin
| File name : audioin.c
| Description : This module contains the main implementation for the audioIn
| component.
| Reference(s) : wavein, audioout, audioin.chm, audioin.doc, audioin.hlp,
| SltGl00001_audioin_gl1.doc
| Status : Version 1.2
+ -------------------------------------------------------------------------*/
/* Feb/25/2002: First QNX/SH4 "draft" version. Version 1.1 */
/* Nov/25/2004: clean up and minor changes like choice of the codec */
/* frame size which is now automatically selected */
/*--------------------------------------------------------------------------*/
#if !defined(ANDROID) || defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_4__)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <sched.h>
#include <time.h>
#include <unistd.h>
#include <sys/select.h>
#include "plog.h"
#include "audioin.h"
#if defined(ANDROID)
#include "audioinwrapper.h"
#else
#include <alsa/asoundlib.h>
#endif
// #define SAVE_RAW_AUDIO 1
#ifdef SAVE_RAW_AUDIO
#include <sys/time.h>
#include <stdio.h>
static FILE *audio_data;
static struct timeval buffer_save_audio;
#endif
/*#define FILTER_ON*/
#ifdef FILTER_ON
#include "filter.h"
#endif
/* -------------------------------------------------------------------------+
| EXTERNAL DATA (+ meaning) |
+ -------------------------------------------------------------------------*/
/* none */
/* -------------------------------------------------------------------------+
| MACROS |
+ -------------------------------------------------------------------------*/
#define NR_OF_CHANNELS 1
#if defined(ANDROID)
/* size in samples */
/* We really no longer use this for ANDROID but more changes are needed to remove it. SteveR */
#define SAMPLES_BUFFER_SIZE (8*1024)
#define SAMPLES_BUFFER_HIGH_WATERMARK (6*1024)
#else
#define SAMPLES_BUFFER_SIZE (50*4410)
#define SAMPLES_BUFFER_HIGH_WATERMARK (40*4410)
#endif
/* IMPORTANT NOTE:
Here a "frame" is an ALSA term. A frame is comprised of 1 sample if mono,
and 2 samples if stereo. This should be distinguished from what the
ASR engine and lhs_audioin*() API functions refer to as a frame which is
a set of consecutive samples.
(see http://www.alsa-project.org/alsa-doc/alsa-lib/pcm.html) */
#if defined(ANDROID)
#define CODEC_FRAGMENT_SIZE_IN_FRAMES 1024
#else
//read the equivalent of 100 ms per buffer. Note: we are recording at 44 kHz
#define CODEC_FRAGMENT_SIZE_IN_FRAMES 4410
#endif
/* -------------------------------------------------------------------------+
| TYPE DEFINITIONS |
+ -------------------------------------------------------------------------*/
/* -------------------------------------------------------------------------+
| GLOBAL CONSTANTS |
+ -------------------------------------------------------------------------*/
/* -------------------------------------------------------------------------+
| GLOBAL VARIABLES |
+ -------------------------------------------------------------------------*/
#if !defined(ANDROID)
static snd_pcm_t *ghPCM; /* handle to the PCM recording device */
#endif
static int gCodecFragmentSizeInFrames = CODEC_FRAGMENT_SIZE_IN_FRAMES; /* fragment size used by the codec driver */
static audioinSample gSamplesBufferCircularFifo[SAMPLES_BUFFER_SIZE]; /* circular buffer that buffers the incoming samples */
static int gWriteIndexPointer = 0; /* write pointer in the circular FIFO samples buffer */
static int gReadIndexPointer = 0; /* read pointer in the circular FIFO samples buffer */
static AUDIOIN_INFO gAudioInInfo; /* to store the info about the acquisition */
static pthread_mutex_t gAudioMutex; /* to prevent using the read/write pointers at the same time in both threads */
static pthread_cond_t gThreadRunning; /* synchronize when the AcquisitionThreadID is running*/
static int gThreadRunningSignaled = 0;
static pthread_cond_t gOpenExCalled; /* synchronize when the lhs_audioinOpenEx is called*/
static int gOpenExCalledSignaled = 0;
static pthread_cond_t gCloseCalled; /* synchronize when the lhs_audioinClose is called*/
static int gCloseCalledSignaled = 0;
static pthread_t AcquisitionThreadID; /* acquisition thread id */
static int gInitialized = 0; /* did we initialize some of the variables*/
static int gTerminateThread = 0;
static struct timeval timer; /* timer used by select to relinquish cpu times */
static int gRecordingVolume = -1; /* recording volume ; number between 0 and 15 */
static int bRecord = 0; /* recording state is off */
static int bClose = 1; /* audio pipe is closed */
#ifdef FILTER_ON
static FIR_struct *pFIR = NULL; /* pointer to FIR structure */
#endif
#ifdef AUDIOIN_SUPPORT_CALLBACK
static pCallbackFunc gpCallback = NULL;
static void *gpCallbackInstance = NULL;
static unsigned long gnCallbackSamples = 0;
#endif
/* -------------------------------------------------------------------------+
| LOCAL FUNCTION PROTOTYPES |
+ -------------------------------------------------------------------------*/
static void *AcquisitionThread(void *data); /* Entry function for the acquisition thread */
static int OpenAndPrepareSound(unsigned long ulFrequency);
/**
* returns 0 if success
*/
static int Initialize(AUDIOIN_H * phAudioIn)
{
int doneWaiting = 0;
if( gInitialized == 1 )
return 0;
/* creates the mutex that will be used to lock/unlock access to some variables/code */
if (pthread_mutex_init(&gAudioMutex, NULL) != 0)
{
return 1;
}
if(pthread_cond_init(&gThreadRunning, 0) != 0 )
{
return 1;
}
if(pthread_cond_init(&gOpenExCalled, 0) != 0 )
{
return 1;
}
if(pthread_cond_init(&gCloseCalled, 0) != 0 )
{
return 1;
}
pthread_mutex_lock(&gAudioMutex);
/* create a thread with very high priority that will do the acquisition */
if (pthread_create(&AcquisitionThreadID, NULL, AcquisitionThread, phAudioIn) != 0)
{
return 1;
}
//wait for the thread to run
while (!doneWaiting)
{
int rc = pthread_cond_wait(&gThreadRunning, &gAudioMutex);
switch (rc)
{
case 0:
if (!gThreadRunningSignaled)
{
// Avoid spurious wakeups
continue;
}
else
{
gThreadRunningSignaled = 0;
doneWaiting = 1;
break;
}
break;
default:
pthread_mutex_unlock(&gAudioMutex);
return 1;
}
}
pthread_mutex_unlock(&gAudioMutex);
//thread is now running.
gInitialized = 1;
return 0;
}
#if 0
/* disable this unused function for now until we decide what to do with this */
/**
* returns 0 if success
*/
static int UnInitialize()
{
//signal the thread that it has to stop running.
pthread_mutex_lock ( &gAudioMutex );
gTerminateThread = 1;
//signal to tell that our thread is now running.
if ( pthread_cond_signal ( &gOpenExCalled ) != 0 )
{
pthread_mutex_unlock ( &gAudioMutex );
PLogError ( "Audio In Error pthread_cond_signal\n" );
return 1;
}
gOpenExCalledSignaled = 1;
pthread_mutex_unlock ( &gAudioMutex );
/* wait until thread exits */
if (pthread_join(AcquisitionThreadID, NULL) != 0)
{
return 1;
}
/* destroy the mutex */
if (pthread_mutex_destroy(&gAudioMutex) !=0 )
{
return 1;
}
if( pthread_cond_destroy(&gThreadRunning) != 0 )
{
return 1;
}
if( pthread_cond_destroy(&gOpenExCalled) != 0 )
{
return 1;
}
if( pthread_cond_destroy(&gCloseCalled) != 0 )
{
return 1;
}
gInitialized = 0;
return 0;
}
#endif
/* -------------------------------------------------------------------------+
| LOCAL FUNCTION (should be static) |
+ -------------------------------------------------------------------------*/
static void setRecordOn(void)
{
bRecord = 1;
}
static void setRecordOff(void)
{
bRecord = 0;
}
static int getRecord(void)
{
return bRecord;
}
static void setCloseOn(void)
{
bClose = 1;
}
static void setCloseOff(void)
{
bClose = 0;
}
static int getClose(void)
{
return bClose;
}
/**************************************************************
* AcquisitionThread *
* *
* This function is the entry function of a thread created by *
* lhs_audioinOpen and which is responsible of getting the *
* samples from the codec and store them in a big circular *
* FIFO buffer. *
* The priority of this thread has been set to high in order *
* to prevent codec buffer overrun. Since the FIFO is limited *
* in size (5 sec default ; see SAMPLES_BUFFER_SIZE *
* parameter), the application must still be fast enough to *
* prevent FIFO overflow/overrun *
**************************************************************/
#if defined(ANDROID)
void *AcquisitionThread ( void *data )
{
int doneWaiting = 0;
audioinSample *CodecBuffer;
long x;
long y;
#ifdef AUDIOIN_SUPPORT_CALLBACK
AUDIOIN_H *phAudioIn = (AUDIOIN_H *)data;
AUDIOIN_WAVEHDR *pwhdr;
#endif
pthread_mutex_lock ( &gAudioMutex );
//signal to tell that our thread is now running.
if ( pthread_cond_signal ( &gThreadRunning ) != 0 )
{
pthread_mutex_unlock ( &gAudioMutex );
PLogError ( "Audio In Error pthread_cond_signal\n" );
exit ( 1 );
}
gThreadRunningSignaled = 1;
while( 1 )
{
while (!doneWaiting)
{
int rc = pthread_cond_wait(&gOpenExCalled, &gAudioMutex);
switch (rc)
{
case 0:
if (!gOpenExCalledSignaled)
{
// Avoid spurious wakeups
continue;
}
else
{
gOpenExCalledSignaled = 0;
doneWaiting = 1;
break;
}
break;
default:
PLogError ( "Audio In Error pthread_cond_signal\n" );
pthread_mutex_unlock(&gAudioMutex);
return ( (void *)NULL );
}
}
doneWaiting = 0;
pthread_mutex_unlock(&gAudioMutex);
if( gTerminateThread == 1 )
break;
/* buffer of 16 bits samples */
CodecBuffer = (audioinSample *)malloc ( gCodecFragmentSizeInFrames * sizeof ( audioinSample ) );
if ( CodecBuffer == NULL )
{
PLogError ( "Audio In Error malloc\n" );
exit ( 1 );
}
pwhdr = malloc ( sizeof ( AUDIOIN_WAVEHDR ) );
if ( pwhdr == NULL )
{
PLogError ( "Audio In Error malloc\n" );
exit ( 1 );
}
while ( !getClose ( ) )
{
int iReadFrames = 0; /* number of frames acquired by the codec */
/* NOTE: here a frame is comprised of 1 sample if mono, 2 samples if stereo, etc */
int iReadSamples = 0; /* number of samples acquired by the codec */
int frames_to_read; /* Actual number to read */
int frames_read; /* Frames read on one read */
iReadFrames = 0;
do
{
frames_to_read = gCodecFragmentSizeInFrames - iReadFrames;
/* AudioRead() - output: number of frames (mono: 1 sample, stereo: 2 samples)*/
frames_read = AudioRead ( CodecBuffer + iReadFrames, frames_to_read );
if ( frames_read > 0 )
iReadFrames += frames_read;
}
while ( ( iReadFrames < gCodecFragmentSizeInFrames ) && ( frames_read > 0 ) );
iReadSamples = iReadFrames;
if ( getRecord ( ) ) /* else continue to read from driver but discard samples */
{
if ( iReadSamples < 0 )
{
iReadSamples = 0;
gAudioInInfo.eStatusInfo = AUDIOIN_HWOVERRUN;
}
else
{
#ifdef FILTER_ON
/* x: index for start of input samples; y: index for output sample */
for ( x = 0, y = 0; x < iReadSamples; x += pFIR->factor_down )
{
FIR_downsample ( pFIR->factor_down, &( CodecBuffer[x] ), &( CodecBuffer[y++] ), pFIR );
}
/* update the number samples */
iReadSamples = y;
#endif
pthread_mutex_lock ( &gAudioMutex );
if ( gAudioInInfo.u32SamplesAvailable + iReadSamples > SAMPLES_BUFFER_SIZE )
{
gAudioInInfo.u32SamplesAvailable = SAMPLES_BUFFER_SIZE;
gAudioInInfo.eStatusInfo = AUDIOIN_FIFOOVERRUN;
}
else
{
if ( gAudioInInfo.u32SamplesAvailable + iReadSamples > SAMPLES_BUFFER_HIGH_WATERMARK )
{
gAudioInInfo.eStatusInfo = AUDIOIN_HIGHWATERMARK;
}
else if ( gAudioInInfo.eStatusInfo != AUDIOIN_FIFOOVERRUN )
{
gAudioInInfo.eStatusInfo = AUDIOIN_NORMAL;
}
gAudioInInfo.u32SamplesAvailable += iReadSamples;
}
if ( gWriteIndexPointer + iReadSamples <= SAMPLES_BUFFER_SIZE )
{
memcpy ( &( gSamplesBufferCircularFifo[gWriteIndexPointer] ), CodecBuffer,
iReadSamples * sizeof ( audioinSample ) );
gWriteIndexPointer += iReadSamples;
if ( gWriteIndexPointer >= SAMPLES_BUFFER_SIZE )
gWriteIndexPointer = 0;
}
else
{
int NbToCopy;
NbToCopy = SAMPLES_BUFFER_SIZE - gWriteIndexPointer;
memcpy ( &( gSamplesBufferCircularFifo [gWriteIndexPointer] ), CodecBuffer,
NbToCopy * sizeof ( audioinSample ) );
gWriteIndexPointer = 0;
memcpy ( gSamplesBufferCircularFifo, &( CodecBuffer [NbToCopy] ),
( iReadSamples-NbToCopy ) * sizeof ( audioinSample ) );
gWriteIndexPointer = iReadSamples - NbToCopy;
}
#ifdef AUDIOIN_SUPPORT_CALLBACK
/* Callback notification. Ideally this audio acquisition thread should be very lean.
It should simply read from the low level driver, store the filtered samples in
the FIFO, then go back to reading from the driver. The additional data copy
for the callback function is ok despite the overhead incurred, but only because
there's some buffering done by the low level driver. This design should be
revisited to make it more general purpose.
*/
if ( gpCallback != NULL )
{
pwhdr->nBufferLength = iReadSamples * sizeof ( audioinSample );
pwhdr->nBytesRecorded = pwhdr->nBufferLength;
pwhdr->status = AUDIOIN_NORMAL;
pwhdr->pData = CodecBuffer;
/* pass samples to callback function who should deallocate the buffer and structure */
gpCallback ( *phAudioIn, AUDIOIN_MSG_DATA, gpCallbackInstance, pwhdr, NULL );
}
#endif
/* samples are available to read */
pthread_mutex_unlock ( &gAudioMutex );
timer.tv_sec = 0;
timer.tv_usec = 200;
select ( 0, NULL, NULL, NULL, &timer );
}
} /* if (getRecord()) */
} /* while (!getClose()) */
if ( AudioClose ( ) !=0 )
{
PLogError ( "Audio In Error Closing Hardware\n" );
}
free ( CodecBuffer );
pthread_mutex_lock ( &gAudioMutex );
//signal to tell that our thread is now running.
if ( pthread_cond_signal ( &gCloseCalled ) != 0 )
{
pthread_mutex_unlock ( &gAudioMutex );
PLogError ( "Audio In Error pthread_cond_signal\n" );
exit ( 1 );
}
gCloseCalledSignaled = 1;
}
pthread_exit ( (void *)NULL );
return ( (void *)NULL );
}
#else
/* non-ANDROID version */
void *AcquisitionThread ( void *data )
{
int doneWaiting = 0;
audioinSample *CodecBuffer;
#ifdef FILTER_ON
long x;
long y;
#endif
#ifdef AUDIOIN_SUPPORT_CALLBACK
AUDIOIN_H *phAudioIn = (AUDIOIN_H *)data;
#endif
pthread_mutex_lock ( &gAudioMutex );
//signal to tell that our thread is now running.
if ( pthread_cond_signal ( &gThreadRunning ) != 0 )
{
pthread_mutex_unlock ( &gAudioMutex );
PLogError ( "Audio In Error pthread_cond_signal\n" );
exit ( 1 );
}
gThreadRunningSignaled = 1;
while( 1 )
{
while (!doneWaiting)
{
int rc = pthread_cond_wait(&gOpenExCalled, &gAudioMutex);
switch (rc)
{
case 0:
if (!gOpenExCalledSignaled)
{
// Avoid spurious wakeups
continue;
}
else
{
gOpenExCalledSignaled = 0;
doneWaiting = 1;
break;
}
break;
default:
PLogError ( "Audio In Error pthread_cond_wait\n" );
pthread_mutex_unlock(&gAudioMutex);
return ( (void *)NULL );
}
}
doneWaiting = 0;
pthread_mutex_unlock(&gAudioMutex);
if( gTerminateThread == 1 )
break;
/* buffer of 16 bits samples */
CodecBuffer = (audioinSample *)malloc ( gCodecFragmentSizeInFrames * sizeof ( audioinSample ) );
if ( CodecBuffer == NULL )
{
PLogError ( "Audio In Error pthread_cond_signal\n" );
exit ( 1 );
}
while ( !getClose ( ) )
{
int iReadFrames = 0; /* number of frames acquired by the codec */
/* NOTE: here a frame is comprised of 1 sample if mono, 2 samples if stereo, etc */
int iReadSamples = 0; /* number of samples acquired by the codec */
if ( ( iReadFrames = snd_pcm_readi ( ghPCM, (void *)CodecBuffer, gCodecFragmentSizeInFrames ) ) < 0 )
{
if ( iReadFrames == -EBADFD )
{
PLogError ( "Audio In Error PCM Not In The Right State\n" );
}
else if ( iReadFrames == -EPIPE )
{
snd_pcm_prepare(ghPCM);
PLogError ( "Audio In Error Overrun\n" );
}
else if ( iReadFrames == -ESTRPIPE )
{
PLogError ( "Audio In Error Stream Suspended\n" );
}
}
iReadSamples = iReadFrames;
if ( getRecord ( ) ) /* else continue to read from driver but discard samples */
{
if ( iReadSamples < 0 )
{
iReadSamples = 0;
gAudioInInfo.eStatusInfo = AUDIOIN_HWOVERRUN;
}
else
{
#ifdef FILTER_ON
/* x: index for start of input samples; y: index for output sample */
for ( x = 0, y = 0; x < iReadSamples; x += pFIR->factor_down )
{
FIR_downsample ( pFIR->factor_down, &( CodecBuffer[x] ), &( CodecBuffer[y++] ), pFIR );
}
/* update the number samples */
iReadSamples = y;
#endif
#ifdef SAVE_RAW_AUDIO
if ( iReadSamples > 0 )
fwrite ( CodecBuffer, 2, iReadSamples, audio_data );
#endif
pthread_mutex_lock ( &gAudioMutex );
if ( gAudioInInfo.u32SamplesAvailable + iReadSamples > SAMPLES_BUFFER_SIZE )
{
gAudioInInfo.u32SamplesAvailable = SAMPLES_BUFFER_SIZE;
gAudioInInfo.eStatusInfo = AUDIOIN_FIFOOVERRUN;
}
else
{
if ( gAudioInInfo.u32SamplesAvailable + iReadSamples > SAMPLES_BUFFER_HIGH_WATERMARK )
{
gAudioInInfo.eStatusInfo = AUDIOIN_HIGHWATERMARK;
}
else if ( gAudioInInfo.eStatusInfo != AUDIOIN_FIFOOVERRUN )
{
gAudioInInfo.eStatusInfo = AUDIOIN_NORMAL;
}
gAudioInInfo.u32SamplesAvailable += iReadSamples;
}
if ( gWriteIndexPointer + iReadSamples <= SAMPLES_BUFFER_SIZE )
{
memcpy ( &( gSamplesBufferCircularFifo[gWriteIndexPointer] ), CodecBuffer,
iReadSamples * sizeof ( audioinSample ) );
gWriteIndexPointer += iReadSamples;
if ( gWriteIndexPointer >= SAMPLES_BUFFER_SIZE )
gWriteIndexPointer = 0;
}
else
{
int NbToCopy;
NbToCopy = SAMPLES_BUFFER_SIZE - gWriteIndexPointer;
memcpy ( &( gSamplesBufferCircularFifo [gWriteIndexPointer] ), CodecBuffer,
NbToCopy * sizeof ( audioinSample ) );
gWriteIndexPointer = 0;
memcpy ( gSamplesBufferCircularFifo, &( CodecBuffer [NbToCopy] ),
( iReadSamples-NbToCopy ) * sizeof ( audioinSample ) );
gWriteIndexPointer = iReadSamples - NbToCopy;
}
#ifdef AUDIOIN_SUPPORT_CALLBACK
/* Callback notification. Ideally this audio acquisition thread should be very lean.
It should simply read from the low level driver, store the filtered samples in
the FIFO, then go back to reading from the driver. The additional data copy
for the callback function is ok despite the overhead incurred, but only because
there's some buffering done by the low level driver. This design should be
revisited to make it more general purpose.
*/
while ( ( gpCallback != NULL ) && ( gAudioInInfo.u32SamplesAvailable >= gnCallbackSamples ) )
{
AUDIOIN_WAVEHDR *pwhdr;
pwhdr = malloc ( sizeof ( AUDIOIN_WAVEHDR ) );
if ( pwhdr != NULL )
{
pwhdr->nBufferLength = gnCallbackSamples * sizeof ( audioinSample );
pwhdr->nBytesRecorded = pwhdr->nBufferLength;
pwhdr->status = gAudioInInfo.eStatusInfo;
pwhdr->pData = malloc ( pwhdr->nBufferLength );
if ( pwhdr->pData != NULL )
{
if ( gReadIndexPointer + gnCallbackSamples <= SAMPLES_BUFFER_SIZE )
{
memcpy ( pwhdr->pData, &( gSamplesBufferCircularFifo [gReadIndexPointer] ),
pwhdr->nBufferLength );
gReadIndexPointer += gnCallbackSamples;
if ( gReadIndexPointer >= SAMPLES_BUFFER_SIZE )
gReadIndexPointer = 0;
}
else
{
size_t nSamplesPart1 = SAMPLES_BUFFER_SIZE - gReadIndexPointer;
size_t nSamplesPart2 = gnCallbackSamples - nSamplesPart1;
memcpy ( pwhdr->pData, &( gSamplesBufferCircularFifo [gReadIndexPointer] ),
nSamplesPart1*sizeof ( audioinSample ) );
gReadIndexPointer = 0;
memcpy ( pwhdr->pData + nSamplesPart1 * sizeof (audioinSample ),
gSamplesBufferCircularFifo, nSamplesPart2 * sizeof ( audioinSample ) );
gReadIndexPointer = nSamplesPart2;
}
gAudioInInfo.u32SamplesAvailable -= gnCallbackSamples;
/* pass samples to callback function who should deallocate the buffer and structure */
gpCallback ( *phAudioIn, AUDIOIN_MSG_DATA, gpCallbackInstance, pwhdr, NULL );
}
else
{
// error
}
}
else
{
// error
}
}
#endif
/* samples are available to read */
pthread_mutex_unlock ( &gAudioMutex );
timer.tv_sec = 0;
timer.tv_usec = 200;
select ( 0, NULL, NULL, NULL, &timer );
}
} /* if (getRecord()) */
} /* while (!getClose()) */
if ( snd_pcm_close ( ghPCM ) !=0 )
{
PLogError ( "Audio In Error Closing Hardware\n" );
}
free ( CodecBuffer );
pthread_mutex_lock ( &gAudioMutex );
//signal to tell that our thread is now running.
if ( pthread_cond_signal ( &gCloseCalled ) != 0 )
{
pthread_mutex_unlock ( &gAudioMutex );
PLogError ( "Audio In Error pthread_cond_signal\n" );
exit ( 1 );
}
gCloseCalledSignaled = 1;
}
pthread_exit ( (void *)NULL );
return ( (void *)NULL );
}
#endif
/**************************************************************
* OpenAndPrepareSound *
*************************************************************/
static int OpenAndPrepareSound(unsigned long ulFrequency)
{
#if defined(ANDROID)
/* Only support certain frequencies. Modify this to check frequency
against a structure of valid frequencies */
#ifdef FILTER_ON
if ( ulFrequency == 11025 )
{
if ( AudioSetInputFormat ( 44100, NR_OF_CHANNELS ) != 0 ) /* sample at 44100 then downsample */
{
PLogError ( "Audio In Error OpenAndPrepareSound - AudioSetInputFormat failed!\n");
return LHS_E_AUDIOIN_COULDNOTOPENDEVICE;
}
}
else
{
PLogError ( "Audio In Error OpenAndPrepareSound - invalid frequency!");
return LHS_E_AUDIOIN_COULDNOTOPENDEVICE;
}
#else
if ( ( ulFrequency == 11025 ) || ( ulFrequency == 8000 ) )
{
if ( AudioSetInputFormat ( ulFrequency, NR_OF_CHANNELS ) != 0 )
{
PLogError ( "Audio In Error OpenAndPrepareSound - AudioSetInputFormat failed!");
return LHS_E_AUDIOIN_COULDNOTOPENDEVICE;
}
}
else
{
PLogError ( "Audio In Error OpenAndPrepareSound - invalid frequency!");
return LHS_E_AUDIOIN_COULDNOTOPENDEVICE;
}
#endif
/* set some variables */
gAudioInInfo.u32SamplesAvailable = 0;
/* Open Audio driver */
if (AudioOpen() < 0)
{
PLogError ( "Audio In Error OpenAndPrepareSound - AudioOpen failed!");
return ~LHS_AUDIOIN_OK;
}
#else
snd_pcm_hw_params_t *hwparams;
unsigned int exact_rate;
int dir;
int rc;
/* step 1 : open the sound device */
/* ------------------------------ */
if ((rc = snd_pcm_open(&ghPCM, "default", SND_PCM_STREAM_CAPTURE, 0)) < 0)
{
PLogError ( "Audio In Error snd_pcm_open() (rc = %d: %s)\n", rc, snd_strerror(rc));
return LHS_E_AUDIOIN_COULDNOTOPENDEVICE;
}
if ((rc = snd_pcm_hw_params_malloc(&hwparams)) < 0)
{
PLogError ( "Audio In Error snd_pcm_hw_params_malloc() (rc = %d: %s)\n", rc, snd_strerror(rc));
return LHS_E_AUDIOIN_COULDNOTOPENDEVICE;
}
/* step 2 : configuring the audio channel */
/* -------------------------------------- */
if ((rc = snd_pcm_hw_params_any(ghPCM, hwparams)) < 0)
{
PLogError ( "Audio In Error snd_pcm_hw_params_any() (rc = %d: %s)\n", rc, snd_strerror(rc));
return LHS_E_AUDIOIN_COULDNOTOPENDEVICE;
}
if ((rc = snd_pcm_hw_params_set_access(ghPCM, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
PLogError ( "Audio In Error snd_pcm_hw_params_set_access() (rc = %d: %s)\n", rc, snd_strerror(rc));
return LHS_E_AUDIOIN_COULDNOTOPENDEVICE;
}
if ((rc = snd_pcm_hw_params_set_format(ghPCM, hwparams, SND_PCM_FORMAT_S16_LE)) < 0)
{
PLogError ( "Audio In Error snd_pcm_hw_params_set_format() (rc = %d: %s)\n", rc, snd_strerror(rc));
return LHS_E_AUDIOIN_COULDNOTOPENDEVICE;
}
#ifdef FILTER_ON
if (ulFrequency == 11025)
{
exact_rate = 44100;
}
else
return LHS_E_AUDIOIN_COULDNOTOPENDEVICE;
#else
exact_rate = ulFrequency;
#endif
dir = 0;
#if 0
/* This version seems to have problems when the code is compiled into a shared library.
The subsequent call to snd_pcm_hw_params() fails. */
if ((rc = snd_pcm_hw_params_set_rate_near(ghPCM, hwparams, &exact_rate, &dir)) < 0)
{
PLogError ( "Audio In Error snd_pcm_hw_params_set_rate_near() (rc = %d: %s)\n", rc, snd_strerror(rc));
return LHS_E_AUDIOIN_COULDNOTOPENDEVICE;
}
#else
/* This version works better and in fact makes more sense. */
if ((rc = snd_pcm_hw_params_set_rate(ghPCM, hwparams, exact_rate, dir)) < 0)
{
PLogError ( "Audio In Error snd_pcm_hw_params_set_rate() (rc = %d: %s)\n", rc, snd_strerror(rc));
return LHS_E_AUDIOIN_COULDNOTOPENDEVICE;
}
#endif
if ((rc = snd_pcm_hw_params_set_channels(ghPCM, hwparams, NR_OF_CHANNELS)) < 0)
{
PLogError ( "Audio In Error snd_pcm_hw_params_set_channels() (rc = %d: %s)\n", rc, snd_strerror(rc));
return LHS_E_AUDIOIN_COULDNOTOPENDEVICE;
}
if ((rc = snd_pcm_hw_params(ghPCM, hwparams)) < 0)
{
PLogError ( "Audio In Error snd_pcm_hw_params() (rc = %d: %s)\n", rc, snd_strerror(rc));
return LHS_E_AUDIOIN_COULDNOTOPENDEVICE;
}
/* step 3 : preparing for read */
/* --------------------------- */
/*prepare the channel */
if ((rc = snd_pcm_prepare(ghPCM)) < 0)
{
PLogError ( "Audio In Error snd_pcm_prepare() (rc = %d: %s)\n", rc, snd_strerror(rc));
return LHS_E_AUDIOIN_COULDNOTOPENDEVICE;
}
/* set some variables */
gAudioInInfo.u32SamplesAvailable = 0;
#endif
/* prepare to read samples */
setCloseOff();
return 0;
}
/* -------------------------------------------------------------------------+
| GLOBAL FUNCTIONS (prototypes in header file) |
+ -------------------------------------------------------------------------*/
/**************************************************************
* lhs_audioinOpenEx *
* *
* notes : *
* -the input parameters are in fact not used but present *
* to ensure compatibility with Win32 implementations *
**************************************************************/
LHS_AUDIOIN_ERROR lhs_audioinOpenEx (
unsigned long u32AudioInID, /*@parm [in] Audio-in device ID (ranges from 0 to a number of available
devices on the system). You can also use the following flag
instead of a device identifier.
<nl><nl><bold WAVE_MAPPER> = The function selects a
waveform-audio input device capable of recording in the
specified format. <bold Header:> Declared in Mmsystem.h from
the Windows Multimedia: Platform SDK.*/
unsigned long u32Frequency, /*@parm [in] Frequency of the recognition engine in Hz. */
unsigned long u32NbrOfFrames, /*@parm [in] Number of frames buffered internally. */
unsigned long u32SamplesPerFrame, /*@parm [in] Size, in samples, of each individual frame. */
AUDIOIN_H * phAudioIn /*@parm [out] Handle to the audio-in device */
)
{
//initialize some of the static variables.
if( Initialize(phAudioIn) )
return ~LHS_AUDIOIN_OK;
/* prepare sound */
if (OpenAndPrepareSound(u32Frequency) != 0)
{
return LHS_E_AUDIOIN_COULDNOTOPENDEVICE;
}
//signal the thread that it has to stop running.
pthread_mutex_lock ( &gAudioMutex );
//signal to tell that our thread is now running.
if ( pthread_cond_signal ( &gOpenExCalled ) != 0 )
{
pthread_mutex_unlock ( &gAudioMutex );
PLogError ( "Audio In Error pthread_cond_signal\n" );
exit ( 1 );
}
gOpenExCalledSignaled = 1;
pthread_mutex_unlock ( &gAudioMutex );
#ifdef FILTER_ON
/* need to make this more generic to support different filters */
pFIR = FIR_construct(filter_length, ps16FilterCoeff_up1_down4, u16ScaleFilterCoeff_up1_down4, FACTOR_UP, FACTOR_DOWN);
if (pFIR == NULL)
{
// TO DO: HANDLE THIS (or modify for static allocation)
}
#endif
/* set the status to normal */
gAudioInInfo.eStatusInfo = AUDIOIN_NORMAL;
/* do not care, but some applications are checking a NULL handle */
*phAudioIn = (void *)10;
#ifdef AUDIOIN_SUPPORT_CALLBACK
gpCallback = NULL;
gpCallbackInstance = NULL;
gnCallbackSamples = 0;
#endif
return LHS_AUDIOIN_OK;
}
/**************************************************************
* lhs_audioinOpen *
* *
* notes : *
* -the input parameters are in fact not used but present *
* to ensure compatibility with Win32 implementation *
**************************************************************/
LHS_AUDIOIN_ERROR lhs_audioinOpen (
unsigned long u32AudioInID, /*@parm [in] Audio-in device ID (ranges from 0 to a number of available
devices on the system). You can also use the following flag
instead of a device identifier.
<nl><nl><bold WAVE_MAPPER> = The function selects a
waveform-audio input device capable of recording in the
specified format. <bold Header:> Declared in Mmsystem.h from
the Windows Multimedia: Platform SDK.*/
unsigned long u32Frequency, /*@parm [in] Frequency of the recognition engine in Hz. */
AUDIOIN_H * phAudioIn /*@parm [out] Handle to the audio-in device */
)
{
return lhs_audioinOpenEx(u32AudioInID, u32Frequency, 0, 0, phAudioIn);
} /* lhs_audioinOpen */
#ifdef AUDIOIN_SUPPORT_CALLBACK
/**************************************************************
* lhs_audioinOpenCallback *
* *
* notes : *
* -the input parameters are in fact not used but present *
* to ensure compatibility with Win32 implementation *
**************************************************************/
LHS_AUDIOIN_ERROR lhs_audioinOpenCallback (
unsigned long u32AudioInID, /*@parm [in] Audio-in device ID (ranges from 0 to a number of available
devices on the system). You can also use the following flag
instead of a device identifier.
<nl><nl><bold WAVE_MAPPER> = The function selects a
waveform-audio input device capable of recording in the
specified format. <bold Header:> Declared in Mmsystem.h from
the Windows Multimedia: Platform SDK.*/
unsigned long u32Frequency, /*@parm [in] Frequency of the recognition engine in Hz. */
unsigned long u32NbrOfSamples, /*@parm [in] <nl><bold Input:> Number of samples requested per callback */
pCallbackFunc pCallback, /*@parm [in] callback function */
void *pCallbackInstance, /*@parm [in] callback instance */
AUDIOIN_H * phAudioIn /*@parm [out] Handle to the audio-in device */
)
{
LHS_AUDIOIN_ERROR lhsErr;
#ifdef FILTER_ON
gCodecFragmentSizeInFrames = u32NbrOfSamples * 4;
#else
gCodecFragmentSizeInFrames = u32NbrOfSamples;
#endif
if ((pCallback == NULL) || (u32NbrOfSamples == 0))
{
return LHS_E_AUDIOIN_INVALIDARG;
}
lhsErr = lhs_audioinOpenEx(u32AudioInID, u32Frequency, 0, 0, phAudioIn);
if (lhsErr != LHS_AUDIOIN_OK)
{
return lhsErr;
}
/* install callback */
gpCallback = pCallback;
gpCallbackInstance = pCallbackInstance;
gnCallbackSamples = u32NbrOfSamples;
/* callback notification */
gpCallback(*phAudioIn, AUDIOIN_MSG_OPEN, gpCallbackInstance, NULL, NULL);
return LHS_AUDIOIN_OK;
} /* lhs_audioinOpenCallback */
#endif
/**************************************************************
* lhs_audioinClose *
* *
* notes : *
* -the input parameters are in fact not used but present *
* to ensure compatibility with Win32 implementations *
**************************************************************/
LHS_AUDIOIN_ERROR lhs_audioinClose(AUDIOIN_H *phAudioIn)
{
int doneWaiting = 0;
/* Validate the handle */
if ((phAudioIn == NULL) || (*phAudioIn == NULL))
{
return LHS_E_AUDIOIN_NULLPOINTER;
}
/* stop recording audio samples */
setRecordOff();
/* stop reading audio samples */
setCloseOn();
//wait for the thread to stop reading samples.
pthread_mutex_lock ( &gAudioMutex );
while (!doneWaiting)
{
int rc = pthread_cond_wait(&gCloseCalled, &gAudioMutex);
switch (rc)
{
case 0:
if (!gCloseCalledSignaled)
{
// Avoid spurious wakeups
continue;
}
else
{
gCloseCalledSignaled = 0;
doneWaiting = 1;
break;
}
break;
default:
PLogError ( "Audio In Error pthread_cond_wait\n" );
pthread_mutex_unlock(&gAudioMutex);
return ~LHS_AUDIOIN_OK;
}
}
pthread_mutex_unlock(&gAudioMutex);
#ifdef FILTER_ON
FIR_deconstruct(pFIR);
#endif
#ifdef AUDIOIN_SUPPORT_CALLBACK
/* callback notification */
if (gpCallback != NULL) gpCallback(*phAudioIn, AUDIOIN_MSG_CLOSE, gpCallbackInstance, NULL, NULL);
#endif
return LHS_AUDIOIN_OK;
}
/**************************************************************
* lhs_audioinStart *
* *
* notes : *
* -the input parameters are in fact not used but present *
* to ensure compatibility with Win32 implementations *
* -in fact the recording is never stopped or started, when *
* non in 'start' status, the samples are just ignored *
**************************************************************/
LHS_AUDIOIN_ERROR lhs_audioinStart(AUDIOIN_H hAudioIn)
{
#ifdef SAVE_RAW_AUDIO
char file_name [256];
gettimeofday ( &buffer_save_audio, NULL );
sprintf ( file_name, "data_%ld_%ld.raw", buffer_save_audio.tv_sec, buffer_save_audio.tv_usec );
audio_data = fopen ( file_name, "w" );
#endif
if (hAudioIn == NULL)
{
return LHS_E_AUDIOIN_NULLPOINTER;
}
pthread_mutex_lock ( &gAudioMutex );
#ifdef FILTER_ON
FIR_reset(pFIR);
#endif
gWriteIndexPointer = 0;
gReadIndexPointer = 0;
gAudioInInfo.u32SamplesAvailable = 0;
/* start recording */
setRecordOn();
#ifdef AUDIOIN_SUPPORT_CALLBACK
/* callback notification */
if (gpCallback != NULL) gpCallback(hAudioIn, AUDIOIN_MSG_START, gpCallbackInstance, NULL, NULL);
#endif
pthread_mutex_unlock ( &gAudioMutex );
return LHS_AUDIOIN_OK;
}
/**************************************************************
* lhs_audioinStop *
* *
* notes : *
* -the input parameters are in fact not used but present *
* to ensure compatibility with Win32 implementations *
* -in fact the recording is never stopped or started, when *
* non in 'start' status, the samples are just ignored *
**************************************************************/
LHS_AUDIOIN_ERROR lhs_audioinStop(AUDIOIN_H hAudioIn)
{
#ifdef SAVE_RAW_AUDIO
fclose ( audio_data );
#endif
if (hAudioIn == NULL)
{
return LHS_E_AUDIOIN_NULLPOINTER;
}
pthread_mutex_lock ( &gAudioMutex );
/* stop recording (discard samples) */
setRecordOff();
#ifdef AUDIOIN_SUPPORT_CALLBACK
/* callback notification */
if (gpCallback != NULL) gpCallback(hAudioIn, AUDIOIN_MSG_STOP, gpCallbackInstance, NULL, NULL);
#endif
pthread_mutex_unlock ( &gAudioMutex );
return LHS_AUDIOIN_OK;
}
/**************************************************************
* lhs_audioinGetSamples *
* *
* notes : *
**************************************************************/
LHS_AUDIOIN_ERROR lhs_audioinGetSamples(AUDIOIN_H hAudioIn, unsigned long * u32NbrOfSamples, void * pAudioBuffer, AUDIOIN_INFO * pgAudioInInfo)
{
unsigned long cSamples;
//unsigned long nToCopy;
/* Check if the handle is valid */
if (hAudioIn == NULL)
{
return LHS_E_AUDIOIN_NULLPOINTER;
}
cSamples = 0;
while (1)
{
/* wait until we have enough samples */
if (*u32NbrOfSamples <= gAudioInInfo.u32SamplesAvailable)
{
/* lock the code to prevent dual access to some variables */
pthread_mutex_lock(&gAudioMutex);
/* TO DO: consider copying in chunks (like in AquisitionThread)
rather than 1 sample at a time. */
/* copy all samples into the input buffer */
while ((cSamples < *u32NbrOfSamples))
{
((audioinSample *)pAudioBuffer)[cSamples++] = gSamplesBufferCircularFifo[gReadIndexPointer++];
/* adapt the parameters */
gAudioInInfo.u32SamplesAvailable -= 1;
/* adapt circular buffer */
if (gReadIndexPointer >= SAMPLES_BUFFER_SIZE)
{
gReadIndexPointer = 0;
}
/* enough samples */
if (cSamples == *u32NbrOfSamples)
{
/* return the audioin info structure */
memcpy(pgAudioInInfo, &gAudioInInfo, sizeof(AUDIOIN_INFO));
pthread_mutex_unlock(&gAudioMutex);
return LHS_AUDIOIN_OK;
}
}
}
else
{
/* relinquish CPU. select() is more reliable than usleep(). */
timer.tv_sec = 0;
timer.tv_usec = 10000;
select(0, NULL, NULL, NULL, &timer);
}
} /* while (1) */
}
/**************************************************************
* lhs_audioinGetVersion *
* *
* notes : not implemented *
**************************************************************/
LHS_AUDIOIN_ERROR lhs_audioinGetVersion(unsigned long *pu32Version)
{
return LHS_E_AUDIOIN_NOTIMPLEMENTED;
}
/**************************************************************
* lhs_audioinGetVolume/lhs_audioinSetVolume *
* *
* notes : not implemented *
**************************************************************/
LHS_AUDIOIN_ERROR lhs_audioinGetVolume(AUDIOIN_H hAudioIn, unsigned long *pu32Volume)
{
*pu32Volume = gRecordingVolume;
return LHS_AUDIOIN_OK;
}
LHS_AUDIOIN_ERROR lhs_audioinSetVolume(AUDIOIN_H hAudioIn, unsigned long u32Volume)
{
gRecordingVolume = u32Volume;
return LHS_E_AUDIOIN_NOTIMPLEMENTED;
}
/**************************************************************
* lhs_audioinErrorGetString *
* *
* notes : not implemented *
**************************************************************/
const char *lhs_audioinErrorGetString(const LHS_AUDIOIN_ERROR Error)
{
return ("unknown error");
}
#else
/******************************************************************************/
/* STUB FUNCTIONS FOR SIMULATOR BUILD (DOES NOT SUPPORT THREADS) */
/* This code is enabled if both ANDROID and __ARM_ARCH_5__ are defined. */
/******************************************************************************/
#include "audioin.h"
LHS_AUDIOIN_ERROR lhs_audioinOpenEx (
unsigned long u32AudioInID, /*@parm [in] Audio-in device ID (ranges from 0 to a number of available
devices on the system). You can also use the following flag
instead of a device identifier.
<nl><nl><bold WAVE_MAPPER> = The function selects a
waveform-audio input device capable of recording in the
specified format. <bold Header:> Declared in Mmsystem.h from
the Windows Multimedia: Platform SDK.*/
unsigned long u32Frequency, /*@parm [in] Frequency of the recognition engine in Hz. */
unsigned long u32NbrOfFrames, /*@parm [in] Number of frames buffered internally. */
unsigned long u32SamplesPerFrame, /*@parm [in] Size, in samples, of each individual frame. */
AUDIOIN_H * phAudioIn /*@parm [out] Handle to the audio-in device */
)
{
return LHS_E_AUDIOIN_NOTIMPLEMENTED;
}
LHS_AUDIOIN_ERROR lhs_audioinOpen (
unsigned long u32AudioInID, /*@parm [in] Audio-in device ID (ranges from 0 to a number of available
devices on the system). You can also use the following flag
instead of a device identifier.
<nl><nl><bold WAVE_MAPPER> = The function selects a
waveform-audio input device capable of recording in the
specified format. <bold Header:> Declared in Mmsystem.h from
the Windows Multimedia: Platform SDK.*/
unsigned long u32Frequency, /*@parm [in] Frequency of the recognition engine in Hz. */
AUDIOIN_H * phAudioIn /*@parm [out] Handle to the audio-in device */
)
{
return LHS_E_AUDIOIN_NOTIMPLEMENTED;
}
#ifdef AUDIOIN_SUPPORT_CALLBACK
LHS_AUDIOIN_ERROR lhs_audioinOpenCallback(unsigned long u32AudioInID, unsigned long u32Frequency, unsigned long u32NbrOfSamples, pCallbackFunc pCallback, void* pCallbackInstance, AUDIOIN_H * phAudioIn)
{
return LHS_E_AUDIOIN_NOTIMPLEMENTED;
}
#endif
LHS_AUDIOIN_ERROR lhs_audioinClose(AUDIOIN_H *phAudioIn)
{
return LHS_E_AUDIOIN_NOTIMPLEMENTED;
}
LHS_AUDIOIN_ERROR lhs_audioinStart(AUDIOIN_H hAudioIn)
{
return LHS_E_AUDIOIN_NOTIMPLEMENTED;
}
LHS_AUDIOIN_ERROR lhs_audioinStop(AUDIOIN_H hAudioIn)
{
return LHS_E_AUDIOIN_NOTIMPLEMENTED;
}
LHS_AUDIOIN_ERROR lhs_audioinGetSamples(AUDIOIN_H hAudioIn, unsigned long * u32NbrOfSamples, void * pAudioBuffer, AUDIOIN_INFO * pgAudioInInfo)
{
return LHS_E_AUDIOIN_NOTIMPLEMENTED;
}
LHS_AUDIOIN_ERROR lhs_audioinGetVersion(unsigned long *pu32Version)
{
return LHS_E_AUDIOIN_NOTIMPLEMENTED;
}
LHS_AUDIOIN_ERROR lhs_audioinGetVolume(AUDIOIN_H hAudioIn, unsigned long *pu32Volume)
{
return LHS_E_AUDIOIN_NOTIMPLEMENTED;
}
LHS_AUDIOIN_ERROR lhs_audioinSetVolume(AUDIOIN_H hAudioIn, unsigned long u32Volume)
{
return LHS_E_AUDIOIN_NOTIMPLEMENTED;
}
const char *lhs_audioinErrorGetString(const LHS_AUDIOIN_ERROR Error)
{
return "LHS_E_AUDIOIN_NOTIMPLEMENTED";
}
#endif