diff --git a/dde_oss/src/drivers/oss/include/audio.h b/dde_oss/src/drivers/oss/include/audio.h index 0174aeb89..0cf11aec3 100644 --- a/dde_oss/src/drivers/oss/include/audio.h +++ b/dde_oss/src/drivers/oss/include/audio.h @@ -22,6 +22,6 @@ int audio_init(); /** * Play data of size */ -int audio_play(short *data, int size); +int audio_play(short *data, unsigned size); #endif /* _INCLUDE__AUDIO_H_ */ diff --git a/dde_oss/src/drivers/oss/include/os.h b/dde_oss/src/drivers/oss/include/os.h index 2653baa87..f98ecee32 100644 --- a/dde_oss/src/drivers/oss/include/os.h +++ b/dde_oss/src/drivers/oss/include/os.h @@ -196,7 +196,7 @@ enum { #define GET_PROCESS_PID(p) -1 #define KERNEL_MALLOC(size) (dde_kit_large_malloc(size)) -#define KERNEL_FREE(ptr) (dde_kit_large_free(ptr)) +#define KERNEL_FREE(ptr) (dde_kit_large_free(ptr)) void * dma_alloc(oss_native_word *phys, size_t size); #define CONTIG_MALLOC(osdev, sz, memlimit, phaddr, handle) dma_alloc(phaddr, sz) diff --git a/dde_oss/src/drivers/oss/main.cc b/dde_oss/src/drivers/oss/main.cc index 6cbe34a9a..f0d624467 100644 --- a/dde_oss/src/drivers/oss/main.cc +++ b/dde_oss/src/drivers/oss/main.cc @@ -1,5 +1,5 @@ /* - * \brief Audio-session-entry point + * \brief Audio-out session entry point * \author Sebastian Sumpf * \date 2012-11-20 */ @@ -19,7 +19,7 @@ extern "C" { #include #include #include -#include +#include #include #include @@ -27,202 +27,253 @@ extern "C" { using namespace Genode; + static const bool verbose = false; static bool audio_out_active = false; +enum Channel_number { LEFT, RIGHT, MAX_CHANNELS, INVALID = MAX_CHANNELS }; + namespace Audio_out { - - class Session_component; - - enum Channel_number { LEFT, RIGHT, MAX_CHANNELS, INVALID = MAX_CHANNELS }; - + class Session_component; + class Out; + class Root; + struct Root_policy; static Session_component *channel_acquired[MAX_CHANNELS]; +} - class Session_component : public Session_rpc_object - { - private: - Ram_dataspace_capability _ds; - Channel_number _channel; +class Audio_out::Session_component : public Audio_out::Session_rpc_object +{ + private: - Signal_dispatcher _process_packet_dispatcher; + Channel_number _channel; + Signal_context_capability _ctx_cap; + Signal_transmitter _signal; - Ram_dataspace_capability _alloc_dataspace(size_t size) - { - _ds = env()->ram_session()->alloc(size); - return _ds; - } + public: - void _process_packets(unsigned) - { - /* handle audio-out packets */ - Session_component *left = channel_acquired[LEFT], - *right = channel_acquired[RIGHT]; - while (left->channel()->packet_avail() && - right->channel()->packet_avail() && - left->channel()->ready_to_ack() && - right->channel()->ready_to_ack()) { + Session_component(Channel_number channel, Signal_context_capability ctx_cap) + : Session_rpc_object(ctx_cap), _channel(channel), _ctx_cap(ctx_cap) + { + Audio_out::channel_acquired[_channel] = this; + _signal.context(ctx_cap); + } - /* get packets for channels */ - Packet_descriptor p[MAX_CHANNELS]; - static short data[2 * PERIOD]; - p[LEFT] = left->channel()->get_packet(); - p[RIGHT] = right->channel()->get_packet(); + ~Session_component() + { + Audio_out::channel_acquired[_channel] = 0; + } - /* convert float to S16LE */ - for (int i = 0; i < 2 * PERIOD; i += 2) { - data[i] = left->channel()->packet_content(p[LEFT])[i / 2] * 32767; - data[i + 1] = right->channel()->packet_content(p[RIGHT])[i / 2] * 32767; - } + void start() + { + Session_rpc_object::start(); + /* this will trigger Audio_out::Out::handle */ + _signal.submit(); + } +}; - if (verbose) - PDBG("play packet"); - /* send to driver */ - int err; - if (audio_out_active) - if ((err = audio_play(data, 4 * PERIOD))) - PWRN("Error %d during playback", err); +class Audio_out::Out : public Driver_context +{ + private: - /* acknowledge packet to the client */ - if (p[LEFT].valid()) - left->channel()->acknowledge_packet(p[LEFT]); + bool _active() { + return channel_acquired[LEFT] && channel_acquired[RIGHT] && + channel_acquired[LEFT]->active() && channel_acquired[RIGHT]->active(); + } - if (p[RIGHT].valid()) - right->channel()->acknowledge_packet(p[RIGHT]); + Stream *left() { return channel_acquired[LEFT]->stream(); } + Stream *right() { return channel_acquired[RIGHT]->stream(); } + + void _advance_position(Packet *l, Packet *r) + { + bool full_left = left()->full(); + bool full_right = right()->full(); + + left()->pos(left()->packet_position(l)); + right()->pos(right()->packet_position(r)); + + left()->increment_position(); + right()->increment_position(); + + Session_component *channel_left = channel_acquired[LEFT]; + Session_component *channel_right = channel_acquired[RIGHT]; + + if (full_left) + channel_left->alloc_submit(); + + if (full_right) + channel_right->alloc_submit(); + + channel_left->progress_submit(); + channel_right->progress_submit(); + } + + bool _play_packet() + { + Packet *p_left = left()->get(left()->pos()); + Packet *p_right = right()->get(right()->pos()); + + bool found = false; + for (int i = 0; i < QUEUE_SIZE; i++) { + if (p_left->valid() && p_right->valid()) { + found = true; + break; } + + p_left = left()->next(p_left); + p_right = right()->next(p_right); } - public: + if (!found) + return false; - Session_component(Channel_number channel, size_t buffer_size, - Rpc_entrypoint &ep, Signal_receiver &sig_rec) - : Session_rpc_object(_alloc_dataspace(buffer_size), ep), _channel(channel), - _process_packet_dispatcher(sig_rec, *this, - &Session_component::_process_packets) - { - Audio_out::channel_acquired[_channel] = this; - Session_rpc_object::_channel.sigh_packet_avail(_process_packet_dispatcher); - Session_rpc_object::_channel.sigh_ready_to_ack(_process_packet_dispatcher); + /* convert float to S16LE */ + static short data[2 * PERIOD]; + for (int i = 0; i < 2 * PERIOD; i += 2) { + data[i] = p_left->content()[i / 2] * 32767; + data[i + 1] = p_right->content()[i / 2] * 32767; } - ~Session_component() - { - Audio_out::channel_acquired[_channel] = 0; + p_left->invalidate(); + p_right->invalidate(); - env()->ram_session()->free(_ds); + if (verbose) + PDBG("play packet"); + + /* send to driver */ + int err; + if (audio_out_active) + if ((err = audio_play(data, 4 * PERIOD))) + PWRN("Error %d during playback", err); + + p_left->mark_as_played(); + p_right->mark_as_played(); + + _advance_position(p_left, p_right); + + return true; + } + + + public: + + void handle() + { + while (true) { + if (!_active()) { + //TODO: may stop device + return; + } + + /* play one packet */ + if (!_play_packet()) + return; + + /* give others a try */ + Service_handler::s()->check_signal(false); } + } - /********************************* - ** Audio-out-session interface ** - *********************************/ + const char *debug() { return "Audio out"; } +}; - void flush() - { - while (channel()->packet_avail()) - channel()->acknowledge_packet(channel()->get_packet()); - } - void sync_session(Session_capability audio_out_session) { } +static bool channel_number_from_string(const char *name, + Channel_number *out_number) +{ + static struct Names { + const char *name; + Channel_number number; + } names[] = { + { "left", LEFT }, { "front left", LEFT }, + { "right", RIGHT }, { "front right", RIGHT }, + { 0, INVALID } }; - static bool channel_number_from_string(const char *name, - Channel_number *out_number) + for (Names *n = names; n->name; ++n) + if (!Genode::strcmp(name, n->name)) { + *out_number = n->number; + return true; + } + + return false; +} + + +/** + * Session creation policy for our service + */ +struct Audio_out::Root_policy +{ + void aquire(const char *args) { - static struct Names { - const char *name; - Channel_number number; - } names[] = { - { "left", LEFT }, { "front left", LEFT }, - { "right", RIGHT }, { "front right", RIGHT }, - { 0, INVALID } - }; + size_t ram_quota = + Arg_string::find_arg(args, "ram_quota" ).ulong_value(0); + size_t session_size = + align_addr(sizeof(Audio_out::Session_component), 12); - for (Names *n = names; n->name; ++n) - if (!Genode::strcmp(name, n->name)) { - *out_number = n->number; - return true; - } + if ((ram_quota < session_size) || + (sizeof(Stream) > ram_quota - session_size)) { + PERR("insufficient 'ram_quota', got %zd, need %zd", + ram_quota, sizeof(Stream) + session_size); + throw ::Root::Quota_exceeded(); + } - return false; + char channel_name[16]; + Channel_number channel_number; + Arg_string::find_arg(args, "channel").string(channel_name, + sizeof(channel_name), + "left"); + if (!channel_number_from_string(channel_name, &channel_number)) + throw ::Root::Invalid_args(); + if (Audio_out::channel_acquired[channel_number]) + throw ::Root::Unavailable(); } - /** - * Session creation policy for our service - */ - struct Root_policy - { - void aquire(const char *args) - { - size_t ram_quota = - Arg_string::find_arg(args, "ram_quota" ).ulong_value(0); - size_t buffer_size = - Arg_string::find_arg(args, "buffer_size").ulong_value(0); - size_t session_size = - align_addr(sizeof(Session_component), 12); + void release() { } +}; - if ((ram_quota < session_size) || - (buffer_size > ram_quota - session_size)) { - PERR("insufficient 'ram_quota', got %zd, need %zd", - ram_quota, buffer_size + session_size); - throw Root::Quota_exceeded(); - } + +namespace Audio_out { + typedef Root_component Root_component; +} + + +/** + * Root component, handling new session requests. + */ +class Audio_out::Root : public Audio_out::Root_component +{ + private: + + Signal_context_capability _ctx_cap; + + protected: + + Session_component *_create_session(const char *args) + { + if (!audio_out_active) + throw Root::Unavailable(); char channel_name[16]; - Channel_number channel_number; + Channel_number channel_number = INVALID; Arg_string::find_arg(args, "channel").string(channel_name, sizeof(channel_name), "left"); - if (!channel_number_from_string(channel_name, &channel_number)) - throw Root::Invalid_args(); - if (channel_acquired[channel_number]) - throw Root::Unavailable(); + channel_number_from_string(channel_name, &channel_number); + + return new (md_alloc()) + Session_component(channel_number, _ctx_cap); } - void release() { } - }; + public: - typedef Root_component Root_component; - - /* - * Root component, handling new session requests. - */ - class Root : public Root_component - { - private: - - Rpc_entrypoint &_channel_ep; - Signal_receiver &_sig_rec; - - protected: - - Session_component *_create_session(const char *args) - { - size_t buffer_size = - Arg_string::find_arg(args, "buffer_size").ulong_value(0); - - if (!audio_out_active) - throw Root::Unavailable(); - - char channel_name[16]; - Channel_number channel_number = INVALID; - Arg_string::find_arg(args, "channel").string(channel_name, - sizeof(channel_name), - "left"); - channel_number_from_string(channel_name, &channel_number); - - return new (md_alloc()) - Session_component(channel_number, buffer_size, _channel_ep, _sig_rec); - } - - public: - - Root(Rpc_entrypoint &session_ep, Allocator *md_alloc, Signal_receiver &sig_rec) - : Root_component(&session_ep, md_alloc), _channel_ep(session_ep), _sig_rec(sig_rec) - { } - }; -} + Root(Rpc_entrypoint *session_ep, Allocator *md_alloc, Signal_context_capability ctx_cap) + : Root_component(session_ep, md_alloc), _ctx_cap(ctx_cap) + { } +}; int main() @@ -242,7 +293,8 @@ int main() audio_out_active = audio_init() ? false : true; if (audio_out_active) { - static Audio_out::Root audio_root(ep, env()->heap(), recv); + static Audio_out::Out out; + static Audio_out::Root audio_root(&ep, env()->heap(), recv.manage(&out)); env()->parent()->announce(ep.manage(&audio_root)); } diff --git a/dde_oss/src/drivers/oss/os.cc b/dde_oss/src/drivers/oss/os.cc index ce6b73d05..4c2fe2eb9 100644 --- a/dde_oss/src/drivers/oss/os.cc +++ b/dde_oss/src/drivers/oss/os.cc @@ -350,7 +350,7 @@ int audio_init() } /* set fragment policy (TODO: make configurable) */ - int policy = 8; + int policy = 1; if (ioctl_dsp(SNDCTL_DSP_POLICY, &policy) == -1) PERR("Error setting policy"); @@ -379,12 +379,12 @@ int audio_init() } -int audio_play(short *data, int size) +int audio_play(short *data, unsigned size) { uio_t io = { (char *)data, size, UIO_WRITE }; int ret; - if ((ret = dsp_drv->write(0, 0, &io, size)) != size) + if ((ret = dsp_drv->write(0, 0, &io, size)) != (int)size) PERR("Error writing data s: %d r: %d func %p", size, ret, dsp_drv->write); Irq::check_irq(); diff --git a/libports/run/avplay.run b/libports/run/avplay.run index 923f52564..92f137048 100644 --- a/libports/run/avplay.run +++ b/libports/run/avplay.run @@ -1,10 +1,29 @@ +# +# Build +# + build { core init drivers/timer - drivers/framebuffer drivers/pci drivers/input drivers/audio_out - app/avplay + drivers/framebuffer drivers/pci drivers/input drivers/oss + server/mixer + app/avplay drivers/acpi } +# +# Download media file +# + +set media_url "ftp://ftp.untergrund.net/users/ae/dhstv/escape-chotro.mp4" +if {![file exists bin/mediafile]} { + puts "downloading media file from $media_url" + catch { exec wget -O bin/mediafile $media_url } +} + +# +# Generate config +# + create_boot_directory set config { @@ -36,11 +55,28 @@ append_if [have_spec sdl] config { } -append_if [have_spec pci] config { +append_if [have_spec acpi] config { + + + + + + + + + + + +} + +append_if [expr [have_spec pci] && ![have_spec acpi]] config { - - - } + + + + + +} append_if [have_spec vesa] config { @@ -59,10 +95,23 @@ append config { - - - - + + + + + + + + + + + + + + + + + @@ -70,14 +119,23 @@ append config { + + + + + } install_config $config +# +# Boot modules +# + set boot_modules { - core init timer audio_out_drv avplay + core init timer oss_drv mixer avplay ld.lib.so libc.lib.so libc_log.lib.so libc_rom.lib.so libm.lib.so pthread.lib.so zlib.lib.so sdl.lib.so avfilter.lib.so avutil.lib.so avcodec.lib.so avformat.lib.so swscale.lib.so mediafile @@ -85,6 +143,7 @@ set boot_modules { lappend_if [have_spec linux] boot_modules fb_sdl lappend_if [have_spec pci] boot_modules pci_drv +lappend_if [have_spec pci] boot_modules acpi_drv lappend_if [have_spec vesa] boot_modules vesa_drv lappend_if [have_spec ps2] boot_modules ps2_drv diff --git a/libports/src/lib/sdl/audio/SDL_genodeaudio.cc b/libports/src/lib/sdl/audio/SDL_genodeaudio.cc index 8b87d5ab2..fbbad5022 100644 --- a/libports/src/lib/sdl/audio/SDL_genodeaudio.cc +++ b/libports/src/lib/sdl/audio/SDL_genodeaudio.cc @@ -1,6 +1,7 @@ /* * \brief Genode-specific audio backend * \author Christian Prochaska + * \author Sebastian Sumpf * \date 2012-03-13 * * based on the dummy SDL audio driver @@ -16,14 +17,12 @@ #include #include #include -#include +#include #include + enum { - AUDIO_OUT_SAMPLE_SIZE = sizeof(float), - AUDIO_OUT_CHANNELS = 2, - AUDIO_OUT_FREQ = 44100, - AUDIO_OUT_SAMPLES = 1024, + AUDIO_CHANNELS = 2, }; using Genode::env; @@ -52,15 +51,11 @@ extern "C" { /* 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]; + Uint8 *mixbuf; + Uint32 mixlen; + Audio_out::Connection *audio[AUDIO_CHANNELS]; }; @@ -82,7 +77,10 @@ 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); + + Genode::config()->xml_node().sub_node("sdl_audio_volume") + .attribute("value").value(&config_volume); + volume = (float)config_volume / 100; } catch (Genode::Config::Invalid) { } @@ -108,10 +106,8 @@ static int GENODEAUD_Available(void) 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]); - } + for (int channel = 0; channel < AUDIO_CHANNELS; channel++) + destroy(env()->heap(), device->hidden->audio[channel]); SDL_free(device->hidden); SDL_free(device); @@ -148,23 +144,19 @@ static SDL_AudioDevice *GENODEAUD_CreateDevice(int devindex) _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()); + for (int channel = 0; channel < AUDIO_CHANNELS; channel++) { 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(); + _this->hidden->audio[channel] = new (env()->heap()) + Audio_out::Connection(channel_names[channel]); + _this->hidden->audio[channel]->start(); } 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]); - } + + while(--channel > 0) + destroy(env()->heap(), _this->hidden->audio[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)); @@ -183,27 +175,32 @@ AudioBootStrap GENODEAUD_bootstrap = { /* 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()); - } + for (int channel = 0; channel < AUDIO_CHANNELS; channel++) { + Audio_out::Connection *connection = _this->hidden->audio[channel]; + + /* wait until allocation is possible */ + if (connection->stream()->full()) + connection->wait_for_alloc(); + } } static void GENODEAUD_PlayAudio(_THIS) { - Packet_descriptor p[AUDIO_OUT_CHANNELS]; + Audio_out::Packet *p[AUDIO_CHANNELS]; + + for (int channel = 0; channel < AUDIO_CHANNELS; channel++) + while (1) { + Audio_out::Connection *connection = _this->hidden->audio[channel]; - 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); + p[channel] = connection->stream()->alloc(); break; - } catch (Stream::Packet_alloc_failed) { + } catch (Audio_out::Stream::Alloc_failed) { /* wait for next finished packet */ - _this->hidden->stream[channel]->release_packet(_this->hidden->stream[channel]->get_acked_packet()); + connection->wait_for_alloc(); } + } if (signal_receiver()->pending()) { signal_receiver()->wait_for_signal(); @@ -211,13 +208,13 @@ static void GENODEAUD_PlayAudio(_THIS) 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 sample = 0; sample < Audio_out::PERIOD; sample++) + for (int channel = 0; channel < AUDIO_CHANNELS; channel++) + p[channel]->content()[sample] = + volume * (float)(((int16_t*)_this->hidden->mixbuf)[sample * AUDIO_CHANNELS + channel]) / 32768; - for (int channel = 0; channel < AUDIO_OUT_CHANNELS; channel++) - _this->hidden->stream[channel]->submit_packet(p[channel]); + for (int channel = 0; channel < AUDIO_CHANNELS; channel++) + _this->hidden->audio[channel]->submit(p[channel]); } @@ -243,10 +240,10 @@ static int GENODEAUD_OpenAudio(_THIS, SDL_AudioSpec *spec) PDBG("requested samples = %u", spec->samples); PDBG("requested size = %u", spec->size); - spec->channels = AUDIO_OUT_CHANNELS; + spec->channels = AUDIO_CHANNELS; spec->format = AUDIO_S16LSB; - spec->freq = AUDIO_OUT_FREQ; - spec->samples = AUDIO_OUT_SAMPLES; + spec->freq = Audio_out::SAMPLE_RATE; + spec->samples = Audio_out::PERIOD; SDL_CalculateAudioSpec(spec); /* Allocate mixing buffer */ diff --git a/os/run/mixer.run b/os/run/mixer.run new file mode 100644 index 000000000..e25dee346 --- /dev/null +++ b/os/run/mixer.run @@ -0,0 +1,144 @@ +# +# Build +# + +# generic components +set build_components { + core init + drivers/timer + server/mixer + test/audio_out +} + +# platform-specific components +if {[have_spec linux]} { + lappend build_components drivers/audio_out +} else { + lappend build_components drivers/pci + lappend build_components drivers/acpi + lappend build_components drivers/oss +} + +build $build_components +create_boot_directory + + +# +# Config +# + +set config { + + + + + + + + + + + + + + + + + + +} + +if {![have_spec linux]} { + append config { + + + + + + + + + + + + + + + + + + + + + + + + } +} else { + append config { + + + + + + + } +} + +append config { + + + + + + + + + + + + + + + + + sample.raw + vogel.f32 + + + + + + + +} + +install_config $config + + +# +# Boot modules +# + +# generic modules +set boot_modules { + core init + timer + test-audio_out + sample.raw + vogel.f32 + mixer +} + +# platform-specific components +if {[have_spec linux]} { + lappend boot_modules audio_out_drv +} else { + lappend boot_modules oss_drv + lappend boot_modules acpi_drv + lappend boot_modules pci_drv +} + +build_boot_image $boot_modules +append qemu_args "-m 256 -soundhw es1370 -nographic" +run_genode_until forever diff --git a/os/src/drivers/audio_out/linux/alsa.c b/os/src/drivers/audio_out/linux/alsa.c index 6a7d705f0..993b24d89 100644 --- a/os/src/drivers/audio_out/linux/alsa.c +++ b/os/src/drivers/audio_out/linux/alsa.c @@ -39,13 +39,13 @@ int audio_drv_init(void) if ((err = snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) return -5; - if ((err = snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &rate, 0)) < 0) + if ((err = snd_pcm_hw_params_set_rate(playback_handle, hw_params, rate, 0)) < 0) return -6; if ((err = snd_pcm_hw_params_set_channels(playback_handle, hw_params, 2)) < 0) return -7; - if ((err = snd_pcm_hw_params_set_period_size(playback_handle, hw_params, 4096, 0)) < 0) + if ((err = snd_pcm_hw_params_set_period_size(playback_handle, hw_params, 2048, 0)) < 0) return -8; if ((err = snd_pcm_hw_params_set_periods(playback_handle, hw_params, 4, 0)) < 0) diff --git a/os/src/drivers/audio_out/linux/main.cc b/os/src/drivers/audio_out/linux/main.cc index 3132b0886..2d98162de 100644 --- a/os/src/drivers/audio_out/linux/main.cc +++ b/os/src/drivers/audio_out/linux/main.cc @@ -1,9 +1,12 @@ /* - * \brief Audio-out driver for Linux + * \brief Audio_out-out driver for Linux * \author Christian Helmuth + * \author Sebastian Sumpf * \date 2010-05-11 * * FIXME session and driver shutdown not implemented (audio_drv_stop) + * + * 2013-01-09: Adapted to news audio interface */ /* @@ -17,7 +20,7 @@ #include #include #include -#include +#include #include #include "alsa.h" @@ -26,222 +29,242 @@ using namespace Genode; static const bool verbose = false; -namespace Audio_out { +enum Channel_number { LEFT, RIGHT, MAX_CHANNELS, INVALID = MAX_CHANNELS }; - class Session_component; - - enum Channel_number { LEFT, RIGHT, MAX_CHANNELS, INVALID = MAX_CHANNELS }; +namespace Audio_out +{ + class Session_component; + class Out; + class Root; + struct Root_policy; static Session_component *channel_acquired[MAX_CHANNELS]; - static Semaphore channel_sema; +}; - class Session_component : public Session_rpc_object - { - private: +class Audio_out::Session_component : public Audio_out::Session_rpc_object +{ + private: - Ram_dataspace_capability _ds; - Channel_number _channel; + Channel_number _channel; - Ram_dataspace_capability _alloc_dataspace(size_t size) - { - _ds = env()->ram_session()->alloc(size); - return _ds; - } + public: - public: - - Session_component(Channel_number channel, size_t buffer_size, - Rpc_entrypoint &ep) - : - Session_rpc_object(_alloc_dataspace(buffer_size), ep), - _channel(channel) - { - Audio_out::channel_acquired[_channel] = this; - channel_sema.up(); - } - - ~Session_component() - { - channel_sema.down(); - Audio_out::channel_acquired[_channel] = 0; - - env()->ram_session()->free(_ds); - } - - - /********************************* - ** Audio-out session interface ** - *********************************/ - - void flush() - { - while (channel()->packet_avail()) - channel()->acknowledge_packet(channel()->get_packet()); - } - - void sync_session(Session_capability audio_out_session) { } - }; - - static bool channel_number_from_string(const char *name, - Channel_number *out_number) - { - static struct Names { - const char *name; - Channel_number number; - } names[] = { - { "left", LEFT }, { "front left", LEFT }, - { "right", RIGHT }, { "front right", RIGHT }, - { 0, INVALID } - }; - - for (Names *n = names; n->name; ++n) - if (!strcmp(name, n->name)) { - *out_number = n->number; - return true; - } - - return false; - } - - /** - * Session creation policy for our service - */ - struct Root_policy - { - void aquire(const char *args) + Session_component(Channel_number channel, Signal_context_capability data_cap) + : + Session_rpc_object(data_cap), + _channel(channel) { - size_t ram_quota = - Arg_string::find_arg(args, "ram_quota" ).ulong_value(0); - size_t buffer_size = - Arg_string::find_arg(args, "buffer_size").ulong_value(0); - size_t session_size = - align_addr(sizeof(Session_component), 12); - - if ((ram_quota < session_size) || - (buffer_size > ram_quota - session_size)) { - PERR("insufficient 'ram_quota', got %zd, need %zd", - ram_quota, buffer_size + session_size); - throw Root::Quota_exceeded(); - } - - char channel_name[16]; - Channel_number channel_number; - Arg_string::find_arg(args, "channel").string(channel_name, - sizeof(channel_name), - "left"); - if (!channel_number_from_string(channel_name, &channel_number)) - throw Root::Invalid_args(); - if (channel_acquired[channel_number]) - throw Root::Unavailable(); + Audio_out::channel_acquired[_channel] = this; } - void release() { } + ~Session_component() + { + Audio_out::channel_acquired[_channel] = 0; + } +}; + + +static bool channel_number_from_string(const char *name, + Channel_number *out_number) +{ + static struct Names { + const char *name; + Channel_number number; + } names[] = { + { "left", LEFT }, { "front left", LEFT }, + { "right", RIGHT }, { "front right", RIGHT }, + { 0, INVALID } }; - typedef Root_component Root_component; + for (Names *n = names; n->name; ++n) + if (!strcmp(name, n->name)) { + *out_number = n->number; + return true; + } - /* - * Root component, handling new session requests. - */ - class Root : public Root_component, - private Thread<0x2000> - { - private: - - Semaphore _startup_sema; /* thread startup sync */ - Rpc_entrypoint &_channel_ep; - - bool _packets_available(Session_component *channel) - { - if (channel && channel->channel()->packet_avail()) - return true; - else - return false; - } - - void entry() - { - audio_drv_adopt_myself(); - - /* indicate thread-startup completion */ - _startup_sema.up(); - - /* handle audio-out packets */ - while (true) { - for (int i = 0; i < MAX_CHANNELS; ++i) - channel_sema.down(); - - Session_component *left = channel_acquired[LEFT], - *right = channel_acquired[RIGHT]; - - /* get packets for channels */ - Packet_descriptor p[MAX_CHANNELS]; - static short data[2 * PERIOD]; - - p[LEFT] = left->channel()->get_packet(); - for (int i = 0; i < 2 * PERIOD; i += 2) - data[i] = left->channel()->packet_content(p[LEFT])[i / 2] * 32767; - p[RIGHT] = right->channel()->get_packet(); - for (int i = 1; i < 2 * PERIOD; i += 2) - data[i] = right->channel()->packet_content(p[RIGHT])[i / 2] * 32767; - - if (verbose) - PDBG("play packet"); - - /* blocking-write packet to ALSA */ - while (int err = audio_drv_play(data, PERIOD)) { - if (verbose) PERR("Error %d during playback", err); - audio_drv_stop(); - audio_drv_start(); - } - - /* acknowledge packet to the client */ - if (p[LEFT].valid()) - left->channel()->acknowledge_packet(p[LEFT]); - if (p[RIGHT].valid()) - right->channel()->acknowledge_packet(p[RIGHT]); - - for (int i = 0; i < MAX_CHANNELS; ++i) - channel_sema.up(); - } - - } - - protected: - - Session_component *_create_session(const char *args) - { - size_t buffer_size = - Arg_string::find_arg(args, "buffer_size").ulong_value(0); - - char channel_name[16]; - Channel_number channel_number = INVALID; - Arg_string::find_arg(args, "channel").string(channel_name, - sizeof(channel_name), - "left"); - channel_number_from_string(channel_name, &channel_number); - - return new (md_alloc()) - Session_component(channel_number, buffer_size, _channel_ep); - } - - public: - - Root(Rpc_entrypoint *session_ep, Allocator *md_alloc) - : Root_component(session_ep, md_alloc), _channel_ep(*session_ep) - { - /* synchronize with root thread startup */ - start(); - _startup_sema.down(); - } - }; + return false; } + /* - * Manually initialize the 'lx_environ' pointer, needed because 'nic_drv' is not - * using the normal Genode startup code. + * Root component, handling new session requests. + */ +class Audio_out::Out : public Thread<1024 * sizeof(addr_t)> +{ + private: + + Signal_receiver *_data_recv; /* data is available */ + + bool _active() { + return channel_acquired[LEFT] && channel_acquired[RIGHT] && + channel_acquired[LEFT]->active() && channel_acquired[RIGHT]->active(); + } + + Stream *left() { return channel_acquired[LEFT]->stream(); } + Stream *right() { return channel_acquired[RIGHT]->stream(); } + + void _advance_position(Packet *l, Packet *r) + { + bool full_left = left()->full(); + bool full_right = right()->full(); + + left()->pos(left()->packet_position(l)); + right()->pos(right()->packet_position(l)); + + left()->increment_position(); + right()->increment_position(); + + Session_component *channel_left = channel_acquired[LEFT]; + Session_component *channel_right = channel_acquired[RIGHT]; + + if (full_left) + channel_left->alloc_submit(); + + if (full_right) + channel_right->alloc_submit(); + + channel_left->progress_submit(); + channel_right->progress_submit(); + } + + bool _play_packet() + { + Packet *p_left = left()->get(left()->pos()); + Packet *p_right = right()->get(left()->pos()); + + bool found = false; + for (int i = 0; i < QUEUE_SIZE; i++) { + if (p_left->valid() && p_right->valid()) { + found = true; + break; + } + + p_left = left()->next(p_left); + p_right = right()->next(p_right); + } + + if (!found) + return false; + + /* convert float to S16LE */ + static short data[2 * PERIOD]; + + for (int i = 0; i < 2 * PERIOD; i += 2) { + data[i] = p_left->content()[i / 2] * 32767; + data[i + 1] = p_right->content()[i / 2] * 32767; + } + + p_left->invalidate(); + p_right->invalidate(); + + if (verbose) + PDBG("play packet"); + + /* blocking-write packet to ALSA */ + while (int err = audio_drv_play(data, PERIOD)) { + if (verbose) PERR("Error %d during playback", err); + audio_drv_stop(); + audio_drv_start(); + } + + p_left->mark_as_played(); + p_right->mark_as_played(); + + _advance_position(p_left, p_right); + + return true; + } + + public: + + Out(Signal_receiver *data_recv) + : Thread("audio_out"), _data_recv(data_recv) { } + + void entry() + { + audio_drv_adopt_myself(); + + /* handle audio out */ + while (true) + if (!_active() || !_play_packet()) + _data_recv->wait_for_signal(); + } +}; + + +/** + * Session creation policy for our service + */ +struct Audio_out::Root_policy +{ + void aquire(const char *args) + { + size_t ram_quota = + Arg_string::find_arg(args, "ram_quota" ).ulong_value(0); + size_t session_size = + align_addr(sizeof(Audio_out::Session_component), 12); + + if ((ram_quota < session_size) || + (sizeof(Stream) > ram_quota - session_size)) { + PERR("insufficient 'ram_quota', got %zd, need %zd", + ram_quota, sizeof(Stream) + session_size); + throw ::Root::Quota_exceeded(); + } + + char channel_name[16]; + Channel_number channel_number; + Arg_string::find_arg(args, "channel").string(channel_name, + sizeof(channel_name), + "left"); + if (!channel_number_from_string(channel_name, &channel_number)) + throw ::Root::Invalid_args(); + if (Audio_out::channel_acquired[channel_number]) + throw ::Root::Unavailable(); + } + + void release() { } +}; + + +namespace Audio_out { + typedef Root_component Root_component; +} + + +class Audio_out::Root : public Audio_out::Root_component +{ + private: + + Signal_context_capability _data_cap; + + protected: + + Session_component *_create_session(const char *args) + { + char channel_name[16]; + Channel_number channel_number = INVALID; + Arg_string::find_arg(args, "channel").string(channel_name, + sizeof(channel_name), + "left"); + channel_number_from_string(channel_name, &channel_number); + + return new (md_alloc()) + Session_component(channel_number, _data_cap); + } + + public: + + Root(Rpc_entrypoint *session_ep, Allocator *md_alloc, Signal_context_capability data_cap) + : Root_component(session_ep, md_alloc), _data_cap(data_cap) + { } +}; + + +/* + * Manually initialize the 'lx_environ' pointer, needed because 'nic_drv' is + * not using the normal Genode startup code. */ extern char **environ; char **lx_environ = environ; @@ -249,9 +272,10 @@ char **lx_environ = environ; int main(int argc, char **argv) { - enum { STACK_SIZE = 4096 }; - static Cap_connection cap; - static Rpc_entrypoint ep(&cap, STACK_SIZE, "audio_ep"); + /* setup data available signal */ + static Signal_receiver data_recv; + static Signal_context data_context; + static Signal_context_capability data_cap(data_recv.manage(&data_context)); /* init ALSA */ int err = audio_drv_init(); @@ -261,9 +285,18 @@ int main(int argc, char **argv) } audio_drv_start(); + /* start output thread */ + Audio_out::Out *out = new(env()->heap()) Audio_out::Out(&data_recv); + out->start(); + + enum { STACK_SIZE = 1024 * sizeof(addr_t) }; + static Cap_connection cap; + static Rpc_entrypoint ep(&cap, STACK_SIZE, "audio_ep"); + /* setup service */ - static Audio_out::Root audio_root(&ep, env()->heap()); + static Audio_out::Root audio_root(&ep, env()->heap(), data_cap); env()->parent()->announce(ep.manage(&audio_root)); + sleep_forever(); return 0; } diff --git a/os/src/server/mixer/mixer.cc b/os/src/server/mixer/mixer.cc index d5348b073..9df8df353 100644 --- a/os/src/server/mixer/mixer.cc +++ b/os/src/server/mixer/mixer.cc @@ -1,14 +1,11 @@ /* - * \brief Audio Mixer + * \brief Audio_out Mixer * \author Sebastian Sumpf - * \author Christian Helmuth - * \author Stefan Kalkowski - * \date 2009-12-02 + * \date 2012-12-20 * - * The mixer supports up to MAX_TRACKS (virtual) two-channel stereo input - * sessions and, therefore, provides audio-out sessions for "front left" and - * "front right". The mixer itself uses two audio-out sessions - front left and - * right. + * The mixer impelements the audio session on the server side. For each channel + * (currently 'left' and 'right' only) it supports multiple client sessions and + * mixes its input into a single client audio session. */ /* @@ -18,675 +15,401 @@ * under the terms of the GNU General Public License version 2. */ +#include #include #include -#include -#include -#include -#include +#include +#include #include -#include -#include +#include +#include using namespace Genode; -static const bool verbose = false; -static bool audio_out_active = false; -static Genode::Lock session_lock; -static Rpc_entrypoint *audio_out_ep() +static bool verbose = false; + +enum Channel_number { LEFT, RIGHT, MAX_CHANNELS }; + + +namespace Audio_out { - static Cap_connection cap; - - enum { STACK_SIZE = 4096 }; - static Rpc_entrypoint _audio_out_ep(&cap, STACK_SIZE, "audio_ep"); - - return &_audio_out_ep; + class Session_elem; + class Session_component; + class Root; + class Channel; + class Mixer; } +static Audio_out::Channel *_channels[MAX_CHANNELS]; + + +static int channel_number_from_string(const char *name) +{ + static struct Names { + const char *name; + Channel_number number; + } names[] = { + { "left", LEFT }, { "front left", LEFT }, + { "right", RIGHT }, { "front right", RIGHT }, + { 0, MAX_CHANNELS } + }; + + for (Names *n = names; n->name; ++n) + if (!strcmp(name, n->name)) + return n->number; + + return -1; +} + + +/** + * Makes audio a list element + */ +struct Audio_out::Session_elem : Audio_out::Session_rpc_object, + List::Element +{ + Session_elem(Signal_context_capability data_cap) + : Session_rpc_object(data_cap) { } +}; + + +/** + * One channel containing multiple sessions + */ +class Audio_out::Channel +{ + private: + + List _list; + + public: + + void insert(Session_elem *session) { + _list.insert(session); } + + Session_elem *first() { + return _list.first(); + } +}; + + +/** + * The mixer + */ +class Audio_out::Mixer : public Thread<1024 * sizeof(addr_t)> +{ + private: + + Lock _sleep_lock; + + Connection _left; /* left output */ + Connection _right; /* right output */ + Connection *_out[MAX_CHANNELS]; + + Signal_receiver *_data_recv; /* data availble signal receiver + (send from clients) */ + + void _sleep() { _sleep_lock.lock(); } + + Mixer() + : + Thread("mixer"), + _sleep_lock(Lock::LOCKED), _left("left", false, true), + _right("right", false, true) + { + _out[LEFT] = &_left; + _out[RIGHT] = &_right; + start(); + } + + /* check for active sessions */ + bool _check_active() + { + bool active = false; + for (int i = 0; i < MAX_CHANNELS; i++) { + Session_elem *session = _channels[i]->first(); + while (session) { + active |= session->active(); + session = session->next(); + } + } + return active; + } + + void _advance_session(Session_elem *session, unsigned pos) + { + if (session->stopped()) + return; + + Stream *stream = session->stream(); + bool full = stream->full(); + + /* mark packets as played and icrement position pointer */ + while (stream->pos() != pos) { + stream->get(stream->pos())->mark_as_played(); + stream->increment_position(); + } + + /* send 'progress' sginal */ + session->progress_submit(); + + /* send 'alloc' signal */ + if (full) + session->alloc_submit(); + } + + /* advance 'positions' of all client sessions */ + void _advance_position() + { + for (int i = 0; i < MAX_CHANNELS; i++) { + Session_elem *session = _channels[i]->first(); + unsigned pos = _out[i]->stream()->pos(); + while (session) { + _advance_session(session, pos); + session = session->next(); + } + } + } + + /* mix one packet */ + void _mix_packet(Packet *out, Packet *in, bool clear) + { + /* when clear is set, set input an zero out remainder */ + if (clear) { + out->content(in->content(), PERIOD); + } else { + /* mix */ + for (int i = 0; i < PERIOD; i++) { + out->content()[i] += in->content()[i]; + + if (out->content()[i] > 1) out->content()[i] = 1; + if (out->content()[i] < -1) out->content()[i] = -1; + } + } + /* mark packet as processed */ + in->invalidate(); + } + + /* get packet at offset */ + Packet *session_packet(Session *session, unsigned offset) + { + Stream *s = session->stream(); + return s->get(s->pos() + offset); + } + + /* mix all session of one channel */ + bool _mix_channel(Channel_number nr, unsigned out_pos, unsigned offset) + { + Stream *out_stream = _out[nr]->stream(); + Packet *out = out_stream->get(out_pos + offset); + + bool clear = true; + bool mix_all = false; + bool out_valid = out->valid(); + + for (Session_elem *session = _channels[nr]->first(); + session; + session = session->next()) { + + if (session->stopped()) + continue; + + Packet *in = session_packet(session, offset); + + /* + * When there already is an out packet, start over and mix + * everything. + */ + if (in->valid() && out_valid && !mix_all) { + clear = true; + mix_all = true; + session = _channels[nr]->first(); + in = session_packet(session, offset); + } + + /* skip if packet has been processed or was already played */ + if ((!in->valid() && !mix_all) || in->played()) + continue; + + _mix_packet(out, in, clear); + + if (verbose) + PDBG("mix: ch %u in %u -> out %u all %d o: %u", + nr, session->stream()->packet_position(in), + out_stream->packet_position(out), mix_all, offset); + + clear = false; + } + + return !clear; + } + + void _mix() + { + unsigned pos[MAX_CHANNELS]; + pos[LEFT] = _out[LEFT]->stream()->pos(); + pos[RIGHT] = _out[RIGHT]->stream()->pos(); + + /* + * Look for packets that are valid, mix channels in an alternating + * way. + */ + for (int i = 0; i < QUEUE_SIZE; i++) { + bool mix_one = true; + for (int j = LEFT; j < MAX_CHANNELS; j++) + mix_one &= _mix_channel((Channel_number)j, pos[j], i); + + if (mix_one) { + _out[LEFT]->submit(_out[LEFT]->stream()->get(pos[LEFT] + i)); + _out[RIGHT]->submit(_out[RIGHT]->stream()->get(pos[RIGHT] + i)); + } + } + } + + #define FOREACH_CHANNEL(func) ({ \ + for (int i = 0; i < MAX_CHANNELS; i++) \ + _out[i]->func(); \ + }) + void _wait_for_progress() { FOREACH_CHANNEL(wait_for_progress); } + void _start() { FOREACH_CHANNEL(start); } + void _stop() { FOREACH_CHANNEL(stop); } + + public: + + void entry() + { + _start(); + + while (true) { + + if (!_check_active()) { + _stop(); + _sleep(); + _start(); + continue; + } + + _mix(); + + /* advance position of clients */ + _advance_position(); + + if (!_left.stream()->empty()) + _wait_for_progress(); + else + _data_recv->wait_for_signal(); + } + } + + void wakeup() { _sleep_lock.unlock(); } + + /* sync client position with output position */ + void sync_pos(Channel_number channel, Stream *stream) { + stream->pos(_out[channel]->stream()->pos()); } + + void data_recv(Signal_receiver *recv) { _data_recv = recv; } + + static Mixer *m() + { + static Mixer _m; + return &_m; + } +}; + + +class Audio_out::Session_component : public Audio_out::Session_elem +{ + private: + + Channel_number _channel; + + public: + + Session_component(Channel_number channel, + Signal_context_capability data_cap) + : Session_elem(data_cap), _channel(channel) { } + + void start() + { + Session_rpc_object::start(); + /* sync audio position with mixer */ + Mixer::m()->sync_pos(_channel, stream()); + Mixer::m()->wakeup(); + } +}; + + namespace Audio_out { - - enum { OUT_QUEUE_SIZE = 1 }; - - template - class Ring - { - private: - - LT *_next; - LT *_prev; - - public: - - Ring() - : _next(static_cast(this)), - _prev(static_cast(this)) { } - - LT *next() { return _next; }; - LT *prev() { return _prev; }; - - /** - * Conflate with another ring. - */ - bool conflate(LT *le) - { - /* test whether the given ring is part of this one */ - LT *e = static_cast(this); - while (e->Ring::_next != this) { - if (e->Ring::_next == le) - return false; - e = e->Ring::_next; - } - - /* wire this->next with le->prev */ - _next->Ring::_prev = le->Ring::_prev; - le->Ring::_prev->Ring::_next = _next; - _next = le; - le->Ring::_prev = static_cast(this); - return true; - } - - /** - * Remove this object from the ring. - */ - void remove() - { - _prev->Ring::_next = _next; - _next->Ring::_prev = _prev; - _next = static_cast(this); - _prev = static_cast(this); - } - }; - - enum Channel_number { LEFT, RIGHT, MAX_CHANNELS, INVALID = MAX_CHANNELS }; - - static bool channel_number_from_string(const char *name, - Channel_number *out_number) - { - static struct Names { - const char *name; - Channel_number number; - } names[] = { - { "left", LEFT }, { "front left", LEFT }, - { "right", RIGHT }, { "front right", RIGHT }, - { 0, INVALID } - }; - - for (Names *n = names; n->name; ++n) - if (!strcmp(name, n->name)) { - *out_number = n->number; - return true; - } - - return false; - } - - static const char * channel_string_from_number(Channel_number number) - { - static const char *names[MAX_CHANNELS + 1] = { - "front left", "front right", "invalid" - }; - - return names[number]; - } - - static Semaphore open_tracks; - static int num_open_tracks[MAX_CHANNELS]; - - /* - * The mixer uses only one signal receiver and context for all input - * tracks. - */ - static Signal_receiver avail_recv; - static Signal_context avail_ctx; - static Signal_context_capability avail_cap(avail_recv.manage(&avail_ctx)); - - enum { MAX_TRACKS = 16 }; - - - class Communication_buffer : Ram_dataspace_capability - { - public: - - Communication_buffer(size_t size) - : Ram_dataspace_capability(env()->ram_session()->alloc(size)) - { } - - ~Communication_buffer() { env()->ram_session()->free(*this); } - - Dataspace_capability dataspace() { return *this; } - }; - - - class Session_component : public List::Element, - public Ring, - private Communication_buffer, - public Session_rpc_object - { - private: - - Channel_number _channel; - - public: - - Session_component(Channel_number channel, size_t buffer_size, - Rpc_entrypoint &ep) - : - Communication_buffer(buffer_size), - Session_rpc_object(Communication_buffer::dataspace(), ep), - _channel(channel) - { - if (verbose) PDBG("new session %p", this); - - track_list()->insert(this); - num_open_tracks[_channel]++; - - open_tracks.up(); - } - - ~Session_component() - { - Genode::Lock::Guard lock_guard(session_lock); - open_tracks.down(); - - num_open_tracks[_channel]--; - track_list()->remove(this); - Ring::remove(); - - if (verbose) PDBG("session %p closed", this); - } - - static List *track_list() - { - static List _track_list; - return &_track_list; - } - - /* - * We only need one central signal context within the mixer. - */ - Signal_context_capability _sigh_packet_avail() - { - return avail_cap; - } - - Channel_number channel_number() const { return _channel; } - - bool all_channel_packet_avail() { - if (!channel()->packet_avail()) - return false; - - Session_component *s = this; - while (s->Ring::next() != this) { - s = s->Ring::next(); - if (!s->channel()->packet_avail()) - return false; - } - return true; - } - - void flush() - { - while (channel()->packet_avail()) - channel()->acknowledge_packet(channel()->get_packet()); - } - - void sync_session(Session_capability audio_out_session) - { - Object_pool::Guard - sc(audio_out_ep()->lookup_and_lock(audio_out_session)); - - /* check if recipient is a valid session component */ - if (!sc) return; - - if (this->conflate(sc)) - track_list()->remove(this); - } - }; - - /** - * Session creation policy for our service - */ - struct Root_policy - { - void aquire(const char *args) - { - size_t ram_quota = - Arg_string::find_arg(args, "ram_quota" ).ulong_value(0); - size_t buffer_size = - Arg_string::find_arg(args, "buffer_size").ulong_value(0); - size_t session_size = - align_addr(sizeof(Session_component), 12); - - if ((ram_quota < session_size) || - (buffer_size > ram_quota - session_size)) { - PERR("insufficient 'ram_quota', got %zd, need %zd", - ram_quota, buffer_size + session_size); - throw Root::Quota_exceeded(); - } - - char channel_name[16]; - Channel_number channel_number; - Arg_string::find_arg(args, "channel").string(channel_name, - sizeof(channel_name), - "left"); - if (!channel_number_from_string(channel_name, &channel_number)) - throw Root::Invalid_args(); - if (!(num_open_tracks[channel_number] < MAX_TRACKS)) { - PERR("throw"); - throw Root::Unavailable(); - } - } - - void release() { } - }; - - typedef Root_component Root_component; - - class Root : public Root_component - { - private: - - Rpc_entrypoint &_channel_ep; - - protected: - - Session_component *_create_session(const char *args) - { - size_t buffer_size = - Arg_string::find_arg(args, "buffer_size").ulong_value(0); - - char channel_name[16]; - Channel_number channel_number = INVALID; - Arg_string::find_arg(args, "channel").string(channel_name, - sizeof(channel_name), - "left"); - channel_number_from_string(channel_name, &channel_number); - - Session_component *session = new (md_alloc()) - Session_component(channel_number, buffer_size, - _channel_ep); - - PDBG("Added new \"%s\" channel %d/%d", - channel_name, num_open_tracks[channel_number], MAX_TRACKS); - - return session; - } - - public: - - Root(Rpc_entrypoint *session_ep, Allocator *md_alloc) - : Root_component(session_ep, md_alloc), _channel_ep(*session_ep) { } - }; - - class Packet_cache - { - private: - - class Track_packet; - class Channel_packet; - - - typedef Tslab Track_packet_slab; - typedef Tslab Channel_packet_slab; - - Track_packet_slab _track_packet_slab; - Channel_packet_slab _channel_packet_slab; - - Genode::Lock _lock; - - - class Track_packet : public List::Element - { - private: - - Packet_descriptor _packet; - Session::Channel::Sink *_sink; - - public: - - Track_packet(Packet_descriptor p, - Session::Channel::Sink *sink) - : _packet(p), _sink(sink) {} - - - bool session_active() - { - for (Session_component *session = Session_component::track_list()->first(); - session; - session = session->List::Element::next()) - - for (Session_component *sc = session; - true; - sc = sc->Ring::next()) { - if (sc->channel() == _sink) - return true; - - if (sc->Ring::next() == session) - break; - } - - return false; - } - - void acknowledge() { - if (session_active()) - _sink->acknowledge_packet(_packet); } - }; - - - class Channel_packet : public List::Element - { - private: - - List _track_packets; - Packet_descriptor _packet; - Track_packet_slab *_slab; - - public: - - Channel_packet(Packet_descriptor p, Track_packet_slab *slab) - : _packet(p), _slab(slab) { } - - void add(Track_packet *p) { _track_packets.insert(p); } - - void acknowledge() - { - Track_packet *p = _track_packets.first(); - while (p) { - p->acknowledge(); - _track_packets.remove(p); - _slab->free(p); - p = _track_packets.first(); - } - } - - bool packet(Packet_descriptor p) - { - return p.size() == _packet.size() && - p.offset() == _packet.offset(); - } - }; - - - List _channel_packets[MAX_CHANNELS]; - Connection *_out_stream[MAX_CHANNELS]; - - public: - - Packet_cache(Connection *output_stream[MAX_CHANNELS]) - : _track_packet_slab(Genode::env()->heap()), - _channel_packet_slab(Genode::env()->heap()) - { - for (int chn = 0; chn < MAX_CHANNELS; ++chn) - _out_stream[chn] = output_stream[chn]; - } - - void ack_packets() - { - for (int chn = 0; chn < MAX_CHANNELS; ++chn) { - Session::Channel::Source *stream = _out_stream[chn]->stream(); - - Packet_descriptor p = stream->get_acked_packet(); - - if (verbose) PDBG("ack channel %d", chn); - - Genode::Lock::Guard lock_guard(_lock); - Channel_packet *ch_packet = _channel_packets[chn].first(); - while (ch_packet) { - if (ch_packet->packet(p)) { - ch_packet->acknowledge(); - _channel_packets[chn].remove(ch_packet); - _channel_packet_slab.free(ch_packet); - break; - } - ch_packet = ch_packet->next(); - } - stream->release_packet(p); - } - } - - void put(Packet_descriptor p, Session::Channel::Sink **sinks, - Packet_descriptor *cli_p, int count, int chn) - { - Genode::Lock::Guard lock_guard(_lock); - - Channel_packet *ch_packet = new (&_channel_packet_slab) - Channel_packet(p, &_track_packet_slab); - for (int i = 0; i < count; ++i) { - Track_packet *t_packet = new (&_track_packet_slab) - Track_packet(cli_p[i], sinks[i]); - ch_packet->add(t_packet); - } - _channel_packets[chn].insert(ch_packet); - } - }; - - class Mixer : private Genode::Thread<4096> - { - private: - - struct Mixer_packets - { - Session::Channel::Sink *sink[MAX_TRACKS]; - Packet_descriptor packet[MAX_TRACKS]; - int count; - } _packets[MAX_CHANNELS]; - - Connection *_out_stream[MAX_CHANNELS]; - Semaphore _packet_sema; /* packets available */ - Packet_cache *_cache; - Semaphore _startup_sema; /* thread startup sync */ - - class Receiver : private Genode::Thread<4096> - { - private: - - Packet_cache *_cache; - Semaphore _startup_sema; - Semaphore *_packet_sema; - - void entry() - { - /* indicate thread-startup completion */ - _startup_sema.up(); - while (true) { - _cache->ack_packets(); - _packet_sema->up(); - } - } - - public: - - Receiver(Packet_cache *cache, Semaphore *packet_sema) - : Genode::Thread<4096>("ack"), _cache(cache), - _packet_sema(packet_sema) - { - if (audio_out_active) { - start(); - _startup_sema.down(); - } - } - }; - - Receiver _receiver; - - bool _get_packets() - { - bool packet_avail = false; - - for (int i = 0; i < MAX_CHANNELS; ++i) - _packets[i].count = 0; - - for (Session_component *session = Session_component::track_list()->first(); - session; - session = session->List::Element::next()) { - - if (!session->all_channel_packet_avail()) - continue; - - for (Session_component *sc = session; - true; - sc = sc->Ring::next()) { - Channel_number chn = sc->channel_number(); - int cnt = _packets[chn].count; - - _packets[chn].packet[cnt] = sc->channel()->get_packet(); - _packets[chn].sink[cnt] = sc->channel(); - - _packets[chn].count++; - packet_avail = true; - - if (sc->Ring::next() == session) - break; - } - } - - return packet_avail; - } - - Packet_descriptor _mix_one_channel(Mixer_packets *packets, - Session::Channel::Source *stream) - { - static int alloc_cnt, alloc_fail_cnt; - Packet_descriptor p; - - while (1) - try { - p = stream->alloc_packet(FRAME_SIZE * PERIOD); - if (verbose) - alloc_cnt++; - break; - } catch (Session::Channel::Source::Packet_alloc_failed) { - PERR("Packet allocation failed %d %d", - ++alloc_fail_cnt, alloc_cnt); - } - - float *out = stream->packet_content(p); - for (int size = 0; size < PERIOD; ++size) { - float sample = 0; - for (int i = 0; i < packets->count; ++i) { - sample += packets->sink[i]->packet_content(packets->packet[i])[size]; - } - - if (sample > 1) sample = 1; - if (sample < -1) sample = -1; - - out[size] = sample; - } - - return p; - } - - void _mix(Packet_descriptor p[MAX_CHANNELS]) - { - /* block if no packets are available */ - session_lock.lock(); - while (!_get_packets()) { - session_lock.unlock(); - avail_recv.wait_for_signal(); - session_lock.lock(); - } - _packet_sema.down(); - - /* mix packets */ - for (int chn = 0; chn < MAX_CHANNELS; ++chn) - p[chn] = _mix_one_channel(&_packets[chn], - _out_stream[chn]->stream()); - session_lock.unlock(); - - /* put packets into packet cache */ - for (int chn = 0; chn < MAX_CHANNELS; ++chn) - _cache->put(p[chn], _packets[chn].sink, _packets[chn].packet, - _packets[chn].count, chn); - } - - void entry() - { - /* indicate thread-startup completion */ - _startup_sema.up(); - - /* just acknowledge packets if we don't have an audio out stream */ - while (!audio_out_active) { - - while (!_get_packets()) - avail_recv.wait_for_signal(); - - for (int chn = 0; chn < MAX_CHANNELS; ++chn) - for (int i = 0; i < _packets[chn].count; i++) - _packets[chn].sink[i]->acknowledge_packet(_packets[chn].packet[i]); - } - - while (1) { - open_tracks.down(); - - /* check and mix sources */ - Packet_descriptor p[MAX_CHANNELS]; - _mix(p); - - /* submit to out */ - for (int chn = 0; chn < MAX_CHANNELS; ++chn) { - Session::Channel::Source *stream = _out_stream[chn]->stream(); - - stream->submit_packet(p[chn]); - } - - if (verbose) - PDBG("packet submitted"); - - open_tracks.up(); - } - } - - public: - - Mixer(Connection *output_stream[MAX_CHANNELS], Packet_cache *cache) - : Genode::Thread<4096>("tx"), _packet_sema(OUT_QUEUE_SIZE), - _cache(cache), _receiver(cache, &_packet_sema) - { - for (int chn = 0; chn < MAX_CHANNELS; ++chn) - _out_stream[chn] = output_stream[chn]; - - /* synchronize with mixer thread startup */ - start(); - _startup_sema.down(); - } - }; + typedef Root_component Root_component; } -int main(int argc, char **argv) +class Audio_out::Root : public Audio_out::Root_component { - PDBG("-- Genode Audio Mixer --"); + Signal_context_capability _data_cap; - using namespace Audio_out; + Session_component *_create_session(const char *args) + { + char channel_name[16]; + Arg_string::find_arg(args, "channel").string(channel_name, + sizeof(channel_name), + "left"); + size_t ram_quota = + Arg_string::find_arg(args, "ram_quota" ).ulong_value(0); - /* setup audio-out connections for output */ - static Audio_out::Connection *output_stream[MAX_CHANNELS]; + size_t session_size = align_addr(sizeof(Session_component), 12); - try { - for (int i = 0; i < MAX_CHANNELS; ++i) { - Allocator_avl *block_alloc = new (env()->heap()) Allocator_avl(env()->heap()); - output_stream[i] = new (env()->heap()) - Audio_out::Connection(channel_string_from_number((Channel_number)i), block_alloc, OUT_QUEUE_SIZE * FRAME_SIZE * PERIOD + 0x400); + if ((ram_quota < session_size) || + (sizeof(Stream) > ram_quota - session_size)) { + PERR("insufficient 'ram_quota', got %zd, need %zd", + ram_quota, sizeof(Stream) + session_size); + throw Root::Quota_exceeded(); } - audio_out_active = true; - } catch (...) { - PWRN("no audio driver found - dropping incoming packets"); + + int ch = channel_number_from_string(channel_name); + if (ch < 0) + throw Root::Invalid_args(); + + Session_component *session = new (md_alloc()) + Session_component((Channel_number)ch, _data_cap); + + PDBG("Added new \"%s\" nr: %u s: %p",channel_name, ch, session); + _channels[ch]->insert(session); + return session; } - /* initialize packet cache */ - static Packet_cache cache(output_stream); + public: - /* setup service */ - static Audio_out::Root mixer_root(audio_out_ep(), env()->heap()); - env()->parent()->announce(audio_out_ep()->manage(&mixer_root)); + Root(Rpc_entrypoint *session_ep, + Signal_context_capability data_cap, + Allocator *md_alloc) + : Root_component(session_ep, md_alloc), _data_cap(data_cap) { } +}; - /* start mixer */ - static Mixer mixer(output_stream, &cache); + +int main() +{ + static Cap_connection cap; + enum { STACK_SIZE = 1024 * sizeof(addr_t) }; + + for (int i = 0; i < MAX_CHANNELS; i++) + _channels[i] = new (env()->heap()) Audio_out::Channel(); + + static Signal_receiver data_recv; + static Signal_context data_context; + static Signal_context_capability data_cap(data_recv.manage(&data_context)); + + /* start mixer thread */ + Audio_out::Mixer::m()->data_recv(&data_recv); + + static Rpc_entrypoint ep(&cap, STACK_SIZE, "audio_ep"); + static Audio_out::Root root(&ep, data_cap, env()->heap()); + env()->parent()->announce(ep.manage(&root)); sleep_forever(); return 0; } + diff --git a/os/src/test/audio_out/main.cc b/os/src/test/audio_out/main.cc index c8329bb9d..090081825 100644 --- a/os/src/test/audio_out/main.cc +++ b/os/src/test/audio_out/main.cc @@ -15,13 +15,17 @@ * under the terms of the GNU General Public License version 2. */ -#include +#include #include #include -#include -#include +#include +#include #include + +using namespace Genode; + + using namespace Genode; using namespace Audio_out; @@ -29,11 +33,12 @@ static const bool verbose = false; enum { CHN_CNT = 2, /* number of channels */ + FRAME_SIZE = sizeof(float), PERIOD_CSIZE = FRAME_SIZE * PERIOD, /* size of channel packet (bytes) */ PERIOD_FSIZE = CHN_CNT * PERIOD_CSIZE, /* size of period in file (bytes) */ - }; + static const char *channel_names[] = { "front left", "front right" }; @@ -41,22 +46,18 @@ class Track : Thread<8192> { private: - typedef Audio_out::Session::Channel::Source Stream; - const char *_file; Audio_out::Connection *_audio_out[CHN_CNT]; - Stream *_stream[CHN_CNT]; public: - Track(const char *file, Allocator_avl *block_alloc[CHN_CNT]) : _file(file) + Track(const char *file) : Thread("track"), _file(file) { for (int i = 0; i < CHN_CNT; ++i) { + /* allocation signal for first channel only */ _audio_out[i] = new (env()->heap()) - Audio_out::Connection(channel_names[i], block_alloc[i]); - _stream[i] = _audio_out[i]->stream(); - if (i > 0) - _audio_out[i]->sync_session(_audio_out[i-1]->session_capability()); + Audio_out::Connection(channel_names[i], + i == 0 ? true : false); } } @@ -82,8 +83,9 @@ class Track : Thread<8192> PDBG("%s size is %zu Bytes (attached to %p)", _file, ds_client.size(), base); - Packet_descriptor p[CHN_CNT]; - size_t file_size = ds_client.size(); + size_t file_size = ds_client.size(); + for (int i = 0; i < CHN_CNT; ++i) + _audio_out[i]->start(); while (1) { @@ -100,46 +102,40 @@ class Track : Thread<8192> ? (file_size - offset) / CHN_CNT / FRAME_SIZE : PERIOD; - for (int chn = 0; chn < CHN_CNT; ++chn) - while (1) - try { - p[chn] = _stream[chn]->alloc_packet(PERIOD_CSIZE); - break; - } catch (Stream::Packet_alloc_failed) { - /* wait for next finished packet */ - _stream[chn]->release_packet(_stream[chn]->get_acked_packet()); - } + Packet *p[CHN_CNT]; + while (1) + try { + p[0] = _audio_out[0]->stream()->alloc(); + break; + } catch (Audio_out::Stream::Alloc_failed) { + _audio_out[0]->wait_for_alloc(); + } + + unsigned pos = _audio_out[0]->stream()->packet_position(p[0]); + /* sync other channels with first one */ + for (int chn = 1; chn < CHN_CNT; ++chn) + p[chn] = _audio_out[chn]->stream()->get(pos); /* copy channel contents into sessions */ float *content = (float *)(base + offset); for (unsigned c = 0; c < CHN_CNT * chunk; c += CHN_CNT) for (int i = 0; i < CHN_CNT; ++i) - _stream[i]->packet_content(p[i])[c / 2] = content[c + i]; + p[i]->content()[c / 2] = content[c + i]; /* handle last packet gracefully */ if (chunk < PERIOD) { for (int i = 0; i < CHN_CNT; ++i) - memset((_stream[i]->packet_content(p[i]) + chunk), + memset(p[i]->content() + chunk, 0, PERIOD_CSIZE - FRAME_SIZE * chunk); } if (verbose) - PDBG("%s submit packet %zu", _file, cnt); + PDBG("%s submit packet %zu", _file, + _audio_out[0]->stream()->packet_position((p[0]))); - for (int i = 0; i < CHN_CNT; ++i) - _stream[i]->submit_packet(p[i]); - - for (int i = 0; i < CHN_CNT; ++i) - while (_stream[i]->ack_avail() || - !_stream[i]->ready_to_submit()) { - _stream[i]->release_packet(_stream[i]->get_acked_packet()); - } + for (int i = 0; i < CHN_CNT; i++) + _audio_out[i]->submit(p[i]); } - - /* ack remaining packets */ - for (int i = 0; i < CHN_CNT; ++i) - while (_stream[i]->ack_avail()) - _stream[i]->release_packet(_stream[i]->get_acked_packet()); } } @@ -189,7 +185,7 @@ static int process_config(const char ***files) int main(int argc, char **argv) { - PDBG("--- Audio-out test ---\n"); + PDBG("--- Audio_out test ---\n"); const char *defaults[] = { "1.raw", "2.raw" }; const char **files = defaults; @@ -203,11 +199,9 @@ int main(int argc, char **argv) } Track *track[cnt]; - Allocator_avl *block_alloc[cnt][CHN_CNT]; + for (int i = 0; i < cnt; ++i) { - for (int j = 0; j < CHN_CNT; ++j) - block_alloc[i][j] = new (env()->heap()) Allocator_avl(env()->heap()); - track[i] = new (env()->heap()) Track(files[i], block_alloc[i]); + track[i] = new (env()->heap()) Track(files[i]); } /* start playback after constrution of all tracks */ @@ -217,3 +211,5 @@ int main(int argc, char **argv) sleep_forever(); return 0; } + +