mirror of
https://github.com/libretro/px68k-libretro.git
synced 2026-11-07 04:25:57 -10:00
Roland SC-55 MIDI emulation.
This commit is contained in:
1 parent
a935187697
commit
392ffba9e7
9 files changed
+16936
No files matched your search
@@ -71,6 +71,8 @@ SOURCES_C += \
|
||||
$(CORE_DIR)/libretro/peace.c \
|
||||
$(CORE_DIR)/libretro/zipdisk.c \
|
||||
$(CORE_DIR)/libretro/state.c \
|
||||
$(CORE_DIR)/sc55.c \
|
||||
$(CORE_DIR)/sc55_libretro.c \
|
||||
$(CORE_DIR)/libretro.c
|
||||
|
||||
SOURCES_CXX += \
|
||||
|
||||
+46
@@ -18,6 +18,7 @@
|
||||
#include "libretro/winx68k.h"
|
||||
#include "libretro/dosio.h"
|
||||
#include "libretro/zipdisk.h"
|
||||
#include "sc55_libretro.h"
|
||||
#include "libretro/dswin.h"
|
||||
#include "libretro/windraw.h"
|
||||
#include "libretro/joystick.h"
|
||||
@@ -302,6 +303,11 @@ static bool libretro_supports_option_categories = 0;
|
||||
|
||||
void midi_out_short_msg(size_t msg)
|
||||
{
|
||||
if (px68k_sc55_enabled())
|
||||
{
|
||||
px68k_sc55_midi_short(msg);
|
||||
return;
|
||||
}
|
||||
if (libretro_supports_midi_output && midi_cb.output_enabled())
|
||||
{
|
||||
midi_cb.write(msg & 0xFF, 0); /* status byte */
|
||||
@@ -312,6 +318,11 @@ void midi_out_short_msg(size_t msg)
|
||||
|
||||
void midi_out_long_msg(uint8_t *s, size_t len)
|
||||
{
|
||||
if (px68k_sc55_enabled())
|
||||
{
|
||||
px68k_sc55_midi_long(s, len);
|
||||
return;
|
||||
}
|
||||
if (libretro_supports_midi_output && midi_cb.output_enabled())
|
||||
{
|
||||
int i;
|
||||
@@ -320,8 +331,18 @@ void midi_out_long_msg(uint8_t *s, size_t len)
|
||||
}
|
||||
}
|
||||
|
||||
/* A non-NULL handle is required by x68k/midi.c before it will dispatch
|
||||
* MIDI bytes. Use a private sentinel for the internal synthesizer: no
|
||||
* external MIDI handle is involved in this case. */
|
||||
static int px68k_sc55_midi_handle;
|
||||
|
||||
int midi_out_open(void **phmo)
|
||||
{
|
||||
if (px68k_sc55_enabled())
|
||||
{
|
||||
if (phmo) *phmo = &px68k_sc55_midi_handle;
|
||||
return 0;
|
||||
}
|
||||
if (libretro_supports_midi_output && midi_cb.output_enabled())
|
||||
{
|
||||
*phmo = &midi_cb;
|
||||
@@ -1399,6 +1420,29 @@ static void update_variables(int running)
|
||||
|
||||
update_variable_midi_interface(running);
|
||||
|
||||
/* The internal SC-55 is an alternative MIDI destination, independent of
|
||||
* RetroArch's external MIDI interface. Search the same roots as PrBoom and
|
||||
* apply the SC-55-only gain independently of the PX68K audio volume. */
|
||||
{
|
||||
struct retro_variable sc55_var;
|
||||
struct retro_variable sc55_volume_var;
|
||||
int sc55_enabled = 0;
|
||||
int sc55_volume = 100;
|
||||
sc55_var.key = "px68k_sc55_emulation";
|
||||
sc55_var.value = NULL;
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &sc55_var) && sc55_var.value &&
|
||||
!strcmp(sc55_var.value, "enabled"))
|
||||
sc55_enabled = 1;
|
||||
sc55_volume_var.key = "px68k_sc55_volume";
|
||||
sc55_volume_var.value = NULL;
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &sc55_volume_var) && sc55_volume_var.value)
|
||||
sc55_volume = atoi(sc55_volume_var.value);
|
||||
px68k_sc55_set_volume(sc55_volume);
|
||||
px68k_sc55_configure(sc55_enabled, retro_system_directory, retro_content_directory);
|
||||
if (sc55_enabled && px68k_sc55_enabled())
|
||||
Config.MIDI_SW = 1;
|
||||
}
|
||||
|
||||
strcpy(key, "px68k_joytype");
|
||||
var.key = key;
|
||||
for (i = 0; i < 2; i++)
|
||||
@@ -2173,6 +2217,7 @@ void retro_deinit(void)
|
||||
CDROM_Cleanup();
|
||||
#endif
|
||||
MIDI_Cleanup();
|
||||
px68k_sc55_shutdown();
|
||||
WinX68k_Cleanup();
|
||||
WinDraw_Cleanup();
|
||||
|
||||
@@ -2805,6 +2850,7 @@ void retro_run(void)
|
||||
audio_samples_discard(nsamples - soundbuf_size);
|
||||
}
|
||||
raudio_callback(soundbuf, NULL, soundbuf_size << 2);
|
||||
px68k_sc55_mix(soundbuf, (size_t)soundbuf_size);
|
||||
|
||||
if (libretro_supports_midi_output && midi_cb.output_enabled())
|
||||
midi_cb.flush();
|
||||
|
||||
@@ -171,6 +171,73 @@ struct retro_core_option_v2_definition option_defs_us[] = {
|
||||
},
|
||||
"Default"
|
||||
},
|
||||
{
|
||||
"px68k_sc55_emulation",
|
||||
"Roland SC-55 Emulation",
|
||||
NULL,
|
||||
"Use the internal Roland SC-55 synthesizer instead of RetroArch MIDI output. Requires compatible SC-55 ROM files in the System Directory or PrBoom-compatible subfolders.",
|
||||
NULL,
|
||||
"audio",
|
||||
{
|
||||
{ "disabled", NULL },
|
||||
{ "enabled", NULL },
|
||||
{ NULL, NULL },
|
||||
},
|
||||
"disabled"
|
||||
},
|
||||
{
|
||||
"px68k_sc55_volume",
|
||||
"SC-55 Emulation Volume",
|
||||
NULL,
|
||||
"Adjust the volume of the internal Roland SC-55 synthesizer relative to the PX68K audio. 100% is the default level.",
|
||||
NULL,
|
||||
"audio",
|
||||
{
|
||||
{ "0", "0%" },
|
||||
{ "5", "5%" },
|
||||
{ "10", "10%" },
|
||||
{ "15", "15%" },
|
||||
{ "20", "20%" },
|
||||
{ "25", "25%" },
|
||||
{ "30", "30%" },
|
||||
{ "35", "35%" },
|
||||
{ "40", "40%" },
|
||||
{ "45", "45%" },
|
||||
{ "50", "50%" },
|
||||
{ "55", "55%" },
|
||||
{ "60", "60%" },
|
||||
{ "65", "65%" },
|
||||
{ "70", "70%" },
|
||||
{ "75", "75%" },
|
||||
{ "80", "80%" },
|
||||
{ "85", "85%" },
|
||||
{ "90", "90%" },
|
||||
{ "95", "95%" },
|
||||
{ "100", "100%" },
|
||||
{ "105", "105%" },
|
||||
{ "110", "110%" },
|
||||
{ "115", "115%" },
|
||||
{ "120", "120%" },
|
||||
{ "125", "125%" },
|
||||
{ "130", "130%" },
|
||||
{ "135", "135%" },
|
||||
{ "140", "140%" },
|
||||
{ "145", "145%" },
|
||||
{ "150", "150%" },
|
||||
{ "155", "155%" },
|
||||
{ "160", "160%" },
|
||||
{ "165", "165%" },
|
||||
{ "170", "170%" },
|
||||
{ "175", "175%" },
|
||||
{ "180", "180%" },
|
||||
{ "185", "185%" },
|
||||
{ "190", "190%" },
|
||||
{ "195", "195%" },
|
||||
{ "200", "200%" },
|
||||
{ NULL, NULL },
|
||||
},
|
||||
"100"
|
||||
},
|
||||
{
|
||||
"px68k_midi_output",
|
||||
"MIDI Output (Restart)",
|
||||
|
||||
@@ -198,6 +198,73 @@ struct retro_core_option_v2_definition option_defs_fr[] = {
|
||||
},
|
||||
"Default"
|
||||
},
|
||||
{
|
||||
"px68k_sc55_emulation",
|
||||
"Roland SC-55 Emulation",
|
||||
NULL,
|
||||
"Use the internal Roland SC-55 synthesizer instead of RetroArch MIDI output. Requires compatible SC-55 ROM files in the System Directory or PrBoom-compatible subfolders.",
|
||||
NULL,
|
||||
"audio",
|
||||
{
|
||||
{ "disabled", NULL },
|
||||
{ "enabled", NULL },
|
||||
{ NULL, NULL },
|
||||
},
|
||||
"disabled"
|
||||
},
|
||||
{
|
||||
"px68k_sc55_volume",
|
||||
"SC-55 Emulation Volume",
|
||||
NULL,
|
||||
"Adjust the volume of the internal Roland SC-55 synthesizer relative to the PX68K audio. 100% is the default level.",
|
||||
NULL,
|
||||
"audio",
|
||||
{
|
||||
{ "0", "0%" },
|
||||
{ "5", "5%" },
|
||||
{ "10", "10%" },
|
||||
{ "15", "15%" },
|
||||
{ "20", "20%" },
|
||||
{ "25", "25%" },
|
||||
{ "30", "30%" },
|
||||
{ "35", "35%" },
|
||||
{ "40", "40%" },
|
||||
{ "45", "45%" },
|
||||
{ "50", "50%" },
|
||||
{ "55", "55%" },
|
||||
{ "60", "60%" },
|
||||
{ "65", "65%" },
|
||||
{ "70", "70%" },
|
||||
{ "75", "75%" },
|
||||
{ "80", "80%" },
|
||||
{ "85", "85%" },
|
||||
{ "90", "90%" },
|
||||
{ "95", "95%" },
|
||||
{ "100", "100%" },
|
||||
{ "105", "105%" },
|
||||
{ "110", "110%" },
|
||||
{ "115", "115%" },
|
||||
{ "120", "120%" },
|
||||
{ "125", "125%" },
|
||||
{ "130", "130%" },
|
||||
{ "135", "135%" },
|
||||
{ "140", "140%" },
|
||||
{ "145", "145%" },
|
||||
{ "150", "150%" },
|
||||
{ "155", "155%" },
|
||||
{ "160", "160%" },
|
||||
{ "165", "165%" },
|
||||
{ "170", "170%" },
|
||||
{ "175", "175%" },
|
||||
{ "180", "180%" },
|
||||
{ "185", "185%" },
|
||||
{ "190", "190%" },
|
||||
{ "195", "195%" },
|
||||
{ "200", "200%" },
|
||||
{ NULL, NULL },
|
||||
},
|
||||
"100"
|
||||
},
|
||||
{
|
||||
"px68k_midi_output",
|
||||
"MIDI Output (Restart)",
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Roland SC-55 emulation: a C89 single-file port of the Nuked-SC55
|
||||
* backend. See sc55.c for the copyright notices.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*/
|
||||
#ifndef SC55_H
|
||||
#define SC55_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct sc55 sc55_t;
|
||||
|
||||
enum
|
||||
{
|
||||
SC55_MODEL_MK2, /* SC-55mkII */
|
||||
SC55_MODEL_MK1, /* SC-55 */
|
||||
SC55_MODEL_ST, /* SC-55ST */
|
||||
SC55_MODEL_SC155, /* SC-155 */
|
||||
SC55_MODEL_CM300, /* CM-300, SCC-1 */
|
||||
SC55_MODEL_SCB55, /* SCB-55 */
|
||||
SC55_MODEL_RLP3237, /* RLP-3237 */
|
||||
SC55_MODEL_COUNT
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SC55_ROM_ROM1,
|
||||
SC55_ROM_ROM2,
|
||||
SC55_ROM_SMROM, /* sub-MCU: mkII and ST */
|
||||
SC55_ROM_WAVEROM1,
|
||||
SC55_ROM_WAVEROM2,
|
||||
SC55_ROM_WAVEROM3,
|
||||
SC55_ROM_COUNT
|
||||
};
|
||||
|
||||
/* NULL if the allocation fails. */
|
||||
sc55_t *sc55_new(int model);
|
||||
void sc55_free(sc55_t *s);
|
||||
|
||||
/* `data` is the dump as it is on disk; wave ROMs are unscrambled here.
|
||||
* Returns 0 if the image does not fit the slot. */
|
||||
int sc55_load_rom(sc55_t *s, int slot, const unsigned char *data, size_t len);
|
||||
|
||||
/* Call once every ROM the model needs is loaded. */
|
||||
void sc55_reset(sc55_t *s);
|
||||
|
||||
/* Queue MIDI bytes for the unit's serial input. The queue holds
|
||||
* sc55_midi_room() more bytes; anything past that overwrites what has
|
||||
* not been read yet. */
|
||||
void sc55_midi(sc55_t *s, const unsigned char *data, size_t len);
|
||||
size_t sc55_midi_room(const sc55_t *s);
|
||||
|
||||
/* One bit per voice the firmware has keyed on in the PCM chip. */
|
||||
uint32_t sc55_voices(const sc55_t *s);
|
||||
|
||||
/* Native output rate in Hz: 66207 for the mkII, 64000 for the mk1. */
|
||||
unsigned sc55_rate(const sc55_t *s);
|
||||
|
||||
/* Run the unit until it has produced `count` stereo frames and store
|
||||
* them in `frames` as left/right pairs. Full scale is +-2^29. */
|
||||
size_t sc55_run(sc55_t *s, int32_t *frames, size_t count);
|
||||
|
||||
#endif
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
/* Libretro integration glue for the Nuked-SC55 emulator. */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include "sc55.h"
|
||||
#include "sc55_libretro.h"
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 1024
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#define PXSLASH '\\'
|
||||
#else
|
||||
#define PXSLASH '/'
|
||||
#endif
|
||||
|
||||
static sc55_t *unit;
|
||||
static int active_enabled;
|
||||
static char saved_system[PATH_MAX];
|
||||
static char saved_content[PATH_MAX];
|
||||
static int32_t native_audio[4096 * 2];
|
||||
static size_t native_count;
|
||||
static double native_phase;
|
||||
static unsigned native_rate = 66207;
|
||||
static int volume_percent = 100;
|
||||
|
||||
static void copy_path(char *out, size_t cap, const char *in)
|
||||
{
|
||||
if (!in) in = "";
|
||||
snprintf(out, cap, "%s", in);
|
||||
}
|
||||
|
||||
static int load_file_slot(sc55_t *dev, int slot, const char *path)
|
||||
{
|
||||
FILE *f = fopen(path, "rb");
|
||||
long size;
|
||||
unsigned char *buf;
|
||||
int ok;
|
||||
if (!f) return 0;
|
||||
if (fseek(f, 0, SEEK_END) != 0 || (size = ftell(f)) <= 0 || fseek(f, 0, SEEK_SET) != 0)
|
||||
{ fclose(f); return 0; }
|
||||
buf = (unsigned char*)malloc((size_t)size);
|
||||
if (!buf) { fclose(f); return 0; }
|
||||
if (fread(buf, 1, (size_t)size, f) != (size_t)size)
|
||||
{ free(buf); fclose(f); return 0; }
|
||||
fclose(f);
|
||||
ok = sc55_load_rom(dev, slot, buf, (size_t)size);
|
||||
free(buf);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int try_named_set(int model, const char *const names[SC55_ROM_COUNT],
|
||||
const char *const *dirs, size_t ndirs)
|
||||
{
|
||||
sc55_t *dev = sc55_new(model);
|
||||
int slot;
|
||||
size_t d;
|
||||
char path[PATH_MAX * 2];
|
||||
if (!dev) return 0;
|
||||
for (slot = 0; slot < SC55_ROM_COUNT; slot++)
|
||||
{
|
||||
int loaded = 0;
|
||||
if (!names[slot]) continue;
|
||||
for (d = 0; d < ndirs && !loaded; d++)
|
||||
{
|
||||
if (!dirs[d] || !dirs[d][0]) continue;
|
||||
snprintf(path, sizeof(path), "%s%c%s", dirs[d], PXSLASH, names[slot]);
|
||||
loaded = load_file_slot(dev, slot, path);
|
||||
}
|
||||
if (!loaded) { sc55_free(dev); return 0; }
|
||||
}
|
||||
sc55_reset(dev);
|
||||
unit = dev;
|
||||
native_rate = sc55_rate(unit);
|
||||
native_count = 0;
|
||||
native_phase = 0.0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int load_unit(void)
|
||||
{
|
||||
char sys_prboom[PATH_MAX], content_sc55[PATH_MAX], sys_sc55[PATH_MAX];
|
||||
char sys_sc_55[PATH_MAX], sys_roland[PATH_MAX], sys_nuked[PATH_MAX];
|
||||
char pr_sc55[PATH_MAX], pr_sc_55[PATH_MAX], pr_roland[PATH_MAX], pr_nuked[PATH_MAX];
|
||||
const char *dirs[16];
|
||||
size_t n = 0;
|
||||
static const char *const mk2[SC55_ROM_COUNT] =
|
||||
{ "rom1.bin", "rom2.bin", "rom_sm.bin", "waverom1.bin", "waverom2.bin", NULL };
|
||||
static const char *const mk1[SC55_ROM_COUNT] =
|
||||
{ "sc55_rom1.bin", "sc55_rom2.bin", NULL, "sc55_waverom1.bin", "sc55_waverom2.bin", "sc55_waverom3.bin" };
|
||||
snprintf(sys_prboom, sizeof(sys_prboom), "%s%cprboom", saved_system, PXSLASH);
|
||||
snprintf(content_sc55, sizeof(content_sc55), "%s%csc55", saved_content, PXSLASH);
|
||||
snprintf(sys_sc55, sizeof(sys_sc55), "%s%csc55", saved_system, PXSLASH);
|
||||
snprintf(sys_sc_55, sizeof(sys_sc_55), "%s%csc-55", saved_system, PXSLASH);
|
||||
snprintf(sys_roland, sizeof(sys_roland), "%s%croland", saved_system, PXSLASH);
|
||||
snprintf(sys_nuked, sizeof(sys_nuked), "%s%cnuked", saved_system, PXSLASH);
|
||||
snprintf(pr_sc55, sizeof(pr_sc55), "%s%csc55", sys_prboom, PXSLASH);
|
||||
snprintf(pr_sc_55, sizeof(pr_sc_55), "%s%csc-55", sys_prboom, PXSLASH);
|
||||
snprintf(pr_roland, sizeof(pr_roland), "%s%croland", sys_prboom, PXSLASH);
|
||||
snprintf(pr_nuked, sizeof(pr_nuked), "%s%cnuked", sys_prboom, PXSLASH);
|
||||
if (saved_content[0]) dirs[n++] = saved_content;
|
||||
if (saved_system[0]) dirs[n++] = sys_prboom;
|
||||
if (saved_system[0]) dirs[n++] = saved_system;
|
||||
if (saved_content[0]) dirs[n++] = content_sc55;
|
||||
if (saved_system[0]) dirs[n++] = sys_sc55;
|
||||
if (saved_system[0]) dirs[n++] = sys_sc_55;
|
||||
if (saved_system[0]) dirs[n++] = sys_roland;
|
||||
if (saved_system[0]) dirs[n++] = sys_nuked;
|
||||
if (saved_system[0]) dirs[n++] = pr_sc55;
|
||||
if (saved_system[0]) dirs[n++] = pr_sc_55;
|
||||
if (saved_system[0]) dirs[n++] = pr_roland;
|
||||
if (saved_system[0]) dirs[n++] = pr_nuked;
|
||||
if (try_named_set(SC55_MODEL_MK2, mk2, dirs, n)) return 1;
|
||||
if (try_named_set(SC55_MODEL_MK1, mk1, dirs, n)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void px68k_sc55_shutdown(void)
|
||||
{
|
||||
if (unit) sc55_free(unit);
|
||||
unit = NULL;
|
||||
active_enabled = 0;
|
||||
native_count = 0;
|
||||
native_phase = 0.0;
|
||||
}
|
||||
|
||||
void px68k_sc55_configure(int enabled, const char *system_dir, const char *content_dir)
|
||||
{
|
||||
char sys[PATH_MAX], content[PATH_MAX];
|
||||
copy_path(sys, sizeof(sys), system_dir);
|
||||
copy_path(content, sizeof(content), content_dir);
|
||||
enabled = enabled != 0;
|
||||
if (!enabled)
|
||||
{
|
||||
if (unit || active_enabled) px68k_sc55_shutdown();
|
||||
saved_system[0] = saved_content[0] = '\0';
|
||||
return;
|
||||
}
|
||||
if (unit && active_enabled && !strcmp(saved_system, sys) && !strcmp(saved_content, content))
|
||||
return;
|
||||
px68k_sc55_shutdown();
|
||||
copy_path(saved_system, sizeof(saved_system), sys);
|
||||
copy_path(saved_content, sizeof(saved_content), content);
|
||||
if (load_unit()) active_enabled = 1;
|
||||
else fprintf(stderr, "PX68K SC-55: ROM set not found. Expected PrBoom-compatible filenames in the System Directory, System/prboom, or SC-55 subfolders.\n");
|
||||
}
|
||||
|
||||
int px68k_sc55_enabled(void) { return active_enabled && unit != NULL; }
|
||||
|
||||
void px68k_sc55_set_volume(int value)
|
||||
{
|
||||
if (value < 0) value = 0;
|
||||
if (value > 200) value = 200;
|
||||
volume_percent = value;
|
||||
}
|
||||
|
||||
void px68k_sc55_midi_short(size_t msg)
|
||||
{
|
||||
uint8_t bytes[3];
|
||||
size_t len = 3;
|
||||
if (!px68k_sc55_enabled()) return;
|
||||
bytes[0] = (uint8_t)(msg & 0xff);
|
||||
bytes[1] = (uint8_t)((msg >> 8) & 0xff);
|
||||
bytes[2] = (uint8_t)((msg >> 16) & 0xff);
|
||||
if ((bytes[0] & 0xf0) == 0xc0 || (bytes[0] & 0xf0) == 0xd0) len = 2;
|
||||
sc55_midi(unit, bytes, len);
|
||||
}
|
||||
|
||||
void px68k_sc55_midi_long(const uint8_t *data, size_t len)
|
||||
{
|
||||
if (px68k_sc55_enabled() && data && len) sc55_midi(unit, data, len);
|
||||
}
|
||||
|
||||
static int16_t clamp16(int32_t x)
|
||||
{
|
||||
if (x > 32767) return 32767;
|
||||
if (x < -32768) return -32768;
|
||||
return (int16_t)x;
|
||||
}
|
||||
|
||||
void px68k_sc55_mix(int16_t *stereo, size_t frames)
|
||||
{
|
||||
size_t i;
|
||||
double ratio;
|
||||
if (!px68k_sc55_enabled() || !stereo || !frames) return;
|
||||
ratio = (double)native_rate / 44100.0;
|
||||
if (ratio <= 0.0) return;
|
||||
/* Keep one native sample on the left of the interpolation point. */
|
||||
if (native_phase >= (double)(native_count > 1 ? native_count - 1 : 0))
|
||||
{
|
||||
size_t drop = (size_t)native_phase;
|
||||
if (drop >= native_count) drop = native_count ? native_count - 1 : 0;
|
||||
if (drop)
|
||||
{
|
||||
memmove(native_audio, native_audio + drop * 2, (native_count - drop) * 2 * sizeof(int32_t));
|
||||
native_count -= drop;
|
||||
native_phase -= (double)drop;
|
||||
}
|
||||
}
|
||||
{
|
||||
double last_needed = native_phase + (double)(frames - 1) * ratio + 1.0;
|
||||
size_t need = last_needed >= 0.0 ? (size_t)last_needed + 1 : 2;
|
||||
if (need > 4095) need = 4095;
|
||||
if (need > native_count)
|
||||
{
|
||||
size_t add = need - native_count;
|
||||
if (native_count + add > 4096) add = 4096 - native_count;
|
||||
if (add && sc55_run(unit, native_audio + native_count * 2, add) == add)
|
||||
native_count += add;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < frames; i++)
|
||||
{
|
||||
size_t idx = (size_t)native_phase;
|
||||
double frac = native_phase - (double)idx;
|
||||
int32_t l, r;
|
||||
if (idx + 1 >= native_count) break;
|
||||
l = (int32_t)((double)native_audio[idx * 2] * (1.0 - frac) + (double)native_audio[(idx + 1) * 2] * frac);
|
||||
r = (int32_t)((double)native_audio[idx * 2 + 1] * (1.0 - frac) + (double)native_audio[(idx + 1) * 2 + 1] * frac);
|
||||
/* Scale after converting to the 16-bit mixer domain to avoid overflow. */
|
||||
l = (int32_t)(((int64_t)(l >> 14) * volume_percent) / 100);
|
||||
r = (int32_t)(((int64_t)(r >> 14) * volume_percent) / 100);
|
||||
stereo[i * 2] = clamp16((int32_t)stereo[i * 2] + l);
|
||||
stereo[i * 2 + 1] = clamp16((int32_t)stereo[i * 2 + 1] + r);
|
||||
native_phase += ratio;
|
||||
}
|
||||
if (native_phase >= 1.0)
|
||||
{
|
||||
size_t drop = (size_t)native_phase;
|
||||
if (drop >= native_count) drop = native_count ? native_count - 1 : 0;
|
||||
if (drop)
|
||||
{
|
||||
memmove(native_audio, native_audio + drop * 2, (native_count - drop) * 2 * sizeof(int32_t));
|
||||
native_count -= drop;
|
||||
native_phase -= (double)drop;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef PX68K_SC55_LIBRETRO_H
|
||||
#define PX68K_SC55_LIBRETRO_H
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
void px68k_sc55_configure(int enabled, const char *system_dir, const char *content_dir);
|
||||
int px68k_sc55_enabled(void);
|
||||
void px68k_sc55_midi_short(size_t packed_msg);
|
||||
void px68k_sc55_midi_long(const uint8_t *data, size_t len);
|
||||
void px68k_sc55_mix(int16_t *stereo, size_t frames);
|
||||
void px68k_sc55_set_volume(int volume_percent);
|
||||
void px68k_sc55_shutdown(void);
|
||||
#endif
|
||||
Reference in new issue
Block a user