diff --git a/repos/dde_bsd/recipes/src/bsd_audio_drv/used_apis b/repos/dde_bsd/recipes/src/bsd_audio_drv/used_apis index d4828db7e1..8d1a0fe5d2 100644 --- a/repos/dde_bsd/recipes/src/bsd_audio_drv/used_apis +++ b/repos/dde_bsd/recipes/src/bsd_audio_drv/used_apis @@ -4,5 +4,7 @@ format audio_in_session audio_out_session platform_session +play_session +record_session report_session timer_session diff --git a/repos/dde_bsd/run/audio_in.run b/repos/dde_bsd/run/audio_in.run index 182948f62d..a759c3d755 100644 --- a/repos/dde_bsd/run/audio_in.run +++ b/repos/dde_bsd/run/audio_in.run @@ -10,30 +10,71 @@ if {[have_spec linux]} { exit 0 } +# select use of 'Audio_in/Audio_out' or 'Record/Play' sessions +proc use_record_play_sessions { } { return 1 } -# -# Build -# +proc build_targets { } { -set build_components { - core init timer - drivers/acpi - drivers/platform - app/pci_decode - server/report_rom - drivers/audio - test/audio_in + set targets { + core init timer + drivers/acpi drivers/platform app/pci_decode server/report_rom + drivers/audio + } + + if {[use_record_play_sessions]} { + lappend targets server/record_play_mixer + } else { + lappend targets test/audio_in + } } -build $build_components +build [build_targets] create_boot_directory -# -# Config -# +proc audio_driver_config_attr { } { -append config { + if {[use_record_play_sessions]} { + return {report_mixer="yes" record_play="yes"} + } else { + return {report_mixer="yes"} + } +} + +proc record_play_start_nodes { } { + + if {![use_record_play_sessions]} { return "" } + + return { + + + + + + + + + + + + + + + } +} + +proc audio_in_out_start_nodes { } { + + if {[use_record_play_sessions]} { return "" } + + return { + + + + } +} + +install_config { @@ -67,12 +108,7 @@ append config { - - - - - - + @@ -81,12 +117,7 @@ append config { - - - - - - + @@ -98,13 +129,7 @@ append config { - - - - - - - + @@ -118,31 +143,19 @@ append config { - + - - - + + } [record_play_start_nodes] { + } [audio_in_out_start_nodes] { + } -install_config $config - - -# -# Boot modules -# - -set boot_modules { - core ld.lib.so init timer - platform_drv acpi_drv pci_decode report_rom - pci_audio_drv test-audio_in -} - -build_boot_image $boot_modules +build_boot_image [build_artifacts] run_genode_until forever diff --git a/repos/dde_bsd/run/audio_out.run b/repos/dde_bsd/run/audio_out.run index def5a1f68d..17d48353bf 100644 --- a/repos/dde_bsd/run/audio_out.run +++ b/repos/dde_bsd/run/audio_out.run @@ -10,16 +10,87 @@ if {[have_spec linux]} { exit 0 } +# select use of 'Audio_in/Audio_out' or 'Record/Play' sessions +proc use_record_play_sessions { } { return 1 } create_boot_directory -build { - core init timer - drivers/acpi - drivers/platform - app/pci_decode - server/report_rom - drivers/audio - test/audio_out + +proc build_targets { } { + + set targets { + core init timer + drivers/acpi drivers/platform app/pci_decode server/report_rom + drivers/audio + } + + if {[use_record_play_sessions]} { + lappend targets server/record_play_mixer app/waveform_player \ + test/audio_play lib/vfs + } else { + lappend targets test/audio_out + } +} + +build [build_targets] + +proc audio_driver_config_attr { } { + + if {[use_record_play_sessions]} { + return {report_mixer="yes" record_play="yes"} + } else { + return {report_mixer="yes"} + } +} + +proc record_play_start_nodes { } { + + if {![use_record_play_sessions]} { + return "" } + + return { + + + + + + + + + + + + + + + + + + + + + + + + + } +} + +proc audio_in_out_start_nodes { } { + + if {[use_record_play_sessions]} { + return "" } + + return { + + + + sample.f32 + + + + + + } } install_config { @@ -45,7 +116,7 @@ install_config { - + @@ -55,12 +126,7 @@ install_config { - - - - - - + @@ -69,12 +135,7 @@ install_config { - - - - - - + @@ -86,13 +147,7 @@ install_config { - - - - - - - + @@ -102,43 +157,28 @@ install_config { - - + + - - - - sample.raw - - - - - + } [record_play_start_nodes] { + } [audio_in_out_start_nodes] { + } # # Get sample file # -if {![file exists bin/sample.raw]} { +if {![file exists bin/sample.f32]} { puts "" puts "The sample file is missing. Please take a look at" - puts "repos/dde_bsd/README, create 'sample.raw' and put" + puts "repos/dde_bsd/README, create 'sample.f32' and put" puts "the file into './bin'. afterwards" puts "" exit 1 } -build_boot_image { - core ld.lib.so init timer - platform_drv acpi_drv pci_decode report_rom - pci_audio_drv test-audio_out sample.raw -} +build_boot_image [list {*}[build_artifacts] sample.f32] - -# -# For obvious reasons the timeout depends on the total -# length of the used sample file. -# -run_genode_until {.*played.*1 time\(s\)} 60 +run_genode_until forever diff --git a/repos/dde_bsd/src/drivers/audio/main.cc b/repos/dde_bsd/src/drivers/audio/main.cc index 7b0e72b62a..dc1dbeef49 100644 --- a/repos/dde_bsd/src/drivers/audio/main.cc +++ b/repos/dde_bsd/src/drivers/audio/main.cc @@ -15,6 +15,8 @@ /* Genode includes */ #include #include +#include +#include #include #include #include @@ -467,6 +469,144 @@ class Audio_in::Root : public Audio_in::Root_component }; +struct Stereo_output : Noncopyable +{ + static constexpr unsigned SAMPLES_PER_PERIOD = Audio_in::PERIOD; + static constexpr unsigned CHANNELS = 2; + + Env &_env; + + Record::Connection _left { _env, "left" }; + Record::Connection _right { _env, "right" }; + + struct Recording : private Noncopyable + { + bool depleted = false; + + /* 16 bit per sample, interleaved left and right */ + int16_t data[SAMPLES_PER_PERIOD*CHANNELS] { }; + + void clear() { for (auto &e : data) e = 0; } + + void from_record_sessions(Record::Connection &left, Record::Connection &right) + { + using Samples_ptr = Record::Connection::Samples_ptr; + + bool const orig_depleted = depleted; + + Record::Num_samples const num_samples { SAMPLES_PER_PERIOD }; + + auto clamped = [&] (float v) + { + return (v > 1.0) ? 1.0 + : (v < -1.0) ? -1.0 + : v; + }; + + auto float_to_s16 = [&] (float v) { return int16_t(clamped(v)*32767); }; + + left.record(num_samples, + [&] (Record::Time_window const tw, Samples_ptr const &samples) { + depleted = false; + + for (unsigned i = 0; i < SAMPLES_PER_PERIOD; i++) + data[i*CHANNELS] = float_to_s16(samples.start[i]); + + right.record_at(tw, num_samples, + [&] (Samples_ptr const &samples) { + for (unsigned i = 0; i < SAMPLES_PER_PERIOD; i++) + data[i*CHANNELS + 1] = float_to_s16(samples.start[i]); + }); + }, + [&] { + depleted = true; + clear(); + } + ); + + if (orig_depleted != depleted && depleted) + log("recording depleted"); + } + }; + + Recording _recording { }; + + Signal_handler _output_handler { + _env.ep(), *this, &Stereo_output::_handle_output }; + + void _handle_output() + { + _recording.from_record_sessions(_left, _right); + Audio::play(_recording.data, sizeof(_recording.data)); + } + + Stereo_output(Env &env) : _env(env) + { + Audio::play_sigh(_output_handler); + + /* submit two silent packets to get the driver going */ + Audio::play(_recording.data, sizeof(_recording.data)); + Audio::play(_recording.data, sizeof(_recording.data)); + } +}; + + +struct Stereo_input : Noncopyable +{ + static constexpr unsigned SAMPLES_PER_PERIOD = Audio_in::PERIOD; + static constexpr unsigned CHANNELS = 2; + + Env &_env; + + Play::Connection _left { _env, "left" }; + Play::Connection _right { _env, "right" }; + + /* 16 bit per sample, interleaved left and right */ + int16_t data[SAMPLES_PER_PERIOD*CHANNELS] { }; + + struct Frame { float left, right; }; + + void _for_each_frame(auto const &fn) const + { + float const scale = 1.0f/32768; + + for (unsigned i = 0; i < SAMPLES_PER_PERIOD; i++) + fn(Frame { .left = scale*float(data[i*CHANNELS]), + .right = scale*float(data[i*CHANNELS + 1]) }); + } + + Play::Time_window _time_window { }; + + Signal_handler _input_handler { + _env.ep(), *this, &Stereo_input::_handle_input }; + + void _handle_input() + { + if (int const err = Audio::record(data, sizeof(data))) { + if (err && err != 35) + warning("error ", err, " during recording"); + return; + } + + Play::Duration const duration_us { 11*1000 }; /* hint for first period */ + _time_window = _left.schedule_and_enqueue(_time_window, duration_us, + [&] (auto &submit) { + _for_each_frame([&] (Frame const frame) { + submit(frame.left); }); }); + + _right.enqueue(_time_window, + [&] (auto &submit) { + _for_each_frame([&] (Frame const frame) { + submit(frame.right); }); }); + } + + Stereo_input(Env &env) : _env(env) + { + Audio::record_sigh(_input_handler); + } +}; + + /********** ** Main ** **********/ @@ -487,17 +627,30 @@ struct Main Audio::update_config(_env, _config.xml()); } + bool const _record_play = _config.xml().attribute_value("record_play", false); + Constructible _out { }; Constructible _out_root { }; Constructible _in { }; Constructible _in_root { }; + Constructible _stereo_output { }; + Constructible _stereo_input { }; + Signal_handler
_announce_session_handler { _env.ep(), *this, &Main::_handle_announce_session }; void _handle_announce_session() { + if (_record_play) { + _stereo_output.construct(_env); + _stereo_input .construct(_env); + return; + } + + /* Audio_out/Audio_in mode */ + _out.construct(_env); Audio::play_sigh(_out->sigh());