Skip to content

Commit

Permalink
Issue#214: Scale Music 5000 speed with rest of emulator.
Browse files Browse the repository at this point in the history
The Music5000 audio output was previously limiting the speed of the
emulator itself when trying to select speeds greater than 100%.

This commit changes the sampling rate declared to the host for the Music
5000 to be the right multiple of the real sampling rate for the emulator
to run at that speed.  For example, if we choose 200% for the emulator
speed we tell Allegro the sampling rate for the Music 5000 voice is
93750Hz, twice the real Music 5000 sample rate of 46875Hz.

This will also change the pitch of the Music 5000 output at speeds other
than 100%, rather like varying the speed of a tape recorder.
  • Loading branch information
Steve Fosdick committed Dec 4, 2023
1 parent 66d9f16 commit a481dc3
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 54 deletions.
14 changes: 13 additions & 1 deletion src/gui-allegro.c
Original file line number Diff line number Diff line change
Expand Up @@ -1236,6 +1236,18 @@ static void change_mode7_font(ALLEGRO_EVENT *event)
mode7_font_index = newix;
}

static void toggle_music5000(void)
{
if (sound_music5000) {
sound_music5000 = false;
music5000_close();
}
else {
sound_music5000 = true;
music5000_init(emuspeed);
}
}

static const char all_dext[] = "*.ssd;*.dsd;*.img;*.adf;*.ads;*.adm;*.adl;*.sdd;*.ddd;*.fdi;*.imd;*.hfe"
"*.SSD;*.DSD;*.IMG;*.ADF;*.ADS;*.ADM;*.ADL;*.SDD;*.DDD;*.FDI;*.IMD;*.HFE";

Expand Down Expand Up @@ -1421,7 +1433,7 @@ void gui_allegro_event(ALLEGRO_EVENT *event)
sound_beebsid = !sound_beebsid;
break;
case IDM_SOUND_MUSIC5000:
sound_music5000 = !sound_music5000;
toggle_music5000();
break;
case IDM_SOUND_MFILT:
music5000_fno = radio_event_with_deselect(event, music5000_fno);
Expand Down
3 changes: 2 additions & 1 deletion src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ void main_init(int argc, char *argv[])
sound_init();
sid_init();
sid_settype(sidmethod, cursid);
music5000_init(queue);
music5000_init(EMU_SPEED_NORMAL);
paula_init();
ddnoise_init();
tapenoise_init(queue);
Expand Down Expand Up @@ -637,6 +637,7 @@ void main_setspeed(int speed)
al_set_timer_speed(timer, main_calc_timer(speed));
vid_fskipmax = emu_speeds[speed].fskipmax;
log_debug("main: main_setspeed: vid_fskipmax=%d", vid_fskipmax);
music5000_init(speed);
al_start_timer(timer);
}
}
Expand Down
156 changes: 105 additions & 51 deletions src/music5000.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <string.h>

#include "b-em.h"
#include "main.h"
#include <allegro5/allegro_audio.h>
#include "sound.h"
#include "savestate.h"
Expand Down Expand Up @@ -68,16 +69,17 @@ static const uint8_t PanArray[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 5, 4, 3,
size_t buflen_m5 = BUFLEN_M5;
FILE *music5000_fp;

static ALLEGRO_VOICE *voice;
static ALLEGRO_MIXER *mixer;
static ALLEGRO_AUDIO_STREAM *stream;
static ALLEGRO_VOICE *music5000_voice;
static ALLEGRO_MIXER *music5000_mixer;
static ALLEGRO_AUDIO_STREAM *music5000_stream;
static bool rec_started;

static ushort antilogtable[128];

static uint16_t *music5000_buf;
static int music5000_bufpos = 0;
static int music5000_time = 0;
static unsigned music5000_freq;

static void synth_reset(struct synth *s)
{
Expand All @@ -102,26 +104,6 @@ static void synth_loadstate(struct synth *s, FILE *f)
fread_unlocked(s->ram, sizeof s->ram, 1, f);
}

void music5000_loadstate(FILE *f) {
int ch, pc;

if ((ch = getc_unlocked(f)) != EOF) {
if (ch == 'M') {
sound_music5000 = true;
pc = savestate_load_var(f);
if (pc == 9) {
synth_loadstate(&m5000, f);
synth_loadstate(&m3000, f);
}
else
log_warn("music5000: old, unsupported Music 5000 state");
} else if (ch == 'm')
sound_music5000 = false;
else
log_warn("music5000: invalid Music 5000 state from savestate file");
}
}

static void synth_savestate(struct synth *s, FILE *f)
{
fwrite_unlocked(&s->sleft, sizeof s->sleft, 1, f);
Expand All @@ -140,33 +122,65 @@ void music5000_savestate(FILE *f) {
putc_unlocked('m', f);
}

void music5000_init(ALLEGRO_EVENT_QUEUE *queue)
void music5000_init(int speed)
{
if ((voice = al_create_voice(FREQ_M5, ALLEGRO_AUDIO_DEPTH_INT16, ALLEGRO_CHANNEL_CONF_2))) {
if ((mixer = al_create_mixer(FREQ_M5, ALLEGRO_AUDIO_DEPTH_INT16, ALLEGRO_CHANNEL_CONF_2))) {
if (al_attach_mixer_to_voice(mixer, voice)) {
if ((stream = al_create_audio_stream(8, buflen_m5, FREQ_M5, ALLEGRO_AUDIO_DEPTH_INT16, ALLEGRO_CHANNEL_CONF_2))) {
if (al_attach_audio_stream_to_mixer(stream, mixer)) {
if ((music5000_buf = al_get_audio_stream_fragment(stream))) {
for (int n = 0; n < 128; n++) {
//12-bit antilog as per AM6070 datasheet
int S = n & 15, C = n >> 4;
antilogtable[n] = (ushort)(2 * (pow(2.0, C)*(S + 16.5) - 16.5));
if (sound_music5000) {
unsigned new_freq = FREQ_M5;
if (speed < NUM_EMU_SPEEDS)
new_freq *= emu_speeds[speed].multiplier;
if (new_freq != music5000_freq) {
ALLEGRO_VOICE *new_voice = al_create_voice(new_freq, ALLEGRO_AUDIO_DEPTH_INT16, ALLEGRO_CHANNEL_CONF_2);
if (new_voice) {
ALLEGRO_MIXER *new_mixer = al_create_mixer(new_freq, ALLEGRO_AUDIO_DEPTH_INT16, ALLEGRO_CHANNEL_CONF_2);
if (new_mixer) {
if (al_attach_mixer_to_voice(new_mixer, new_voice)) {
ALLEGRO_AUDIO_STREAM *new_stream = al_create_audio_stream(8, buflen_m5, new_freq, ALLEGRO_AUDIO_DEPTH_INT16, ALLEGRO_CHANNEL_CONF_2);
if (new_stream) {
if (al_attach_audio_stream_to_mixer(new_stream, new_mixer)) {
void *new_frag = al_get_audio_stream_fragment(new_stream);
if (new_frag) {
if (music5000_freq == 0) { // Not previously running.
for (int n = 0; n < 128; n++) {
//12-bit antilog as per AM6070 datasheet
int S = n & 15, C = n >> 4;
antilogtable[n] = (ushort)(2 * (pow(2.0, C)*(S + 16.5) - 16.5));
}
music5000_reset();
}
if (music5000_stream)
al_destroy_audio_stream(music5000_stream);
music5000_stream = new_stream;
if (music5000_mixer)
al_destroy_mixer(music5000_mixer);
music5000_mixer = new_mixer;
if (music5000_voice)
al_destroy_voice(music5000_voice);
music5000_voice = new_voice;
music5000_freq = new_freq;
return;
}
else
log_error("sound: unable to get buffer fragment for Music 5000");
}
music5000_reset();
else
log_error("sound: unable to attach stream to mixer for Music 5000");
al_destroy_audio_stream(new_stream);
}
else
log_error("sound: unable to get buffer fragment for Music 5000");
} else
log_error("sound: unable to attach stream to mixer for Music 5000");
} else
log_error("sound: unable to create stream for Music 5000");
} else
log_error("sound: unable to attach mixer to voice for Music 5000");
} else
log_error("sound: unable to create mixer for Music 5000");
} else
log_error("sound: unable to create voice for Music 5000");
log_error("sound: unable to create stream for Music 5000 at %uHz", new_freq);
}
else
log_error("sound: unable to attach mixer to voice for Music 5000");
al_destroy_mixer(new_mixer);
}
else
log_error("sound: unable to create mixer for Music 5000 at %uHz", new_freq);
al_destroy_voice(new_voice);
}
else
log_error("sound: unable to create voice for Music 5000 at %uHz", new_freq);
}
}
}

FILE *music5000_rec_start(const char *filename)
Expand Down Expand Up @@ -224,6 +238,46 @@ void music5000_close(void)
{
if (music5000_fp)
music5000_rec_stop();
if (music5000_stream) {
al_destroy_audio_stream(music5000_stream);
music5000_stream = NULL;
}
if (music5000_mixer) {
al_destroy_mixer(music5000_mixer);
music5000_mixer = NULL;
}
if (music5000_voice) {
al_destroy_voice(music5000_voice);
music5000_voice = NULL;
}
music5000_freq = 0;
}

void music5000_loadstate(FILE *f) {
int ch, pc;

if ((ch = getc_unlocked(f)) != EOF) {
if (ch == 'M') {
if (!sound_music5000) {
sound_music5000 = true;
music5000_init(emuspeed);
}
pc = savestate_load_var(f);
if (pc == 9) {
synth_loadstate(&m5000, f);
synth_loadstate(&m3000, f);
}
else
log_warn("music5000: old, unsupported Music 5000 state");
} else if (ch == 'm') {
if (sound_music5000) {
music5000_close();
sound_music5000 = false;
}
}
else
log_warn("music5000: invalid Music 5000 state from savestate file");
}
}

static uint8_t page = 0;
Expand Down Expand Up @@ -495,7 +549,7 @@ void music5000_poll(int cycles)
music5000_time -= cycles;
if (music5000_time < 0) {
if (!music5000_buf) {
music5000_buf = al_get_audio_stream_fragment(stream);
music5000_buf = al_get_audio_stream_fragment(music5000_stream);
log_debug("music5000: late buffer allocation %s", music5000_buf ? "worked" : "failed");
}
if (music5000_buf) {
Expand All @@ -504,9 +558,9 @@ void music5000_poll(int cycles)
music5000_get_sample(fcp);
music5000_get_sample(fcp);
if (music5000_bufpos >= (buflen_m5*2)) {
al_set_audio_stream_fragment(stream, music5000_buf);
al_set_audio_stream_playing(stream, true);
music5000_buf = al_get_audio_stream_fragment(stream);
al_set_audio_stream_fragment(music5000_stream, music5000_buf);
al_set_audio_stream_playing(music5000_stream, true);
music5000_buf = al_get_audio_stream_fragment(music5000_stream);
music5000_bufpos = 0;
}
}
Expand All @@ -518,7 +572,7 @@ void music5000_poll(int cycles)
bool music5000_ok(void)
{
if (sound_music5000) {
unsigned frags = al_get_available_audio_stream_fragments(stream);
unsigned frags = al_get_available_audio_stream_fragments(music5000_stream);
if (frags < 2)
return false;
if (frags >= 8)
Expand Down
2 changes: 1 addition & 1 deletion src/music5000.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#ifndef MUSIC5000_INC
#define MUSIC5000_INC

void music5000_init(ALLEGRO_EVENT_QUEUE *queue);
void music5000_init(int speed);
void music5000_close(void);
void music5000_loadstate(FILE *f);
void music5000_savestate(FILE *f);
Expand Down

0 comments on commit a481dc3

Please sign in to comment.