diff --git a/libports/lib/mk/sdl.mk b/libports/lib/mk/sdl.mk index a2bc74149..a7f57eca0 100644 --- a/libports/lib/mk/sdl.mk +++ b/libports/lib/mk/sdl.mk @@ -93,7 +93,8 @@ SRC_C += SDL_audio.c \ SDL_mixer_MMX.c \ SDL_mixer_MMX_VC.c \ SDL_wave.c \ - SDL_dummyaudio.c + SDL_genodeaudio.c +INC_DIR += $(SDL_DIR)/src/audio # file I/O subsystem SRC_C += SDL_rwops.c @@ -114,6 +115,7 @@ CC_OPT_SDL_wave += -Wno-unused-but-set-variable # backend pathes vpath % $(REP_DIR)/src/lib/sdl +vpath % $(REP_DIR)/src/lib/sdl/audio vpath % $(REP_DIR)/src/lib/sdl/video # contribution pathes @@ -125,7 +127,6 @@ vpath %.c $(SDL_DIR)/src/stdlib vpath %.c $(SDL_DIR)/src/video vpath %.c $(SDL_DIR)/src/video/dummy vpath %.c $(SDL_DIR)/src/audio -vpath %.c $(SDL_DIR)/src/audio/dummy vpath %.c $(SDL_DIR)/src/thread vpath %.c $(SDL_DIR)/src/thread/pthread vpath %.c $(SDL_DIR)/src/timer diff --git a/libports/ports/sdl.mk b/libports/ports/sdl.mk index 8ae569564..92f903e17 100644 --- a/libports/ports/sdl.mk +++ b/libports/ports/sdl.mk @@ -25,6 +25,7 @@ $(CONTRIB_DIR)/$(SDL): $(DOWNLOAD_DIR)/$(SDL_TGZ) $(VERBOSE)tar xfz $< -C $(CONTRIB_DIR) && touch $@ $(VERBOSE)rm -f $@/include/SDL_config.h $(VERBOSE)patch -p0 -i src/lib/sdl/SDL_video.patch + $(VERBOSE)patch -d $(CONTRIB_DIR)/$(SDL) -p1 -i ../../src/lib/sdl/SDL_audio.patch # # Install SDL headers diff --git a/libports/src/lib/sdl/SDL_audio.patch b/libports/src/lib/sdl/SDL_audio.patch new file mode 100644 index 000000000..58b0ca6d5 --- /dev/null +++ b/libports/src/lib/sdl/SDL_audio.patch @@ -0,0 +1,26 @@ +diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c +--- a/src/audio/SDL_audio.c ++++ b/src/audio/SDL_audio.c +@@ -113,6 +113,9 @@ + #if SDL_AUDIO_DRIVER_EPOCAUDIO + &EPOCAudio_bootstrap, + #endif ++#if SDL_AUDIO_DRIVER_GENODE ++ &GENODEAUD_bootstrap, ++#endif + NULL + }; + SDL_AudioDevice *current_audio = NULL; +diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h +--- a/src/audio/SDL_sysaudio.h ++++ b/src/audio/SDL_sysaudio.h +@@ -177,6 +177,9 @@ + #if SDL_AUDIO_DRIVER_EPOCAUDIO + extern AudioBootStrap EPOCAudio_bootstrap; + #endif ++#if SDL_AUDIO_DRIVER_GENODE ++extern AudioBootStrap GENODEAUD_bootstrap; ++#endif + + /* This is the current audio device */ + extern SDL_AudioDevice *current_audio; diff --git a/libports/src/lib/sdl/SDL_config_genode.h b/libports/src/lib/sdl/SDL_config_genode.h index dfb476e8d..89af77dc9 100644 --- a/libports/src/lib/sdl/SDL_config_genode.h +++ b/libports/src/lib/sdl/SDL_config_genode.h @@ -38,8 +38,8 @@ #include #include -/* Enable the dummy audio driver (src/audio/dummy/\*.c) */ -#define SDL_AUDIO_DRIVER_DUMMY 1 +/* Enable the Genode audio driver */ +#define SDL_AUDIO_DRIVER_GENODE 1 /* Enable the stub cdrom driver (src/cdrom/dummy/\*.c) */ #define SDL_CDROM_DISABLED 1 diff --git a/libports/src/lib/sdl/audio/SDL_genodeaudio.cc b/libports/src/lib/sdl/audio/SDL_genodeaudio.cc new file mode 100644 index 000000000..746efd9e2 --- /dev/null +++ b/libports/src/lib/sdl/audio/SDL_genodeaudio.cc @@ -0,0 +1,264 @@ +/* + * \brief Genode-specific audio backend + * \author Christian Prochaska + * \date 2012-03-13 + * + * based on the dummy SDL audio driver + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include + +enum { + AUDIO_OUT_SAMPLE_SIZE = sizeof(float), + AUDIO_OUT_CHANNELS = 2, + AUDIO_OUT_FREQ = 44100, + AUDIO_OUT_SAMPLES = 1024, +}; + +using Genode::env; +using Genode::Allocator_avl; +using Genode::Signal_context; +using Genode::Signal_receiver; + +static const char *channel_names[] = { "front left", "front right" }; +static float volume = 1.0; +static Signal_context config_signal_context; + +extern "C" { + +#include "SDL_config.h" + +/* Output audio to Genode audio service. */ + +#include "SDL_rwops.h" +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "SDL_audiomem.h" +#include "SDL_audio_c.h" +#include "SDL_audiodev_c.h" +#include "SDL_genodeaudio.h" + +/* The tag name used by Genode audio */ +#define GENODEAUD_DRIVER_NAME "genode" + +typedef Audio_out::Session::Channel::Source Stream; + + +struct SDL_PrivateAudioData { + Uint8 *mixbuf; + Uint32 mixlen; + Genode::Allocator_avl *block_alloc[AUDIO_OUT_CHANNELS]; + Audio_out::Connection *audio_out[AUDIO_OUT_CHANNELS]; + Stream *stream[AUDIO_OUT_CHANNELS]; +}; + + +/* + * The first 'Signal_receiver' object in a process creates a signal receiver + * thread. Currently this must not happen before the main program has started + * or else the thread's context area would get overmapped on Genode/Linux when + * the main program calls 'main_thread_bootstrap()' from '_main()'. + */ +static Signal_receiver *signal_receiver() +{ + static Signal_receiver _signal_receiver; + return &_signal_receiver; +} + + +static void read_config() +{ + /* read volume from config file */ + try { + unsigned int config_volume; + Genode::config()->xml_node().sub_node("sdl_audio_volume").attribute("value").value(&config_volume); + volume = (float)config_volume / 100; + } + catch (Genode::Config::Invalid) { } + catch (Genode::Xml_node::Nonexistent_sub_node) { } + catch (Genode::Xml_node::Nonexistent_attribute) { } +} + + +/* Audio driver functions */ +static int GENODEAUD_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void GENODEAUD_WaitAudio(_THIS); +static void GENODEAUD_PlayAudio(_THIS); +static Uint8 *GENODEAUD_GetAudioBuf(_THIS); +static void GENODEAUD_CloseAudio(_THIS); + + +/* Audio driver bootstrap functions */ +static int GENODEAUD_Available(void) +{ + return 1; +} + + +static void GENODEAUD_DeleteDevice(SDL_AudioDevice *device) +{ + for (int channel = 0; channel < AUDIO_OUT_CHANNELS; channel++) { + destroy(env()->heap(), device->hidden->audio_out[channel]); + destroy(env()->heap(), device->hidden->block_alloc[channel]); + } + + SDL_free(device->hidden); + SDL_free(device); +} + + +static SDL_AudioDevice *GENODEAUD_CreateDevice(int devindex) +{ + SDL_AudioDevice *_this; + + /* Initialize all variables that we clean on shutdown */ + _this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( _this ) { + SDL_memset(_this, 0, (sizeof *_this)); + _this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *_this->hidden)); + } + if ( (_this == NULL) || (_this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( _this ) { + SDL_free(_this); + } + return(0); + } + SDL_memset(_this->hidden, 0, (sizeof *_this->hidden)); + + /* Set the function pointers */ + _this->OpenAudio = GENODEAUD_OpenAudio; + _this->WaitAudio = GENODEAUD_WaitAudio; + _this->PlayAudio = GENODEAUD_PlayAudio; + _this->GetAudioBuf = GENODEAUD_GetAudioBuf; + _this->CloseAudio = GENODEAUD_CloseAudio; + + _this->free = GENODEAUD_DeleteDevice; + + /* connect to 'Audio_out' service */ + for (int channel = 0; channel < AUDIO_OUT_CHANNELS; channel++) { + _this->hidden->block_alloc[channel] = new (env()->heap()) Allocator_avl(env()->heap()); + try { + _this->hidden->audio_out[channel] = new (env()->heap()) + Audio_out::Connection(channel_names[channel], _this->hidden->block_alloc[channel]); + _this->hidden->stream[channel] = _this->hidden->audio_out[channel]->stream(); + } catch(Genode::Parent::Service_denied) { + PERR("Could not connect to 'Audio_out' service."); + destroy(env()->heap(), _this->hidden->block_alloc[channel]); + while(--channel > 0) { + destroy(env()->heap(), _this->hidden->audio_out[channel]); + destroy(env()->heap(), _this->hidden->block_alloc[channel]); + } + return NULL; + } + if (channel > 0) + _this->hidden->audio_out[channel]->sync_session(_this->hidden->audio_out[channel-1]->session_capability()); + } + + Genode::config()->sigh(signal_receiver()->manage(&config_signal_context)); + read_config(); + + return _this; +} + + +AudioBootStrap GENODEAUD_bootstrap = { + GENODEAUD_DRIVER_NAME, "Genode audio driver", + GENODEAUD_Available, GENODEAUD_CreateDevice +}; + + +/* This function waits until it is possible to write a full sound buffer */ +static void GENODEAUD_WaitAudio(_THIS) +{ + for (int channel = 0; channel < AUDIO_OUT_CHANNELS; channel++) + while (_this->hidden->stream[channel]->ack_avail() || + !_this->hidden->stream[channel]->ready_to_submit()) { + _this->hidden->stream[channel]->release_packet(_this->hidden->stream[channel]->get_acked_packet()); + } +} + + +static void GENODEAUD_PlayAudio(_THIS) +{ + Packet_descriptor p[AUDIO_OUT_CHANNELS]; + + for (int channel = 0; channel < AUDIO_OUT_CHANNELS; channel++) + while (1) + try { + p[channel] = _this->hidden->stream[channel]->alloc_packet(AUDIO_OUT_SAMPLE_SIZE * AUDIO_OUT_SAMPLES); + break; + } catch (Stream::Packet_alloc_failed) { + /* wait for next finished packet */ + _this->hidden->stream[channel]->release_packet(_this->hidden->stream[channel]->get_acked_packet()); + } + + if (signal_receiver()->pending()) { + signal_receiver()->wait_for_signal(); + Genode::config()->reload(); + read_config(); + } + + for (int sample = 0; sample < AUDIO_OUT_SAMPLES; sample++) + for (int channel = 0; channel < AUDIO_OUT_CHANNELS; channel++) + _this->hidden->stream[channel]->packet_content(p[channel])[sample] = + volume * (float)(((int16_t*)_this->hidden->mixbuf)[sample * AUDIO_OUT_CHANNELS + channel]) / 32768; + + for (int channel = 0; channel < AUDIO_OUT_CHANNELS; channel++) + _this->hidden->stream[channel]->submit_packet(p[channel]); +} + + +static Uint8 *GENODEAUD_GetAudioBuf(_THIS) +{ + return(_this->hidden->mixbuf); +} + + +static void GENODEAUD_CloseAudio(_THIS) +{ + if ( _this->hidden->mixbuf != NULL ) { + SDL_FreeAudioMem(_this->hidden->mixbuf); + _this->hidden->mixbuf = NULL; + } +} + + +static int GENODEAUD_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + PDBG("requested freq = %d", spec->freq); + PDBG("requested format = 0x%x", spec->format); + PDBG("requested samples = %u", spec->samples); + PDBG("requested size = %u", spec->size); + + spec->channels = AUDIO_OUT_CHANNELS; + spec->format = AUDIO_S16LSB; + spec->freq = AUDIO_OUT_FREQ; + spec->samples = AUDIO_OUT_SAMPLES; + SDL_CalculateAudioSpec(spec); + + /* Allocate mixing buffer */ + _this->hidden->mixlen = spec->size; + _this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(_this->hidden->mixlen); + if ( _this->hidden->mixbuf == NULL ) { + return(-1); + } + SDL_memset(_this->hidden->mixbuf, spec->silence, spec->size); + + /* We're ready to rock and roll. :-) */ + return(0); +} + +} diff --git a/libports/src/lib/sdl/audio/SDL_genodeaudio.h b/libports/src/lib/sdl/audio/SDL_genodeaudio.h new file mode 100644 index 000000000..add43f7f0 --- /dev/null +++ b/libports/src/lib/sdl/audio/SDL_genodeaudio.h @@ -0,0 +1,26 @@ +/* + * \brief Genode-specific audio backend + * \author Christian Prochaska + * \date 2012-03-13 + * + * based on the dummy SDL audio driver + */ + +/* + * Copyright (C) 2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include "SDL_config.h" + +#ifndef _SDL_genodeaudio_h +#define _SDL_genodeaudio_h + +#include "SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *_this + +#endif /* _SDL_genodeaudio_h */