From 1794cc4ea19dec040140072552cac55982957a8a Mon Sep 17 00:00:00 2001 From: Thomas Perl Date: Thu, 27 Mar 2014 00:11:48 +0100 Subject: [PATCH] Mixer abstraction. Fixes #39 Most modules ported, some still missing --- audio/audio.c | 2 +- makefile | 1 + mixer/mixer.c | 163 ++++++++++++++++++++++++++ mixer/mixer.h | 97 ++++++++++++++++ modules/common.h | 8 -- modules/cuttherope.c | 41 +++---- modules/fruitninja.c | 70 ++++-------- modules/marmalade.c | 70 +++--------- modules/openframeworks.c | 52 ++++----- modules/worldofgoo.c | 2 +- platform/common/sdl_mixer_impl.h | 189 +++++++++++++++++++++++++++++++ platform/fremantle.c | 2 + platform/harmattan.c | 2 + platform/pandora.c | 2 + 14 files changed, 534 insertions(+), 167 deletions(-) create mode 100644 mixer/mixer.c create mode 100644 mixer/mixer.h create mode 100644 platform/common/sdl_mixer_impl.h diff --git a/audio/audio.c b/audio/audio.c index 7208826..1e76060 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -47,7 +47,7 @@ apkenv_audio_open(int frequency, enum AudioFormat format, int channels, g_audio->config.format = format; g_audio->config.channels = channels; g_audio->config.buffer = buffer; - g_audio->config.callback = (void (*)(void *, Uint8 *, int))callback; + g_audio->config.callback = (void (*)(void *, void *, int))callback; g_audio->config.user_data = user_data; return g_audio->open(g_audio); } diff --git a/makefile b/makefile index 6b6dd70..7e3aaf8 100644 --- a/makefile +++ b/makefile @@ -16,6 +16,7 @@ SOURCES += $(wildcard debug/*.c) SOURCES += $(wildcard debug/wrappers/*.c) SOURCES += $(wildcard accelerometer/*.c) SOURCES += $(wildcard audio/*.c) +SOURCES += $(wildcard mixer/*.c) # Platform-specific targets and configuration PLATFORM_INSTALL_TARGETS := diff --git a/mixer/mixer.c b/mixer/mixer.c new file mode 100644 index 0000000..a4ac515 --- /dev/null +++ b/mixer/mixer.c @@ -0,0 +1,163 @@ +/** + * apkenv Mixer + * Copyright (c) 2014, Thomas Perl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **/ + +#include "mixer.h" + + +struct Mixer * +g_mixer = 0; + +void apkenv_mixer_register(struct Mixer *mixer) +{ + g_mixer = mixer; +} + +int +apkenv_mixer_open(int frequency, enum AudioFormat format, int channels, int buffer) +{ + if (g_mixer) { + g_mixer->config.frequency = frequency; + g_mixer->config.format = format; + g_mixer->config.channels = channels; + g_mixer->config.buffer = buffer; + g_mixer->open(g_mixer); + } +} + +void +apkenv_mixer_close() +{ + if (g_mixer) { + g_mixer->close(g_mixer); + } +} + +struct MixerMusic * +apkenv_mixer_load_music(const char *filename) +{ + if (g_mixer) { + return g_mixer->load_music(g_mixer, filename); + } + + return 0; +} + +struct MixerMusic * +apkenv_mixer_load_music_buffer(const char *buffer, size_t size) +{ + if (g_mixer) { + return g_mixer->load_music_buffer(g_mixer, buffer, size); + } + + return 0; +} + +struct MixerSound * +apkenv_mixer_load_sound(const char *filename) +{ + if (g_mixer) { + return g_mixer->load_sound(g_mixer, filename); + } + + return 0; +} + +struct MixerSound * +apkenv_mixer_load_sound_buffer(const char *buffer, size_t size) +{ + if (g_mixer) { + return g_mixer->load_sound_buffer(g_mixer, buffer, size); + } + + return 0; +} + +void +apkenv_mixer_free_music(struct MixerMusic *music) +{ + if (g_mixer) { + g_mixer->free_music(g_mixer, music); + } +} + +void +apkenv_mixer_free_sound(struct MixerSound *sound) +{ + if (g_mixer) { + g_mixer->free_sound(g_mixer, sound); + } +} + +void +apkenv_mixer_play_music(struct MixerMusic *music, int do_loop) +{ + if (g_mixer) { + g_mixer->play_music(g_mixer, music, do_loop); + } +} + +void +apkenv_mixer_play_sound(struct MixerSound *sound, int do_loop) +{ + if (g_mixer) { + g_mixer->play_sound(g_mixer, sound, do_loop); + } +} + +void +apkenv_mixer_stop_music(struct MixerMusic *music) +{ + if (g_mixer) { + g_mixer->stop_music(g_mixer, music); + } +} + +void +apkenv_mixer_stop_sound(struct MixerSound *sound) +{ + if (g_mixer) { + g_mixer->stop_sound(g_mixer, sound); + } +} + +void +apkenv_mixer_volume_music(struct MixerMusic *music, float volume) +{ + if (g_mixer) { + g_mixer->volume_music(g_mixer, music, volume); + } +} + +void +apkenv_mixer_volume_sound(struct MixerSound *sound, float volume) +{ + if (g_mixer) { + g_mixer->volume_sound(g_mixer, sound, volume); + } +} diff --git a/mixer/mixer.h b/mixer/mixer.h new file mode 100644 index 0000000..8617ba1 --- /dev/null +++ b/mixer/mixer.h @@ -0,0 +1,97 @@ +/** + * apkenv Mixer + * Copyright (c) 2014, Thomas Perl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **/ + +#ifndef APKENV_MIXER_H +#define APKENV_MIXER_H + +#include "../audio/audio.h" + +#include + +struct MixerMusic; +struct MixerSound; + +struct Mixer { + // Initialize the mixer + int (*open)(struct Mixer *mixer); + + // Close the mixer + void (*close)(struct Mixer *mixer); + + // Loading of data + struct MixerMusic *(*load_music)(struct Mixer *mixer, + const char *filename); + struct MixerMusic *(*load_music_buffer)(struct Mixer *mixer, + const char *buffer, size_t size); + struct MixerSound *(*load_sound)(struct Mixer *mixer, + const char *filename); + struct MixerSound *(*load_sound_buffer)(struct Mixer *mixer, + const char *buffer, size_t size); + + // Freeing of data + void (*free_music)(struct Mixer *mixer, struct MixerMusic *music); + void (*free_sound)(struct Mixer *mixer, struct MixerSound *sound); + + // Playback + void (*play_music)(struct Mixer *mixer, struct MixerMusic *music, + int do_loop); + void (*play_sound)(struct Mixer *mixer, struct MixerSound *sound, + int do_loop); + void (*stop_music)(struct Mixer *mixer, struct MixerMusic *music); + void (*stop_sound)(struct Mixer *mixer, struct MixerSound *sound); + + // Volume control + void (*volume_music)(struct Mixer *mixer, struct MixerMusic *music, + float volume); + void (*volume_sound)(struct Mixer *mixer, struct MixerSound *sound, + float volume); + + // Internal structure to store the config from init + struct AudioConfig config; +}; + + +void apkenv_mixer_register(struct Mixer *mixer); + +int apkenv_mixer_open(int frequency, enum AudioFormat format, int channels, int buffer); +void apkenv_mixer_close(); +struct MixerMusic *apkenv_mixer_load_music(const char *filename); +struct MixerMusic *apkenv_mixer_load_music_buffer(const char *buffer, size_t size); +struct MixerSound *apkenv_mixer_load_sound(const char *filename); +struct MixerSound *apkenv_mixer_load_sound_buffer(const char *buffer, size_t size); +void apkenv_mixer_free_music(struct MixerMusic *music); +void apkenv_mixer_free_sound(struct MixerSound *sound); +void apkenv_mixer_play_music(struct MixerMusic *music, int do_loop); +void apkenv_mixer_play_sound(struct MixerSound *sound, int do_loop); +void apkenv_mixer_stop_music(struct MixerMusic *music); +void apkenv_mixer_stop_sound(struct MixerSound *sound); +void apkenv_mixer_volume_music(struct MixerMusic *music, float volume); +void apkenv_mixer_volume_sound(struct MixerSound *sound, float volume); + +#endif /* APKENV_MIXER_H */ diff --git a/modules/common.h b/modules/common.h index 3c84a0d..15a8d14 100644 --- a/modules/common.h +++ b/modules/common.h @@ -92,12 +92,4 @@ enum { typedef void (*jni_onload_t)(JavaVM *vm, void *reserved) SOFTFP; typedef void (*jni_onunload_t)(JavaVM *vm, void *reserved) SOFTFP; -/* older SDL_mixer compatibility */ -#ifdef MIX_MAJOR_VERSION -#if ((MIX_MAJOR_VERSION << 16) | (MIX_MINOR_VERSION << 8) | MIX_PATCHLEVEL) < 0x1020a -#define Mix_Init(x) -#define Mix_Quit() -#endif -#endif - #endif /* APKENV_MODULES_COMMON_H */ diff --git a/modules/cuttherope.c b/modules/cuttherope.c index 860ec9a..950658a 100644 --- a/modules/cuttherope.c +++ b/modules/cuttherope.c @@ -36,11 +36,10 @@ #include #include #include - -#include -#include +#include #include "common.h" +#include "../mixer/mixer.h" typedef void (*cuttherope_init_t)(JNIEnv *env, jobject obj, jobject resourceLoader, jobject soundManager, jobject preferences, jobject saveManager, jobject flurry, jobject videoPlayer, jobject scorer, @@ -61,7 +60,7 @@ typedef void (*cuttherope_nativeplaybackfinished_t)(JNIEnv *env, jobject p0, jin #define MAX_SOUNDS 256 typedef struct { char* name; - Mix_Chunk* sound; + struct MixerSound *sound; } Sound; #define MAX_IMAGES 200 @@ -99,7 +98,7 @@ struct SupportModulePriv { const char* home; Sound sounds[MAX_SOUNDS]; Image images[MAX_IMAGES]; - Mix_Music* music; + struct MixerMusic *music; char musicpath[PATH_MAX]; KeyValue keyvalues[MAX_KEYVALUES]; int want_exit; @@ -330,8 +329,7 @@ cuttherope_CallVoidMethodV(JNIEnv* env, jobject p1, jmethodID p2, va_list p3) char* buffer; size_t size; if (GLOBAL_J(env)->read_file(filepath,&buffer,&size)) { - SDL_RWops *rw = SDL_RWFromMem(buffer, size); - Mix_Chunk *sound = Mix_LoadWAV_RW(rw, 1); + struct MixerSound *sound = apkenv_mixer_load_sound_buffer(buffer, size); cuttherope_priv.sounds[soundId].sound = sound; MODULE_DEBUG_PRINTF("loadSound id=%d %s.\n",soundId,sound?"ok":"failed"); } @@ -362,7 +360,7 @@ cuttherope_CallVoidMethodV(JNIEnv* env, jobject p1, jmethodID p2, va_list p3) if (soundId>=0 && soundIdname,"stopMusic")==0) { if(cuttherope_priv.music!=NULL) { - Mix_HaltMusic(); + apkenv_mixer_stop_music(cuttherope_priv.music); } } else @@ -599,20 +597,7 @@ cuttherope_init(struct SupportModule *self, int width, int height, const char *h self->priv->home = strdup(home); // init sound stuff - Mix_Init(MIX_INIT_OGG); - - int audio_rate = 22050; - uint16_t audio_format = AUDIO_S16SYS; - int audio_channels = 2; - int audio_buffers = 1024; - - if(Mix_OpenAudio(audio_rate,audio_format,audio_channels,audio_buffers)<0) - { - printf("Mix_OpenAudio failed %s.\n",Mix_GetError()); - exit(-1); - } - - Mix_AllocateChannels(16); + apkenv_mixer_open(22050, AudioFormat_S16SYS, 2, 1024); memset(self->priv->sounds,0,sizeof(self->priv->sounds)); self->priv->music = NULL; @@ -703,7 +688,7 @@ cuttherope_update(struct SupportModule *self) static void cuttherope_deinit(struct SupportModule *self) { - Mix_CloseAudio(); + apkenv_mixer_close(); save_preferences(); //not saving here in harmattan } diff --git a/modules/fruitninja.c b/modules/fruitninja.c index b40607b..cfe01da 100644 --- a/modules/fruitninja.c +++ b/modules/fruitninja.c @@ -36,11 +36,11 @@ #define MAX_SOUNDS 150 #include "common.h" +#include "../mixer/mixer.h" + #include #include #include -#include "SDL/SDL.h" -#include "SDL/SDL_mixer.h" // Typedefs. Got these from classes.dex (http://stackoverflow.com/questions/1249973/decompiling-dex-into-java-sourcecode) @@ -80,14 +80,14 @@ struct GlobalState *global; typedef struct { char* name; - Mix_Chunk *sound; + struct MixerSound *sound; } SFXInfo; SFXInfo SFX[MAX_SOUNDS+1]; //data); int i; @@ -202,21 +191,20 @@ fruitninja_jnienv_CallStaticVoidMethodV(JNIEnv* p0, jclass p1, jmethodID p2, va_ MODULE_DEBUG_PRINTF("module: Play music: %s\n", Music_Path); - music = Mix_LoadMUS( Music_Path ); - Mix_PlayMusic( music, 0 ); // -1 should loop music? Not for me? - Mix_HookMusicFinished(musicFinished); + music = apkenv_mixer_load_music(Music_Path); + apkenv_mixer_play_music(music, 1); } else if( strcmp( p2->name, "SetMusicVolume" ) == 0 ){ - int musicvol = va_arg(p3, double) * MIX_MAX_VOLUME; - MODULE_DEBUG_PRINTF("module: SetMusicVolume: %i\n", musicvol); - Mix_VolumeMusic(musicvol); + double musicvol = va_arg(p3, double); + MODULE_DEBUG_PRINTF("module: SetMusicVolume: %.2f\n", musicvol); + apkenv_mixer_volume_music(music, musicvol); } else if( strcmp( p2->name, "SetSFXVolume" ) == 0 ){ - int soundvol = va_arg(p3, double) * MIX_MAX_VOLUME; + double soundvol = va_arg(p3, double); int i; - MODULE_DEBUG_PRINTF("module: SetSFXVolume: %i\n", soundvol); + MODULE_DEBUG_PRINTF("module: SetSFXVolume: %.2f\n", soundvol); for(i=0; i <= MAX_SOUNDS; i++) { if ( SFX[i].sound == NULL ) break; - Mix_VolumeChunk(SFX[i].sound, soundvol); + apkenv_mixer_volume_sound(SFX[i].sound, soundvol); } } else { MODULE_DEBUG_PRINTF("module_fruitninja_jnienv_CallStaticVoidMethodV(%x, %s, %s)\n", jcl->name, p2->name, p2->sig); @@ -359,18 +347,7 @@ fruitninja_init(struct SupportModule *self, int width, int height, const char *h global = GLOBAL_M; self->priv->myHome = strdup(home); - /* Init audio. I am too lazy to find out if fruitninja reports right - settings so I just hardcode these into here... */ - int audio_rate = 22050; - Uint16 audio_format = AUDIO_S16SYS; - int audio_channels = 2; - int audio_buffers = 1024; - - if(Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers) != 0) { - fprintf(stderr, "Unable to initialize audio: %s\n", Mix_GetError()); - exit(1); - } - Mix_AllocateChannels(16); + apkenv_mixer_open(22050, AudioFormat_S16SYS, 2, 1024); /* Load sounds */ global->foreach_file("assets/sound", load_sound_callback); @@ -413,15 +390,18 @@ fruitninja_update(struct SupportModule *self) static void fruitninja_deinit(struct SupportModule *self) { - if( Mix_PlayingMusic() != 0 ) Mix_FreeMusic( music ); + if (music) { + apkenv_mixer_stop_music(music); + apkenv_mixer_free_music(music); + } int i; for(i=0; i <= MAX_SOUNDS; i++) { if ( SFX[i].sound == NULL ) break; - Mix_FreeChunk(SFX[i].sound); + apkenv_mixer_free_sound(SFX[i].sound); } - Mix_CloseAudio(); + apkenv_mixer_close(); } static void diff --git a/modules/marmalade.c b/modules/marmalade.c index 2d29731..eac80c0 100644 --- a/modules/marmalade.c +++ b/modules/marmalade.c @@ -31,9 +31,9 @@ #include #include #include -#include -#include + #include "common.h" +#include "../mixer/mixer.h" #define ORIENTATION_LANDSCAPE 2 #define ORIENTATION_PORTRAIT 1 @@ -329,7 +329,7 @@ static int sound_started; static int sound_volume; // 0-100 /* mix audio buffer */ -static void my_audio_mixer(void *udata, Uint8 *stream, int len) +static void my_audio_mixer(void *udata, void *stream, int len) { static short soundbuf[AUDIO_CHUKSIZE * AUDIO_CHANNELS]; struct dummy_array arr; @@ -365,8 +365,7 @@ static void my_audio_mixer(void *udata, Uint8 *stream, int len) #define CHANNELS 16 struct audio_player_state { - SDL_RWops *rw; - Mix_Music *music; + struct MixerMusic *music; int playing; int really_playing; // SDL_Mixer can only play 1, marmalade can play many.. } player_state[CHANNELS]; @@ -378,7 +377,7 @@ static void audioStop(unsigned int channel) if (player_state[channel].really_playing) { player_state[channel].really_playing = 0; - Mix_HaltMusic(); + apkenv_mixer_stop_music(player_state[channel].music); } if (player_state[channel].playing) { player_state[channel].playing = 0; @@ -405,35 +404,27 @@ static int audioPlay(const char *filename, int repeats, long long file_offset, player = &player_state[channel]; if (player->music != NULL) { - Mix_FreeMusic(player->music); + apkenv_mixer_free_music(player->music); player->music = NULL; } - if (player->rw != NULL) { - SDL_RWclose(player->rw); - player->rw = NULL; - } ext = strrchr(filename, '.'); if (ext != NULL && strcasecmp(ext, ".apk") == 0 && marmalade_priv.global->apk_in_mem != NULL) { mem = (const char *)marmalade_priv.global->apk_in_mem + file_offset; - player->rw = SDL_RWFromConstMem(mem, file_size); - player->music = Mix_LoadMUS_RW(player->rw); + player->music = apkenv_mixer_load_music_buffer(mem, file_size); } else { if (file_offset) fprintf(stderr, "TODO: offset %lld for %s\n", file_offset, filename); - player->music = Mix_LoadMUS(filename); - } - if (player->music == NULL) { - fprintf(stderr, "failed to play %s: %s\n", filename, Mix_GetError()); - return -1; + player->music = apkenv_mixer_load_music(filename); } - Mix_PlayMusic(player->music, repeats > 0 ? repeats : -1); - for (i = 0; i < CHANNELS; i++) + apkenv_mixer_play_music(player->music, repeats < 0); + for (i = 0; i < CHANNELS; i++) { player_state[i].really_playing = 0; + } player_state[channel].playing = player_state[channel].really_playing = 1; return 0; } @@ -456,40 +447,13 @@ static int my_soundInit(void) // marmalade gives useless values int audio_rate = AUDIO_RATE; - - Uint16 audio_format = AUDIO_S16SYS; int audio_channels = AUDIO_CHANNELS; int audio_buffers = AUDIO_CHUKSIZE; - int act_audio_rate; - int act_audio_channels; - Uint16 act_audio_format; - - Mix_Init(MIX_INIT_OGG | MIX_INIT_MP3); - - if(Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers) != 0) { - MODULE_DEBUG_PRINTF("marmalade: Unable to initialize audio: %s\n", Mix_GetError()); - exit(1); - } - - Mix_AllocateChannels(16); - - if(Mix_QuerySpec(&act_audio_rate, &act_audio_format, &act_audio_channels) != 0) - { - if(act_audio_rate != audio_rate || act_audio_format != audio_format || act_audio_channels != audio_channels) - { - MODULE_DEBUG_PRINTF("marmalade actual audio settings differ from set: set/act [rate,format,channels]: [%d/%d,%d/%d,%d/%d]\n",audio_rate,act_audio_rate,audio_format,act_audio_format,audio_channels,act_audio_channels); - } - - audio_rate = act_audio_rate; - } - else - { - MODULE_DEBUG_PRINTF("Mix_QuerySpec failed.\n"); - } + apkenv_mixer_open(audio_rate, AudioFormat_S16SYS, audio_channels, audio_buffers); - Mix_SetPostMix(my_audio_mixer, NULL); - Mix_HookMusicFinished(music_finished); + // TODO Mix_SetPostMix(my_audio_mixer, NULL); + // TODO Mix_HookMusicFinished(music_finished); MODULE_DEBUG_PRINTF("marmalade initializing audio done.\n"); return audio_rate; @@ -698,10 +662,10 @@ marmalade_CallVoidMethodV(JNIEnv* env, jobject p1, jmethodID p2, va_list p3) } else if(method_is(audioSetVolume)) { - jint volume = va_arg(p3, int); + float volume = va_arg(p3, int) / 100.f; jint which = va_arg(p3, int); - MODULE_DEBUG_PRINTF("audioSetVolume(%d, %d) obj=%p\n", volume, which, p1); - Mix_VolumeMusic(volume * MIX_MAX_VOLUME / 100); + MODULE_DEBUG_PRINTF("audioSetVolume(%.2f, %d) obj=%p\n", volume, which, p1); + // TODO apkenv_mixer_volume_music(..., volume) } else if(method_is(audioStop)) { diff --git a/modules/openframeworks.c b/modules/openframeworks.c index 9cb2f08..c52ae2f 100644 --- a/modules/openframeworks.c +++ b/modules/openframeworks.c @@ -31,9 +31,9 @@ #include #include #include -#include #include "common.h" +#include "../mixer/mixer.h" /* from OF 0.7.4 source */ typedef void (*openframeworks_setAppDataDir_t)(JNIEnv *env, jobject thiz, @@ -82,11 +82,10 @@ static struct SupportModulePriv openframeworks_priv; static void publish_superhexagon_score(int level, int score); struct player_sound { - Mix_Music *music; - Mix_Chunk *chunk; + struct MixerMusic *music; + struct MixerSound *chunk; int loop; int music_playing; - int chunk_channel; }; struct _jobject { @@ -137,11 +136,10 @@ openframeworks_jnienv_CallVoidMethodV(JNIEnv *env, jobject p1, jmethodID p2, va_ struct player_sound *player = calloc(1, sizeof(*player)); const char *ext = arg->data + strlen(arg->data) - 3; if (strcasecmp(ext, "wav") == 0) - player->chunk = Mix_LoadWAV(arg->data); + player->chunk = apkenv_mixer_load_sound(arg->data); else - player->music = Mix_LoadMUS(arg->data); + player->music = apkenv_mixer_load_music(arg->data); player->loop = 0; - player->chunk_channel = -1; obj->priv = player; } else if (strcmp(p2->name, "setLoop") == 0) @@ -153,11 +151,11 @@ openframeworks_jnienv_CallVoidMethodV(JNIEnv *env, jobject p1, jmethodID p2, va_ else if (strcmp(p2->name, "play") == 0) { MODULE_DEBUG_PRINTF("play obj %p\n", obj); - if (player->chunk != NULL) - player->chunk_channel = - Mix_PlayChannel(-1, player->chunk, player->loop); + if (player->chunk != NULL) { + apkenv_mixer_play_sound(player->chunk, player->loop); + } else if (player->music != NULL) { - Mix_PlayMusic(player->music, player->loop ? -1 : 1); + apkenv_mixer_play_music(player->music, player->loop); player->music_playing = 1; } else fprintf(stderr, "play issued with nothing set to play\n"); @@ -166,12 +164,9 @@ openframeworks_jnienv_CallVoidMethodV(JNIEnv *env, jobject p1, jmethodID p2, va_ { MODULE_DEBUG_PRINTF("stop obj %p\n", obj); if (player->chunk != NULL) { - if (player->chunk_channel >= 0) { - Mix_HaltChannel(player->chunk_channel); - player->chunk_channel = -1; - } + apkenv_mixer_stop_sound(player->chunk); } else { - Mix_HaltMusic(); + apkenv_mixer_stop_music(player->music); player->music_playing = 0; } } @@ -182,18 +177,19 @@ openframeworks_jnienv_CallVoidMethodV(JNIEnv *env, jobject p1, jmethodID p2, va_ if (player->music != NULL && player->music_playing) { // note: SDL_mixer has seek broken when compiled with tremor // http://bugzilla.libsdl.org/show_bug.cgi?id=1807 - Mix_SetMusicPosition((double)arg / 1000.0); + //Mix_SetMusicPosition((double)arg / 1000.0); + // FIXME } } else if (strcmp(p2->name, "setVolume") == 0) { - double arg = va_arg(p3, double); + double volume = va_arg(p3, double); MODULE_DEBUG_PRINTF("setVolume %.3f, obj %p\n", arg, obj); - int sdl_volume = (int)(arg * MIX_MAX_VOLUME); - if (player->chunk != NULL) - Mix_VolumeChunk(player->chunk, sdl_volume); - else if (player->music != NULL && player->music_playing) - Mix_VolumeMusic(sdl_volume); + if (player->chunk != NULL) { + apkenv_mixer_volume_sound(player->chunk, volume); + } else if (player->music != NULL && player->music_playing) { + apkenv_mixer_volume_music(player->music, volume); + } } } @@ -324,13 +320,7 @@ openframeworks_init(struct SupportModule *self, int width, int height, const cha } /* init sound */ - Mix_Init(MIX_INIT_OGG); - - if (Mix_OpenAudio(44100, AUDIO_S16SYS, 2, 1024) < 0) - { - fprintf(stderr, "Mix_OpenAudio failed: %s.\n", Mix_GetError()); - exit(-1); - } + apkenv_mixer_open(44100, AudioFormat_S16SYS, 2, 1024); /* note: order is important here */ self->priv->JNI_OnLoad(VM_M, NULL); @@ -405,7 +395,7 @@ openframeworks_deinit(struct SupportModule *self) /* onStop(), onDestroy(), exit() never called? */ openframeworks_pause(self); - Mix_CloseAudio(); + apkenv_mixer_close(); } static int diff --git a/modules/worldofgoo.c b/modules/worldofgoo.c index 5dc3531..9927859 100644 --- a/modules/worldofgoo.c +++ b/modules/worldofgoo.c @@ -417,7 +417,7 @@ worldofgoo_init(struct SupportModule *self, int width, int height, const char *h self->priv->apk_in_mem = GLOBAL_M->apk_in_mem; build_apk_index(GLOBAL_M->apk_filename); - Mix_Init(MIX_INIT_OGG); + //Mix_Init(MIX_INIT_OGG); /* init sound, must use 32000Hz because music is at that rate, * and SDL_mixer doesn't resample to/from 32000 */ diff --git a/platform/common/sdl_mixer_impl.h b/platform/common/sdl_mixer_impl.h new file mode 100644 index 0000000..a80a08d --- /dev/null +++ b/platform/common/sdl_mixer_impl.h @@ -0,0 +1,189 @@ +/** + * apkenv Mixer + * Copyright (c) 2014, Thomas Perl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + **/ + +#include "../../mixer/mixer.h" + +#include +#include +#include + +/* older SDL_mixer compatibility */ +#ifdef MIX_MAJOR_VERSION +#if ((MIX_MAJOR_VERSION << 16) | (MIX_MINOR_VERSION << 8) | MIX_PATCHLEVEL) < 0x1020a +#define Mix_Init(x) +#define Mix_Quit() +#endif +#endif + +struct MixerMusic { + Mix_Music *music; +}; + +struct MixerSound { + Mix_Chunk *chunk; + int channel; +}; + + +int +sdl_mixer_open(struct Mixer *mixer) +{ + Mix_Init(MIX_INIT_OGG | MIX_INIT_MP3); + + int fmt; + switch (mixer->config.format) { + case AudioFormat_S16SYS: + fmt = AUDIO_S16SYS; + break; + default: + assert(0); + break; + } + + int result = (Mix_OpenAudio(mixer->config.frequency, fmt, mixer->config.channels, mixer->config.buffer) == 0); + Mix_AllocateChannels(16); + + return result; +} + +void +sdl_mixer_close(struct Mixer *mixer) +{ + Mix_CloseAudio(); +} + +struct MixerMusic * +sdl_mixer_load_music(struct Mixer *mixer, const char *filename) +{ + struct MixerMusic *music = calloc(1, sizeof(struct MixerMusic)); + music->music = Mix_LoadMUS(filename); + return music; +} + +struct MixerMusic * +sdl_mixer_load_music_buffer(struct Mixer *mixer, const char *buffer, size_t size) +{ + struct MixerMusic *music = calloc(1, sizeof(struct MixerMusic)); + SDL_RWops *rw = SDL_RWFromConstMem(buffer, size); + music->music = Mix_LoadMUS_RW(rw); + SDL_RWclose(rw); + return music; + +} + +struct MixerSound * +sdl_mixer_load_sound(struct Mixer *mixer, const char *filename) +{ + struct MixerSound *sound = calloc(1, sizeof(struct MixerSound)); + sound->chunk = Mix_LoadWAV(filename); + return sound; +} + +struct MixerSound * +sdl_mixer_load_sound_buffer(struct Mixer *mixer, const char *buffer, size_t size) +{ + struct MixerSound *sound = calloc(1, sizeof(struct MixerSound)); + SDL_RWops *rw = SDL_RWFromConstMem(buffer, size); + sound->chunk = Mix_LoadWAV_RW(rw, 0); + SDL_RWclose(rw); + return sound; +} + +void +sdl_mixer_free_music(struct Mixer *mixer, struct MixerMusic *music) +{ + Mix_FreeMusic(music->music); + free(music); +} + +void +sdl_mixer_free_sound(struct Mixer *mixer, struct MixerSound *sound) +{ + Mix_FreeChunk(sound->chunk); + free(sound); +} + +void +sdl_mixer_play_music(struct Mixer *mixer, struct MixerMusic *music, int do_loop) +{ + Mix_PlayMusic(music->music, do_loop ? -1 : 0); +} + +void +sdl_mixer_play_sound(struct Mixer *mixer, struct MixerSound *sound, int do_loop) +{ + sound->channel = Mix_PlayChannel(-1, sound->chunk, do_loop ? -1 : 0); +} + +void +sdl_mixer_stop_music(struct Mixer *mixer, struct MixerMusic *music) +{ + Mix_HaltMusic(); +} + +void +sdl_mixer_stop_sound(struct Mixer *mixer, struct MixerSound *sound) +{ + Mix_HaltChannel(sound->channel); +} + +void +sdl_mixer_volume_music(struct Mixer *mixer, struct MixerMusic *music, float volume) +{ + Mix_VolumeMusic(volume * MIX_MAX_VOLUME); +} + +void +sdl_mixer_volume_sound(struct Mixer *mixer, struct MixerSound *sound, float volume) +{ + Mix_VolumeChunk(sound->chunk, volume * MIX_MAX_VOLUME); +} + + +static struct Mixer +g_sdl_mixer = { + sdl_mixer_open, + sdl_mixer_close, + sdl_mixer_load_music, + sdl_mixer_load_music_buffer, + sdl_mixer_load_sound, + sdl_mixer_load_sound_buffer, + sdl_mixer_free_music, + sdl_mixer_free_sound, + sdl_mixer_play_music, + sdl_mixer_play_sound, + sdl_mixer_stop_music, + sdl_mixer_stop_sound, + sdl_mixer_volume_music, + sdl_mixer_volume_sound, + { 0 }, +}; + +static struct Mixer * +sdl_mixer = &g_sdl_mixer; diff --git a/platform/fremantle.c b/platform/fremantle.c index 8af3f05..4e0441c 100644 --- a/platform/fremantle.c +++ b/platform/fremantle.c @@ -36,6 +36,7 @@ #include #include "common/sdl_audio_impl.h" +#include "common/sdl_mixer_impl.h" struct PlatformPriv { SDL_Surface *screen; @@ -121,6 +122,7 @@ fremantle_init(int gles_version) apkenv_accelerometer_register(n900_accelerometer); apkenv_audio_register(sdl_audio); + apkenv_mixer_register(sdl_mixer); return 1; } diff --git a/platform/harmattan.c b/platform/harmattan.c index 24efe3b..63c0849 100644 --- a/platform/harmattan.c +++ b/platform/harmattan.c @@ -38,6 +38,7 @@ #include "common/sdl_accelerometer_impl.h" #include "common/sdl_audio_impl.h" +#include "common/sdl_mixer_impl.h" struct PlatformPriv { SDL_Surface *screen; @@ -81,6 +82,7 @@ harmattan_init(int gles_version) apkenv_accelerometer_register(sdl_accelerometer); apkenv_audio_register(sdl_audio); + apkenv_mixer_register(sdl_mixer); return 1; } diff --git a/platform/pandora.c b/platform/pandora.c index 75c76f1..ce96c65 100644 --- a/platform/pandora.c +++ b/platform/pandora.c @@ -39,6 +39,7 @@ #include "common/sdl_accelerometer_impl.h" #include "common/sdl_audio_impl.h" +#include "common/sdl_mixer_impl.h" #include #include @@ -235,6 +236,7 @@ pandora_init(int gles_version) // XXX: Does Pandora expose the accelerometer via SDL? apkenv_accelerometer_register(sdl_accelerometer); apkenv_audio_register(sdl_audio); + apkenv_mixer_register(sdl_mixer); return 1; }