Web: add panning, move mixing to wasm

This commit is contained in:
2024-11-02 11:14:24 -05:00
parent 8b9152dde9
commit b4b9131f39
5 changed files with 175 additions and 73 deletions
+128 -43
View File
@@ -12,9 +12,14 @@
#define BREAK_FRAME 1
#define BREAK_POINT 2
// Extra properties
#define EXT_PIXELS 0
#define EXT_SAMPLES 1
// Anaglyph colors
#define STEREO_CYAN 0x00C6F0
#define STEREO_GREEN 0x00B400
#define STEREO_MAGENTA 0xC800FF
#define STEREO_RED 0xFF0000
// Element counts
#define NUM_SAMPLES (41700 / 50 * 2)
@@ -24,9 +29,11 @@
typedef struct {
int32_t breaks;
uint32_t left[256];
float panning;
uint8_t pixels[384 * 224 * 4];
uint32_t right[256];
float samples[41700 * 2];
float samples[NUM_SAMPLES];
float volume;
} Ext;
@@ -43,6 +50,36 @@ int wasmOnFrame(VB *sim) {
/////////////////////////////// Module Exports ////////////////////////////////
// Specify anaglyph colors
EMSCRIPTEN_KEEPALIVE void SetAnaglyph(VB *sim, uint32_t left, uint32_t right) {
Ext *ext = (Ext *) vbGetUserData(sim);
// Erase all RGB values
for (int x = 0; x < 256; x++)
ext->left[x] = ext->right[x] = 0xFF000000;
// Process all RGB channels
for (int c = 0, shift = 16; c < 3; c++, shift -= 8) {
double max; // Magnitude of channel value
uint32_t *dest; // Lookup data
// Select the magnitude and lookup channel
dest = ext->left;
max = (left >> shift & 0xFF) / 255.0;
if (max == 0) {
dest = ext->right;
max = (right >> shift & 0xFF) / 255.0;
if (max == 0)
continue;
}
// Compute the resulting RGB values
for (int x = 0; x < 256; x++)
*dest++ |= (uint32_t) (x * max + 0.5) << (16 - shift);
}
}
// Instantiate a simulation
EMSCRIPTEN_KEEPALIVE void* CreateSim() {
size_t sizeOfSim = vbSizeOf();
@@ -55,8 +92,11 @@ EMSCRIPTEN_KEEPALIVE void* CreateSim() {
// Configure extra
Ext *ext = (Ext *) (pointer + sizeOfSim);
ext->breaks = 0;
ext->breaks = 0;
ext->panning = 0.0f;
ext->volume = 1.0f;
vbSetUserData(sim, ext);
SetAnaglyph(sim, STEREO_RED, STEREO_CYAN);
// Initialize pixels with opaque black
for (unsigned x = 0; x < 384 * 224; x++)
@@ -99,6 +139,16 @@ EMSCRIPTEN_KEEPALIVE int Emulate(VB **sims, unsigned count, uint32_t *clocks) {
return vbEmulateEx(sims, count, clocks);
}
// Retrieve a sim's pixel pointer
EMSCRIPTEN_KEEPALIVE void* GetExtPixels(VB *sim) {
return ((Ext *) vbGetUserData(sim))->pixels;
}
// Retrieve a sim's sample pointer
EMSCRIPTEN_KEEPALIVE void* GetExtSamples(VB *sim) {
return ((Ext *) vbGetUserData(sim))->samples;
}
// Retrieve the break condition flags for a sim
EMSCRIPTEN_KEEPALIVE int32_t GetBreaks(VB *sim) {
return ((Ext *) vbGetUserData(sim))->breaks;
@@ -127,16 +177,6 @@ EMSCRIPTEN_KEEPALIVE void GetDasm(uint32_t *buffer, void *dasm, int count) {
}
}
// Retrieve an extra property
EMSCRIPTEN_KEEPALIVE void* GetExt(VB *sim, int id) {
Ext *ext = (Ext *) vbGetUserData(sim);
switch (id) {
case EXT_PIXELS : return ext->pixels;
case EXT_SAMPLES: return ext->samples;
}
return NULL;
}
// Retrieve anaglyph-tinted pixels from a sim
EMSCRIPTEN_KEEPALIVE void GetPixels(VB *sim) {
Ext *ext = (Ext *) vbGetUserData(sim);
@@ -146,6 +186,71 @@ EMSCRIPTEN_KEEPALIVE void GetPixels(VB *sim) {
*(uint32_t *) pixels = ext->left[pixels[0]] | ext->right[pixels[1]];
}
// Mix audio samples for output
EMSCRIPTEN_KEEPALIVE void Mix(float *buffer, VB **sims, int count) {
// Process all sims
for (int x = 0; x < count; x++) {
Ext *ext = (Ext *) vbGetUserData(sims[x]);
float *samples = ext->samples;
float v = ext->volume;
// First sim initializes buffer
if (x == 0) {
if (ext->panning < 0) {
float l = -ext->panning * v;
float r = (1.0f + ext->panning) * v;
for (unsigned y = 0; y < NUM_SAMPLES; y += 2) {
buffer[y ] = samples[y] * v + samples[y + 1] * l;
buffer[y + 1] = samples[y + 1] * r;
}
} else if (ext->panning > 0) {
float r = ext->panning * v;
float l = (1.0f - ext->panning) * v;
for (unsigned y = 0; y < NUM_SAMPLES; y += 2) {
buffer[y ] = samples[y] * l;
buffer[y + 1] = samples[y + 1] * v + samples[y] * r;
}
} else {
for (unsigned y = 0; y < NUM_SAMPLES; y++)
buffer[y] = samples[y] * v;
}
}
// Subsequent sims add to buffer
else {
if (ext->panning < 0) {
float l = -ext->panning * v;
float r = (1.0f + ext->panning) * v;
for (unsigned y = 0; y < NUM_SAMPLES; y += 2) {
buffer[y ] += samples[y] * v + samples[y + 1] * l;
buffer[y + 1] += samples[y + 1] * r;
}
} else if (ext->panning > 0) {
float r = ext->panning * v;
float l = (1.0f - ext->panning) * v;
for (unsigned y = 0; y < NUM_SAMPLES; y += 2) {
buffer[y ] += samples[y] * l;
buffer[y + 1] += samples[y + 1] * v + samples[y] * r;
}
} else {
for (unsigned y = 0; y < NUM_SAMPLES; y++)
buffer[y] += samples[y] * v;
}
}
}
// Clipping
for (unsigned y = 0; y < NUM_SAMPLES; y++) {
if (buffer[y] < -1.0f)
buffer[y] = -1.0f;
else if (buffer[y] > +1.0f)
buffer[y] = +1.0f;
}
}
// Determine the size in bytes of a pointer
EMSCRIPTEN_KEEPALIVE int PointerSize() {
return sizeof (void *);
@@ -156,32 +261,12 @@ EMSCRIPTEN_KEEPALIVE void* Realloc(void *data, size_t size) {
return realloc(data, size);
}
// Specify anaglyph colors
EMSCRIPTEN_KEEPALIVE void SetAnaglyph(VB *sim, uint32_t left, uint32_t right) {
Ext *ext = (Ext *) vbGetUserData(sim);
// Erase all RGB values
for (int x = 0; x < 256; x++)
ext->left[x] = ext->right[x] = 0xFF000000;
// Process all RGB channels
for (int c = 0, shift = 16; c < 3; c++, shift -= 8) {
double max; // Magnitude of channel value
uint32_t *dest; // Lookup data
// Select the magnitude and lookup channel
dest = ext->left;
max = (left >> shift & 0xFF) / 255.0;
if (max == 0) {
dest = ext->right;
max = (right >> shift & 0xFF) / 255.0;
if (max == 0)
continue;
}
// Compute the resulting RGB values
for (int x = 0; x < 256; x++)
*dest++ |= (uint32_t) (x * max + 0.5) << (16 - shift);
}
// Specify audio panning
EMSCRIPTEN_KEEPALIVE void SetPanning(VB *sim, float panning) {
((Ext *) vbGetUserData(sim))->panning = panning;
}
// Specify audio volume
EMSCRIPTEN_KEEPALIVE void SetVolume(VB *sim, float volume) {
((Ext *) vbGetUserData(sim))->volume = volume;
}