app/retro_frontend: simple sample rate conversion

Simple conversion using a nearest-sample method on an additional
thread.

The core output buffer occasionally underruns as it lacks
synchronization, but this keeps latency down.

Fix #60
This commit is contained in:
Emery Hemingway
2016-12-18 18:56:49 +01:00
committed by Norman Feske
parent 6b7340ef23
commit b445dba833
7 changed files with 367 additions and 257 deletions

View File

@@ -15,6 +15,7 @@ set unzip [check_installed unzip]
set build_components {
core init
app/retro_frontend
drivers/audio
drivers/framebuffer
drivers/input
drivers/timer
@@ -29,7 +30,8 @@ proc platform_drv_policy {} {
return {
<policy label_prefix="ps2_drv"> <device name="PS2"/> </policy>
<policy label_prefix="usb_drv"> <pci class="USB"/> </policy>
<policy label_prefix="fb_drv"> <pci class="VGA"/> </policy>}
<policy label_prefix="fb_drv"> <pci class="VGA"/> </policy>
<policy label_prefix="audio_drv"> <pci class="AUDIO"/> <pci class="HDAUDIO"/> </policy>}
}
append_platform_drv_build_components
@@ -95,6 +97,10 @@ append_if [have_spec ps2] config {
<alias name="input_drv" child="ps2_drv"/>}
append config {
<start name="audio_drv">
<resource name="RAM" quantum="8M"/>
<provides><service name="Audio_out"/></provides>
</start>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
@@ -112,10 +118,13 @@ append config {
<resource name="RAM" quantum="2M"/>
<provides> <service name="Input"/> </provides>
<config>
<map from="KEY_A" to="BTN_A"/>
<map from="KEY_S" to="BTN_B"/>
<map from="KEY_LEFTCTRL" to="BTN_A"/>
<map from="KEY_LEFTALT" to="BTN_B"/>
<!-- Retroarch mappings -->
<map from="KEY_Z" to="BTN_B"/>
<map from="KEY_A" to="BTN_Y"/>
<map from="KEY_X" to="BTN_A"/>
<map from="KEY_S" to="BTN_X"/>
<map from="KEY_Q" to="BTN_TL"/>
<map from="KEY_W" to="BTN_TR"/>
<map from="KEY_ENTER" to="BTN_START"/>
<map from="KEY_RIGHTSHIFT" to="BTN_SELECT"/>
<map from="KEY_LEFT" to="BTN_LEFT"/>
@@ -166,6 +175,7 @@ if {![file exist bin/Driar.nes]} {
# generic modules
set boot_modules {
core init ld.lib.so
audio_drv
fb_upscale
input_remap
libc.lib.so

View File

@@ -15,6 +15,7 @@ set unzip [check_installed unzip]
set build_components {
core init
app/retro_frontend
drivers/audio
drivers/framebuffer
drivers/input
drivers/timer
@@ -29,7 +30,9 @@ proc platform_drv_policy {} {
return {
<policy label_prefix="ps2_drv"> <device name="PS2"/> </policy>
<policy label_prefix="usb_drv"> <pci class="USB"/> </policy>
<policy label_prefix="fb_drv"> <pci class="VGA"/> </policy>}
<policy label_prefix="fb_drv"> <pci class="VGA"/> </policy>
<policy label_prefix="audio_drv"> <pci class="AUDIO"/> <pci class="HDAUDIO"/> </policy>}
}
append_platform_drv_build_components
@@ -95,6 +98,10 @@ append_if [have_spec ps2] config {
<alias name="input_drv" child="ps2_drv"/>}
append config {
<start name="audio_drv">
<resource name="RAM" quantum="8M"/>
<provides><service name="Audio_out"/></provides>
</start>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
@@ -112,12 +119,13 @@ append config {
<resource name="RAM" quantum="2M"/>
<provides> <service name="Input"/> </provides>
<config>
<map from="KEY_A" to="BTN_A"/>
<map from="KEY_S" to="BTN_B"/>
<map from="KEY_D" to="BTN_Y"/>
<map from="KEY_LEFTCTRL" to="BTN_A"/>
<map from="KEY_LEFTALT" to="BTN_B"/>
<map from="KEY_LEFTMETA" to="BTN_Y"/>
<!-- Retroarch mappings -->
<map from="KEY_Z" to="BTN_B"/>
<map from="KEY_A" to="BTN_Y"/>
<map from="KEY_X" to="BTN_A"/>
<map from="KEY_S" to="BTN_X"/>
<map from="KEY_Q" to="BTN_TL"/>
<map from="KEY_W" to="BTN_TR"/>
<map from="KEY_ENTER" to="BTN_START"/>
<map from="KEY_RIGHTSHIFT" to="BTN_SELECT"/>
<map from="KEY_LEFT" to="BTN_LEFT"/>
@@ -168,6 +176,7 @@ if {![file exist "bin/superbossgaiden.sfc"]} {
# generic modules
set boot_modules {
core init ld.lib.so
audio_drv
fb_upscale
input_remap
libc.lib.so

View File

@@ -0,0 +1,259 @@
/*
* \brief Retro_frontend audio
* \author Emery Hemingway
* \date 2016-12-13
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _RETRO_FRONTEND__AUDIO_H_
#define _RETRO_FRONTEND__AUDIO_H_
/* Genode includes */
#include <audio_out_session/connection.h>
#include <base/attached_ram_dataspace.h>
#include <util/reconstructible.h>
#include <base/thread.h>
#include <base/log.h>
namespace Retro_frontend {
#include <libretro.h>
template <typename TYPE, Genode::size_t CAPACITY>
struct Ring_buffer;
struct Stereo_out;
enum { LEFT, RIGHT, NUM_CHANNELS };
enum {
SHIFT = 16,
SHIFT_ONE = 1 << SHIFT
};
unsigned audio_shift_factor = SHIFT_ONE;
static unsigned audio_input_period = 0;
}
/**
* Ring buffer using virtual addressing and Buffer Overflow™ technology
*/
template <typename TYPE, Genode::size_t CAPACITY>
struct Retro_frontend::Ring_buffer : Genode::Lock
{
enum { BUFFER_SIZE = sizeof(TYPE)*CAPACITY };
Genode::Env &env;
Genode::addr_t map_first;
Genode::addr_t map_second;
TYPE *buffer;
Genode::size_t wpos = 0;
Genode::size_t rpos = 0;
Genode::Ram_dataspace_capability buffer_ds = env.ram().alloc(BUFFER_SIZE);
Ring_buffer(Genode::Env &env) : env(env)
{
{
/* a hack to find the right sized void in the address space */
Genode::Attached_ram_dataspace filler(env.ram(), env.rm(), BUFFER_SIZE*2);
map_first = (Genode::addr_t)filler.local_addr<TYPE>();
}
map_second = map_first+BUFFER_SIZE;
/* attach the buffer in two consecutive regions */
map_first = env.rm().attach_at(buffer_ds, map_first, BUFFER_SIZE);
map_second = env.rm().attach_at(buffer_ds, map_second, BUFFER_SIZE);
if ((map_first+BUFFER_SIZE) != map_second) {
Genode::error("failed to map ring buffer to consecutive regions");
throw Genode::Exception();
}
buffer = (TYPE *)map_first;
}
~Ring_buffer()
{
env.rm().detach(map_second);
env.rm().detach(map_first);
env.ram().free(buffer_ds);
}
Genode::size_t read_avail() const
{
if (wpos > rpos) return wpos - rpos;
else return (wpos - rpos + CAPACITY) % CAPACITY;
}
Genode::size_t write_avail() const
{
if (wpos > rpos) return ((rpos - wpos + CAPACITY) % CAPACITY) - 2;
else if (wpos < rpos) return rpos - wpos;
else return CAPACITY - 2;
}
Genode::size_t write(TYPE const *src, Genode::size_t len)
{
using Genode::size_t;
len = Genode::min(len, write_avail());
TYPE *wbuf = &buffer[wpos];
for (size_t i = 0; i < len; ++i)
wbuf[i] = src[i];
wpos = (wpos + len) % CAPACITY;
return len;
}
void drain_period(float *periodl, float *periodr)
{
using namespace Genode;
size_t const avail = read_avail();
if (avail < audio_input_period*2) {
Genode::memset(periodl, 0x00, sizeof(float)*Audio_out::PERIOD);
Genode::memset(periodr, 0x00, sizeof(float)*Audio_out::PERIOD);
return;
}
int16_t *rbuf = &buffer[rpos];
size_t buf_off;
for (size_t pkt_off = 0; pkt_off < Audio_out::PERIOD; ++pkt_off)
{
buf_off = 2*((pkt_off*audio_shift_factor)>>SHIFT);
periodl[pkt_off] = rbuf[buf_off+0] / 32768.0f;
periodr[pkt_off] = rbuf[buf_off+1] / 32768.0f;
}
rpos = (rpos+audio_input_period*2) % CAPACITY;
}
};
/**
* Thread for converting samples at the core sample rate
* to the native sample rate
*/
struct Retro_frontend::Stereo_out : Genode::Thread
{
Audio_out::Connection left;
Audio_out::Connection right;
Ring_buffer<int16_t, 4096> buffer;
Genode::Lock run_lock { Genode::Lock::LOCKED };
bool running = false;
void entry() override;
Stereo_out(Genode::Env &env)
:
Genode::Thread(env, "audio-sync", 8*1024,
env.cpu().affinity_space().location_of_index(1),
Weight(Genode::Cpu_session::Weight::DEFAULT_WEIGHT-1),
env.cpu()),
left( env, "left", false, true),
right(env, "right", false, true),
buffer(env)
{
start();
}
void start_stream()
{
running = true;
run_lock.unlock();
}
void stop_stream()
{
running = false;
}
};
static Genode::Constructible<Retro_frontend::Stereo_out> stereo_out;
static
void audio_sample_noop(int16_t left, int16_t right) { }
static /* not called in pratice */
void audio_sample_callback(int16_t left, int16_t right)
{
stereo_out->buffer.lock();
stereo_out->buffer.write(&left, 1);
stereo_out->buffer.write(&right, 1);
stereo_out->buffer.unlock();
}
static
size_t audio_sample_batch_noop(const int16_t *data, size_t frames) { return 0; }
static
size_t audio_sample_batch_callback(const int16_t *data, size_t frames)
{
Genode::Lock::Guard guard(stereo_out->buffer);
return stereo_out->buffer.write(data, frames*2)/2;
}
void Retro_frontend::Stereo_out::entry()
{
Audio_out::Packet *p[NUM_CHANNELS];
for (;;) {
run_lock.lock();
p[LEFT] = left.stream()->next();
/* stuff the buffer a bit */
for (auto i = 0; i < 4; ++i)
p[LEFT] = left.stream()->next(p[LEFT]);
left.start();
right.start();
while (running) {
unsigned const ppos = left.stream()->packet_position(p[LEFT]);
p[RIGHT] = right.stream()->get(ppos);
buffer.lock();
buffer.drain_period(p[LEFT]->content(), p[RIGHT]->content());
buffer.unlock();
left.submit(p[LEFT]);
right.submit(p[RIGHT]);
p[LEFT] = left.stream()->next(p[LEFT]);
left.wait_for_progress();
}
/* empty the buffer to resync when started again */
while (!p[LEFT]->played())
left.wait_for_progress();
left.stop();
right.stop();
}
}
#endif

View File

@@ -16,10 +16,6 @@
#include "frontend.h"
namespace Libretro {
#include <libretro.h>
/**
* Doesn't work
@@ -49,6 +45,8 @@ void log_callback(enum retro_log_level level, const char *fmt, ...)
static
bool environment_callback(unsigned cmd, void *data)
{
using namespace Retro_frontend;
switch (cmd) {
case RETRO_ENVIRONMENT_GET_OVERSCAN:
@@ -232,14 +230,14 @@ bool environment_callback(unsigned cmd, void *data)
{
retro_framebuffer *fb = (retro_framebuffer*)data;
if (!global_frontend->framebuffer.constructed())
if (!framebuffer.constructed())
return false;
Framebuffer::Mode mode = global_frontend->framebuffer->mode;
::Framebuffer::Mode mode = framebuffer->mode;
fb->width = (unsigned)mode.width();
fb->height = (unsigned)mode.height();
fb->data = global_frontend->framebuffer->ds.local_addr<void>();
fb->data = framebuffer->ds.local_addr<void>();
fb->pitch = mode.width() * 2;
fb->format = RETRO_PIXEL_FORMAT_RGB565;
fb->memory_flags = RETRO_MEMORY_TYPE_CACHED;
@@ -257,15 +255,17 @@ void video_refresh_callback(const void *data,
unsigned src_width, unsigned src_height,
size_t src_pitch)
{
using namespace Retro_frontend;
using namespace Genode;
if (data == NULL) /* frame duping? */
return;
uint8_t const *src = (uint8_t const*)data;
uint8_t *dst = global_frontend->framebuffer->ds.local_addr<uint8_t>();
uint8_t *dst = framebuffer->ds.local_addr<uint8_t>();
unsigned const dst_width = global_frontend->framebuffer->mode.width();
unsigned const dst_height = global_frontend->framebuffer->mode.height();
unsigned const dst_width = framebuffer->mode.width();
unsigned const dst_height = framebuffer->mode.height();
unsigned const width = min(src_width, dst_width);
unsigned const height = min(src_height, dst_height);
@@ -277,179 +277,7 @@ void video_refresh_callback(const void *data,
memcpy(&dst[y*dst_pitch], &src[y*src_pitch], dst_pitch);
}
global_frontend->framebuffer->session.refresh(0, 0, width, height);
}
template <Genode::size_t CAPACITY>
struct Ring_buffer
{
Genode::size_t wpos { 0 };
Genode::size_t rpos { 0 };
char _data[CAPACITY];
Ring_buffer() { }
Genode::size_t read_avail() const
{
if (wpos > rpos) return wpos - rpos;
else return (wpos - rpos + CAPACITY) % CAPACITY;
}
Genode::size_t write_avail() const
{
if (wpos > rpos) return ((rpos - wpos + CAPACITY) % CAPACITY) - 2;
else if (wpos < rpos) return rpos - wpos;
else return CAPACITY - 2;
}
Genode::size_t write(void const *src, Genode::size_t len)
{
Genode::size_t const avail = write_avail();
if (avail == 0) return 0;
Genode::size_t const limit_len = len > avail ? avail : len;
Genode::size_t const total = wpos + len;
Genode::size_t first, rest;
if (total > CAPACITY) {
first = CAPACITY - wpos;
rest = total % CAPACITY;
} else {
first = limit_len;
rest = 0;
}
Genode::memcpy(&_data[wpos], src, first);
wpos = (wpos + first) % CAPACITY;
if (rest) {
Genode::memcpy(&_data[wpos], ((char const*)src) + first, rest);
wpos = (wpos + rest) % CAPACITY;
}
return limit_len;
}
Genode::size_t read(void *dst, Genode::size_t len)
{
Genode::size_t const avail = read_avail();
if (avail == 0) return 0;
Genode::size_t const limit_len = len > avail ? avail : len;
Genode::size_t const total = rpos + len;
Genode::size_t first, rest;
if (total > CAPACITY) {
first = CAPACITY - rpos;
rest = total % CAPACITY;
} else {
first = limit_len;
rest = 0;
}
Genode::memcpy(dst, &_data[rpos], first);
rpos = (rpos + first) % CAPACITY;
if (rest) {
Genode::memcpy(((char*)dst) + first, &_data[rpos], rest);
rpos = (rpos + rest) % CAPACITY;
}
return limit_len;
}
};
static Ring_buffer<4*1024> audio_frame_buffer;
static Audio_out::Packet *audio_alloc_pos;
static
void audio_sample_noop(int16_t left, int16_t right) { }
static
void audio_sample_callback(int16_t left, int16_t right)
{
Audio_out::Stream *sl = global_frontend->stereo_out->left.stream();
Audio_out::Stream *sr = global_frontend->stereo_out->right.stream();
Audio_out::Packet *pl = 0;
Audio_out::Packet *pr = 0;
if (audio_alloc_pos == nullptr)
audio_alloc_pos = global_frontend->stereo_out->left.stream()->next();
try {
int16_t tmp[Audio_out::PERIOD * 2];
size_t const n = audio_frame_buffer.read(tmp, sizeof(tmp));
(void)n;
pl = sl->next(audio_alloc_pos);
pr = sr->next(audio_alloc_pos);
float *left_content = pl->content();
float *right_content = pr->content();
for (int i = 0; i < Audio_out::PERIOD; i++) {
left_content[i] = (float)(left) / 32768.0f;
right_content[i] = (float)(right) / 32768.0f;
}
global_frontend->stereo_out->left.submit(pl);
global_frontend->stereo_out->right.submit(pr);
audio_alloc_pos = pl;
} catch (...) {
Genode::error(__func__, " failed to submit audio frames");
}
}
static
size_t audio_sample_batch_noop(const int16_t *data, size_t frames) { return 0; }
static
size_t audio_sample_batch_callback(const int16_t *data, size_t frames)
{
audio_frame_buffer.write(data, frames * 2 * 2);
if (audio_frame_buffer.read_avail() > Audio_out::PERIOD * 2 * 2) {
Audio_out::Stream *sl = global_frontend->stereo_out->left.stream();
Audio_out::Stream *sr = global_frontend->stereo_out->right.stream();
Audio_out::Packet *pl = 0;
Audio_out::Packet *pr = 0;
if (audio_alloc_pos == nullptr) {
audio_alloc_pos = global_frontend->stereo_out->left.stream()->next();
}
try {
int16_t tmp[Audio_out::PERIOD * 2];
size_t const n = audio_frame_buffer.read(tmp, sizeof(tmp));
(void)n;
pl = sl->next(audio_alloc_pos);
pr = sr->next(audio_alloc_pos);
float *left_content = pl->content();
float *right_content = pr->content();
for (int i = 0; i < Audio_out::PERIOD; i++) {
left_content[i] = (float)(tmp[i * 2 + 0]) / 32768.0f;
right_content[i] = (float)(tmp[i * 2 + 1]) / 32768.0f;
}
global_frontend->stereo_out->left.submit(pl);
global_frontend->stereo_out->right.submit(pr);
audio_alloc_pos = pl;
} catch (...) {
Genode::error(__func__, " failed to submit audio frames");
}
}
return frames;
framebuffer->session.refresh(0, 0, width, height);
}
@@ -461,10 +289,10 @@ static
int16_t input_state_callback(unsigned port, unsigned device,
unsigned index, unsigned id)
{
using namespace Retro_frontend;
Controller *controller = &global_frontend->controller;
return controller ? controller->event(device, index, id) : 0;
}
}
#endif

View File

@@ -11,10 +11,13 @@
* under the terms of the GNU General Public License version 2.
*/
/* libc includes */
#include <libc/component.h>
#include "frontend.h"
#include "callbacks.h"
Libretro::Frontend::Frontend(Genode::Env &env) : env(env)
Retro_frontend::Frontend::Frontend(Libc::Env &env) : env(env)
{
/* set the global frontend pointer for callbacks */
global_frontend = this;
@@ -55,7 +58,6 @@ Libretro::Frontend::Frontend(Genode::Env &env) : env(env)
shared_object.lookup<Retro_set_audio_sample_batch>
("retro_set_audio_sample_batch")(&audio_sample_batch_callback);
} catch (...) {
shared_object.lookup<Retro_set_audio_sample>
@@ -143,18 +145,17 @@ Libretro::Frontend::Frontend(Genode::Env &env) : env(env)
}
Genode::size_t Component::stack_size() { return 16*1024*sizeof(Genode::addr_t); }
/* each core will drive the stack differently, so be generous */
Genode::size_t Component::stack_size() { return 64*1024*sizeof(Genode::addr_t); }
void Component::construct(Genode::Env &env)
void Libc::Component::construct(Libc::Env &env)
{
using namespace Genode;
using namespace Retro_frontend;
Libretro::init_keyboard_map();
init_keyboard_map();
try {
static Libretro::Frontend inst(env);
return;
}
try { static Frontend inst(env); }
catch (Shared_object::Invalid_rom_module) {
error("failed to load core"); }
@@ -162,6 +163,8 @@ void Component::construct(Genode::Env &env)
catch (Shared_object::Invalid_symbol) {
error("failed to load required symbols from core"); }
catch (...) { error("failed to init core"); }
env.parent().exit(-1);
catch (...) {
error("failed to init core");
env.parent().exit(-1);
}
}

View File

@@ -15,7 +15,6 @@
#define _RETRO_FRONTEND__FRONTEND_H_
/* Genode includes */
#include <gems/file.h>
#include <audio_out_session/connection.h>
#include <input_session/connection.h>
#include <input/event_queue.h>
@@ -36,20 +35,41 @@
#include <unistd.h>
/* Local includes */
#include "audio.h"
#include "input.h"
namespace Libretro {
#include <libretro.h>
namespace Retro_frontend {
#include <libretro.h>
struct Frontend;
struct Framebuffer;
}
/* Global frontend instance */
static Libretro::Frontend *global_frontend = (Libretro::Frontend*)(Genode::addr_t)666;
static Retro_frontend::Frontend *global_frontend = nullptr;
struct Retro_frontend::Framebuffer
{
::Framebuffer::Connection session;
::Framebuffer::Mode mode;
Genode::Attached_dataspace ds;
Framebuffer(Genode::Env &env, ::Framebuffer::Mode mode)
: session(env, mode), ds(env.rm(), session.dataspace())
{ update_mode(); }
void update_mode() { mode = session.mode(); }
};
static Genode::Constructible<Retro_frontend::Framebuffer> framebuffer;
/**
* Object to encapsulate Genode services
*/
struct Libretro::Frontend
struct Retro_frontend::Frontend
{
typedef void (*Retro_set_environment)(retro_environment_t);
typedef void (*Retro_set_video_refresh)(retro_video_refresh_t);
@@ -80,11 +100,12 @@ struct Libretro::Frontend
typedef void *(*Retro_get_memory_data)(unsigned id);
typedef size_t (*Retro_get_memory_size)(unsigned id);
Genode::Env &env;
Genode::Heap heap { env.ram(), env.rm() };
Libc::Env &env;
Genode::Attached_rom_dataspace config_rom { env, "config" };
Genode::Heap heap { env.ram(), env.rm() };
typedef Genode::String<128> Name;
Name const name { config_rom.xml().attribute_value("core", Name()) };
@@ -284,42 +305,11 @@ struct Libretro::Frontend
Genode::Signal_handler<Frontend> core_runner
{ env.ep(), *this, &Frontend::run };
Timer::Connection timer { env };
Timer::Connection timer { env };
struct Framebuffer
{
::Framebuffer::Connection session;
::Framebuffer::Mode mode;
Genode::Attached_dataspace ds;
Framebuffer(Genode::Env &env, ::Framebuffer::Mode mode)
: session(env, mode), ds(env.rm(), session.dataspace())
{ update_mode(); }
void update_mode() { mode = session.mode(); }
};
Genode::Constructible<Framebuffer> framebuffer;
struct Stereo_out
{
Audio_out::Connection left;
Audio_out::Connection right;
Stereo_out(Genode::Env &env)
:
left(env, "left", false, false),
right(env, "right", false, false)
{ }
};
Genode::Constructible<Stereo_out> stereo_out;
Genode::Reporter variable_reporter { "variables" };
Genode::Reporter subsystem_reporter { "subsystems" };
Genode::Reporter controller_reporter { "controllers" };
Genode::Reporter variable_reporter { env, "variables" };
Genode::Reporter subsystem_reporter { env, "subsystems" };
Genode::Reporter controller_reporter { env, "controllers" };
Controller controller { env };
@@ -361,6 +351,10 @@ struct Libretro::Frontend
Genode::log("using framebuffer sync as timing source");
framebuffer->session.sync_sigh(core_runner);
}
/* start the audio streams */
if (stereo_out.constructed())
stereo_out->start_stream();
}
}
@@ -384,7 +378,7 @@ struct Libretro::Frontend
Genode::Signal_handler<Frontend> resume_handler
{ env.ep(), *this, &Frontend::handle_input };
Frontend(Genode::Env &env);
Frontend(Libc::Env &env);
~Frontend()
{
@@ -394,6 +388,8 @@ struct Libretro::Frontend
void set_av_info(retro_system_av_info &av_info)
{
using namespace Retro_frontend;
framebuffer.construct(
env, ::Framebuffer::Mode(av_info.geometry.base_width,
av_info.geometry.base_height,
@@ -401,14 +397,16 @@ struct Libretro::Frontend
framebuffer->session.mode_sigh(mode_handler);
/* start audio streams */
if (stereo_out.constructed()) {
stereo_out->left.start();
stereo_out->right.start();
double ratio = (double)Audio_out::SAMPLE_RATE / av_info.timing.sample_rate;
audio_shift_factor = SHIFT_ONE / ratio;
audio_input_period = Audio_out::PERIOD * (1.0f / ratio);
}
quarter_fps = av_info.timing.fps / 4;
fb_sync_sample_count = 0;
framebuffer->session.sync_sigh(fb_sync_sampler);
}
@@ -420,6 +418,9 @@ struct Libretro::Frontend
framebuffer->session.sync_sigh(Genode::Signal_context_capability());
timer.sigh(Genode::Signal_context_capability());
/* stop playpack */
stereo_out->stop_stream();
/* set signal handler for unpausing */
controller.input.sigh(resume_handler);

View File

@@ -18,7 +18,7 @@
#include <input_session/connection.h>
#include <input/event_queue.h>
namespace Libretro {
namespace Retro_frontend {
#include <libretro.h>
struct Controller;
@@ -27,7 +27,7 @@ namespace Libretro {
}
struct Libretro::Controller
struct Retro_frontend::Controller
{
enum {
RETRO_JOYPAD_MAX = RETRO_DEVICE_ID_JOYPAD_R3+1,
@@ -152,7 +152,7 @@ struct Libretro::Controller
};
namespace Libretro {
namespace Retro_frontend {
void init_keyboard_map()
{