Moved fdd audio emulation to a separate file

This commit is contained in:
Toni Riikonen
2025-09-06 22:02:17 +03:00
parent 47ea0e594d
commit 27d7c69110
6 changed files with 421 additions and 298 deletions

View File

@@ -10,9 +10,12 @@
#
# Authors: David Hrdlička, <hrdlickadavid@outlook.com>
# Jasmine Iwanek, <jriwanek@gmail.com>
# Toni Riikonen, <riikonen.toni@gmail.com>
#
# 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)

View File

@@ -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;
}
}

339
src/floppy/fdd_audio.c Normal file
View File

@@ -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, <riikonen.toni@gmail.com>
*
* Copyright 2025 Toni Riikonen.
*/
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#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;
}
}

View File

@@ -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];

View File

@@ -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, <riikonen.toni@gmail.com>
*
* Copyright 2025 Toni Riikonen.
*/
#ifndef EMU_FDD_AUDIO_H
#define EMU_FDD_AUDIO_H
#include <stdint.h>
#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*/

View File

@@ -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;