Floppy disks sound emulation prototype for reviewing (#6166)

* Initial spindle emulation working for windows atleast

* Spingle motor spin-up, spin-down implemented with smooth transitions to motor-on loop.

* Moved fdd audio emulation to a separate file

* Multiple drives sound emulation

* Single sector movement sound emulations implemented

* Rename project to Immersive86Box and update details

Updated README to reflect the new project name and added details about the Immersive86Box features and future plans.

* Revise contribution guidelines in CONTRIBUTING.md

* Update vulnerability reporting instructions

* System fan-sound next feature after basic fdd sound emulation is ready

* v0.5 multitrack audio seek sfx

* Removed unnecessary stuff

* no .vs folder for git

* Added currently used fdd sound effects and readme.txt for source of the files and intallation instructions

* Add audio emulation installation instructions

Added instructions for audio emulation installation.

* Code and audio samples merged

* Simplify audio emulation installation instructions

* FDC seeking fixed, not instant anymore drive is set to busy during the operation and when it's finished at call fdc to set the appropriate fdc flags.

Also added time logic to fdd to calculate seek duration and a callback function for it.

* FDD sound samples volume control

* Menu options to enable / disable fdd sound for all drives.
DISABLE_FDD_AUDIO definition added, to disable the feature via cmake/build.

* Revert readme etc. changes

* Revert "Revise contribution guidelines in CONTRIBUTING.md"

This reverts commit 98a0478225bbf8bf0fb0e7edfd5c00636ecc0eed.

* Revert "Update vulnerability reporting instructions"

This reverts commit 7d32cb659b018b26bdaa4a1e06ee9c3d09278aac.

* Fixed merge issue

* Removed excess files

* Fixed PCJr seeking not to break the FDC implementation. Now seeking will take the "correct" amount of time for each system and the seek time is based on the track count. E.g. 40 track FDD system causes 40 track seek time to be 80/40 * 6ms * 40 tracks + 50ms = 480ms + 50ms -> 530ms.

80 track system full seek is 80/80 * 6ms * 80 + 50ms = 530ms, 40 track seek would take 240 + 50 = 290ms.

* Fixed PS/1, PS/2 and PS/55 FDD issues.

* FDD_AUDIO: Updating samples looked in executablePath/samples and if now found there, looks in the executable directory

* Updated installation instructions

* Removed samples path strcat use

* fdd_audio 5.25 samples and support added

* FDD audio timing/volume tunings

* Timing fixes for authentity and special longer timings for PCJr

* Fixed second drive motor keeps running when first drive is only accessed.

* Fixed PCJr random failure issue, timings

* CodeQL fix for load_wav-function. Check the filename to be proper filename

* Revert "Fixed second drive motor keeps running when first drive is only accessed."

This reverts commit 307b173ae7d40c9efafed8d432e01cce9808b111.

* Teac 5.25" drive samples added. Added per drive audio selection to FDD settings.

* Fixed mistake in samples folder recreation

---------

Co-authored-by: Toni Riikonen <domppari@hotmail.com>
This commit is contained in:
Toni Riikonen
2025-09-22 02:03:07 +03:00
committed by GitHub
parent 810f17c50f
commit 1859e7cf68
39 changed files with 1269 additions and 90 deletions

2
.gitignore vendored
View File

@@ -8,6 +8,8 @@ Makefile
/src/*.exe
/src/86Box
/src/include/86box/version.h
/src/.vs
/src/out
# Legacy Makefile
/src/*.o

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

13
samples/readme.txt Normal file
View File

@@ -0,0 +1,13 @@
These samples have been edited from Shiru's recordings.
Originals can be found here:
http://shiru.untergrund.net/files/flopster.zip
Original samples can edited and used separately from the Flopster VSTi under the
Creative Commons Attribution (CC-BY) license terms.
Installation instructions
- Currently these samples need to be manually copied to the 86box executable path under '/samples'
or to the same directory with 86box executable.
- The samples are 48kHz 16bit 1 channel PCM WAVE samples. Immersive86box code does not make any resampling,
and assumes that the host audio system is using 48kHz 16bit output.

View File

@@ -239,6 +239,7 @@ char monitor_edid_path[1024] = { 0 }; /* (C) Path to
double video_gl_input_scale = 1.0; /* (C) OpenGL 3.x input scale */
int video_gl_input_scale_mode = FULLSCR_SCALE_FULL; /* (C) OpenGL 3.x input stretch mode */
int color_scheme = 0; /* (C) Color scheme of UI (Windows-only) */
int fdd_sounds_enabled = 1; /* (C) Floppy drive sounds enabled */
// Accelerator key array
struct accelKey acc_keys[NUM_ACCELS];

View File

@@ -265,6 +265,7 @@ load_general(void)
do_auto_pause = ini_section_get_int(cat, "do_auto_pause", 0);
force_constant_mouse = ini_section_get_int(cat, "force_constant_mouse", 0);
fdd_sounds_enabled = ini_section_get_int(cat, "fdd_sounds_enabled", 1);
p = ini_section_get_string(cat, "uuid", NULL);
if (p != NULL)
@@ -1436,6 +1437,15 @@ load_floppy_and_cdrom_drives(void)
sprintf(temp, "fdd_%02i_check_bpb", c + 1);
ini_section_delete_var(cat, temp);
}
sprintf(temp, "fdd_%02i_audio", c + 1);
int def_prof = FDD_AUDIO_PROFILE_NONE;
int prof = ini_section_get_int(cat, temp, def_prof);
if (prof < 0 || prof >= FDD_AUDIO_PROFILE_MAX)
prof = def_prof;
fdd_set_audio_profile(c, prof);
if (prof == def_prof)
ini_section_delete_var(cat, temp);
for (int i = 0; i < MAX_PREV_IMAGES; i++) {
fdd_image_history[c][i] = (char *) calloc((MAX_IMAGE_PATH_LEN + 1) << 1, sizeof(char));
sprintf(temp, "fdd_%02i_image_history_%02i", c + 1, i + 1);
@@ -2479,6 +2489,11 @@ save_general(void)
else
ini_section_delete_var(cat, "force_constant_mouse");
if (fdd_sounds_enabled == 1)
ini_section_delete_var(cat, "fdd_sounds_enabled");
else
ini_section_set_int(cat, "fdd_sounds_enabled", fdd_sounds_enabled);
char cpu_buf[128] = { 0 };
plat_get_cpu_string(cpu_buf, 128);
ini_section_set_string(cat, "host_cpu", cpu_buf);
@@ -3416,6 +3431,14 @@ save_floppy_and_cdrom_drives(void)
else
save_image_file(cat, temp, fdd_image_history[c][i]);
}
sprintf(temp, "fdd_%02i_audio", c + 1);
int def_prof = FDD_AUDIO_PROFILE_NONE;
int prof = fdd_get_audio_profile(c);
if (prof == def_prof)
ini_section_delete_var(cat, temp);
else
ini_section_set_int(cat, temp, prof);
}
for (c = 0; c < CDROM_NUM; c++) {

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

@@ -11,9 +11,11 @@
*
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* Toni Riikonen, <riikonen.toni@gmail.com>
*
* Copyright 2008-2020 Sarah Walker.
* Copyright 2016-2020 Miran Grca.
* Copyright 2025 Toni Riikonen.
*/
#include <stdio.h>
#include <stdint.h>
@@ -662,6 +664,33 @@ real_drive(fdc_t *fdc, int drive)
return drive;
}
/* FDD notifies FDC when seek operation is complete */
void
fdc_seek_complete_interrupt(fdc_t *fdc, int drive)
{
if (!fdc) {
fdc_log("ERROR: fdc_seek_complete_interrupt called with NULL fdc!\n");
return;
}
if (FDC_FLAG_PCJR & fdc->flags) {
fdc->fintr = 1;
fdc->interrupt = -4;
fdc_callback(fdc);
return;
}
fdc_log("FDD %c: Seek complete interrupt\n", 0x41 + drive);
fdc->fintr = 1;
fdc->interrupt = -3;
fdc->st0 = 0x20 | (drive & 3);
if (fdd_get_head(drive))
fdc->st0 |= 0x04;
fdc_callback(fdc);
}
void
fdc_seek(fdc_t *fdc, int drive, int params)
{
@@ -1901,14 +1930,7 @@ fdc_callback(void *priv)
case 0x0f: /*Seek*/
fdc->st0 = 0x20 | (fdc->params[0] & 3);
fdc->stat = 0x80 | (1 << fdc->rw_drive);
if (fdc->flags & FDC_FLAG_PCJR) {
fdc->fintr = 1;
fdc->interrupt = -4;
timer_set_delay_u64(&fdc->timer, 1024 * TIMER_USEC);
} else {
fdc->interrupt = -3;
fdc_callback(fdc);
}
// Interrupts and callbacks in the fdd callback function
return;
case 0x10: /*Version*/
case 0x18: /*NSC*/

View File

@@ -11,16 +11,20 @@
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
* Toni Riikonen, <riikonen.toni@gmail.com>
*
* Copyright 2008-2019 Sarah Walker.
* Copyright 2016-2019 Miran Grca.
* Copyright 2018-2019 Fred N. van Kempen.
* Copyright 2025 Toni Riikonen.
*/
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include <stdlib.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/timer.h>
@@ -37,6 +41,7 @@
#include <86box/fdd_mfm.h>
#include <86box/fdd_td0.h>
#include <86box/fdc.h>
#include <86box/fdd_audio.h>
/* Flags:
Bit 0: 300 rpm supported;
@@ -78,9 +83,11 @@ char floppyfns[FDD_NUM][512];
char *fdd_image_history[FDD_NUM][FLOPPY_IMAGE_HISTORY];
pc_timer_t fdd_poll_time[FDD_NUM];
pc_timer_t fdd_seek_timer[FDD_NUM];
static int fdd_notfound = 0;
static int driveloaders[FDD_NUM];
static int fdd_audio_profile[FDD_NUM] = { 0 };
int writeprot[FDD_NUM];
int fwriteprot[FDD_NUM];
@@ -163,7 +170,7 @@ static const struct {
/* 3.5" HD, Equivalent to TEAC FD-235HF */
{ 86, FLAG_RPM_300 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP | FLAG_PS2, "3.5\" 1.44M", "35_2hd" },
/* TODO: 3.5" DD, Equivalent to TEAC FD-235GF */
// { 86, FLAG_RPM_300 | FLAG_RPM_360 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP, "3.5\" 1.25M", "35_2hd_2mode" },
// { 86, FLAG_RPM_300 | FLAG_RPM_360 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP, "3.5\" 1.25M", "35_2hd_2mode" },
/* 3.5" HD PC-98 */
{ 86, FLAG_RPM_300 | FLAG_RPM_360 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP | FLAG_INVERT_DENSEL, "3.5\" 1.25M PC-98", "35_2hd_nec" },
/* 3.5" HD 3-Mode, Equivalent to TEAC FD-235HG */
@@ -182,11 +189,32 @@ int fdd_do_log = ENABLE_FDD_LOG;
static void
fdd_log(const char *fmt, ...)
{
va_list ap;
va_list ap, ap_copy;
char timebuf[32];
char fullbuf[1056]; // 32 + 1024 bytes for timestamp + message
if (fdd_do_log) {
uint32_t ticks = plat_get_ticks();
uint32_t seconds = ticks / 1000;
uint32_t milliseconds = ticks % 1000;
snprintf(timebuf, sizeof(timebuf), "[%07u.%03u] ", seconds, milliseconds);
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_copy(ap_copy, ap);
strcpy(fullbuf, timebuf);
vsnprintf(fullbuf + strlen(timebuf), sizeof(fullbuf) - strlen(timebuf), fmt, ap_copy);
va_end(ap_copy);
va_end(ap);
va_start(ap, fmt);
va_end(ap);
char *msg = fullbuf;
va_start(ap, fmt);
pclog_ex("%s", (va_list) &msg);
va_end(ap);
}
}
@@ -194,6 +222,24 @@ fdd_log(const char *fmt, ...)
# define fdd_log(fmt, ...)
#endif
void
fdd_set_audio_profile(int drive, int profile)
{
if (drive < 0 || drive >= FDD_NUM)
return;
if (profile < 0 || profile >= FDD_AUDIO_PROFILE_MAX)
profile = 0;
fdd_audio_profile[drive] = profile;
}
int
fdd_get_audio_profile(int drive)
{
if (drive < 0 || drive >= FDD_NUM)
return 0;
return fdd_audio_profile[drive];
}
char *
fdd_getname(int type)
{
@@ -242,12 +288,26 @@ fdd_forced_seek(int drive, int track_diff)
fdd_do_seek(drive, fdd[drive].track);
}
static void
fdd_seek_complete_callback(void *priv)
{
DRIVE *drive = (DRIVE *) priv;
fdd_log("fdd_seek_complete_callback(drive=%d) - TIMER FIRED! seek_in_progress=1\n", drive->id);
fdd_log("Notifying FDC of seek completion\n");
fdd_do_seek(drive->id, fdd[drive->id].track);
fdc_seek_complete_interrupt(fdd_fdc, drive->id);
}
void
fdd_seek(int drive, int track_diff)
{
fdd_log("fdd_seek(drive=%d, track_diff=%d)\n", drive, track_diff);
if (!track_diff)
return;
int old_track = fdd[drive].track;
fdd[drive].track += track_diff;
if (fdd[drive].track < 0)
@@ -258,12 +318,36 @@ fdd_seek(int drive, int track_diff)
fdd_changed[drive] = 0;
/* Trigger appropriate audio for track movements */
int actual_track_diff = abs(old_track - fdd[drive].track);
if (actual_track_diff == 1) {
/* Single track movement */
fdd_audio_play_single_track_step(drive, old_track, fdd[drive].track);
} else if (actual_track_diff > 1) {
/* Multi-track seek */
fdd_audio_play_multi_track_seek(drive, old_track, fdd[drive].track);
}
if (old_track + track_diff < 0) {
fdd_do_seek(drive, fdd[drive].track);
return;
}
if (!fdd_seek_timer[drive].callback) {
timer_add(&(fdd_seek_timer[drive]), fdd_seek_complete_callback, &drives[drive], 0);
}
double initial_seek_time = FDC_FLAG_PCJR & fdd_fdc->flags ? 40000.0 : 15000.0;
double track_seek_time = FDC_FLAG_PCJR & fdd_fdc->flags ? 10000.0 : 6000.0;
uint64_t seek_time_us = (initial_seek_time + (abs(actual_track_diff) * track_seek_time)) * TIMER_USEC;
timer_set_delay_u64(&fdd_seek_timer[drive], seek_time_us);
}
int
fdd_track0(int drive)
{
fdd_log("fdd_track0(drive=%d)\n", drive);
/* If drive is disabled, TRK0 never gets set. */
if (!drive_types[fdd[drive].type].max_track)
return 0;
@@ -406,6 +490,7 @@ fdd_is_double_sided(int drive)
void
fdd_set_head(int drive, int head)
{
fdd_log("fdd_set_head(%d, %d)\n", drive, head);
if (head && !fdd_is_double_sided(drive))
fdd[drive].head = 0;
else
@@ -453,14 +538,13 @@ fdd_get_densel(int drive)
void
fdd_load(int drive, char *fn)
{
fdd_log("fdd_load(%d, %s)\n", drive, fn);
int c = 0;
int size;
const char *p;
FILE *fp;
int offs = 0;
fdd_log("FDD: loading drive %d with '%s'\n", drive, fn);
if (!fn)
return;
if (strstr(fn, "wp://") == fn) {
@@ -493,7 +577,6 @@ fdd_load(int drive, char *fn)
c++;
}
}
fdd_log("FDD: could not load '%s' %s\n", fn, p);
drive_empty[drive] = 1;
fdd_set_head(drive, 0);
memset(floppyfns[drive], 0, sizeof(floppyfns[drive]));
@@ -503,8 +586,6 @@ fdd_load(int drive, char *fn)
void
fdd_close(int drive)
{
fdd_log("FDD: closing drive %d\n", drive);
d86f_stop(drive); /* Call this first of all to make sure the 86F poll is back to idle state. */
if (loaders[driveloaders[drive]].close)
loaders[driveloaders[drive]].close(drive);
@@ -546,11 +627,14 @@ fdd_byteperiod(int drive)
void
fdd_set_motor_enable(int drive, int motor_enable)
{
/* I think here is where spin-up and spin-down should be implemented. */
if (motor_enable && !motoron[drive])
fdd_log("fdd_set_motor_enable(%d, %d)\n", drive, motor_enable);
fdd_audio_set_motor_enable(drive, motor_enable);
if (motor_enable && !motoron[drive]) {
timer_set_delay_u64(&fdd_poll_time[drive], fdd_byteperiod(drive));
else if (!motor_enable)
} else if (!motor_enable && motoron[drive]) {
timer_disable(&fdd_poll_time[drive]);
}
motoron[drive] = motor_enable;
}
@@ -615,6 +699,7 @@ fdd_reset(void)
void
fdd_readsector(int drive, int sector, int track, int side, int density, int sector_size)
{
fdd_log("fdd_readsector(%d, %d, %d, %d, %d, %d)\n", drive, sector, track, side, density, sector_size);
if (drives[drive].readsector)
drives[drive].readsector(drive, sector, track, side, density, sector_size);
else
@@ -624,6 +709,7 @@ fdd_readsector(int drive, int sector, int track, int side, int density, int sect
void
fdd_writesector(int drive, int sector, int track, int side, int density, int sector_size)
{
fdd_log("fdd_writesector(%d, %d, %d, %d, %d, %d)\n", drive, sector, track, side, density, sector_size);
if (drives[drive].writesector)
drives[drive].writesector(drive, sector, track, side, density, sector_size);
else
@@ -688,6 +774,10 @@ fdd_init(void)
for (i = 0; i < FDD_NUM; i++) {
fdd_load(i, floppyfns[i]);
}
if (fdd_sounds_enabled) {
fdd_audio_init();
}
}
void

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

@@ -0,0 +1,667 @@
/*
* 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>
#include <ctype.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>
#include <86box/plat.h>
#include <86box/path.h>
#ifndef DISABLE_FDD_AUDIO
/* Audio sample structure */
typedef struct {
const char *filename;
int16_t *buffer;
int samples;
float volume;
} audio_sample_t;
/* Single step audio state */
typedef struct {
int position;
int active;
} single_step_state_t;
/* Multi-track seek audio state */
typedef struct {
int position;
int active;
int duration_samples;
int from_track;
int to_track;
} multi_seek_state_t;
/* Drive type specific audio samples */
typedef struct {
audio_sample_t spindlemotor_start;
audio_sample_t spindlemotor_loop;
audio_sample_t spindlemotor_stop;
audio_sample_t single_track_step;
audio_sample_t multi_track_seek;
} drive_audio_samples_t;
/* 5.25" Teac FD-55GFR sample set */
static drive_audio_samples_t samples_teac = {
.spindlemotor_start = {
.filename = "TeacFD-55GFR_5.25_1.2MB_motor_start_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 3.0f
},
.spindlemotor_loop = {
.filename = "TeacFD-55GFR_5.25_1.2MB_motor_loop_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 3.0f
},
.spindlemotor_stop = {
.filename = "TeacFD-55GFR_5.25_1.2MB_motor_stop_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 3.0f
},
.single_track_step = {
.filename = "TeacFD-55GFR_5.25_1.2MB_track_step_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 2.0f
},
.multi_track_seek = {
.filename = "TeacFD-55GFR_5.25_1.2MB_seekupdown_80_tracks1100ms_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 2.0f
}
};
/* 3.5" drive audio samples (Mitsumi) */
static drive_audio_samples_t samples_35 = {
.spindlemotor_start = {
.filename = "mitsumi_spindle_motor_start_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 0.2f
},
.spindlemotor_loop = {
.filename = "mitsumi_spindle_motor_loop_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 0.2f
},
.spindlemotor_stop = {
.filename = "mitsumi_spindle_motor_stop_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 0.2f
},
.single_track_step = {
.filename = "mitsumi_track_step_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 1.0f
},
.multi_track_seek = {
.filename = "mitsumi_seek_80_tracks_495ms_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 1.0f
}
};
/* 5.25" drive audio samples (Panasonic) */
static drive_audio_samples_t samples_525 = {
.spindlemotor_start = {
.filename = "Panasonic_JU-475-5_5.25_1.2MB_motor_start_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 1.0f
},
.spindlemotor_loop = {
.filename = "Panasonic_JU-475-5_5.25_1.2MB_motor_loop_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 1.0f
},
.spindlemotor_stop = {
.filename = "Panasonic_JU-475-5_5.25_1.2MB_motor_stop_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 1.0f
},
.single_track_step = {
.filename = "Panasonic_JU-475-5_5.25_1.2MB_track_step_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 2.0f
},
.multi_track_seek = {
.filename = "Panasonic_JU-475-5_5.25_1.2MB_seekup_40_tracks_285ms_5ms_per_track_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 2.0f
}
};
/* 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] = {};
/* Single step audio state for each drive */
static single_step_state_t single_step_state[FDD_NUM] = {};
/* Multi-track seek audio state for each drive */
static multi_seek_state_t multi_seek_state[FDD_NUM] = {};
extern uint64_t motoron[FDD_NUM];
extern char exe_path[2048]; /* path (dir) of executable */
extern int fdd_get_audio_profile(int drive); /* from fdd.h */
/* Forward declaration */
static int16_t *load_wav(const char *filename, int *sample_count);
static drive_audio_samples_t *
get_drive_samples(int drive)
{
switch (fdd_get_audio_profile(drive)) {
case FDD_AUDIO_PROFILE_PANASONIC:
return &samples_525;
case FDD_AUDIO_PROFILE_TEAC:
return &samples_teac;
case FDD_AUDIO_PROFILE_MITSUMI:
return &samples_35;
default:
return NULL;
}
}
static int16_t *
load_wav(const char *filename, int *sample_count)
{
FILE *f = NULL;
char full_path[2048];
if (!filename || strlen(filename) == 0) {
return NULL;
}
if (strstr(filename, "..") != NULL || strchr(filename, '/') != NULL || strchr(filename, '\\') != NULL) {
return NULL;
}
for (const char *p = filename; *p; p++) {
if (!isalnum(*p) && *p != '.' && *p != '_' && *p != '-') {
return NULL;
}
}
path_append_filename(full_path, exe_path, "roms");
path_append_filename(full_path, full_path, "samples");
path_append_filename(full_path, full_path, "fdd");
path_append_filename(full_path, full_path, filename);
f = fopen(full_path, "rb");
if (!f) {
path_append_filename(full_path, exe_path, "samples");
path_append_filename(full_path, full_path, filename);
f = fopen(full_path, "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 for both drive types */
samples_35.spindlemotor_start.buffer = load_wav(samples_35.spindlemotor_start.filename, &samples_35.spindlemotor_start.samples);
samples_35.spindlemotor_loop.buffer = load_wav(samples_35.spindlemotor_loop.filename, &samples_35.spindlemotor_loop.samples);
samples_35.spindlemotor_stop.buffer = load_wav(samples_35.spindlemotor_stop.filename, &samples_35.spindlemotor_stop.samples);
samples_35.single_track_step.buffer = load_wav(samples_35.single_track_step.filename, &samples_35.single_track_step.samples);
samples_35.multi_track_seek.buffer = load_wav(samples_35.multi_track_seek.filename, &samples_35.multi_track_seek.samples);
samples_525.spindlemotor_start.buffer = load_wav(samples_525.spindlemotor_start.filename, &samples_525.spindlemotor_start.samples);
samples_525.spindlemotor_loop.buffer = load_wav(samples_525.spindlemotor_loop.filename, &samples_525.spindlemotor_loop.samples);
samples_525.spindlemotor_stop.buffer = load_wav(samples_525.spindlemotor_stop.filename, &samples_525.spindlemotor_stop.samples);
samples_525.single_track_step.buffer = load_wav(samples_525.single_track_step.filename, &samples_525.single_track_step.samples);
samples_525.multi_track_seek.buffer = load_wav(samples_525.multi_track_seek.filename, &samples_525.multi_track_seek.samples);
samples_teac.spindlemotor_start.buffer = load_wav(samples_teac.spindlemotor_start.filename, &samples_teac.spindlemotor_start.samples);
samples_teac.spindlemotor_loop.buffer = load_wav(samples_teac.spindlemotor_loop.filename, &samples_teac.spindlemotor_loop.samples);
samples_teac.spindlemotor_stop.buffer = load_wav(samples_teac.spindlemotor_stop.filename, &samples_teac.spindlemotor_stop.samples);
samples_teac.single_track_step.buffer = load_wav(samples_teac.single_track_step.filename, &samples_teac.single_track_step.samples);
samples_teac.multi_track_seek.buffer = load_wav(samples_teac.multi_track_seek.filename, &samples_teac.multi_track_seek.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 single step state */
single_step_state[i].position = 0;
single_step_state[i].active = 0;
/* Initialize multi-track seek state */
multi_seek_state[i].position = 0;
multi_seek_state[i].active = 0;
multi_seek_state[i].duration_samples = 0;
multi_seek_state[i].from_track = -1;
multi_seek_state[i].to_track = -1;
}
/* Initialize sound thread */
sound_fdd_thread_init();
}
void
fdd_audio_close(void)
{
/* Free 3.5" samples */
if (samples_35.spindlemotor_start.buffer) {
free(samples_35.spindlemotor_start.buffer);
samples_35.spindlemotor_start.buffer = NULL;
samples_35.spindlemotor_start.samples = 0;
}
if (samples_35.spindlemotor_loop.buffer) {
free(samples_35.spindlemotor_loop.buffer);
samples_35.spindlemotor_loop.buffer = NULL;
samples_35.spindlemotor_loop.samples = 0;
}
if (samples_35.spindlemotor_stop.buffer) {
free(samples_35.spindlemotor_stop.buffer);
samples_35.spindlemotor_stop.buffer = NULL;
samples_35.spindlemotor_stop.samples = 0;
}
if (samples_35.single_track_step.buffer) {
free(samples_35.single_track_step.buffer);
samples_35.single_track_step.buffer = NULL;
samples_35.single_track_step.samples = 0;
}
if (samples_35.multi_track_seek.buffer) {
free(samples_35.multi_track_seek.buffer);
samples_35.multi_track_seek.buffer = NULL;
samples_35.multi_track_seek.samples = 0;
}
/* Free 5.25" samples */
if (samples_525.spindlemotor_start.buffer) {
free(samples_525.spindlemotor_start.buffer);
samples_525.spindlemotor_start.buffer = NULL;
samples_525.spindlemotor_start.samples = 0;
}
if (samples_525.spindlemotor_loop.buffer) {
free(samples_525.spindlemotor_loop.buffer);
samples_525.spindlemotor_loop.buffer = NULL;
samples_525.spindlemotor_loop.samples = 0;
}
if (samples_525.spindlemotor_stop.buffer) {
free(samples_525.spindlemotor_stop.buffer);
samples_525.spindlemotor_stop.buffer = NULL;
samples_525.spindlemotor_stop.samples = 0;
}
if (samples_525.single_track_step.buffer) {
free(samples_525.single_track_step.buffer);
samples_525.single_track_step.buffer = NULL;
samples_525.single_track_step.samples = 0;
}
if (samples_525.multi_track_seek.buffer) {
free(samples_525.multi_track_seek.buffer);
samples_525.multi_track_seek.buffer = NULL;
samples_525.multi_track_seek.samples = 0;
}
if (samples_teac.spindlemotor_start.buffer) {
free(samples_teac.spindlemotor_start.buffer);
samples_teac.spindlemotor_start.buffer = NULL;
samples_teac.spindlemotor_start.samples = 0;
}
if (samples_teac.spindlemotor_loop.buffer) {
free(samples_teac.spindlemotor_loop.buffer);
samples_teac.spindlemotor_loop.buffer = NULL;
samples_teac.spindlemotor_loop.samples = 0;
}
if (samples_teac.spindlemotor_stop.buffer) {
free(samples_teac.spindlemotor_stop.buffer);
samples_teac.spindlemotor_stop.buffer = NULL;
samples_teac.spindlemotor_stop.samples = 0;
}
if (samples_teac.single_track_step.buffer) {
free(samples_teac.single_track_step.buffer);
samples_teac.single_track_step.buffer = NULL;
samples_teac.single_track_step.samples = 0;
}
if (samples_teac.multi_track_seek.buffer) {
free(samples_teac.multi_track_seek.buffer);
samples_teac.multi_track_seek.buffer = NULL;
samples_teac.multi_track_seek.samples = 0;
}
/* End sound thread */
sound_fdd_thread_end();
}
void
fdd_audio_set_motor_enable(int drive, int motor_enable)
{
if (!fdd_sounds_enabled)
return;
drive_audio_samples_t *samples = get_drive_samples(drive);
if (!samples)
return;
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;
}
}
void
fdd_audio_play_single_track_step(int drive, int from_track, int to_track)
{
if (!fdd_sounds_enabled)
return;
if (drive < 0 || drive >= FDD_NUM)
return;
if (abs(from_track - to_track) != 1)
return; /* Only single track movements */
single_step_state[drive].position = 0;
single_step_state[drive].active = 1;
}
void
fdd_audio_play_multi_track_seek(int drive, int from_track, int to_track)
{
if (!fdd_sounds_enabled)
return;
if (drive < 0 || drive >= FDD_NUM)
return;
int track_diff = abs(from_track - to_track);
if (track_diff <= 1)
return; /* Use single step for 1 track movements */
drive_audio_samples_t *samples = get_drive_samples(drive);
if (!samples || !samples->multi_track_seek.buffer || samples->multi_track_seek.samples == 0)
return; /* No multi-track seek sample loaded */
/* Check if a seek is already active */
if (multi_seek_state[drive].active &&
multi_seek_state[drive].from_track == from_track &&
multi_seek_state[drive].to_track == to_track) {
return;
}
/* Calculate duration based on drive type */
int duration_samples;
if (fdd_is_525(drive)) {
/* 5.25": 285ms for 40 tracks = 7.125ms per track at 48kHz sample rate */
/* 7.125ms = 0.007125s, at 48000 Hz = 342 samples per track */
duration_samples = track_diff * 342;
} else {
/* 3.5": 495ms for 80 tracks = 6.1875ms per track at 48kHz sample rate */
/* 6.1875ms = 0.0061875s, at 48000 Hz = 297 samples per track */
duration_samples = track_diff * 297;
}
/* Clamp to maximum available sample length */
if (duration_samples > samples->multi_track_seek.samples)
duration_samples = samples->multi_track_seek.samples;
/* Start new seek (or restart interrupted seek) */
multi_seek_state[drive].position = 0;
multi_seek_state[drive].active = 1;
multi_seek_state[drive].duration_samples = duration_samples;
multi_seek_state[drive].from_track = from_track;
multi_seek_state[drive].to_track = to_track;
}
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, or any audio is active */
int any_audio_active = 0;
for (int drive = 0; drive < FDD_NUM; drive++) {
if (spindlemotor_state[drive] != MOTOR_STATE_STOPPED ||
single_step_state[drive].active ||
multi_seek_state[drive].active) {
any_audio_active = 1;
break;
}
}
if (!any_audio_active)
return;
float *float_buffer = (float *) buffer;
int samples_in_buffer = length / 2;
/* Process audio for all drives */
for (int drive = 0; drive < FDD_NUM; drive++) {
drive_audio_samples_t *samples = get_drive_samples(drive);
if (!samples)
continue;
for (int i = 0; i < samples_in_buffer; i++) {
float left_sample = 0.0f;
float right_sample = 0.0f;
/* Process motor audio */
if (spindlemotor_state[drive] != MOTOR_STATE_STOPPED) {
switch (spindlemotor_state[drive]) {
case MOTOR_STATE_STARTING:
if (samples->spindlemotor_start.buffer && spindlemotor_pos[drive] < samples->spindlemotor_start.samples) {
/* Play start sound with volume control */
left_sample = (float) samples->spindlemotor_start.buffer[spindlemotor_pos[drive] * 2] / 32768.0f * samples->spindlemotor_start.volume;
right_sample = (float) samples->spindlemotor_start.buffer[spindlemotor_pos[drive] * 2 + 1] / 32768.0f * samples->spindlemotor_start.volume;
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 (samples->spindlemotor_loop.buffer && samples->spindlemotor_loop.samples > 0) {
/* Play loop sound with volume control */
left_sample = (float) samples->spindlemotor_loop.buffer[spindlemotor_pos[drive] * 2] / 32768.0f * samples->spindlemotor_loop.volume;
right_sample = (float) samples->spindlemotor_loop.buffer[spindlemotor_pos[drive] * 2 + 1] / 32768.0f * samples->spindlemotor_loop.volume;
spindlemotor_pos[drive]++;
/* Loop back to beginning */
if (spindlemotor_pos[drive] >= samples->spindlemotor_loop.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) with volume control */
if (samples->spindlemotor_loop.buffer && samples->spindlemotor_loop.samples > 0) {
int loop_pos = spindlemotor_pos[drive] % samples->spindlemotor_loop.samples;
loop_left = (float) samples->spindlemotor_loop.buffer[loop_pos * 2] / 32768.0f * samples->spindlemotor_loop.volume;
loop_right = (float) samples->spindlemotor_loop.buffer[loop_pos * 2 + 1] / 32768.0f * samples->spindlemotor_loop.volume;
}
/* Get stop sample with volume control */
if (samples->spindlemotor_stop.buffer && spindlemotor_pos[drive] < samples->spindlemotor_stop.samples) {
stop_left = (float) samples->spindlemotor_stop.buffer[spindlemotor_pos[drive] * 2] / 32768.0f * samples->spindlemotor_stop.volume;
stop_right = (float) samples->spindlemotor_stop.buffer[spindlemotor_pos[drive] * 2 + 1] / 32768.0f * samples->spindlemotor_stop.volume;
}
/* 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 with volume control */
if (samples->spindlemotor_stop.buffer && spindlemotor_pos[drive] < samples->spindlemotor_stop.samples) {
left_sample = (float) samples->spindlemotor_stop.buffer[spindlemotor_pos[drive] * 2] / 32768.0f * samples->spindlemotor_stop.volume;
right_sample = (float) samples->spindlemotor_stop.buffer[spindlemotor_pos[drive] * 2 + 1] / 32768.0f * samples->spindlemotor_stop.volume;
spindlemotor_pos[drive]++;
} else {
/* Stop sound finished */
spindlemotor_state[drive] = MOTOR_STATE_STOPPED;
/* Note: Timer disabling is handled by fdd.c, not here */
}
}
break;
default:
break;
}
}
/* Process single step audio */
if (single_step_state[drive].active) {
if (samples->single_track_step.buffer && single_step_state[drive].position < samples->single_track_step.samples) {
/* Mix step sound with motor sound with volume control */
float step_left = (float) samples->single_track_step.buffer[single_step_state[drive].position * 2] / 32768.0f * samples->single_track_step.volume;
float step_right = (float) samples->single_track_step.buffer[single_step_state[drive].position * 2 + 1] / 32768.0f * samples->single_track_step.volume;
left_sample += step_left;
right_sample += step_right;
single_step_state[drive].position++;
} else {
/* Step sound finished */
single_step_state[drive].active = 0;
single_step_state[drive].position = 0;
}
}
/* Process multi-track seek audio */
if (multi_seek_state[drive].active) {
if (samples->multi_track_seek.buffer &&
multi_seek_state[drive].position < multi_seek_state[drive].duration_samples &&
multi_seek_state[drive].position < samples->multi_track_seek.samples) {
/* Mix seek sound with motor sound with volume control */
float seek_left = (float) samples->multi_track_seek.buffer[multi_seek_state[drive].position * 2] / 32768.0f * samples->multi_track_seek.volume;
float seek_right = (float) samples->multi_track_seek.buffer[multi_seek_state[drive].position * 2 + 1] / 32768.0f * samples->multi_track_seek.volume;
left_sample += seek_left;
right_sample += seek_right;
multi_seek_state[drive].position++;
} else {
/* Seek sound finished */
multi_seek_state[drive].active = 0;
multi_seek_state[drive].position = 0;
multi_seek_state[drive].duration_samples = 0;
multi_seek_state[drive].from_track = -1;
multi_seek_state[drive].to_track = -1;
}
}
/* Mix this drive's audio into the buffer */
float_buffer[i * 2] += left_sample;
float_buffer[i * 2 + 1] += right_sample;
}
}
}
#else
void fdd_audio_init(void) {}
void fdd_audio_close(void) {}
void fdd_audio_set_motor_enable(int drive, int motor_enable) { (void) drive; (void) motor_enable; }
void fdd_audio_play_single_track_step(int drive, int from_track, int to_track) { (void) drive; (void) from_track; (void) to_track; }
void fdd_audio_play_multi_track_seek(int drive, int from_track, int to_track) { (void) drive; (void) from_track; (void) to_track; }
void fdd_audio_callback(int16_t *buffer, int length) { memset(buffer, 0, length * sizeof(int16_t)); }
#endif /* DISABLE_FDD_AUDIO */

View File

@@ -219,6 +219,7 @@ extern int monitor_edid; /* (C) Which EDID to use. 0=default,
extern char monitor_edid_path[1024]; /* (C) Path to custom EDID */
extern int color_scheme; /* (C) Color scheme of UI (Windows-only) */
extern int fdd_sounds_enabled; /* (C) Enable floppy drive sounds */
#ifndef USE_NEW_DYNAREC
extern FILE *stdlog; /* file to log output to */

View File

@@ -14,10 +14,12 @@
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
* Toni Riikonen, <riikonen.toni@gmail.com>
*
* Copyright 2008-2020 Sarah Walker.
* Copyright 2016-2020 Miran Grca.
* Copyright 2018-2020 Fred N. van Kempen.
* Copyright 2025 Toni Riikonen.
*/
#ifndef EMU_FDC_H
#define EMU_FDC_H
@@ -251,6 +253,7 @@ extern uint8_t fdc_read(uint16_t addr, void *priv);
extern void fdc_reset(void *priv);
extern uint8_t fdc_get_current_drive(void);
extern void fdc_seek_complete_interrupt(fdc_t *fdc, int drive);
#ifdef EMU_DEVICE_H
extern const device_t fdc_xt_device;

View File

@@ -11,10 +11,12 @@
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
* Toni Riikonen, <riikonen.toni@gmail.com>
*
* Copyright 2008-2025 Sarah Walker.
* Copyright 2016-2025 Miran Grca.
* Copyright 2018-2025 Fred N. van Kempen.
* Copyright 2025 Toni Riikonen.
*/
#ifndef EMU_FDD_H
#define EMU_FDD_H
@@ -23,6 +25,13 @@
#define FLOPPY_IMAGE_HISTORY 10
#define SEEK_RECALIBRATE -999
/* Per-drive audio profiles */
#define FDD_AUDIO_PROFILE_NONE 0
#define FDD_AUDIO_PROFILE_MITSUMI 1
#define FDD_AUDIO_PROFILE_PANASONIC 2
#define FDD_AUDIO_PROFILE_TEAC 3
#define FDD_AUDIO_PROFILE_MAX 4
#ifdef __cplusplus
extern "C" {
#endif
@@ -53,6 +62,10 @@ extern int fdd_get_check_bpb(int drive);
extern void fdd_set_type(int drive, int type);
extern int fdd_get_type(int drive);
/* New audio profile accessors */
extern void fdd_set_audio_profile(int drive, int profile);
extern int fdd_get_audio_profile(int drive);
extern int fdd_get_flags(int drive);
extern int fdd_get_densel(int drive);

View File

@@ -0,0 +1,86 @@
/*
* 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
#ifndef DISABLE_FDD_AUDIO
/* 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)
#else
typedef enum {
MOTOR_STATE_STOPPED = 0
} motor_state_t;
#endif /* DISABLE_FDD_AUDIO */
/* 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);
/* Single sector movement audio */
extern void fdd_audio_play_single_track_step(int drive, int from_track, int to_track);
/* Multi-track seek audio */
extern void fdd_audio_play_multi_track_seek(int drive, int from_track, int to_track);
/* 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

@@ -103,12 +103,16 @@ extern void sound_card_reset(void);
extern void sound_cd_thread_end(void);
extern void sound_cd_thread_reset(void);
extern void sound_fdd_thread_init(void);
extern void sound_fdd_thread_end(void);
extern void closeal(void);
extern void inital(void);
extern void givealbuffer(const void *buf);
extern void givealbuffer_music(const void *buf);
extern void givealbuffer_wt(const void *buf);
extern void givealbuffer_cd(const void *buf);
extern void givealbuffer_fdd(const void *buf, const uint32_t size);
#define sb_vibra16c_onboard_relocate_base sb_vibra16s_onboard_relocate_base
#define sb_vibra16cl_onboard_relocate_base sb_vibra16s_onboard_relocate_base

View File

@@ -129,11 +129,12 @@ SettingsFloppyCDROM::SettingsFloppyCDROM(QWidget *parent)
++i;
}
model = new QStandardItemModel(0, 3, this);
model = new QStandardItemModel(0, 4, this);
ui->tableViewFloppy->setModel(model);
model->setHeaderData(0, Qt::Horizontal, tr("Type"));
model->setHeaderData(1, Qt::Horizontal, tr("Turbo"));
model->setHeaderData(2, Qt::Horizontal, tr("Check BPB"));
model->setHeaderData(3, Qt::Horizontal, tr("Audio"));
model->insertRows(0, FDD_NUM);
/* Floppy drives category */
@@ -143,6 +144,26 @@ SettingsFloppyCDROM::SettingsFloppyCDROM(QWidget *parent)
setFloppyType(model, idx, type);
model->setData(idx.siblingAtColumn(1), fdd_get_turbo(i) > 0 ? tr("On") : tr("Off"));
model->setData(idx.siblingAtColumn(2), fdd_get_check_bpb(i) > 0 ? tr("On") : tr("Off"));
int prof = fdd_get_audio_profile(i);
QString profName;
switch (prof) {
case FDD_AUDIO_PROFILE_PANASONIC:
profName = tr("Panasonic");
break;
case FDD_AUDIO_PROFILE_TEAC:
profName = tr("Teac");
break;
case FDD_AUDIO_PROFILE_MITSUMI:
profName = tr("Mitsumi");
break;
default:
profName = tr("None");
break;
}
auto audioIdx = model->index(i, 3);
model->setData(audioIdx, profName);
model->setData(audioIdx, prof, Qt::UserRole);
}
ui->tableViewFloppy->resizeColumnsToContents();
@@ -150,7 +171,22 @@ SettingsFloppyCDROM::SettingsFloppyCDROM(QWidget *parent)
connect(ui->tableViewFloppy->selectionModel(), &QItemSelectionModel::currentRowChanged,
this, &SettingsFloppyCDROM::onFloppyRowChanged);
#ifndef DISABLE_FDD_AUDIO
ui->comboBoxFloppyAudio->setVisible(true);
ui->comboBoxFloppyAudio->addItem(tr("None"), FDD_AUDIO_PROFILE_NONE);
ui->comboBoxFloppyAudio->addItem(tr("Generic Mitsumi 3.5\" 1.44MB"), FDD_AUDIO_PROFILE_MITSUMI);
ui->comboBoxFloppyAudio->addItem(tr("Panasonic JU-475-5 5.25\" 1.2MB"), FDD_AUDIO_PROFILE_PANASONIC);
ui->comboBoxFloppyAudio->addItem(tr("Teac FD-55GFR 5.25\" 1.2MB"), FDD_AUDIO_PROFILE_TEAC);
ui->comboBoxFloppyAudio->setSizeAdjustPolicy(QComboBox::AdjustToContents);
#else
ui->comboBoxFloppyAudio->setVisible(false);
#endif
// Set initial selection and trigger the row changed event to update controls
ui->tableViewFloppy->setCurrentIndex(model->index(0, 0));
// Manually trigger the row changed event to ensure audio selection is updated
onFloppyRowChanged(model->index(0, 0));
cdrom_disabled_icon = QIcon(":/settings/qt/icons/cdrom_disabled.ico");
cdrom_icon = QIcon(":/settings/qt/icons/cdrom.ico");
@@ -233,6 +269,9 @@ SettingsFloppyCDROM::save()
fdd_set_type(i, model->index(i, 0).data(Qt::UserRole).toInt());
fdd_set_turbo(i, model->index(i, 1).data() == tr("On") ? 1 : 0);
fdd_set_check_bpb(i, model->index(i, 2).data() == tr("On") ? 1 : 0);
#ifndef DISABLE_FDD_AUDIO
fdd_set_audio_profile(i, model->index(i, 3).data(Qt::UserRole).toInt());
#endif
}
/* Removable devices category */
@@ -250,6 +289,12 @@ SettingsFloppyCDROM::save()
cdrom[i].speed = model->index(i, 1).data(Qt::UserRole).toUInt();
cdrom_set_type(i, model->index(i, 2).data(Qt::UserRole).toInt());
}
#ifdef DISABLE_FDD_AUDIO
fdd_sounds_enabled = 0;
#else
fdd_sounds_enabled = 1;
#endif
}
void
@@ -259,6 +304,10 @@ SettingsFloppyCDROM::onFloppyRowChanged(const QModelIndex &current)
ui->comboBoxFloppyType->setCurrentIndex(type);
ui->checkBoxTurboTimings->setChecked(current.siblingAtColumn(1).data() == tr("On"));
ui->checkBoxCheckBPB->setChecked(current.siblingAtColumn(2).data() == tr("On"));
int prof = current.siblingAtColumn(3).data(Qt::UserRole).toInt();
int comboIndex = ui->comboBoxFloppyAudio->findData(prof);
ui->comboBoxFloppyAudio->setCurrentIndex(comboIndex);
}
void
@@ -330,6 +379,7 @@ SettingsFloppyCDROM::on_checkBoxCheckBPB_stateChanged(int arg1)
tr("On") : tr("Off"));
}
void
SettingsFloppyCDROM::on_comboBoxFloppyType_activated(int index)
{
@@ -337,6 +387,34 @@ SettingsFloppyCDROM::on_comboBoxFloppyType_activated(int index)
ui->tableViewFloppy->selectionModel()->currentIndex(), index);
}
void
SettingsFloppyCDROM::on_comboBoxFloppyAudio_activated(int)
{
auto idx = ui->tableViewFloppy->selectionModel()->currentIndex();
int prof = ui->comboBoxFloppyAudio->currentData().toInt();
QString profName;
switch (prof) {
case FDD_AUDIO_PROFILE_NONE:
profName = tr("None");
break;
case FDD_AUDIO_PROFILE_PANASONIC:
profName = tr("Panasonic");
break;
case FDD_AUDIO_PROFILE_TEAC:
profName = tr("Teac");
break;
case FDD_AUDIO_PROFILE_MITSUMI:
profName = tr("Mitsumi");
break;
default:
profName = tr("None");
break;
}
auto audioIdx = idx.siblingAtColumn(3);
ui->tableViewFloppy->model()->setData(audioIdx, profName);
ui->tableViewFloppy->model()->setData(audioIdx, prof, Qt::UserRole);
}
void SettingsFloppyCDROM::reloadBusChannels() {
auto selected = ui->comboBoxChannel->currentIndex();
Harddrives::populateBusChannels(ui->comboBoxChannel->model(), ui->comboBoxBus->currentData().toInt(), Harddrives::busTrackClass);

View File

@@ -26,6 +26,7 @@ private slots:
void on_comboBoxFloppyType_activated(int index);
void on_checkBoxTurboTimings_stateChanged(int arg1);
void on_checkBoxCheckBPB_stateChanged(int arg1);
void on_comboBoxFloppyAudio_activated(int index);
void onCDROMRowChanged(const QModelIndex &current);
void on_comboBoxBus_activated(int index);

View File

@@ -66,6 +66,8 @@
</item>
<item>
<widget class="QWidget" name="floppyControls" native="true">
<layout class="QVBoxLayout" name="floppyVerticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="labelFloppyType">
@@ -96,6 +98,39 @@
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="audioLayout">
<item>
<widget class="QLabel" name="labelFloppyAudio">
<property name="text">
<string>Audio:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxFloppyAudio">
<property name="maxVisibleItems">
<number>10</number>
</property>
</widget>
</item>
<item>
<spacer name="audioSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
@@ -205,6 +240,7 @@
<tabstop>comboBoxFloppyType</tabstop>
<tabstop>checkBoxTurboTimings</tabstop>
<tabstop>checkBoxCheckBPB</tabstop>
<tabstop>comboBoxFloppyAudio</tabstop>
<tabstop>tableViewCDROM</tabstop>
<tabstop>comboBoxBus</tabstop>
<tabstop>comboBoxChannel</tabstop>

View File

@@ -36,15 +36,17 @@
#define I_MUSIC 1
#define I_WT 2
#define I_CD 3
#define I_MIDI 4
#define I_FDD 4
#define I_MIDI 5
static int audio[6] = {-1, -1, -1, -1, -1, -1};
static int audio[5] = { -1, -1, -1, -1, -1 };
#ifdef USE_NEW_API
static struct audio_swpar info[5];
#else
static audio_info_t info[5];
static audio_info_t info[6];
#endif
static int freqs[5] = { SOUND_FREQ, MUSIC_FREQ, WT_FREQ, CD_FREQ, 0 };
static int freqs[6] = {SOUND_FREQ, MUSIC_FREQ, WT_FREQ, CD_FREQ, SOUND_FREQ, 0};
void
closeal(void)
@@ -165,6 +167,12 @@ givealbuffer_cd(const void *buf)
givealbuffer_common(buf, I_CD, CD_BUFLEN << 1);
}
void
givealbuffer_fdd(const void *buf, const uint32_t size)
{
givealbuffer_common(buf, I_FDD, (int) size);
}
void
givealbuffer_midi(const void *buf, const uint32_t size)
{

View File

@@ -39,14 +39,16 @@
#define I_MUSIC 1
#define I_WT 2
#define I_CD 3
#define I_MIDI 4
#define I_FDD 4
#define I_MIDI 5
ALuint buffers[4]; /* front and back buffers */
ALuint buffers_music[4]; /* front and back buffers */
ALuint buffers_wt[4]; /* front and back buffers */
ALuint buffers_cd[4]; /* front and back buffers */
ALuint buffers_fdd[4]; /* front and back buffers */
ALuint buffers_midi[4]; /* front and back buffers */
static ALuint source[5]; /* audio source */
static ALuint source[6]; /* audio source - CHANGED FROM 5 TO 6 */
static int midi_freq = 44100;
static int midi_buf_size = 4410;
@@ -103,8 +105,9 @@ closeal(void)
alSourceStopv(sources, source);
alDeleteSources(sources, source);
if (sources == 4)
if (sources >= 6)
alDeleteBuffers(4, buffers_midi);
alDeleteBuffers(4, buffers_fdd);
alDeleteBuffers(4, buffers_cd);
alDeleteBuffers(4, buffers_music);
alDeleteBuffers(4, buffers);
@@ -122,11 +125,13 @@ inital(void)
float *wt_buf = NULL;
float *cd_buf = NULL;
float *midi_buf = NULL;
float *fdd_buf = NULL;
int16_t *buf_int16 = NULL;
int16_t *music_buf_int16 = NULL;
int16_t *wt_buf_int16 = NULL;
int16_t *cd_buf_int16 = NULL;
int16_t *midi_buf_int16 = NULL;
int16_t *fdd_buf_int16 = NULL;
int init_midi = 0;
@@ -140,13 +145,14 @@ inital(void)
if ((strcmp(mdn, "none") != 0) && (strcmp(mdn, SYSTEM_MIDI_INTERNAL_NAME) != 0))
init_midi = 1; /* If the device is neither none, nor system MIDI, initialize the
MIDI buffer and source, otherwise, do not. */
sources = 4 + !!init_midi;
sources = 5 + !!init_midi;
if (sound_is_float) {
buf = (float *) calloc((BUFLEN << 1), sizeof(float));
music_buf = (float *) calloc((MUSICBUFLEN << 1), sizeof(float));
wt_buf = (float *) calloc((WTBUFLEN << 1), sizeof(float));
cd_buf = (float *) calloc((CD_BUFLEN << 1), sizeof(float));
fdd_buf = (float *) calloc((BUFLEN << 1), sizeof(float));
if (init_midi)
midi_buf = (float *) calloc(midi_buf_size, sizeof(float));
} else {
@@ -154,17 +160,22 @@ inital(void)
music_buf_int16 = (int16_t *) calloc((MUSICBUFLEN << 1), sizeof(int16_t));
wt_buf_int16 = (int16_t *) calloc((WTBUFLEN << 1), sizeof(int16_t));
cd_buf_int16 = (int16_t *) calloc((CD_BUFLEN << 1), sizeof(int16_t));
fdd_buf_int16 = (int16_t *) calloc((BUFLEN << 1), sizeof(int16_t));
if (init_midi)
midi_buf_int16 = (int16_t *) calloc(midi_buf_size, sizeof(int16_t));
}
alGenBuffers(4, buffers);
alGenBuffers(4, buffers_cd);
alGenBuffers(4, buffers_fdd);
alGenBuffers(4, buffers_music);
alGenBuffers(4, buffers_wt);
if (init_midi)
alGenBuffers(4, buffers_midi);
// Create sources: 0=main, 1=music, 2=wt, 3=cd, 4=fdd, 5=midi(optional)
alGenSources(sources, source);
if (init_midi)
alGenSources(5, source);
else
@@ -194,6 +205,12 @@ inital(void)
alSourcef(source[I_CD], AL_ROLLOFF_FACTOR, 0.0f);
alSourcei(source[I_CD], AL_SOURCE_RELATIVE, AL_TRUE);
alSource3f(source[I_FDD], AL_POSITION, 0.0f, 0.0f, 0.0f);
alSource3f(source[I_FDD], AL_VELOCITY, 0.0f, 0.0f, 0.0f);
alSource3f(source[I_FDD], AL_DIRECTION, 0.0f, 0.0f, 0.0f);
alSourcef(source[I_FDD], AL_ROLLOFF_FACTOR, 0.0f);
alSourcei(source[I_FDD], AL_SOURCE_RELATIVE, AL_TRUE);
if (init_midi) {
alSource3f(source[I_MIDI], AL_POSITION, 0.0f, 0.0f, 0.0f);
alSource3f(source[I_MIDI], AL_VELOCITY, 0.0f, 0.0f, 0.0f);
@@ -207,6 +224,7 @@ inital(void)
memset(cd_buf, 0, CD_BUFLEN * 2 * sizeof(float));
memset(music_buf, 0, MUSICBUFLEN * 2 * sizeof(float));
memset(wt_buf, 0, WTBUFLEN * 2 * sizeof(float));
memset(fdd_buf, 0, BUFLEN * 2 * sizeof(float));
if (init_midi)
memset(midi_buf, 0, midi_buf_size * sizeof(float));
} else {
@@ -214,6 +232,7 @@ inital(void)
memset(cd_buf_int16, 0, CD_BUFLEN * 2 * sizeof(int16_t));
memset(music_buf_int16, 0, MUSICBUFLEN * 2 * sizeof(int16_t));
memset(wt_buf_int16, 0, WTBUFLEN * 2 * sizeof(int16_t));
memset(fdd_buf_int16, 0, BUFLEN * 2 * sizeof(int16_t));
if (init_midi)
memset(midi_buf_int16, 0, midi_buf_size * sizeof(int16_t));
}
@@ -224,6 +243,7 @@ inital(void)
alBufferData(buffers_music[c], AL_FORMAT_STEREO_FLOAT32, music_buf, MUSICBUFLEN * 2 * sizeof(float), MUSIC_FREQ);
alBufferData(buffers_wt[c], AL_FORMAT_STEREO_FLOAT32, wt_buf, WTBUFLEN * 2 * sizeof(float), WT_FREQ);
alBufferData(buffers_cd[c], AL_FORMAT_STEREO_FLOAT32, cd_buf, CD_BUFLEN * 2 * sizeof(float), CD_FREQ);
alBufferData(buffers_fdd[c], AL_FORMAT_STEREO_FLOAT32, fdd_buf, BUFLEN * 2 * sizeof(float), FREQ);
if (init_midi)
alBufferData(buffers_midi[c], AL_FORMAT_STEREO_FLOAT32, midi_buf, midi_buf_size * (int) sizeof(float), midi_freq);
} else {
@@ -231,6 +251,7 @@ inital(void)
alBufferData(buffers_music[c], AL_FORMAT_STEREO16, music_buf_int16, MUSICBUFLEN * 2 * sizeof(int16_t), MUSIC_FREQ);
alBufferData(buffers_wt[c], AL_FORMAT_STEREO16, wt_buf_int16, WTBUFLEN * 2 * sizeof(int16_t), WT_FREQ);
alBufferData(buffers_cd[c], AL_FORMAT_STEREO16, cd_buf_int16, CD_BUFLEN * 2 * sizeof(int16_t), CD_FREQ);
alBufferData(buffers_fdd[c], AL_FORMAT_STEREO16, fdd_buf_int16, BUFLEN * 2 * sizeof(int16_t), FREQ);
if (init_midi)
alBufferData(buffers_midi[c], AL_FORMAT_STEREO16, midi_buf_int16, midi_buf_size * (int) sizeof(int16_t), midi_freq);
}
@@ -240,12 +261,14 @@ inital(void)
alSourceQueueBuffers(source[I_MUSIC], 4, buffers_music);
alSourceQueueBuffers(source[I_WT], 4, buffers_wt);
alSourceQueueBuffers(source[I_CD], 4, buffers_cd);
alSourceQueueBuffers(source[I_FDD], 4, buffers_fdd);
if (init_midi)
alSourceQueueBuffers(source[I_MIDI], 4, buffers_midi);
alSourcePlay(source[I_NORMAL]);
alSourcePlay(source[I_MUSIC]);
alSourcePlay(source[I_WT]);
alSourcePlay(source[I_CD]);
alSourcePlay(source[I_FDD]);
if (init_midi)
alSourcePlay(source[I_MIDI]);
@@ -256,6 +279,7 @@ inital(void)
free(wt_buf);
free(music_buf);
free(buf);
free(fdd_buf);
} else {
if (init_midi)
free(midi_buf_int16);
@@ -263,6 +287,7 @@ inital(void)
free(wt_buf_int16);
free(music_buf_int16);
free(buf_int16);
free(fdd_buf_int16);
}
initialized = 1;
@@ -327,5 +352,11 @@ givealbuffer_cd(const void *buf)
void
givealbuffer_midi(const void *buf, const uint32_t size)
{
givealbuffer_common(buf, 4, (int) size, midi_freq);
givealbuffer_common(buf, 5, (int) size, midi_freq);
}
void
givealbuffer_fdd(const void *buf, const uint32_t size)
{
givealbuffer_common(buf, 4, (int) size, FREQ);
}

View File

@@ -18,7 +18,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sndio.h>
#include <86box/86box.h>
@@ -30,10 +29,11 @@
#define I_WT 2
#define I_CD 3
#define I_MIDI 4
#define I_FDD 5
static struct sio_hdl* audio[5] = { NULL, NULL, NULL, NULL, NULL };
static struct sio_par info[5];
static int freqs[5] = { SOUND_FREQ, MUSIC_FREQ, WT_FREQ, CD_FREQ, 0 };
static struct sio_hdl* audio[6] = {NULL, NULL, NULL, NULL, NULL, NULL};
static struct sio_par info[6];
static int freqs[6] = { SOUND_FREQ, MUSIC_FREQ, WT_FREQ, CD_FREQ, SOUND_FREQ, 0 };
void
closeal(void)
@@ -148,6 +148,12 @@ givealbuffer_midi(const void *buf, const uint32_t size)
givealbuffer_common(buf, I_MIDI, (int) size);
}
void
givealbuffer_fdd(const void *buf, const uint32_t size)
{
givealbuffer_common(buf, I_FDD, (int) size);
}
void
al_set_midi(const int freq, UNUSED(const int buf_size))
{

View File

@@ -36,6 +36,7 @@
#include <86box/timer.h>
#include <86box/snd_mpu401.h>
#include <86box/sound.h>
#include <86box/fdd_audio.h>
typedef struct {
const device_t *device;
@@ -89,6 +90,13 @@ static int cd_buf_update = CD_BUFLEN / SOUNDBUFLEN;
static volatile int cdaudioon = 0;
static int cd_thread_enable = 0;
static thread_t *sound_fdd_thread_h;
static event_t *sound_fdd_event;
static event_t *sound_fdd_start_event;
static int16_t fdd_out_buffer[SOUNDBUFLEN * 2];
static volatile int fddaudioon = 0;
static int fdd_thread_enable = 0;
static void (*filter_cd_audio)(int channel, double *buffer, void *priv) = NULL;
static void *filter_cd_audio_p = NULL;
@@ -597,6 +605,9 @@ sound_poll(UNUSED(void *priv))
}
}
if (fdd_thread_enable) {
thread_set_event(sound_fdd_event);
}
sound_pos_global = 0;
}
}
@@ -698,17 +709,14 @@ sound_reset(void)
inital();
timer_add(&sound_poll_timer, sound_poll, NULL, 1);
sound_handlers_num = 0;
memset(sound_handlers, 0x00, 8 * sizeof(sound_handler_t));
timer_add(&music_poll_timer, music_poll, NULL, 1);
music_handlers_num = 0;
memset(music_handlers, 0x00, 8 * sizeof(sound_handler_t));
timer_add(&wavetable_poll_timer, wavetable_poll, NULL, 1);
wavetable_handlers_num = 0;
memset(wavetable_handlers, 0x00, 8 * sizeof(sound_handler_t));
@@ -785,3 +793,61 @@ sound_cd_thread_reset(void)
cd_thread_enable = available_cdrom_drives ? 1 : 0;
}
static void
sound_fdd_thread(UNUSED(void *param))
{
thread_set_event(sound_fdd_start_event);
while (fddaudioon) {
thread_wait_event(sound_fdd_event, -1);
thread_reset_event(sound_fdd_event);
if (!fddaudioon)
break;
static float fdd_float_buffer[SOUNDBUFLEN * 2];
memset(fdd_float_buffer, 0, sizeof(fdd_float_buffer));
fdd_audio_callback((int16_t*)fdd_float_buffer, SOUNDBUFLEN * 2);
givealbuffer_fdd(fdd_float_buffer, SOUNDBUFLEN * 2);
}
}
void
sound_fdd_thread_init(void)
{
if (!fddaudioon) {
fddaudioon = 1;
fdd_thread_enable = 1;
sound_fdd_start_event = thread_create_event();
sound_fdd_event = thread_create_event();
sound_fdd_thread_h = thread_create(sound_fdd_thread, NULL);
thread_wait_event(sound_fdd_start_event, -1);
thread_reset_event(sound_fdd_start_event);
}
}
void
sound_fdd_thread_end(void)
{
if (fddaudioon) {
fddaudioon = 0;
fdd_thread_enable = 0;
thread_set_event(sound_fdd_event);
thread_wait(sound_fdd_thread_h);
if (sound_fdd_event) {
thread_destroy_event(sound_fdd_event);
sound_fdd_event = NULL;
}
sound_fdd_thread_h = NULL;
if (sound_fdd_start_event) {
thread_destroy_event(sound_fdd_start_event);
sound_fdd_start_event = NULL;
}
}
}

View File

@@ -53,6 +53,7 @@ static IXAudio2SourceVoice *srcvoicemusic = NULL;
static IXAudio2SourceVoice *srcvoicewt = NULL;
static IXAudio2SourceVoice *srcvoicemidi = NULL;
static IXAudio2SourceVoice *srcvoicecd = NULL;
static IXAudio2SourceVoice *srcvoicefdd = NULL;
#define FREQ SOUND_FREQ
#define BUFLEN SOUNDBUFLEN
@@ -178,11 +179,18 @@ inital(void)
(void) IXAudio2_CreateSourceVoice(xaudio2, &srcvoicecd, &fmt, 0, 2.0f, &callbacks, NULL, NULL);
fmt.nSamplesPerSec = FREQ;
fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8;
fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign;
(void) IXAudio2_CreateSourceVoice(xaudio2, &srcvoicefdd, &fmt, 0, 2.0f, &callbacks, NULL, NULL);
(void) IXAudio2SourceVoice_SetVolume(srcvoice, 1, XAUDIO2_COMMIT_NOW);
(void) IXAudio2SourceVoice_Start(srcvoice, 0, XAUDIO2_COMMIT_NOW);
(void) IXAudio2SourceVoice_Start(srcvoicecd, 0, XAUDIO2_COMMIT_NOW);
(void) IXAudio2SourceVoice_Start(srcvoicemusic, 0, XAUDIO2_COMMIT_NOW);
(void) IXAudio2SourceVoice_Start(srcvoicewt, 0, XAUDIO2_COMMIT_NOW);
(void) IXAudio2SourceVoice_Start(srcvoicefdd, 0, XAUDIO2_COMMIT_NOW);
const char *mdn = midi_out_device_get_internal_name(midi_output_device_current);
@@ -213,6 +221,8 @@ closeal(void)
(void) IXAudio2SourceVoice_FlushSourceBuffers(srcvoicewt);
(void) IXAudio2SourceVoice_Stop(srcvoicecd, 0, XAUDIO2_COMMIT_NOW);
(void) IXAudio2SourceVoice_FlushSourceBuffers(srcvoicecd);
(void) IXAudio2SourceVoice_Stop(srcvoicefdd, 0, XAUDIO2_COMMIT_NOW);
(void) IXAudio2SourceVoice_FlushSourceBuffers(srcvoicefdd);
if (srcvoicemidi) {
(void) IXAudio2SourceVoice_Stop(srcvoicemidi, 0, XAUDIO2_COMMIT_NOW);
(void) IXAudio2SourceVoice_FlushSourceBuffers(srcvoicemidi);
@@ -220,6 +230,7 @@ closeal(void)
}
IXAudio2SourceVoice_DestroyVoice(srcvoicewt);
IXAudio2SourceVoice_DestroyVoice(srcvoicecd);
IXAudio2SourceVoice_DestroyVoice(srcvoicefdd);
IXAudio2SourceVoice_DestroyVoice(srcvoicemusic);
IXAudio2SourceVoice_DestroyVoice(srcvoice);
IXAudio2MasteringVoice_DestroyVoice(mastervoice);
@@ -227,6 +238,7 @@ closeal(void)
srcvoice = NULL;
srcvoicecd = NULL;
srcvoicemidi = NULL;
srcvoicefdd = NULL;
mastervoice = NULL;
xaudio2 = NULL;
@@ -288,6 +300,18 @@ givealbuffer_cd(const void *buf)
givealbuffer_common(buf, srcvoicecd, CD_BUFLEN << 1);
}
void
givealbuffer_fdd(const void *buf, const uint32_t size)
{
if (!initialized)
return;
if (!srcvoicefdd)
return;
givealbuffer_common(buf, srcvoicefdd, size);
}
void
al_set_midi(const int freq, const int buf_size)
{