From 27d7c6911039372cb8eb7f493749013c3e157841 Mon Sep 17 00:00:00 2001 From: Toni Riikonen Date: Sat, 6 Sep 2025 22:02:17 +0300 Subject: [PATCH] Moved fdd audio emulation to a separate file --- src/floppy/CMakeLists.txt | 4 + src/floppy/fdd.c | 303 +----------------------------- src/floppy/fdd_audio.c | 339 ++++++++++++++++++++++++++++++++++ src/include/86box/fdd.h | 1 - src/include/86box/fdd_audio.h | 70 +++++++ src/sound/sound.c | 2 +- 6 files changed, 421 insertions(+), 298 deletions(-) create mode 100644 src/floppy/fdd_audio.c create mode 100644 src/include/86box/fdd_audio.h diff --git a/src/floppy/CMakeLists.txt b/src/floppy/CMakeLists.txt index c16ca06f4..c51d3717c 100644 --- a/src/floppy/CMakeLists.txt +++ b/src/floppy/CMakeLists.txt @@ -10,9 +10,12 @@ # # Authors: David Hrdlička, # Jasmine Iwanek, +# Toni Riikonen, # # Copyright 2020-2021 David Hrdlička. # Copyright 2024 Jasmine Iwanek. +# Copyright 2025 Toni Riikonen. +# All rights reserved. # add_library(fdd OBJECT @@ -31,6 +34,7 @@ add_library(fdd OBJECT fdd_pcjs.c fdd_mfm.c fdd_td0.c + fdd_audio.c ) add_subdirectory(lzw) diff --git a/src/floppy/fdd.c b/src/floppy/fdd.c index f4e382bb1..4ae44f1ff 100644 --- a/src/floppy/fdd.c +++ b/src/floppy/fdd.c @@ -41,70 +41,7 @@ #include <86box/fdd_mfm.h> #include <86box/fdd_td0.h> #include <86box/fdc.h> -#include <86box/sound.h> - -static int16_t *load_wav(const char *filename, int *sample_count); - -// TODO: -// OK 1. Implement spindle motor spin-up and spin-down -// 2. Move audio emulation to separate code file -// 3. Implement sound support for all drives (not only for drive 0) -// 4. Single sector read/write sound emulation -// 5. Multi-track seek sound emulation -// 6. Limit sound emulation only for 3,5" 300 rpm drives, until we have sound samples for other rpm drives - -// Motor sound states -typedef enum { - MOTOR_STATE_STOPPED = 0, - MOTOR_STATE_STARTING, - MOTOR_STATE_RUNNING, - MOTOR_STATE_STOPPING -} motor_state_t; - -static int16_t *spindlemotor_start_wav = NULL; -static int spindlemotor_start_wav_samples = 0; -static char *spindlemotor_start_wav_filename = "mitsumi_spindle_motor_start_48000_16_1_PCM.wav"; - -static int16_t *spindlemotor_loop_wav = NULL; -static int spindlemotor_loop_wav_samples = 0; -static char *spindlemotor_loop_wav_filename = "mitsumi_spindle_motor_loop_48000_16_1_PCM.wav"; - -static int16_t *spindlemotor_stop_wav = NULL; -static int spindlemotor_stop_wav_samples = 0; -static char *spindlemotor_stop_wav_filename = "mitsumi_spindle_motor_stop_48000_16_1_PCM.wav"; - -static int16_t *steptrackup_wav[80]; -static int steptrackup_wav_samples[80]; -static int16_t *steptrackdown_wav[80]; -static int steptrackdown_wav_samples[80]; -static int16_t *seekmultipletracks_wav[79]; // Seek 2, 3, 4 ... 80 tracks = 79 sounds -static int seekmultipletracks_wav_samples[79]; - -static int spindlemotor_pos[FDD_NUM] = {}; -static motor_state_t spindlemotor_state[FDD_NUM] = {}; -static float spindlemotor_fade_volume[FDD_NUM] = {}; -static int spindlemotor_fade_samples_remaining[FDD_NUM] = {}; - -// Fade duration: 75ms at 48kHz = 3600 samples -#define FADE_DURATION_MS 75 -#define FADE_SAMPLES (48000 * FADE_DURATION_MS / 1000) - -// WAV-header -typedef struct { - char riff[4]; - uint32_t size; - char wave[4]; - char fmt[4]; - uint32_t fmt_size; - uint16_t audio_format; - uint16_t num_channels; - uint32_t sample_rate; - uint32_t byte_rate; - uint16_t block_align; - uint16_t bits_per_sample; - char data[4]; - uint32_t data_size; -} wav_header_t; +#include <86box/fdd_audio.h> /* Flags: Bit 0: 300 rpm supported; @@ -262,23 +199,6 @@ fdd_log(const char *fmt, ...) # define fdd_log(fmt, ...) #endif -static const char * -motor_state_name(motor_state_t state) -{ - switch (state) { - case MOTOR_STATE_STOPPED: - return "STOPPED"; - case MOTOR_STATE_STARTING: - return "STARTING"; - case MOTOR_STATE_RUNNING: - return "RUNNING"; - case MOTOR_STATE_STOPPING: - return "STOPPING"; - default: - return "UNKNOWN"; - } -} - char * fdd_getname(int type) { @@ -626,29 +546,10 @@ fdd_byteperiod(int drive) void fdd_set_motor_enable(int drive, int motor_enable) { + fdd_audio_set_motor_enable(drive, motor_enable); + if (motor_enable && !motoron[drive]) { - // Motor starting up - if (spindlemotor_state[drive] == MOTOR_STATE_STOPPING) { - // Interrupt stop sequence and transition back to loop - spindlemotor_state[drive] = MOTOR_STATE_RUNNING; - spindlemotor_pos[drive] = 0; - spindlemotor_fade_volume[drive] = 1.0f; - spindlemotor_fade_samples_remaining[drive] = 0; - } else { - // Normal startup - spindlemotor_state[drive] = MOTOR_STATE_STARTING; - spindlemotor_pos[drive] = 0; - spindlemotor_fade_volume[drive] = 1.0f; - spindlemotor_fade_samples_remaining[drive] = 0; - } timer_set_delay_u64(&fdd_poll_time[drive], fdd_byteperiod(drive)); - } else if (!motor_enable && motoron[drive]) { - // Motor stopping - spindlemotor_state[drive] = MOTOR_STATE_STOPPING; - spindlemotor_pos[drive] = 0; - spindlemotor_fade_volume[drive] = 1.0f; - spindlemotor_fade_samples_remaining[drive] = FADE_SAMPLES; - // Don't disable timer yet - let the stop sound finish } motoron[drive] = motor_enable; } @@ -772,18 +673,10 @@ fdd_init(void) { int i; - spindlemotor_start_wav = load_wav(spindlemotor_start_wav_filename, &spindlemotor_start_wav_samples); - spindlemotor_loop_wav = load_wav(spindlemotor_loop_wav_filename, &spindlemotor_loop_wav_samples); - spindlemotor_stop_wav = load_wav(spindlemotor_stop_wav_filename, &spindlemotor_stop_wav_samples); - for (i = 0; i < FDD_NUM; i++) { - drives[i].poll = 0; - drives[i].seek = 0; - drives[i].readsector = 0; - spindlemotor_pos[i] = 0; - spindlemotor_state[i] = MOTOR_STATE_STOPPED; - spindlemotor_fade_volume[i] = 1.0f; - spindlemotor_fade_samples_remaining[i] = 0; + drives[i].poll = 0; + drives[i].seek = 0; + drives[i].readsector = 0; } img_init(); @@ -796,193 +689,11 @@ fdd_init(void) fdd_load(i, floppyfns[i]); } - sound_fdd_thread_init(); + fdd_audio_init(); } void fdd_do_writeback(int drive) { d86f_handler[drive].writeback(drive); -} - -static int16_t * -load_wav(const char *filename, int *sample_count) -{ - FILE *f = fopen(filename, "rb"); - if (!f) - return NULL; - - wav_header_t hdr; - if (fread(&hdr, sizeof(hdr), 1, f) != 1) { - fclose(f); - return NULL; - } - - if (memcmp(hdr.riff, "RIFF", 4) || memcmp(hdr.wave, "WAVE", 4) || memcmp(hdr.fmt, "fmt ", 4) || memcmp(hdr.data, "data", 4)) { - fclose(f); - return NULL; - } - - // Accept both mono and stereo, 16-bit PCM - if (hdr.audio_format != 1 || hdr.bits_per_sample != 16 || (hdr.num_channels != 1 && hdr.num_channels != 2)) { - fclose(f); - return NULL; - } - - int input_samples = hdr.data_size / 2; // 2 bytes per sample - int16_t *input_data = malloc(hdr.data_size); - if (!input_data) { - fclose(f); - return NULL; - } - - if (fread(input_data, 1, hdr.data_size, f) != hdr.data_size) { - free(input_data); - fclose(f); - return NULL; - } - fclose(f); - - int16_t *output_data; - int output_samples; - - if (hdr.num_channels == 1) { - // Convert mono to stereo - output_samples = input_samples; // Number of stereo sample pairs - output_data = malloc(input_samples * 2 * sizeof(int16_t)); // Allocate for stereo - if (!output_data) { - free(input_data); - return NULL; - } - - // Convert mono to stereo by duplicating each sample - for (int i = 0; i < input_samples; i++) { - output_data[i * 2] = input_data[i]; // Left channel - output_data[i * 2 + 1] = input_data[i]; // Right channel - } - - free(input_data); - } else { - // Already stereo - output_data = input_data; - output_samples = input_samples / 2; // Number of stereo sample pairs - } - - if (sample_count) - *sample_count = output_samples; - - return output_data; -} - -void -fdd_audio_callback(int16_t *buffer, int length) -{ - // Clear buffer - memset(buffer, 0, length * sizeof(int16_t)); - - // Check if any motor is running or transitioning - int any_motor_active = 0; - for (int drive = 0; drive < FDD_NUM; drive++) { - if (spindlemotor_state[drive] != MOTOR_STATE_STOPPED) { - any_motor_active = 1; - break; - } - } - - if (!any_motor_active) - return; - - float *float_buffer = (float *) buffer; - int samples_in_buffer = length / 2; - - // Process audio for drive 0 only for now - int drive = 0; - - if (spindlemotor_state[drive] == MOTOR_STATE_STOPPED) - return; - - for (int i = 0; i < samples_in_buffer; i++) { - float left_sample = 0.0f; - float right_sample = 0.0f; - - switch (spindlemotor_state[drive]) { - case MOTOR_STATE_STARTING: - if (spindlemotor_start_wav && spindlemotor_pos[drive] < spindlemotor_start_wav_samples) { - // Play start sound - left_sample = (float) spindlemotor_start_wav[spindlemotor_pos[drive] * 2] / 32768.0f; - right_sample = (float) spindlemotor_start_wav[spindlemotor_pos[drive] * 2 + 1] / 32768.0f; - spindlemotor_pos[drive]++; - } else { - // Start sound finished, transition to loop - spindlemotor_state[drive] = MOTOR_STATE_RUNNING; - spindlemotor_pos[drive] = 0; - } - break; - - case MOTOR_STATE_RUNNING: - if (spindlemotor_loop_wav && spindlemotor_loop_wav_samples > 0) { - // Play loop sound - left_sample = (float) spindlemotor_loop_wav[spindlemotor_pos[drive] * 2] / 32768.0f; - right_sample = (float) spindlemotor_loop_wav[spindlemotor_pos[drive] * 2 + 1] / 32768.0f; - spindlemotor_pos[drive]++; - - // Loop back to beginning - if (spindlemotor_pos[drive] >= spindlemotor_loop_wav_samples) { - spindlemotor_pos[drive] = 0; - } - } - break; - - case MOTOR_STATE_STOPPING: - if (spindlemotor_fade_samples_remaining[drive] > 0) { - // Mix fading loop sound with rising stop sound - float loop_volume = spindlemotor_fade_volume[drive]; - float stop_volume = 1.0f - loop_volume; - - float loop_left = 0.0f, loop_right = 0.0f; - float stop_left = 0.0f, stop_right = 0.0f; - - // Get loop sample (continue from current position) - if (spindlemotor_loop_wav && spindlemotor_loop_wav_samples > 0) { - int loop_pos = spindlemotor_pos[drive] % spindlemotor_loop_wav_samples; - loop_left = (float) spindlemotor_loop_wav[loop_pos * 2] / 32768.0f; - loop_right = (float) spindlemotor_loop_wav[loop_pos * 2 + 1] / 32768.0f; - } - - // Get stop sample - if (spindlemotor_stop_wav && spindlemotor_pos[drive] < spindlemotor_stop_wav_samples) { - stop_left = (float) spindlemotor_stop_wav[spindlemotor_pos[drive] * 2] / 32768.0f; - stop_right = (float) spindlemotor_stop_wav[spindlemotor_pos[drive] * 2 + 1] / 32768.0f; - } - - // Mix the sounds - left_sample = loop_left * loop_volume + stop_left * stop_volume; - right_sample = loop_right * loop_volume + stop_right * stop_volume; - - spindlemotor_pos[drive]++; - spindlemotor_fade_samples_remaining[drive]--; - - // Update fade volume - spindlemotor_fade_volume[drive] = (float) spindlemotor_fade_samples_remaining[drive] / FADE_SAMPLES; - } else { - // Fade completed, play remaining stop sound - if (spindlemotor_stop_wav && spindlemotor_pos[drive] < spindlemotor_stop_wav_samples) { - left_sample = (float) spindlemotor_stop_wav[spindlemotor_pos[drive] * 2] / 32768.0f; - right_sample = (float) spindlemotor_stop_wav[spindlemotor_pos[drive] * 2 + 1] / 32768.0f; - spindlemotor_pos[drive]++; - } else { - // Stop sound finished - spindlemotor_state[drive] = MOTOR_STATE_STOPPED; - timer_disable(&fdd_poll_time[drive]); - } - } - break; - - default: - break; - } - - float_buffer[i * 2] = left_sample; - float_buffer[i * 2 + 1] = right_sample; - } } \ No newline at end of file diff --git a/src/floppy/fdd_audio.c b/src/floppy/fdd_audio.c new file mode 100644 index 000000000..4c180d7ae --- /dev/null +++ b/src/floppy/fdd_audio.c @@ -0,0 +1,339 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Implementation of the floppy drive audio emulation. + * + * Authors: Toni Riikonen, + * + * Copyright 2025 Toni Riikonen. + */ +#include +#include +#include +#include +#include + +#define HAVE_STDARG_H +#include <86box/86box.h> +#include <86box/timer.h> +#include <86box/fdd.h> +#include <86box/fdd_audio.h> +#include <86box/sound.h> + +// TODO: +// OK 1. Implement spindle motor spin-up and spin-down +// OK 2. Move audio emulation to separate code file +// 3. Implement sound support for all drives (not only for drive 0) +// 4. Single sector read/write sound emulation +// 5. Multi-track seek sound emulation +// 6. Limit sound emulation only for 3,5" 300 rpm drives, until we have sound samples for other rpm drives + +/* Static audio data */ +static int16_t *spindlemotor_start_wav = NULL; +static int spindlemotor_start_wav_samples = 0; +static char *spindlemotor_start_wav_filename = "mitsumi_spindle_motor_start_48000_16_1_PCM.wav"; + +static int16_t *spindlemotor_loop_wav = NULL; +static int spindlemotor_loop_wav_samples = 0; +static char *spindlemotor_loop_wav_filename = "mitsumi_spindle_motor_loop_48000_16_1_PCM.wav"; + +static int16_t *spindlemotor_stop_wav = NULL; +static int spindlemotor_stop_wav_samples = 0; +static char *spindlemotor_stop_wav_filename = "mitsumi_spindle_motor_stop_48000_16_1_PCM.wav"; + +static int16_t *steptrackup_wav[80]; +static int steptrackup_wav_samples[80]; +static int16_t *steptrackdown_wav[80]; +static int steptrackdown_wav_samples[80]; +static int16_t *seekmultipletracks_wav[79]; /* Seek 2, 3, 4 ... 80 tracks = 79 sounds */ +static int seekmultipletracks_wav_samples[79]; + +/* Audio state for each drive */ +static int spindlemotor_pos[FDD_NUM] = {}; +static motor_state_t spindlemotor_state[FDD_NUM] = {}; +static float spindlemotor_fade_volume[FDD_NUM] = {}; +static int spindlemotor_fade_samples_remaining[FDD_NUM] = {}; + +/* External references to FDD timer and motoron state */ +extern pc_timer_t fdd_poll_time[FDD_NUM]; +extern uint64_t motoron[FDD_NUM]; + +/* Forward declaration */ +static int16_t *load_wav(const char *filename, int *sample_count); + +const char * +fdd_audio_motor_state_name(motor_state_t state) +{ + switch (state) { + case MOTOR_STATE_STOPPED: + return "STOPPED"; + case MOTOR_STATE_STARTING: + return "STARTING"; + case MOTOR_STATE_RUNNING: + return "RUNNING"; + case MOTOR_STATE_STOPPING: + return "STOPPING"; + default: + return "UNKNOWN"; + } +} + +static int16_t * +load_wav(const char *filename, int *sample_count) +{ + FILE *f = fopen(filename, "rb"); + if (!f) + return NULL; + + wav_header_t hdr; + if (fread(&hdr, sizeof(hdr), 1, f) != 1) { + fclose(f); + return NULL; + } + + if (memcmp(hdr.riff, "RIFF", 4) || memcmp(hdr.wave, "WAVE", 4) || memcmp(hdr.fmt, "fmt ", 4) || memcmp(hdr.data, "data", 4)) { + fclose(f); + return NULL; + } + + /* Accept both mono and stereo, 16-bit PCM */ + if (hdr.audio_format != 1 || hdr.bits_per_sample != 16 || (hdr.num_channels != 1 && hdr.num_channels != 2)) { + fclose(f); + return NULL; + } + + int input_samples = hdr.data_size / 2; /* 2 bytes per sample */ + int16_t *input_data = malloc(hdr.data_size); + if (!input_data) { + fclose(f); + return NULL; + } + + if (fread(input_data, 1, hdr.data_size, f) != hdr.data_size) { + free(input_data); + fclose(f); + return NULL; + } + fclose(f); + + int16_t *output_data; + int output_samples; + + if (hdr.num_channels == 1) { + /* Convert mono to stereo */ + output_samples = input_samples; /* Number of stereo sample pairs */ + output_data = malloc(input_samples * 2 * sizeof(int16_t)); /* Allocate for stereo */ + if (!output_data) { + free(input_data); + return NULL; + } + + /* Convert mono to stereo by duplicating each sample */ + for (int i = 0; i < input_samples; i++) { + output_data[i * 2] = input_data[i]; /* Left channel */ + output_data[i * 2 + 1] = input_data[i]; /* Right channel */ + } + + free(input_data); + } else { + /* Already stereo */ + output_data = input_data; + output_samples = input_samples / 2; /* Number of stereo sample pairs */ + } + + if (sample_count) + *sample_count = output_samples; + + return output_data; +} + +void +fdd_audio_init(void) +{ + int i; + + /* Load audio samples */ + spindlemotor_start_wav = load_wav(spindlemotor_start_wav_filename, &spindlemotor_start_wav_samples); + spindlemotor_loop_wav = load_wav(spindlemotor_loop_wav_filename, &spindlemotor_loop_wav_samples); + spindlemotor_stop_wav = load_wav(spindlemotor_stop_wav_filename, &spindlemotor_stop_wav_samples); + + /* Initialize audio state for all drives */ + for (i = 0; i < FDD_NUM; i++) { + spindlemotor_pos[i] = 0; + spindlemotor_state[i] = MOTOR_STATE_STOPPED; + spindlemotor_fade_volume[i] = 1.0f; + spindlemotor_fade_samples_remaining[i] = 0; + } + + /* Initialize sound thread */ + sound_fdd_thread_init(); +} + +void +fdd_audio_close(void) +{ + /* Free loaded audio samples */ + if (spindlemotor_start_wav) { + free(spindlemotor_start_wav); + spindlemotor_start_wav = NULL; + } + if (spindlemotor_loop_wav) { + free(spindlemotor_loop_wav); + spindlemotor_loop_wav = NULL; + } + if (spindlemotor_stop_wav) { + free(spindlemotor_stop_wav); + spindlemotor_stop_wav = NULL; + } + + /* TODO: Free seek sound arrays when they are implemented */ + + /* End sound thread */ + sound_fdd_thread_end(); +} + +void +fdd_audio_set_motor_enable(int drive, int motor_enable) +{ + if (motor_enable && !motoron[drive]) { + /* Motor starting up */ + if (spindlemotor_state[drive] == MOTOR_STATE_STOPPING) { + /* Interrupt stop sequence and transition back to loop */ + spindlemotor_state[drive] = MOTOR_STATE_RUNNING; + spindlemotor_pos[drive] = 0; + spindlemotor_fade_volume[drive] = 1.0f; + spindlemotor_fade_samples_remaining[drive] = 0; + } else { + /* Normal startup */ + spindlemotor_state[drive] = MOTOR_STATE_STARTING; + spindlemotor_pos[drive] = 0; + spindlemotor_fade_volume[drive] = 1.0f; + spindlemotor_fade_samples_remaining[drive] = 0; + } + } else if (!motor_enable && motoron[drive]) { + /* Motor stopping */ + spindlemotor_state[drive] = MOTOR_STATE_STOPPING; + spindlemotor_pos[drive] = 0; + spindlemotor_fade_volume[drive] = 1.0f; + spindlemotor_fade_samples_remaining[drive] = FADE_SAMPLES; + /* Don't disable timer yet - let the stop sound finish */ + } +} + +void +fdd_audio_callback(int16_t *buffer, int length) +{ + /* Clear buffer */ + memset(buffer, 0, length * sizeof(int16_t)); + + /* Check if any motor is running or transitioning */ + int any_motor_active = 0; + for (int drive = 0; drive < FDD_NUM; drive++) { + if (spindlemotor_state[drive] != MOTOR_STATE_STOPPED) { + any_motor_active = 1; + break; + } + } + + if (!any_motor_active) + return; + + float *float_buffer = (float *) buffer; + int samples_in_buffer = length / 2; + + /* Process audio for drive 0 only for now */ + int drive = 0; + + if (spindlemotor_state[drive] == MOTOR_STATE_STOPPED) + return; + + for (int i = 0; i < samples_in_buffer; i++) { + float left_sample = 0.0f; + float right_sample = 0.0f; + + switch (spindlemotor_state[drive]) { + case MOTOR_STATE_STARTING: + if (spindlemotor_start_wav && spindlemotor_pos[drive] < spindlemotor_start_wav_samples) { + /* Play start sound */ + left_sample = (float) spindlemotor_start_wav[spindlemotor_pos[drive] * 2] / 32768.0f; + right_sample = (float) spindlemotor_start_wav[spindlemotor_pos[drive] * 2 + 1] / 32768.0f; + spindlemotor_pos[drive]++; + } else { + /* Start sound finished, transition to loop */ + spindlemotor_state[drive] = MOTOR_STATE_RUNNING; + spindlemotor_pos[drive] = 0; + } + break; + + case MOTOR_STATE_RUNNING: + if (spindlemotor_loop_wav && spindlemotor_loop_wav_samples > 0) { + /* Play loop sound */ + left_sample = (float) spindlemotor_loop_wav[spindlemotor_pos[drive] * 2] / 32768.0f; + right_sample = (float) spindlemotor_loop_wav[spindlemotor_pos[drive] * 2 + 1] / 32768.0f; + spindlemotor_pos[drive]++; + + /* Loop back to beginning */ + if (spindlemotor_pos[drive] >= spindlemotor_loop_wav_samples) { + spindlemotor_pos[drive] = 0; + } + } + break; + + case MOTOR_STATE_STOPPING: + if (spindlemotor_fade_samples_remaining[drive] > 0) { + /* Mix fading loop sound with rising stop sound */ + float loop_volume = spindlemotor_fade_volume[drive]; + float stop_volume = 1.0f - loop_volume; + + float loop_left = 0.0f, loop_right = 0.0f; + float stop_left = 0.0f, stop_right = 0.0f; + + /* Get loop sample (continue from current position) */ + if (spindlemotor_loop_wav && spindlemotor_loop_wav_samples > 0) { + int loop_pos = spindlemotor_pos[drive] % spindlemotor_loop_wav_samples; + loop_left = (float) spindlemotor_loop_wav[loop_pos * 2] / 32768.0f; + loop_right = (float) spindlemotor_loop_wav[loop_pos * 2 + 1] / 32768.0f; + } + + /* Get stop sample */ + if (spindlemotor_stop_wav && spindlemotor_pos[drive] < spindlemotor_stop_wav_samples) { + stop_left = (float) spindlemotor_stop_wav[spindlemotor_pos[drive] * 2] / 32768.0f; + stop_right = (float) spindlemotor_stop_wav[spindlemotor_pos[drive] * 2 + 1] / 32768.0f; + } + + /* Mix the sounds */ + left_sample = loop_left * loop_volume + stop_left * stop_volume; + right_sample = loop_right * loop_volume + stop_right * stop_volume; + + spindlemotor_pos[drive]++; + spindlemotor_fade_samples_remaining[drive]--; + + /* Update fade volume */ + spindlemotor_fade_volume[drive] = (float) spindlemotor_fade_samples_remaining[drive] / FADE_SAMPLES; + } else { + /* Fade completed, play remaining stop sound */ + if (spindlemotor_stop_wav && spindlemotor_pos[drive] < spindlemotor_stop_wav_samples) { + left_sample = (float) spindlemotor_stop_wav[spindlemotor_pos[drive] * 2] / 32768.0f; + right_sample = (float) spindlemotor_stop_wav[spindlemotor_pos[drive] * 2 + 1] / 32768.0f; + spindlemotor_pos[drive]++; + } else { + /* Stop sound finished */ + spindlemotor_state[drive] = MOTOR_STATE_STOPPED; + timer_disable(&fdd_poll_time[drive]); + } + } + break; + + default: + break; + } + + float_buffer[i * 2] = left_sample; + float_buffer[i * 2 + 1] = right_sample; + } +} \ No newline at end of file diff --git a/src/include/86box/fdd.h b/src/include/86box/fdd.h index ee3b7056a..d6ade3bc6 100644 --- a/src/include/86box/fdd.h +++ b/src/include/86box/fdd.h @@ -109,7 +109,6 @@ extern void fdd_format(int drive, int side, int density, uint8_t fill); extern int fdd_hole(int drive); extern void fdd_stop(int drive); extern void fdd_do_writeback(int drive); -extern void fdd_audio_callback(int16_t *buffer, int length); extern int motorspin; extern uint64_t motoron[FDD_NUM]; diff --git a/src/include/86box/fdd_audio.h b/src/include/86box/fdd_audio.h new file mode 100644 index 000000000..6d946dbb8 --- /dev/null +++ b/src/include/86box/fdd_audio.h @@ -0,0 +1,70 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Definitions for the floppy drive audio emulation. + * + * Authors: Toni Riikonen, + * + * Copyright 2025 Toni Riikonen. + */ +#ifndef EMU_FDD_AUDIO_H +#define EMU_FDD_AUDIO_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Motor sound states */ +typedef enum { + MOTOR_STATE_STOPPED = 0, + MOTOR_STATE_STARTING, + MOTOR_STATE_RUNNING, + MOTOR_STATE_STOPPING +} motor_state_t; + +/* WAV header structure */ +typedef struct { + char riff[4]; + uint32_t size; + char wave[4]; + char fmt[4]; + uint32_t fmt_size; + uint16_t audio_format; + uint16_t num_channels; + uint32_t sample_rate; + uint32_t byte_rate; + uint16_t block_align; + uint16_t bits_per_sample; + char data[4]; + uint32_t data_size; +} wav_header_t; + +/* Fade duration: 75ms at 48kHz = 3600 samples */ +#define FADE_DURATION_MS 75 +#define FADE_SAMPLES (48000 * FADE_DURATION_MS / 1000) + +/* FDD audio initialization and cleanup */ +extern void fdd_audio_init(void); +extern void fdd_audio_close(void); + +/* Motor control for audio */ +extern void fdd_audio_set_motor_enable(int drive, int motor_enable); + +/* Audio callback function */ +extern void fdd_audio_callback(int16_t *buffer, int length); + +/* State name helper function */ +extern const char *fdd_audio_motor_state_name(motor_state_t state); + +#ifdef __cplusplus +} +#endif + +#endif /*EMU_FDD_AUDIO_H*/ \ No newline at end of file diff --git a/src/sound/sound.c b/src/sound/sound.c index eb18f9302..a57dc1d3d 100644 --- a/src/sound/sound.c +++ b/src/sound/sound.c @@ -38,7 +38,7 @@ #include <86box/timer.h> #include <86box/snd_mpu401.h> #include <86box/sound.h> -#include <86box/fdd.h> +#include <86box/fdd_audio.h> typedef struct { const device_t *device;