Skip to content

Commit

Permalink
Improved audio in web frontend.
Browse files Browse the repository at this point in the history
  • Loading branch information
andreas-jonsson committed Jan 31, 2024
1 parent 9eb26fc commit 46c6e38
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 29 deletions.
18 changes: 15 additions & 3 deletions front/web/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,16 @@ int cga_width = -1;
int cga_height = -1;
vxt_dword cga_border = 0;

float *sampler_get_buffer(struct vxt_pirepheral *p);
struct vxt_pirepheral *sampler_create(vxt_allocator *alloc, int frequency, int num_samples, float (*generate)(int freq));

struct frontend_video_adapter video_adapter = {0};
struct frontend_mouse_adapter mouse_adapter = {0};

vxt_system *sys = NULL;
struct vxt_pirepheral *disk = NULL;
struct vxt_pirepheral *ppi = NULL;
struct vxt_pirepheral *sampler = NULL;

static int log_wrapper(const char *fmt, ...) {
va_list args;
Expand Down Expand Up @@ -168,6 +172,10 @@ static int render_callback(int width, int height, const vxt_byte *rgba, void *us
return 0;
}

static float generate_sample(int freq) {
return (float)vxtu_ppi_generate_sample(ppi, freq) / 32767.0f;
}

int wasm_video_width(void) {
return cga_width;
}
Expand Down Expand Up @@ -215,11 +223,13 @@ int wasm_step_emulation(int cycles) {
return s.cycles;
}

double wasm_generate_sample(int freq) {
return (double)vxtu_ppi_generate_sample(ppi, freq) / 32767.0;
void *wasm_audio_sampler_memory_pointer(void) {
if (!sampler)
return NULL;
return (void*)sampler_get_buffer(sampler);
}

void wasm_initialize_emulator(int v20, int freq) {
void wasm_initialize_emulator(int v20, int freq, int afreq, int bsize) {
vxt_set_logger(&log_wrapper);

struct vxtu_disk_interface intrf = {
Expand All @@ -229,6 +239,7 @@ void wasm_initialize_emulator(int v20, int freq) {
vxtu_disk_set_activity_callback(disk, &js_disk_activity, NULL);

ppi = vxtu_ppi_create(&ALLOCATOR);
sampler = sampler_create(ALLOCATOR, afreq, bsize, &generate_sample);

APPEND_DEVICE(vxtu_memory_create(&ALLOCATOR, 0x0, 0x100000, false));
APPEND_DEVICE(load_bios(glabios_bin, (int)glabios_bin_len, 0xFE000));
Expand All @@ -238,6 +249,7 @@ void wasm_initialize_emulator(int v20, int freq) {
APPEND_DEVICE(vxtu_dma_create(&ALLOCATOR));
APPEND_DEVICE(vxtu_pit_create(&ALLOCATOR));
APPEND_DEVICE(ppi);
APPEND_DEVICE(sampler);
APPEND_DEVICE(disk);

#ifndef VXTU_STATIC_MODULES
Expand Down
82 changes: 82 additions & 0 deletions front/web/sampler.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (c) 2019-2024 Andreas T Jonsson <mail@andreasjonsson.se>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software in
// a product, an acknowledgment (see the following) in the product
// documentation is required.
//
// This product make use of the VirtualXT software emulator.
// Visit https://virtualxt.org for more information.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.

#include <vxt/vxtu.h>

struct sampler {
int frequency;
float (*generate)(int freq);

int buffer_size;
int buffer_len;
float *buffer;
};

static vxt_error timer(struct sampler *a, vxt_timer_id id, int cycles) {
(void)id; (void)cycles;

float sample = a->generate(a->frequency);
if (a->buffer_len >= a->buffer_size)
return VXT_NO_ERROR;

a->buffer[a->buffer_len++] = sample;
return VXT_NO_ERROR;
}

static vxt_error install(struct sampler *a, vxt_system *s) {
vxt_system_install_timer(s, VXT_GET_PIREPHERAL(a), 1000000 / a->frequency);
return VXT_NO_ERROR;
}

static vxt_error destroy(struct sampler *a) {
vxt_allocator *alloc = vxt_system_allocator(VXT_GET_SYSTEM(a));
alloc(a->buffer, 0);
alloc(VXT_GET_PIREPHERAL(a), 0);
return VXT_NO_ERROR;
}

static const char *name(struct sampler *a) {
(void)a; return "Audio Sampler";
}

float *sampler_get_buffer(struct vxt_pirepheral *p) {
struct sampler *a = VXT_GET_DEVICE(sampler, p);
while (a->buffer_len < a->buffer_size)
a->buffer[a->buffer_len++] = a->generate(a->frequency);
a->buffer_len = 0;
return a->buffer;
}

struct vxt_pirepheral *sampler_create(vxt_allocator *alloc, int frequency, int num_samples, float (*generate)(int freq)) VXT_PIREPHERAL_CREATE(alloc, sampler, {
DEVICE->frequency = frequency;
DEVICE->generate = generate;

DEVICE->buffer_len = 0;
DEVICE->buffer_size = num_samples;
DEVICE->buffer = (float*)alloc(NULL, num_samples * sizeof(float));

PIREPHERAL->install = &install;
PIREPHERAL->destroy = &destroy;
PIREPHERAL->name = &name;
PIREPHERAL->timer = &timer;
})
42 changes: 16 additions & 26 deletions front/web/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -451,25 +451,14 @@ function getCanvas() {
return document.getElementById(urlParams.get("canvas") || defaultCanvas);
}

function genAudioSamples(samples, delta) {
const num = delta * samples.freq;
const max = Math.min(samples.len + num, samples.data.length);
for (var i = samples.len; i < max; i++)
samples.data[samples.len++] = samples.func(samples.freq);
}

function nextAudioBuffer(ctx, buffer, samples) {
for (var i = samples.len; i < samples.data.length; i++)
samples.data[i] = samples.func(samples.freq);

buffer.copyToChannel(samples.data, 0);
samples.len = 0;
function nextAudioBuffer(ctx, buffer, func) {
buffer.copyToChannel(func(), 0);

const source = ctx.createBufferSource();
source.buffer = buffer;
source.connect(ctx.destination);
source.onended = () => {
nextAudioBuffer(ctx, buffer, samples);
nextAudioBuffer(ctx, buffer, func);
}
source.start();
}
Expand Down Expand Up @@ -533,7 +522,10 @@ function startEmulator(binary) {
};

const initialize = () => {
C.wasm_initialize_emulator((urlParams.get("v20") == 1) ? 1 : 0, targetFreq * 1000000);
const audioCtx = new (window.AudioContext || window.webkitAudioContext)({latencyHint: "interactive"});
const audioBufferSize = audioCtx.sampleRate * defaultAudioLatency;

C.wasm_initialize_emulator((urlParams.get("v20") == 1) ? 1 : 0, targetFreq * 1000000, audioCtx.sampleRate, audioBufferSize);

if (urlParams.has("storage") && (urlParams.get("storage") != "0")) {
initLocalStorage();
Expand Down Expand Up @@ -584,20 +576,20 @@ function startEmulator(binary) {

window.requestAnimationFrame(renderFrame);

const audioCtx = new (window.AudioContext || window.webkitAudioContext)({latencyHint: "interactive"});
const audioSamples = {
data: new Float32Array(audioCtx.sampleRate * defaultAudioLatency),
len: 0,
freq: audioCtx.sampleRate,
func: C.wasm_generate_sample
};
const audioBuffer = audioCtx.createBuffer(
1,
audioSamples.data.length,
audioBufferSize,
audioCtx.sampleRate
);

nextAudioBuffer(audioCtx, audioBuffer, audioSamples);
nextAudioBuffer(audioCtx, audioBuffer, () => {
const bufferOffset = C.wasm_audio_sampler_memory_pointer();
const sampleDataArray = wasmMemoryArray.slice(
bufferOffset,
bufferOffset + audioBufferSize * 4
);
return new Float32Array(sampleDataArray.buffer);
});

const cycleCap = targetFreq * 15000;
var stepper = { t: performance.now(), c: 0 };
Expand All @@ -611,8 +603,6 @@ function startEmulator(binary) {
cycles = cycleCap;
}

genAudioSamples(audioSamples, delta / 1000);

stepper.c += C.wasm_step_emulation(cycles);
stepper.t = t;
}, 1);
Expand Down

0 comments on commit 46c6e38

Please sign in to comment.