From a481dc3225a05845abbb294b5abefafcbffddfae Mon Sep 17 00:00:00 2001 From: Steve Fosdick Date: Mon, 4 Dec 2023 14:20:39 +0000 Subject: [PATCH] Issue#214: Scale Music 5000 speed with rest of emulator. 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. --- src/gui-allegro.c | 14 ++++- src/main.c | 3 +- src/music5000.c | 156 +++++++++++++++++++++++++++++++--------------- src/music5000.h | 2 +- 4 files changed, 121 insertions(+), 54 deletions(-) diff --git a/src/gui-allegro.c b/src/gui-allegro.c index dc961296..db53f4fd 100644 --- a/src/gui-allegro.c +++ b/src/gui-allegro.c @@ -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"; @@ -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); diff --git a/src/main.c b/src/main.c index 0784410c..a84253da 100644 --- a/src/main.c +++ b/src/main.c @@ -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); @@ -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); } } diff --git a/src/music5000.c b/src/music5000.c index 0833aed2..fb45e970 100644 --- a/src/music5000.c +++ b/src/music5000.c @@ -29,6 +29,7 @@ #include #include "b-em.h" +#include "main.h" #include #include "sound.h" #include "savestate.h" @@ -68,9 +69,9 @@ 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]; @@ -78,6 +79,7 @@ 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) { @@ -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); @@ -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) @@ -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; @@ -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) { @@ -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; } } @@ -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) diff --git a/src/music5000.h b/src/music5000.h index e70471f4..f0faca7d 100644 --- a/src/music5000.h +++ b/src/music5000.h @@ -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);