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:
committed by
Norman Feske
parent
6b7340ef23
commit
b445dba833
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
259
src/app/retro_frontend/audio.h
Normal file
259
src/app/retro_frontend/audio.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user