mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-20 09:46:20 +00:00
gems: simple oscilloscope for audio input
This commit is contained in:
parent
fa167bcdc4
commit
6e20ffb990
2
repos/gems/recipes/pkg/osci/README
Normal file
2
repos/gems/recipes/pkg/osci/README
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
Virtual print button for a touch-screen device
|
1
repos/gems/recipes/pkg/osci/archives
Normal file
1
repos/gems/recipes/pkg/osci/archives
Normal file
@ -0,0 +1 @@
|
||||
_/src/osci
|
1
repos/gems/recipes/pkg/osci/hash
Normal file
1
repos/gems/recipes/pkg/osci/hash
Normal file
@ -0,0 +1 @@
|
||||
2023-01-31 0d3ec33511f8d3c157580a734dfed41504dc34c6
|
10
repos/gems/recipes/pkg/osci/runtime
Normal file
10
repos/gems/recipes/pkg/osci/runtime
Normal file
@ -0,0 +1,10 @@
|
||||
<runtime ram="4M" caps="100" binary="osci">
|
||||
|
||||
<requires> <gui/> <audio_in/> </requires>
|
||||
|
||||
<content>
|
||||
<rom label="ld.lib.so"/>
|
||||
<rom label="osci"/>
|
||||
</content>
|
||||
|
||||
</runtime>
|
2
repos/gems/recipes/src/osci/content.mk
Normal file
2
repos/gems/recipes/src/osci/content.mk
Normal file
@ -0,0 +1,2 @@
|
||||
SRC_DIR = src/app/osci
|
||||
include $(GENODE_DIR)/repos/base/recipes/src/content.inc
|
1
repos/gems/recipes/src/osci/hash
Normal file
1
repos/gems/recipes/src/osci/hash
Normal file
@ -0,0 +1 @@
|
||||
2023-01-31 4b14ae81ac3638c5db44bf52ba6853620c820072
|
10
repos/gems/recipes/src/osci/used_apis
Normal file
10
repos/gems/recipes/src/osci/used_apis
Normal file
@ -0,0 +1,10 @@
|
||||
base
|
||||
os
|
||||
blit
|
||||
gems
|
||||
framebuffer_session
|
||||
input_session
|
||||
gui_session
|
||||
timer_session
|
||||
audio_in_session
|
||||
polygon_gfx
|
197
repos/gems/src/app/osci/main.cc
Normal file
197
repos/gems/src/app/osci/main.cc
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* \brief Oscilloscope showing audio input
|
||||
* \author Norman Feske
|
||||
* \date 2023-01-29
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <base/env.h>
|
||||
#include <base/component.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <gui_session/connection.h>
|
||||
#include <audio_in_session/connection.h>
|
||||
#include <timer_session/connection.h>
|
||||
#include <input/event.h>
|
||||
#include <os/pixel_rgb888.h>
|
||||
#include <polygon_gfx/line_painter.h>
|
||||
#include <gems/gui_buffer.h>
|
||||
|
||||
namespace Osci {
|
||||
using namespace Genode;
|
||||
struct Main;
|
||||
}
|
||||
|
||||
|
||||
struct Osci::Main
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
using Point = Gui_buffer::Point;
|
||||
using Area = Gui_buffer::Area;
|
||||
using Rect = Gui_buffer::Rect;
|
||||
|
||||
Area _size { };
|
||||
Color _background { };
|
||||
Color _color { };
|
||||
int _v_scale { };
|
||||
|
||||
Gui::Connection _gui { _env };
|
||||
|
||||
Timer::Connection _timer { _env };
|
||||
|
||||
Audio_in::Connection _audio_in { _env, "left" };
|
||||
|
||||
Constructible<Gui_buffer> _gui_buffer { };
|
||||
|
||||
struct View
|
||||
{
|
||||
Gui::Connection &_gui;
|
||||
|
||||
Gui::Session::View_handle _handle { _gui.create_view() };
|
||||
|
||||
View(Gui::Connection &gui, Point position, Area size) : _gui(gui)
|
||||
{
|
||||
using Command = Gui::Session::Command;
|
||||
_gui.enqueue<Command::Geometry>(_handle, Rect(position, size));
|
||||
_gui.enqueue<Command::To_front>(_handle, Gui::Session::View_handle());
|
||||
_gui.execute();
|
||||
}
|
||||
|
||||
~View() { _gui.destroy_view(_handle); }
|
||||
};
|
||||
|
||||
Constructible<View> _view { };
|
||||
|
||||
Signal_handler<Main> _timer_handler { _env.ep(), *this, &Main::_handle_timer };
|
||||
|
||||
Attached_rom_dataspace _config { _env, "config" };
|
||||
|
||||
Signal_handler<Main> _config_handler { _env.ep(), *this, &Main::_handle_config };
|
||||
|
||||
struct Captured_audio
|
||||
{
|
||||
enum { SIZE_LOG2 = 10, SIZE = 1 << SIZE_LOG2, MASK = SIZE - 1 };
|
||||
|
||||
float _samples[SIZE] { };
|
||||
|
||||
unsigned _pos = 0;
|
||||
|
||||
void _insert(float value)
|
||||
{
|
||||
_pos = (_pos + 1) & MASK;
|
||||
_samples[_pos] = value;
|
||||
}
|
||||
|
||||
float past_value(unsigned past) const
|
||||
{
|
||||
return _samples[(_pos - past) & MASK];
|
||||
}
|
||||
|
||||
void capture_from_audio_in(Audio_in::Session &audio_in)
|
||||
{
|
||||
Audio_in::Stream &stream = *audio_in.stream();
|
||||
|
||||
while (!stream.empty()) {
|
||||
|
||||
Audio_in::Packet &p = *stream.get(stream.pos());
|
||||
|
||||
if (p.valid()) {
|
||||
float *data_ptr = p.content();
|
||||
|
||||
for (unsigned i = 0; i < Audio_in::PERIOD; i++)
|
||||
_insert(*data_ptr++);
|
||||
|
||||
p.invalidate();
|
||||
p.mark_as_recorded();
|
||||
}
|
||||
|
||||
stream.increment_position();
|
||||
}
|
||||
}
|
||||
} _captured_audio { };
|
||||
|
||||
void _handle_config()
|
||||
{
|
||||
_config.update();
|
||||
|
||||
Xml_node const config = _config.xml();
|
||||
|
||||
_size = Area::from_xml(config);
|
||||
_background = config.attribute_value("background", Color { 0, 0, 0 });
|
||||
_color = config.attribute_value("color", Color { 255, 255, 255 });
|
||||
_v_scale = config.attribute_value("v_scale", 3000);
|
||||
|
||||
_gui_buffer.construct(_gui, _size, _env.ram(), _env.rm(),
|
||||
Gui_buffer::Alpha::OPAQUE, _background);
|
||||
|
||||
_view.construct(_gui, Point::from_xml(config), _size);
|
||||
|
||||
_timer.trigger_periodic(1000*config.attribute_value("period_ms", 20));
|
||||
}
|
||||
|
||||
Line_painter const _line_painter { };
|
||||
|
||||
void _render(Gui_buffer::Pixel_surface &pixel, Gui_buffer::Alpha_surface &)
|
||||
{
|
||||
/*
|
||||
* Draw captured audio from right to left.
|
||||
*/
|
||||
|
||||
Point const centered { 0, int(pixel.size().h()/2) };
|
||||
|
||||
Point previous_p { };
|
||||
|
||||
bool first_iteration = true;
|
||||
|
||||
unsigned const w = pixel.size().w();
|
||||
|
||||
for (unsigned i = 0; i < w; i++) {
|
||||
|
||||
Point p { int(w - i),
|
||||
int(float(_v_scale)*_captured_audio.past_value(i)) };
|
||||
|
||||
p = p + centered;
|
||||
|
||||
if (!first_iteration)
|
||||
_line_painter.paint(pixel, p, previous_p, _color);
|
||||
|
||||
previous_p = p;
|
||||
first_iteration = false;
|
||||
}
|
||||
}
|
||||
|
||||
void _handle_timer()
|
||||
{
|
||||
_captured_audio.capture_from_audio_in(_audio_in);
|
||||
|
||||
_gui_buffer->reset_surface();
|
||||
_gui_buffer->apply_to_surface([&] (auto &pixel, auto &alpha) {
|
||||
_render(pixel, alpha); });
|
||||
|
||||
_gui_buffer->flush_surface();
|
||||
|
||||
_gui.framebuffer()->refresh(0, 0, _size.w(), _size.h());
|
||||
}
|
||||
|
||||
Main(Env &env) : _env(env)
|
||||
{
|
||||
_config.sigh(_config_handler);
|
||||
_handle_config();
|
||||
|
||||
_timer.sigh(_timer_handler);
|
||||
|
||||
_audio_in.start();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env)
|
||||
{
|
||||
static Osci::Main main(env);
|
||||
}
|
3
repos/gems/src/app/osci/target.mk
Normal file
3
repos/gems/src/app/osci/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TARGET = osci
|
||||
SRC_CC = main.cc
|
||||
LIBS = base blit
|
Loading…
x
Reference in New Issue
Block a user