Files
Konrad Beckmann 20d7fefaf9 Import mikmod
2021-08-02 02:19:41 +02:00

324 lines
9.4 KiB
C

/* MikMod sound library
(c) 1998-2014 Miodrag Vallat and others - see file AUTHORS for
complete list.
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Library 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 Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
/* Driver for output to OpenSL ES (for Android native code)
* Adapted from the libmikmod-android project @ https://github.com/0xD34D/libmikmod-android
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "mikmod_internals.h"
#ifdef DRV_OSLES
/* for native audio */
#include <SLES/OpenSLES.h>
#include "SLES/OpenSLES_Android.h"
#define NUMBUFFERS 2 /* number of buffers */
#define BUFFERSIZE 120 /* buffer size in milliseconds */
#ifndef SL_BYTEORDER_NATIVE
#if defined(SL_BYTEORDER_NATIVEBIGENDIAN) || defined(WORDS_BIGENDIAN)
#define SL_BYTEORDER_NATIVE SL_BYTEORDER_BIGENDIAN
#else
#define SL_BYTEORDER_NATIVE SL_BYTEORDER_LITTLEENDIAN
#endif
#endif
/* engine interfaces */
static SLObjectItf engineObject = NULL;
static SLEngineItf engineEngine;
/* output mix interfaces */
static SLObjectItf outputMixObject = NULL;
static SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL;
/* buffer queue player interfaces */
static SLObjectItf bqPlayerObject = NULL;
static SLPlayItf bqPlayerPlay;
static SLEffectSendItf bqPlayerEffectSend;
/* Android-specific buffer queue: */
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
/* aux effect on the output mix, used by the buffer queue player */
static const SLEnvironmentalReverbSettings reverbSettings =
SL_I3DL2_ENVIRONMENT_PRESET_DEFAULT;
static signed char* buffer[NUMBUFFERS]; /* pointers to buffers */
static int buffersout; /* number of buffers playing/about to be played */
static int nextbuffer; /* next buffer to be mixed */
static unsigned long buffersize; /* buffer size in bytes */
/* this callback handler is called every time a buffer finishes playing */
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
{
if (context != NULL || bq != bqPlayerBufferQueue)
return;/* NOT supposed to happen, but... */
--buffersout;
}
/* create buffer queue audio player */
static SLresult createBufferQueueAudioPlayer(void)
{
SLuint32 sample_rate = md_mixfreq * 1000;/*SL_SAMPLINGRATE_44_1;*/
SLresult result;
int channels = (md_mode&DMODE_STEREO)? 2 : 1;
const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND};
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
/* configure audio source */
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, channels, sample_rate,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_NATIVE};
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 */
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk,
2, ids, req);
if (SL_RESULT_SUCCESS != result) return result;
/* realize the player */
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) return result;
/* get the play interface */
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
if (SL_RESULT_SUCCESS != result) return result;
/* get the buffer queue interface */
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
&bqPlayerBufferQueue);
if (SL_RESULT_SUCCESS != result) return result;
/* register callback on the buffer queue */
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
if (SL_RESULT_SUCCESS != result) return result;
/* get the effect send interface */
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_EFFECTSEND,
&bqPlayerEffectSend);
if (SL_RESULT_SUCCESS != result) return result;
/* set the player's state to playing */
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
if (SL_RESULT_SUCCESS != result) return result;
return SL_RESULT_SUCCESS;
}
static BOOL OSLES_IsPresent(void)
{
return 1;
}
static int OSLES_Init(void)
{
int samplesize;
int n;
SLresult result;
const SLInterfaceID ids[1] = {SL_IID_ENVIRONMENTALREVERB};
const SLboolean req[1] = {SL_BOOLEAN_FALSE};
samplesize = 1;
if (md_mode&DMODE_STEREO) samplesize<<=1;
if (md_mode&DMODE_16BITS) samplesize<<=1;
/* create engine */
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
if (SL_RESULT_SUCCESS != result) goto _fail;
/* realize the engine */
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) goto _fail;
/* get the engine interface, which is needed in order to create other objects */
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
if (SL_RESULT_SUCCESS != result) goto _fail;
/* create output mix, with environmental reverb specified as a non-required interface */
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, ids, req);
if (SL_RESULT_SUCCESS != result) goto _fail;
/* realize the output mix */
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) goto _fail;
#if 0
/* get the environmental reverb interface
* this could fail if the environmental reverb effect is not available,
* either because the feature is not present, excessive CPU load, or
* the required MODIFY_AUDIO_SETTINGS permission was not requested and granted
*/
result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB,
&outputMixEnvironmentalReverb);
if (SL_RESULT_SUCCESS == result) {
result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(
outputMixEnvironmentalReverb, &reverbSettings);
}
#endif
result = createBufferQueueAudioPlayer();
if (SL_RESULT_SUCCESS != result) goto _fail;
buffersize = md_mixfreq * samplesize * BUFFERSIZE / 1000;
for (n = 0; n < NUMBUFFERS; n++) {
buffer[n] = MikMod_malloc(buffersize);
if (!buffer[n]) {
_mm_errno = MMERR_OUT_OF_MEMORY;
return 1;
}
}
md_mode |= DMODE_SOFT_MUSIC|DMODE_SOFT_SNDFX;
buffersout = nextbuffer = 0;
return VC_Init();
_fail: _mm_errno = MMERR_OPENING_AUDIO;
return 1;
}
static void OSLES_Exit(void)
{
int n;
/* destroy buffer queue audio player object, and invalidate all associated interfaces */
if (bqPlayerObject != NULL) {
SLuint32 state;
(*bqPlayerPlay)->GetPlayState(bqPlayerPlay, &state);
if (state == SL_PLAYSTATE_PLAYING)
(*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
(*bqPlayerObject)->Destroy(bqPlayerObject);
bqPlayerObject = NULL;
bqPlayerPlay = NULL;
bqPlayerBufferQueue = NULL;
bqPlayerEffectSend = NULL;
}
/* destroy output mix object, and invalidate all associated interfaces */
if (outputMixObject != NULL) {
(*outputMixObject)->Destroy(outputMixObject);
outputMixObject = NULL;
outputMixEnvironmentalReverb = NULL;
}
/* destroy engine object, and invalidate all associated interfaces */
if (engineObject != NULL) {
(*engineObject)->Destroy(engineObject);
engineObject = NULL;
engineEngine = NULL;
}
VC_Exit();
for (n = 0; n < NUMBUFFERS; n++) {
MikMod_free(buffer[n]);
buffer[n] = NULL;
}
}
static void OSLES_Update(void)
{
ULONG done;
SLresult result;
while (buffersout < NUMBUFFERS) {
done = VC_WriteBytes(buffer[nextbuffer], buffersize);
if (!done) break;
/* enqueue another buffer */
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[nextbuffer], done);
if(SL_RESULT_SUCCESS != result)
break;
if (++nextbuffer >= NUMBUFFERS)
nextbuffer %= NUMBUFFERS;
++buffersout;
}
}
static void OSLES_Pause(void)
{
#if 0
SLuint32 state;
if (NULL == bqPlayerPlay) return;
(*bqPlayerPlay)->GetPlayState(bqPlayerPlay, &state);
if (state == SL_PLAYSTATE_PLAYING)
(*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED);
else if (state == SL_PLAYSTATE_PAUSED)
(*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
#endif
}
static int OSLES_Reset(void)
{
return 0;
}
MIKMODAPI MDRIVER drv_osles = {
NULL,
"Android OpenSL ES Driver",
"Android Native Audio OpenSL ES Output v1.0",
0,255,
"osles",
NULL,
NULL,
OSLES_IsPresent,
VC_SampleLoad,
VC_SampleUnload,
VC_SampleSpace,
VC_SampleLength,
OSLES_Init,
OSLES_Exit,
OSLES_Reset,
VC_SetNumVoices,
VC_PlayStart,
VC_PlayStop,
OSLES_Update,
OSLES_Pause,
VC_VoiceSetVolume,
VC_VoiceGetVolume,
VC_VoiceSetFrequency,
VC_VoiceGetFrequency,
VC_VoiceSetPanning,
VC_VoiceGetPanning,
VC_VoicePlay,
VC_VoiceStop,
VC_VoiceStopped,
VC_VoiceGetPosition,
VC_VoiceRealVolume
};
#else
MISSING(drv_osles);
#endif
/* ex:set ts=8: */