diff --git a/repos/ports/lib/mk/virtualbox-devices.mk b/repos/ports/lib/mk/virtualbox-devices.mk index 97e87dd718..03126fb711 100644 --- a/repos/ports/lib/mk/virtualbox-devices.mk +++ b/repos/ports/lib/mk/virtualbox-devices.mk @@ -34,6 +34,14 @@ SRC_CC += GuestHost/HGSMI/HGSMICommon.cpp SRC_CC += Devices/Serial/DevSerial.cpp SRC_CC += Devices/PC/DevIoApic.cpp +SRC_C += Devices/Audio/audio.c +SRC_C += Devices/Audio/audiosniffer.c +SRC_C += Devices/Audio/filteraudio.c +SRC_C += Devices/Audio/mixeng.c +SRC_C += Devices/Audio/noaudio.c +SRC_CC += Devices/Audio/DevIchAc97.cpp +SRC_CC += Devices/Audio/DevIchHda.cpp +SRC_CC += Devices/Audio/DevIchHdaCodec.cpp SRC_CC += Devices/USB/DevOHCI.cpp SRC_CC += Devices/USB/USBProxyDevice.cpp SRC_CC += Devices/USB/VUSBDevice.cpp diff --git a/repos/ports/lib/mk/virtualbox-drivers.mk b/repos/ports/lib/mk/virtualbox-drivers.mk index 8a81deb6c9..7397647280 100644 --- a/repos/ports/lib/mk/virtualbox-drivers.mk +++ b/repos/ports/lib/mk/virtualbox-drivers.mk @@ -12,6 +12,10 @@ SRC_CC += Devices/Serial/DrvChar.cpp SRC_CC += Devices/Serial/DrvRawFile.cpp SRC_CC += Devices/Serial/DrvHostSerial.cpp +SRC_CC += audiodrv.cpp SRC_CC += network.cpp -vpath network.cpp $(REP_DIR)/src/virtualbox +INC_DIR += $(VBOX_DIR)/Devices/Audio + +vpath audiodrv.cpp $(REP_DIR)/src/virtualbox +vpath network.cpp $(REP_DIR)/src/virtualbox diff --git a/repos/ports/ports/virtualbox.hash b/repos/ports/ports/virtualbox.hash index 12a633dffe..96cca8a61b 100644 --- a/repos/ports/ports/virtualbox.hash +++ b/repos/ports/ports/virtualbox.hash @@ -1 +1 @@ -59e99301300f881be06c12cd50fbe8b1164e1845 +d5121c2c57249fd1a5438b583db976d0db85c547 diff --git a/repos/ports/run/vbox_win.inc b/repos/ports/run/vbox_win.inc index a70cc2a51a..479ffec60c 100644 --- a/repos/ports/run/vbox_win.inc +++ b/repos/ports/run/vbox_win.inc @@ -13,6 +13,7 @@ set overlay_image "overlay_${flavor}.vdi" set build_components { server/input_merger drivers/nic + drivers/audio server/nitpicker app/vbox_pointer server/nit_fb @@ -22,6 +23,7 @@ set build_components { set boot_modules { input_merger nic_drv + audio_drv nitpicker vbox_pointer nit_fb @@ -67,6 +69,24 @@ append config_of_app { + + + + + + + + + + + + + + diff --git a/repos/ports/src/virtualbox/audiodrv.cpp b/repos/ports/src/virtualbox/audiodrv.cpp new file mode 100644 index 0000000000..38025863d6 --- /dev/null +++ b/repos/ports/src/virtualbox/audiodrv.cpp @@ -0,0 +1,427 @@ +/* + * \brief Genode audio driver backend + * \author Josef Soentgen + * \date 2015-05-17 + * + * TODO: + * - avoid excessive copying by mixing hw.mix_buf directly to + * Audio_out packet + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* VirtualBox includes */ +#include "VBoxDD.h" +#include "vl_vbox.h" +extern "C" { +#include "audio.h" +} +#include + +#define AUDIO_CAP "genode" +extern "C" { +#include "audio_int.h" +} + + +static bool const verbose = false; +#define PLOGV(...) if (verbose) PLOG(__VA_ARGS__); + + +enum { + AUDIO_CHANNELS = 2, + AUDIO_SAMPLE_SIZE = sizeof(short), + AUDIO_FREQ = 44100, + SIZE_PER_FRAME = AUDIO_CHANNELS * AUDIO_SAMPLE_SIZE, +}; +static const char *channel_names[] = { "front left", "front right" }; + + +struct GenodeVoiceOut +{ + HWVoiceOut hw; + Audio_out::Connection *audio[AUDIO_CHANNELS]; + Audio_out::Packet *packet[AUDIO_CHANNELS]; + uint8_t *packet_buf; + uint8_t *pcm_buf; + int wpos; + unsigned packets; + int last_submitted; +}; + + +struct GenodeVoiceIn { + HWVoiceIn hw; + Audio_in::Connection *audio; + void *pcm_buf; + uint8_t *packet_buf; + int wpos; + int rpos; + unsigned packets; +}; + + +static inline void copy_samples(void *dst, void const *src, int samples) { + Genode::memcpy(dst, src, samples * SIZE_PER_FRAME); } + + +static int write_samples(GenodeVoiceOut *out, int16_t *src, int samples) +{ + int16_t *buf = (int16_t*)out->packet_buf; + + /* fill and submit packet */ + if (out->wpos >= Audio_out::PERIOD) { + /* alloc new packet */ + while (true) { + Audio_out::Connection *c = out->audio[0]; + try { + out->packet[0] = c->stream()->alloc(); + break; + } catch (Audio_out::Stream::Alloc_failed) { + c->wait_for_alloc(); + } + } + + unsigned const pos = out->audio[0]->stream()->packet_position(out->packet[0]); + out->packet[1] = out->audio[1]->stream()->get(pos); + + /* copy */ + float *left_content = out->packet[0]->content(); + float *right_content = out->packet[1]->content(); + + for (int i = 0; i < Audio_out::PERIOD; i++) { + left_content[i] = (float)(buf[i * AUDIO_CHANNELS + 0]) / 32768.0f; + right_content[i] = (float)(buf[i * AUDIO_CHANNELS + 1]) / 32768.0f; + } + + /* submit */ + for (int i = 0; i < AUDIO_CHANNELS; i++) { + out->audio[i]->submit(out->packet[i]); + out->packet[i] = nullptr; + } + out->last_submitted = pos; + + /* move remaining samples if any to front of buffer */ + int const remaining_samples = out->wpos - Audio_out::PERIOD; + if (remaining_samples) { + copy_samples(buf, buf + (2*Audio_out::PERIOD), remaining_samples); + } + out->wpos = remaining_samples; + out->packets++; + } + + /* copy new samples */ + copy_samples(buf + (2*out->wpos), src, samples); + out->wpos += samples; + + return samples; +} + + +static int genode_run_out(HWVoiceOut *hw) +{ + GenodeVoiceOut *out = (GenodeVoiceOut *)hw; + + int const live = audio_pcm_hw_get_live_out(&out->hw); + if (!live) + return 0; + + int const samples = audio_MIN(live, out->hw.samples); + int const rpos = out->hw.rpos; + + out->hw.clip(out->pcm_buf, (st_sample_t*)(out->hw.mix_buf + rpos), samples); + write_samples(out, (int16_t*)out->pcm_buf, samples); + out->hw.rpos = (rpos + samples) % out->hw.samples; + + return samples; +} + + +static int genode_write(SWVoiceOut *sw, void *buf, int len) { + return audio_pcm_sw_write(sw, buf, len); } + + +static int genode_init_out(HWVoiceOut *hw, audsettings_t *as) +{ + GenodeVoiceOut *out = (GenodeVoiceOut *)hw; + + PLOG("--- using Audio_out session ---"); + PLOGV("freq: %d", as->freq); + PLOGV("channels: %d", as->nchannels); + PLOGV("format: %d", as->fmt); + PLOGV("endianness: %d", as->endianness); + + if (as->nchannels != AUDIO_CHANNELS) { + PERR("only %d channels supported (%d were requested)", + AUDIO_CHANNELS, as->nchannels); + return -1; + } + + if (as->freq != AUDIO_FREQ) { + PERR("only %d frequency supported (%d were requested)", + AUDIO_FREQ, as->freq); + return -1; + } + + for (int i = 0; i < AUDIO_CHANNELS; i++) { + try { + out->audio[i] = new (Genode::env()->heap()) + Audio_out::Connection(channel_names[i]); + } catch (...) { + PERR("could not establish Audio_out connection"); + while (--i > 0) + Genode::destroy(Genode::env()->heap(), out->audio[i]); + return -1; + } + + out->packet[i] = nullptr; + } + + out->wpos = 0; + out->last_submitted = 0; + out->packets = 0; + + audio_pcm_init_info(&out->hw.info, as); + out->hw.samples = Audio_out::PERIOD; + + size_t const bytes = (out->hw.samples << out->hw.info.shift); + out->pcm_buf = (uint8_t*)RTMemAllocZ(bytes); + out->packet_buf = (uint8_t*)RTMemAllocZ(2 * bytes); + + return 0; +} + + +static void genode_fini_out(HWVoiceOut *hw) +{ + GenodeVoiceOut *out = (GenodeVoiceOut *)hw; + + for (int i = 0; i < AUDIO_CHANNELS; i++) + Genode::destroy(Genode::env()->heap(), out->audio[i]); + + RTMemFree((void*)out->pcm_buf); + RTMemFree((void*)out->packet_buf); +} + + +static int genode_ctl_out(HWVoiceOut *hw, int cmd, ...) +{ + GenodeVoiceOut *out = (GenodeVoiceOut*)hw; + switch (cmd) { + case VOICE_ENABLE: + { + PLOGV("enable playback"); + out->packets = 0; + + for (int i = 0; i < AUDIO_CHANNELS; i++) { + out->audio[i]->stream()->reset(); + out->audio[i]->start(); + } + + break; + } + case VOICE_DISABLE: + { + PLOGV("disable playback (packets played: %u)", out->packets); + + for (int i = 0; i < AUDIO_CHANNELS; i++) { + out->audio[i]->stop(); + out->audio[i]->stream()->invalidate_all(); + out->packet[i] = nullptr; + } + out->wpos = 0; + + break; + } + } + + return 0; +} + + +static int genode_init_in(HWVoiceIn *hw, audsettings_t *as) +{ + GenodeVoiceIn *in = (GenodeVoiceIn*)hw; + + PLOG("--- using Audio_in session ---"); + PLOGV("freq: %d", as->freq); + PLOGV("channels: %d", as->nchannels); + PLOGV("format: %d", as->fmt); + PLOGV("endianness: %d", as->endianness); + + try { + in->audio = new (Genode::env()->heap()) Audio_in::Connection("left"); + } catch (...) { + PERR("could not establish Audio_in connection"); + return -1; + } + + audio_pcm_init_info(&in->hw.info, as); + in->hw.samples = Audio_in::PERIOD; + in->pcm_buf = RTMemAllocZ(in->hw.samples << in->hw.info.shift); + in->packet_buf = (uint8_t*)RTMemAllocZ(in->hw.samples << in->hw.info.shift); + + in->rpos = 0; + in->wpos = 0; + in->packets = 0; + + return 0; +} + + +static void genode_fini_in(HWVoiceIn *hw) +{ + GenodeVoiceIn *in = (GenodeVoiceIn*)hw; + + Genode::destroy(Genode::env()->heap(), in->audio); + RTMemFree(in->pcm_buf); + RTMemFree(in->packet_buf); +} + + +static int read_samples(GenodeVoiceIn *in, int16_t *dst, int samples) +{ + enum { PACKET_BUF_LEN = 2 * Audio_in::PERIOD }; + + /* + * Acquire next Audio_in packet first and copy the content into the + * packet buffer. + */ + Audio_in::Stream &stream = *in->audio->stream(); + Audio_in::Packet *p = stream.get(stream.pos()); + + if (!p->valid()) + return 0; + + if ((in->wpos + (Audio_in::PERIOD)) > PACKET_BUF_LEN) + in->wpos = 0; + + int16_t *buf = (int16_t*)in->packet_buf + (in->wpos * sizeof(int16_t)); + float *content = p->content(); + for (int i = 0; i < PACKET_BUF_LEN; i++) { + int16_t const v = content[i/2] * 32767; + buf[i] = v; + buf[i+1] = v; + } + + p->invalidate(); + p->mark_as_recorded(); + stream.increment_position(); + + in->packets++; + in->wpos += Audio_in::PERIOD; + + /* + * Copy number of given samples from packet buffer to dst buffer. + */ + int remaining_samples = samples; + if ((in->rpos + remaining_samples) > PACKET_BUF_LEN) { + int n = PACKET_BUF_LEN - in->rpos; + copy_samples(dst, buf + in->rpos, n); + remaining_samples -= n; + dst += (sizeof(int16_t) * n); /* advance in bytes */ + in->rpos = 0; + } + + copy_samples(dst, buf + in->rpos, remaining_samples); + in->rpos = (in->rpos + remaining_samples) % PACKET_BUF_LEN; + + return samples; +} + + +static int genode_run_in(HWVoiceIn *hw) +{ + GenodeVoiceIn *in = (GenodeVoiceIn*)hw; + + int const live = audio_pcm_hw_get_live_in(&in->hw); + if (!(in->hw.samples - live)) + return 0; + + int const dead = in->hw.samples - live; + int const samples = read_samples(in, (int16_t*)in->pcm_buf, dead); + + in->hw.conv(in->hw.conv_buf, in->pcm_buf, samples, &pcm_in_volume); + in->hw.wpos = (in->hw.wpos + samples) % in->hw.samples; + return samples; +} + + +static int genode_read(SWVoiceIn *sw, void *buf, int size) { + return audio_pcm_sw_read(sw, buf, size); } + + +static int genode_ctl_in(HWVoiceIn *hw, int cmd, ...) +{ + GenodeVoiceIn *in = (GenodeVoiceIn*)hw; + switch (cmd) { + case VOICE_ENABLE: + { + PLOGV("enable recording"); + in->audio->start(); + break; + } + case VOICE_DISABLE: + { + PLOGV("disable recording"); + in->audio->stop(); + break; + } + } + + return 0; +} + + +static void *genode_audio_init(void) { + return &oss_audio_driver; } + + +static void genode_audio_fini(void *) { } + + +static struct audio_pcm_ops genode_pcm_ops = { + genode_init_out, + genode_fini_out, + genode_run_out, + genode_write, + genode_ctl_out, + + genode_init_in, + genode_fini_in, + genode_run_in, + genode_read, + genode_ctl_in +}; + + +/* + * We claim to be the OSS driver so that we do not have to + * patch the VirtualBox source because we already claim to + * be FreeBSD. + */ +struct audio_driver oss_audio_driver = { + INIT_FIELD (name = ) "oss", + INIT_FIELD (descr = ) "Genode Audio_out/Audio_in", + INIT_FIELD (options = ) NULL, + INIT_FIELD (init = ) genode_audio_init, + INIT_FIELD (fini = ) genode_audio_fini, + INIT_FIELD (pcm_ops = ) &genode_pcm_ops, + INIT_FIELD (can_be_default = ) 1, + INIT_FIELD (max_voices_out = ) INT_MAX, + INIT_FIELD (max_voices_in = ) INT_MAX, + INIT_FIELD (voice_size_out = ) sizeof(GenodeVoiceOut), + INIT_FIELD (voice_size_in = ) sizeof(GenodeVoiceIn) +}; diff --git a/repos/ports/src/virtualbox/devices.cc b/repos/ports/src/virtualbox/devices.cc index de6dba8b5b..9a90838253 100644 --- a/repos/ports/src/virtualbox/devices.cc +++ b/repos/ports/src/virtualbox/devices.cc @@ -54,6 +54,8 @@ extern "C" int VBoxDevicesRegister(PPDMDEVREGCB pCallbacks, uint32_t u32Version) REGISTER(DeviceE1000); REGISTER(DeviceVMMDev); REGISTER(DeviceOHCI); + REGISTER(DeviceICHAC97); + REGISTER(DeviceICH6_HDA); return VINF_SUCCESS; } diff --git a/repos/ports/src/virtualbox/drivers.cc b/repos/ports/src/virtualbox/drivers.cc index eb65a32795..07acef184d 100644 --- a/repos/ports/src/virtualbox/drivers.cc +++ b/repos/ports/src/virtualbox/drivers.cc @@ -33,6 +33,7 @@ extern "C" int VBoxDriversRegister(PCPDMDRVREGCB pCallbacks, uint32_t u32Version &g_DrvVD, &g_DrvHostInterface, &g_DrvVUSBRootHub, + &g_DrvAUDIO, 0 }; diff --git a/repos/ports/src/virtualbox/libc.cc b/repos/ports/src/virtualbox/libc.cc index bfb337e891..f44a388825 100644 --- a/repos/ports/src/virtualbox/libc.cc +++ b/repos/ports/src/virtualbox/libc.cc @@ -121,6 +121,7 @@ extern "C" char *getenv(const char *name) // "+hgcm.e.l.f" // "+shared_folders.e.l.f" // "+drv_host_serial.e.l.f" +// "+dev_audio.e.l.f" ; if (Genode::strcmp(name, "VBOX_LOG_FLAGS") == 0 || diff --git a/repos/ports/src/virtualbox/patches/vbox_main.patch b/repos/ports/src/virtualbox/patches/vbox_main.patch index a7bcc394cf..6df4399777 100644 --- a/repos/ports/src/virtualbox/patches/vbox_main.patch +++ b/repos/ports/src/virtualbox/patches/vbox_main.patch @@ -1044,16 +1044,14 @@ index caed4be..6d02496 100644 InsertConfigNode(pDevices, "AudioSniffer", &pDev); InsertConfigNode(pDev, "0", &pInst); InsertConfigNode(pInst, "Config", &pCfg); -@@ -2300,9 +2311,12 @@ int Console::configConstructorInner(PUVM pUVM, PVM pVM, AutoWriteLock *pAlock) +@@ -2300,9 +2311,10 @@ int Console::configConstructorInner(PUVM pUVM, PVM pVM, AutoWriteLock *pAlock) /* * AC'97 ICH / SoundBlaster16 audio / Intel HD Audio */ +#endif BOOL fAudioEnabled = FALSE; ComPtr audioAdapter; -+#if 0 hrc = pMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam()); H(); -+#endif if (audioAdapter) hrc = audioAdapter->COMGETTER(Enabled)(&fAudioEnabled); H();