mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-20 17:52:52 +00:00
dde_bsd: support record and play session interface
This patch enhances the audio driver with the option to operate as a client of the record and play services instead of providing the audio-in and audio-out services. The record/play mode can be enabled by setting the 'record_play="yes"' config attribute. The audio_in.run and audio_out.run scripts support the selection of the mode via the 'use_record_play_sessions' hook function. Issue #5097
This commit is contained in:
parent
18c4393106
commit
5073cba536
@ -4,5 +4,7 @@ format
|
||||
audio_in_session
|
||||
audio_out_session
|
||||
platform_session
|
||||
play_session
|
||||
record_session
|
||||
report_session
|
||||
timer_session
|
||||
|
@ -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 {
|
||||
<start name="mixer">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<resource name="CPU" quantum="20"/>
|
||||
<binary name="record_play_mixer"/>
|
||||
<provides> <service name="Record"/> <service name="Play"/> </provides>
|
||||
<config jitter_ms="10">
|
||||
|
||||
<mix name="left"> <play label_suffix="left" /> </mix>
|
||||
<mix name="right"> <play label_suffix="right"/> </mix>
|
||||
|
||||
<policy label_suffix="left" record="left" volume="1.0"/>
|
||||
<policy label_suffix="right" record="right" volume="1.0"/>
|
||||
</config>
|
||||
</start>
|
||||
}
|
||||
}
|
||||
|
||||
proc audio_in_out_start_nodes { } {
|
||||
|
||||
if {[use_record_play_sessions]} { return "" }
|
||||
|
||||
return {
|
||||
<start name="test-audio_in">
|
||||
<resource name="RAM" quantum="8M"/>
|
||||
</start>
|
||||
}
|
||||
}
|
||||
|
||||
install_config {
|
||||
<config>
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
@ -67,12 +108,7 @@ append config {
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<route>
|
||||
<service name="Report"> <child name="report_rom"/> </service>
|
||||
<service name="IO_MEM"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="RM"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
@ -81,12 +117,7 @@ append config {
|
||||
<route>
|
||||
<service name="Report"> <child name="report_rom"/> </service>
|
||||
<service name="ROM" label="system"> <child name="report_rom"/> </service>
|
||||
<service name="IO_MEM"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="RM"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
@ -98,13 +129,7 @@ append config {
|
||||
<route>
|
||||
<service name="ROM" label="devices"> <child name="report_rom"/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
<service name="IRQ"> <parent/> </service>
|
||||
<service name="IO_MEM"> <parent/> </service>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="Timer"> <parent/> </service>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
<config>
|
||||
<policy label="audio_drv -> "> <pci class="AUDIO"/> <pci class="HDAUDIO"/> </policy>
|
||||
@ -118,31 +143,19 @@ append config {
|
||||
<service name="Audio_out"/>
|
||||
<service name="Audio_in"/>
|
||||
</provides>
|
||||
<config>
|
||||
<config } [audio_driver_config_attr] {>
|
||||
<mixer field="outputs.master" value="128"/>
|
||||
<mixer field="record.adc-0:1_source" value="sel2"/>
|
||||
<mixer field="record.adc-0:1" value="128"/>
|
||||
<mixer field="record.enable" value="on"/>
|
||||
</config>
|
||||
</start>
|
||||
<start name="test-audio_in">
|
||||
<resource name="RAM" quantum="8M"/>
|
||||
</start>
|
||||
|
||||
} [record_play_start_nodes] {
|
||||
} [audio_in_out_start_nodes] {
|
||||
|
||||
</config>}
|
||||
|
||||
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
|
||||
|
@ -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 {
|
||||
<start name="mixer">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<resource name="CPU" quantum="20"/>
|
||||
<binary name="record_play_mixer"/>
|
||||
<provides> <service name="Record"/> <service name="Play"/> </provides>
|
||||
<config jitter_ms="10">
|
||||
|
||||
<mix name="left"> <play label_suffix="left" /> </mix>
|
||||
<mix name="right"> <play label_suffix="right"/> </mix>
|
||||
|
||||
<policy label_suffix="left" record="left" volume="1.0"/>
|
||||
<policy label_suffix="right" record="right" volume="1.0"/>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="test-audio_play">
|
||||
<resource name="RAM" quantum="40M"/>
|
||||
<config sample_path="sample.f32">
|
||||
<vfs> <rom name="sample.f32"/> </vfs>
|
||||
</config>
|
||||
<route>
|
||||
<any-service><parent/><any-child/></any-service>
|
||||
</route>
|
||||
</start>
|
||||
}
|
||||
}
|
||||
|
||||
proc audio_in_out_start_nodes { } {
|
||||
|
||||
if {[use_record_play_sessions]} {
|
||||
return "" }
|
||||
|
||||
return {
|
||||
<start name="test-audio_out">
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<config>
|
||||
<filename>sample.f32</filename>
|
||||
</config>
|
||||
<route>
|
||||
<any-service><parent/><any-child/></any-service>
|
||||
</route>
|
||||
</start>
|
||||
}
|
||||
}
|
||||
|
||||
install_config {
|
||||
@ -45,7 +116,7 @@ install_config {
|
||||
<start name="report_rom">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<provides> <service name="Report"/> <service name="ROM"/> </provides>
|
||||
<config>
|
||||
<config verbose="yes">
|
||||
<policy label="pci_decode -> system" report="acpi_drv -> acpi"/>
|
||||
<policy label="platform_drv -> devices" report="pci_decode -> devices"/>
|
||||
</config>
|
||||
@ -55,12 +126,7 @@ install_config {
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<route>
|
||||
<service name="Report"> <child name="report_rom"/> </service>
|
||||
<service name="IO_MEM"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="RM"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
@ -69,12 +135,7 @@ install_config {
|
||||
<route>
|
||||
<service name="Report"> <child name="report_rom"/> </service>
|
||||
<service name="ROM" label="system"> <child name="report_rom"/> </service>
|
||||
<service name="IO_MEM"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="RM"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
@ -86,13 +147,7 @@ install_config {
|
||||
<route>
|
||||
<service name="ROM" label="devices"> <child name="report_rom"/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
<service name="IRQ"> <parent/> </service>
|
||||
<service name="IO_MEM"> <parent/> </service>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="Timer"> <parent/> </service>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
<config>
|
||||
<policy label="audio_drv -> "> <pci class="AUDIO"/> <pci class="HDAUDIO"/> </policy>
|
||||
@ -102,43 +157,28 @@ install_config {
|
||||
<start name="audio_drv">
|
||||
<binary name="pci_audio_drv"/>
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<provides> <service name="Audio_out"/> </provides>
|
||||
<config/>
|
||||
<provides> <service name="Audio_out"/> <service name="Audio_in"/> </provides>
|
||||
<config } [audio_driver_config_attr] {/>
|
||||
</start>
|
||||
|
||||
<start name="test-audio_out">
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<config>
|
||||
<filename>sample.raw</filename>
|
||||
</config>
|
||||
<route>
|
||||
<any-service><parent/><any-child/></any-service>
|
||||
</route>
|
||||
</start>
|
||||
} [record_play_start_nodes] {
|
||||
} [audio_in_out_start_nodes] {
|
||||
|
||||
</config>}
|
||||
|
||||
#
|
||||
# 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
|
||||
|
@ -15,6 +15,8 @@
|
||||
/* Genode includes */
|
||||
#include <audio_in_session/rpc_object.h>
|
||||
#include <audio_out_session/rpc_object.h>
|
||||
#include <record_session/connection.h>
|
||||
#include <play_session/connection.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <base/session_label.h>
|
||||
#include <base/component.h>
|
||||
@ -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<Stereo_output> _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<Stereo_input> _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<Audio_out::Out> _out { };
|
||||
Constructible<Audio_out::Root> _out_root { };
|
||||
|
||||
Constructible<Audio_in::In> _in { };
|
||||
Constructible<Audio_in::Root> _in_root { };
|
||||
|
||||
Constructible<Stereo_output> _stereo_output { };
|
||||
Constructible<Stereo_input> _stereo_input { };
|
||||
|
||||
Signal_handler<Main> _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());
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user