From 52a096f1df09c470968f9d42774bb30ff7f8706e Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Wed, 21 Feb 2024 14:29:03 +0100 Subject: [PATCH] audio: use record/play session interface Issue #31 --- recipes/pkg/a64_audio_drv/runtime | 2 +- recipes/src/a64_audio_drv/used_apis | 2 + run/audio_pinephone.run | 82 +++++++++--------- src/drivers/audio/a64/session.cc | 128 +++++++++++++++++++++++++++- 4 files changed, 171 insertions(+), 43 deletions(-) diff --git a/recipes/pkg/a64_audio_drv/runtime b/recipes/pkg/a64_audio_drv/runtime index 21dc3068..aa179140 100644 --- a/recipes/pkg/a64_audio_drv/runtime +++ b/recipes/pkg/a64_audio_drv/runtime @@ -5,7 +5,7 @@ - + diff --git a/recipes/src/a64_audio_drv/used_apis b/recipes/src/a64_audio_drv/used_apis index f62fe2fe..54978590 100644 --- a/recipes/src/a64_audio_drv/used_apis +++ b/recipes/src/a64_audio_drv/used_apis @@ -4,3 +4,5 @@ platform_session report_session audio_in_session audio_out_session +play_session +record_session diff --git a/run/audio_pinephone.run b/run/audio_pinephone.run index 013b467c..98dd1947 100644 --- a/run/audio_pinephone.run +++ b/run/audio_pinephone.run @@ -1,21 +1,21 @@ # -# Script to test audio driver on PinePhone. Test component acts a loopback -# device. +# Script to test audio driver on PinePhone # build { - core lib/ld init timer lib/libc lib/vfs lib/posix lib/vfs_oss + core lib/ld init timer lib/libc lib/libm lib/vfs lib/posix drivers/platform/a64 drivers/pin/a64 drivers/audio_control/pinephone drivers/audio/a64 server/report_rom - test/audio_in test/oss + server/record_play_mixer + app/waveform_player } create_boot_directory install_config { - + @@ -32,19 +32,20 @@ install_config { - + + - + - + @@ -65,7 +66,7 @@ install_config { - + @@ -85,13 +86,13 @@ install_config { - + - + @@ -100,42 +101,47 @@ install_config { - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + - - - } diff --git a/src/drivers/audio/a64/session.cc b/src/drivers/audio/a64/session.cc index 82f7b763..a4aaf368 100644 --- a/src/drivers/audio/a64/session.cc +++ b/src/drivers/audio/a64/session.cc @@ -13,12 +13,12 @@ #include #include +#include +#include #include #include #include #include -#include -#include #include #include "session.h" @@ -451,9 +451,129 @@ struct Audio_aggregator : Audio::Session }; +struct Record_play_aggregator : Audio::Session +{ + static constexpr unsigned SAMPLES_PER_PERIOD = Audio_in::PERIOD; + static constexpr unsigned CHANNELS = 2; + + Env &_env; + + struct Stereo_output : private Noncopyable + { + Env &_env; + + /* 16 bit per sample, interleaved left and right */ + int16_t data[SAMPLES_PER_PERIOD*CHANNELS] { }; + + Record::Connection _left { _env, "left" }; + Record::Connection _right { _env, "right" }; + + Stereo_output(Env &env) : _env(env) { } + + void clear() { for (auto &e : data) e = 0; } + + void from_record_sessions() + { + using Samples_ptr = Record::Connection::Samples_ptr; + + 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) { + + 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]); + }); + }, + [&] { clear(); } + ); + } + }; + + Stereo_output _stereo_output { _env }; + + struct Stereo_input : private Noncopyable + { + struct Frame { float left, right; }; + + void _for_each_frame(Packet const &packet, auto const &fn) const + { + float const scale = 1.0f/32768; + + for (unsigned i = 0; i < SAMPLES_PER_PERIOD; i++) + fn(Frame { .left = scale*float(packet.data[i*CHANNELS]), + .right = scale*float(packet.data[i*CHANNELS + 1]) }); + } + + Env &_env; + + Play::Connection _left { _env, "mic_left" }; + Play::Connection _right { _env, "mic_right" }; + + Play::Time_window _time_window { }; + + Stereo_input(Env &env) : _env(env) { } + + void from_packet(Packet const &packet) + { + if (!packet.valid()) + return; + + Play::Duration const duration_us { 11*1000 }; + _time_window = _left.schedule_and_enqueue(_time_window, duration_us, + [&] (auto &submit) { + _for_each_frame(packet, [&] (Frame const frame) { + submit(frame.left); }); }); + + _right.enqueue(_time_window, + [&] (auto &submit) { + _for_each_frame(packet, [&] (Frame const frame) { + submit(frame.right); }); }); + } + }; + + Stereo_input _stereo_input { _env }; + + Record_play_aggregator(Env &env) : _env(env) { } + + Packet play_packet() override + { + _stereo_output.from_record_sessions(); + + return { _stereo_output.data, sizeof(Stereo_output::data) }; + } + + void record_packet(Packet packet) override + { + _stereo_input.from_packet(packet); + } +}; + + Audio::Session &Audio::Session::construct(Env &env, Allocator &alloc) { - static Audio_aggregator _audio { env, alloc }; + bool const use_record_play_interface = + Attached_rom_dataspace(env, "config").xml().attribute_value("record_play", false); + + if (!use_record_play_interface) { + static Audio_aggregator _audio { env, alloc }; + return _audio; + } + + static Record_play_aggregator _audio { env }; return _audio; } -