2013-12-28 13:00:21 +01:00
|
|
|
/******************************************************************************\
|
|
|
|
* Copyright (c) 2004-2014
|
|
|
|
*
|
|
|
|
* Author(s):
|
|
|
|
* Volker Fischer
|
|
|
|
*
|
|
|
|
******************************************************************************
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it under
|
|
|
|
* the terms of the GNU General Public License as published by the Free Software
|
|
|
|
* Foundation; either version 2 of the License, or (at your option) any later
|
|
|
|
* version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
|
|
* details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with
|
|
|
|
* this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*
|
|
|
|
\******************************************************************************/
|
|
|
|
|
|
|
|
#include "sound.h"
|
|
|
|
|
|
|
|
|
|
|
|
/* Implementation *************************************************************/
|
|
|
|
CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ), void* arg ) :
|
|
|
|
CSoundBase ( "OpenSL", true, fpNewProcessCallback, arg )
|
|
|
|
{
|
2013-12-29 12:08:13 +01:00
|
|
|
// set up stream format
|
|
|
|
SLDataFormat_PCM streamFormat;
|
|
|
|
streamFormat.formatType = SL_DATAFORMAT_PCM;
|
|
|
|
streamFormat.numChannels = 2;
|
|
|
|
streamFormat.samplesPerSec = SYSTEM_SAMPLE_RATE_HZ * 1000; // unit is mHz
|
|
|
|
streamFormat.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
|
|
|
|
streamFormat.containerSize = 16;
|
|
|
|
streamFormat.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
|
|
|
|
streamFormat.endianness = SL_BYTEORDER_LITTLEENDIAN;
|
2013-12-28 13:00:21 +01:00
|
|
|
|
|
|
|
// create the OpenSL root engine object
|
|
|
|
slCreateEngine ( &engineObject,
|
|
|
|
0,
|
|
|
|
nullptr,
|
|
|
|
0,
|
|
|
|
nullptr,
|
|
|
|
nullptr );
|
|
|
|
|
|
|
|
// realize the engine
|
|
|
|
(*engineObject)->Realize ( engineObject,
|
|
|
|
SL_BOOLEAN_FALSE );
|
|
|
|
|
|
|
|
// get the engine interface (required to create other objects)
|
|
|
|
(*engineObject)->GetInterface ( engineObject,
|
|
|
|
SL_IID_ENGINE,
|
|
|
|
&engine );
|
|
|
|
|
|
|
|
// create the main output mix
|
|
|
|
(*engine)->CreateOutputMix ( engine,
|
|
|
|
&outputMixObject,
|
2013-12-29 12:08:13 +01:00
|
|
|
0,
|
|
|
|
nullptr,
|
|
|
|
nullptr );
|
|
|
|
|
2013-12-29 13:40:23 +01:00
|
|
|
// realize the output mix
|
2013-12-28 13:00:21 +01:00
|
|
|
(*outputMixObject)->Realize ( outputMixObject,
|
|
|
|
SL_BOOLEAN_FALSE );
|
|
|
|
|
2013-12-29 13:40:23 +01:00
|
|
|
// configure the audio (data) source for input
|
|
|
|
SLDataLocator_IODevice micLocator;
|
|
|
|
micLocator.locatorType = SL_DATALOCATOR_IODEVICE;
|
|
|
|
micLocator.deviceType = SL_IODEVICE_AUDIOINPUT;
|
|
|
|
micLocator.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
|
|
|
|
micLocator.device = nullptr;
|
|
|
|
|
|
|
|
SLDataSource inDataSource;
|
|
|
|
inDataSource.pLocator = &micLocator;
|
|
|
|
inDataSource.pFormat = nullptr;
|
|
|
|
|
|
|
|
// configure the input buffer queue
|
|
|
|
SLDataLocator_AndroidSimpleBufferQueue inBufferQueue;
|
|
|
|
inBufferQueue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
|
|
|
|
inBufferQueue.numBuffers = 1; // max number of buffers in queue
|
|
|
|
|
|
|
|
// configure the audio (data) sink for input
|
|
|
|
SLDataSink inDataSink;
|
|
|
|
inDataSink.pLocator = &inBufferQueue;
|
|
|
|
inDataSink.pFormat = &streamFormat;
|
|
|
|
|
|
|
|
// create the audio recorder
|
|
|
|
const SLInterfaceID recorderIds[] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
|
|
|
|
const SLboolean recorderReq[] = { SL_BOOLEAN_TRUE };
|
|
|
|
|
|
|
|
(*engine)->CreateAudioRecorder ( engine,
|
|
|
|
&recorderObject,
|
|
|
|
&inDataSource,
|
|
|
|
&inDataSink,
|
|
|
|
1,
|
|
|
|
recorderIds,
|
|
|
|
recorderReq );
|
|
|
|
|
|
|
|
// realize the audio recorder
|
|
|
|
(*recorderObject)->Realize ( recorderObject,
|
|
|
|
SL_BOOLEAN_FALSE );
|
|
|
|
|
|
|
|
// get the audio recorder interface
|
|
|
|
(*recorderObject)->GetInterface ( recorderObject,
|
|
|
|
SL_IID_RECORD,
|
|
|
|
&recorder );
|
|
|
|
|
|
|
|
// get the audio recorder simple buffer queue interface
|
|
|
|
(*recorderObject)->GetInterface ( recorderObject,
|
|
|
|
SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
|
|
|
&recorderSimpleBufQueue );
|
|
|
|
|
|
|
|
// register the audio input callback
|
|
|
|
(*recorderSimpleBufQueue)->RegisterCallback ( recorderSimpleBufQueue,
|
|
|
|
processInput,
|
|
|
|
this );
|
|
|
|
|
|
|
|
// configure the output buffer queue
|
2013-12-29 12:08:13 +01:00
|
|
|
SLDataLocator_AndroidSimpleBufferQueue outBufferQueue;
|
|
|
|
outBufferQueue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
|
|
|
|
outBufferQueue.numBuffers = 1; // max number of buffers in queue
|
2013-12-28 13:00:21 +01:00
|
|
|
|
2013-12-29 13:40:23 +01:00
|
|
|
// configure the audio (data) source for output
|
|
|
|
SLDataSource outDataSource;
|
|
|
|
outDataSource.pLocator = &outBufferQueue;
|
|
|
|
outDataSource.pFormat = &streamFormat;
|
2013-12-28 13:00:21 +01:00
|
|
|
|
|
|
|
// configure the output mix
|
|
|
|
SLDataLocator_OutputMix outputMix;
|
|
|
|
outputMix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
|
|
|
|
outputMix.outputMix = outputMixObject;
|
|
|
|
|
2013-12-29 13:40:23 +01:00
|
|
|
// configure the audio (data) sink for output
|
|
|
|
SLDataSink outDataSink;
|
|
|
|
outDataSink.pLocator = &outputMix;
|
|
|
|
outDataSink.pFormat = nullptr;
|
2013-12-28 13:00:21 +01:00
|
|
|
|
|
|
|
// create the audio player
|
|
|
|
const SLInterfaceID playerIds[] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
|
|
|
|
const SLboolean playerReq[] = { SL_BOOLEAN_TRUE };
|
|
|
|
|
|
|
|
(*engine)->CreateAudioPlayer ( engine,
|
|
|
|
&playerObject,
|
2013-12-29 13:40:23 +01:00
|
|
|
&outDataSource,
|
|
|
|
&outDataSink,
|
2013-12-28 13:00:21 +01:00
|
|
|
1,
|
|
|
|
playerIds,
|
|
|
|
playerReq );
|
|
|
|
|
|
|
|
// realize the audio player
|
|
|
|
(*playerObject)->Realize ( playerObject,
|
|
|
|
SL_BOOLEAN_FALSE );
|
|
|
|
|
|
|
|
// get the audio player interface
|
|
|
|
(*playerObject)->GetInterface ( playerObject,
|
|
|
|
SL_IID_PLAY,
|
|
|
|
&player );
|
|
|
|
|
|
|
|
// get the audio player simple buffer queue interface
|
|
|
|
(*playerObject)->GetInterface ( playerObject,
|
|
|
|
SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
|
|
|
&playerSimpleBufQueue );
|
|
|
|
|
2013-12-29 12:08:13 +01:00
|
|
|
// register the audio output callback
|
|
|
|
(*playerSimpleBufQueue)->RegisterCallback ( playerSimpleBufQueue,
|
|
|
|
processOutput,
|
|
|
|
this );
|
2013-12-28 13:00:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void CSound::CloseOpenSL()
|
|
|
|
{
|
|
|
|
// clean up
|
2013-12-29 13:40:23 +01:00
|
|
|
(*recorderObject)->Destroy ( recorderObject );
|
2013-12-29 12:08:13 +01:00
|
|
|
(*playerObject)->Destroy ( playerObject );
|
|
|
|
(*outputMixObject)->Destroy ( outputMixObject );
|
|
|
|
(*engineObject)->Destroy ( engineObject );
|
2013-12-28 13:00:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void CSound::Start()
|
|
|
|
{
|
|
|
|
// start the rendering
|
2013-12-29 16:35:49 +01:00
|
|
|
(*recorder)->SetRecordState ( recorder, SL_RECORDSTATE_RECORDING );
|
2013-12-29 12:08:13 +01:00
|
|
|
(*player)->SetPlayState ( player, SL_PLAYSTATE_PLAYING );
|
2013-12-28 13:00:21 +01:00
|
|
|
|
|
|
|
// call base class
|
|
|
|
CSoundBase::Start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSound::Stop()
|
|
|
|
{
|
|
|
|
// stop the audio stream
|
2013-12-29 16:35:49 +01:00
|
|
|
(*recorder)->SetRecordState ( recorder, SL_RECORDSTATE_STOPPED );
|
2013-12-29 12:08:13 +01:00
|
|
|
(*player)->SetPlayState ( player, SL_PLAYSTATE_STOPPED );
|
|
|
|
|
|
|
|
// clear the buffers
|
|
|
|
(*playerSimpleBufQueue)->Clear ( playerSimpleBufQueue );
|
2013-12-28 13:00:21 +01:00
|
|
|
|
|
|
|
// call base class
|
|
|
|
CSoundBase::Stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
int CSound::Init ( const int iNewPrefMonoBufferSize )
|
|
|
|
{
|
2014-01-20 21:42:58 +01:00
|
|
|
|
|
|
|
|
|
|
|
// TODO make use of the following:
|
|
|
|
// String sampleRate = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE));
|
|
|
|
// String framesPerBuffer = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER));
|
|
|
|
|
|
|
|
|
2013-12-28 13:00:21 +01:00
|
|
|
// store buffer size
|
2013-12-29 16:35:49 +01:00
|
|
|
iOpenSLBufferSizeMono = iNewPrefMonoBufferSize;
|
2013-12-28 13:00:21 +01:00
|
|
|
|
|
|
|
// init base class
|
2013-12-29 12:08:13 +01:00
|
|
|
CSoundBase::Init ( iOpenSLBufferSizeMono );
|
2013-12-28 13:00:21 +01:00
|
|
|
|
|
|
|
// set internal buffer size value and calculate stereo buffer size
|
2014-01-21 18:25:46 +01:00
|
|
|
iOpenSLBufferSizeStereo = 2 * iOpenSLBufferSizeMono;
|
2013-12-29 16:35:49 +01:00
|
|
|
|
|
|
|
// create memory for intermediate audio buffer
|
|
|
|
vecsTmpAudioSndCrdStereo.Init ( iOpenSLBufferSizeStereo );
|
2013-12-28 13:00:21 +01:00
|
|
|
|
|
|
|
return iOpenSLBufferSizeMono;
|
|
|
|
}
|
|
|
|
|
2013-12-29 13:40:23 +01:00
|
|
|
void CSound::processInput ( SLAndroidSimpleBufferQueueItf bufferQueue,
|
|
|
|
void* instance )
|
2013-12-28 13:00:21 +01:00
|
|
|
{
|
2014-01-03 09:56:31 +01:00
|
|
|
CSound* pSound = static_cast<CSound*> ( instance );
|
2013-12-28 13:00:21 +01:00
|
|
|
|
2013-12-29 16:35:49 +01:00
|
|
|
// only process if we are running
|
|
|
|
if ( !pSound->bRun )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-12-28 13:00:21 +01:00
|
|
|
QMutexLocker locker ( &pSound->Mutex );
|
|
|
|
|
2013-12-29 13:40:23 +01:00
|
|
|
// enqueue the buffer for record
|
|
|
|
(*bufferQueue)->Enqueue ( bufferQueue,
|
|
|
|
&pSound->vecsTmpAudioSndCrdStereo[0],
|
2013-12-29 16:35:49 +01:00
|
|
|
pSound->iOpenSLBufferSizeStereo * 2 /* 2 bytes */ );
|
2013-12-28 13:00:21 +01:00
|
|
|
|
|
|
|
// call processing callback function
|
|
|
|
pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo );
|
|
|
|
}
|
|
|
|
|
2013-12-29 12:08:13 +01:00
|
|
|
void CSound::processOutput ( SLAndroidSimpleBufferQueueItf bufferQueue,
|
2013-12-29 13:40:23 +01:00
|
|
|
void* instance )
|
2013-12-28 13:00:21 +01:00
|
|
|
{
|
2014-01-03 09:56:31 +01:00
|
|
|
CSound* pSound = static_cast<CSound*> ( instance );
|
2013-12-28 13:00:21 +01:00
|
|
|
|
2013-12-29 16:35:49 +01:00
|
|
|
// only process if we are running
|
|
|
|
if ( !pSound->bRun )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-12-28 13:00:21 +01:00
|
|
|
QMutexLocker locker ( &pSound->Mutex );
|
|
|
|
|
2013-12-29 13:40:23 +01:00
|
|
|
// enqueue the buffer for playback
|
2013-12-29 12:08:13 +01:00
|
|
|
(*bufferQueue)->Enqueue ( bufferQueue,
|
|
|
|
&pSound->vecsTmpAudioSndCrdStereo[0],
|
2013-12-29 16:35:49 +01:00
|
|
|
pSound->iOpenSLBufferSizeStereo * 2 /* 2 bytes */ );
|
2013-12-28 13:00:21 +01:00
|
|
|
}
|