thanks for this. they always require the latest api don’t they. down here im still trying to support API 11
On the subject of latency, i though i’d post some code i’ve been using for SHORT sounds. stuff like button clicks and so forth, you dont need to mix them or buffer them if they are short enough. This has been my main latency problem, whereas music playback, i can put up with a short start latency, so long as it doesnt drop out.
To use this class you just give it a PCM buffer (in memory) and a length, and it plays it. It uses OpenSLES as simply as possible to avoid overhead. As you know, OpenSLES isn’t perfect anyhow.
Juce uses openSLES dynamically, so if you use this you have to manually link with the OpenSLES library.
in Android.mk add this:
/**
*
* Portions Copyright (c) 2013 Voidware Ltd. All Rights Reserved.
*
* This file contains Original Code and/or Modifications of Original Code as
* defined in and that are subject to the Voidware Public Source Licence version
* 1.0 (the 'Licence'). You may not use this file except in compliance with the
* Licence or with expressly written permission from Voidware. Please obtain a
* copy of the Licence at http://www.voidware.com/legal/vpsl1.txt and read it
* before using this file.
*
* The Original Code and all software distributed under the Licence are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
* OR IMPLIED, AND VOIDWARE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING
* WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
*
* Please see the Licence for the specific language governing rights and
* limitations under the Licence.
*/
#ifndef __quicksound_h__
#define __quicksound_h__
#ifdef JUCE_ANDROID
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#endif
class QuickSound
{
// Play short sounds without (much) delay.
public:
QuickSound() { _init(); }
~QuickSound() { _purge(); }
bool isOpen() const { return _isOpen; }
bool open(int sampleRate, int inchans, int outchans)
{
bool res = isOpen();
#ifdef JUCE_ANDROID
if (!res)
{
// open audio device
_sampleRate = sampleRate;
_inChans = inchans;
_outChans = outchans;
res = _createEngine();
if (res)
res = _openPlayDevice();
_isOpen = res;
}
#endif
return res;
}
bool playPCMData(short *buffer, size_t sampleCount)
{
bool res = isOpen();
#ifdef JUCE_ANDROID
if (res)
{
if (_audioLevel > 0)
{
SLresult result;
(*_bqPlayerPlay)->SetPlayState(_bqPlayerPlay, SL_PLAYSTATE_STOPPED);
(*_bqPlayerBufferQueue)->Clear(_bqPlayerBufferQueue);
result =(*_bqPlayerBufferQueue)->Enqueue(_bqPlayerBufferQueue,
buffer,
sampleCount*sizeof(short));
res = result == SL_RESULT_SUCCESS;
if (res)
result = (*_bqPlayerPlay)->SetPlayState(_bqPlayerPlay, SL_PLAYSTATE_PLAYING);
res = result == SL_RESULT_SUCCESS;
}
}
#endif // ANDROID
return res;
}
// vaguely a percentage level
void setVolume(int level)
{
if (level > 100) level = 100;
else if (level < 0) level = 0;
if (level == _audioLevel) return;
_audioLevel = level;
#ifdef JUCE_ANDROID
// attenuation * -50
int millibel = (_audioLevel - 100)* 50;
if (_bqPlayerVolume)
(*_bqPlayerVolume)->SetVolumeLevel(_bqPlayerVolume, millibel);
#endif
}
protected:
#ifdef JUCE_ANDROID
bool _openPlayDevice()
{
SLresult result;
bool res = true;
SLuint32 sr = 0;
if (_outChans > 0)
{
switch(_sampleRate)
{
case 8000:
sr = SL_SAMPLINGRATE_8;
break;
case 11025:
sr = SL_SAMPLINGRATE_11_025;
break;
case 16000:
sr = SL_SAMPLINGRATE_16;
break;
case 22050:
sr = SL_SAMPLINGRATE_22_05;
break;
case 24000:
sr = SL_SAMPLINGRATE_24;
break;
case 32000:
sr = SL_SAMPLINGRATE_32;
break;
case 44100:
sr = SL_SAMPLINGRATE_44_1;
break;
case 48000:
sr = SL_SAMPLINGRATE_48;
break;
case 64000:
sr = SL_SAMPLINGRATE_64;
break;
case 88200:
sr = SL_SAMPLINGRATE_88_2;
break;
case 96000:
sr = SL_SAMPLINGRATE_96;
break;
case 192000:
sr = SL_SAMPLINGRATE_192;
break;
}
if (!sr) return false;
// configure audio source
SLDataLocator_AndroidSimpleBufferQueue loc_bufq =
{SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
const SLInterfaceID ids[] = {SL_IID_VOLUME};
const SLboolean req[] = {SL_BOOLEAN_FALSE};
result = (*_engineEngine)->CreateOutputMix(_engineEngine,
&_outputMixObject,
1, ids, req);
res = result == SL_RESULT_SUCCESS;
if (res)
{
// realize the output mix
result = (*_outputMixObject)->Realize(_outputMixObject,
SL_BOOLEAN_FALSE);
int speakers;
if(_outChans > 1)
speakers = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
else speakers = SL_SPEAKER_FRONT_CENTER;
// select 16 bit PCM integeger little endian
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM,
_outChans,
sr,
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16,
speakers, SL_BYTEORDER_LITTLEENDIAN};
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
// configure audio sink
SLDataLocator_OutputMix loc_outmix =
{SL_DATALOCATOR_OUTPUTMIX, _outputMixObject};
SLDataSink audioSnk = {&loc_outmix, NULL};
// create audio player
const SLInterfaceID ids1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME };
const SLboolean req1[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
result = (*_engineEngine)->CreateAudioPlayer(_engineEngine,
&_bqPlayerObject,
&audioSrc, &audioSnk,
2, ids1, req1);
res = result == SL_RESULT_SUCCESS;
}
if (res)
{
// realize the player
result = (*_bqPlayerObject)->Realize(_bqPlayerObject,
SL_BOOLEAN_FALSE);
res = result == SL_RESULT_SUCCESS;
}
if (res)
{
// get the play interface
result = (*_bqPlayerObject)->GetInterface(_bqPlayerObject,
SL_IID_PLAY,
&_bqPlayerPlay);
res = result == SL_RESULT_SUCCESS;
}
if (res)
{
// get the buffer queue interface
result = (*_bqPlayerObject)->GetInterface(_bqPlayerObject,
SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&_bqPlayerBufferQueue);
res = result == SL_RESULT_SUCCESS;
}
if (res)
{
// get the volume level interface
result = (*_bqPlayerObject)->GetInterface(_bqPlayerObject,
SL_IID_VOLUME,
&_bqPlayerVolume);
res = result == SL_RESULT_SUCCESS;
}
if (res)
{
// register callback on the buffer queue
result = (*_bqPlayerBufferQueue)->RegisterCallback(_bqPlayerBufferQueue,
_bqPlayerCallback, this);
res = result == SL_RESULT_SUCCESS;
}
if (res)
{
// set the player's state to playing
result = (*_bqPlayerPlay)->SetPlayState(_bqPlayerPlay,
SL_PLAYSTATE_PLAYING);
res = result == SL_RESULT_SUCCESS;
}
}
return res;
}
bool _createEngine()
{
SLresult result;
// create engine
result = slCreateEngine(&_engineObject, 0, NULL, 0, NULL, NULL);
bool ok = (result == SL_RESULT_SUCCESS);
if (ok)
{
// realize the engine
result = (*_engineObject)->Realize(_engineObject, SL_BOOLEAN_FALSE);
ok = (result == SL_RESULT_SUCCESS);
if (ok)
{
// get the engine interface, which is needed in order to create other objects
result = (*_engineObject)->GetInterface(_engineObject,
SL_IID_ENGINE,
&_engineEngine);
ok = result == SL_RESULT_SUCCESS;
}
}
return ok;
}
// called every time a buffer finishes playing
void playerCallback(SLAndroidSimpleBufferQueueItf bq)
{
}
#endif
void close()
{
#ifdef JUCE_ANDROID
// invalidate all associated interfaces
if (_bqPlayerObject)
{
(*_bqPlayerObject)->Destroy(_bqPlayerObject);
_bqPlayerObject = 0;
_bqPlayerPlay = 0;
_bqPlayerBufferQueue = 0;
_bqPlayerVolume = 0;
}
if (_outputMixObject)
{
(*_outputMixObject)->Destroy(_outputMixObject);
_outputMixObject = NULL;
}
if (_engineObject)
{
(*_engineObject)->Destroy(_engineObject);
_engineObject = NULL;
_engineEngine = NULL;
}
#endif
}
private:
void _init()
{
_isOpen = false;
_audioLevel = 100;
#ifdef JUCE_ANDROID
_engineObject = 0;
_engineEngine = 0;
_outputMixObject = 0;
_bqPlayerObject = 0;
_bqPlayerPlay = 0;
_bqPlayerBufferQueue = 0;
_bqPlayerVolume = 0;
#endif
}
void _purge()
{
close();
_init();
}
#ifdef JUCE_ANDROID
static void _bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq,
void *context)
{
QuickSound* qs = (QuickSound*)context;
qs->playerCallback(bq);
}
// OpenSL ES interface objects
SLObjectItf _engineObject;
SLEngineItf _engineEngine;
SLObjectItf _outputMixObject;
// buffer queue player interfaces
SLObjectItf _bqPlayerObject;
SLPlayItf _bqPlayerPlay;
SLAndroidSimpleBufferQueueItf _bqPlayerBufferQueue;
SLVolumeItf _bqPlayerVolume;
#endif
int _sampleRate;
int _inChans;
int _outChans;
bool _isOpen;
int _audioLevel;
};
#endif // __quicksound_h__