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/*.exe
/src/86Box /src/86Box
/src/include/86box/version.h /src/include/86box/version.h
/src/.vs
/src/out
# Legacy Makefile # Legacy Makefile
/src/*.o /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 */ 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 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 color_scheme = 0; /* (C) Color scheme of UI (Windows-only) */
int fdd_sounds_enabled = 1; /* (C) Floppy drive sounds enabled */
// Accelerator key array // Accelerator key array
struct accelKey acc_keys[NUM_ACCELS]; 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); do_auto_pause = ini_section_get_int(cat, "do_auto_pause", 0);
force_constant_mouse = ini_section_get_int(cat, "force_constant_mouse", 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); p = ini_section_get_string(cat, "uuid", NULL);
if (p != NULL) if (p != NULL)
@@ -1436,6 +1437,15 @@ load_floppy_and_cdrom_drives(void)
sprintf(temp, "fdd_%02i_check_bpb", c + 1); sprintf(temp, "fdd_%02i_check_bpb", c + 1);
ini_section_delete_var(cat, temp); 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++) { for (int i = 0; i < MAX_PREV_IMAGES; i++) {
fdd_image_history[c][i] = (char *) calloc((MAX_IMAGE_PATH_LEN + 1) << 1, sizeof(char)); 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); sprintf(temp, "fdd_%02i_image_history_%02i", c + 1, i + 1);
@@ -2479,6 +2489,11 @@ save_general(void)
else else
ini_section_delete_var(cat, "force_constant_mouse"); 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 }; char cpu_buf[128] = { 0 };
plat_get_cpu_string(cpu_buf, 128); plat_get_cpu_string(cpu_buf, 128);
ini_section_set_string(cat, "host_cpu", cpu_buf); ini_section_set_string(cat, "host_cpu", cpu_buf);
@@ -3416,6 +3431,14 @@ save_floppy_and_cdrom_drives(void)
else else
save_image_file(cat, temp, fdd_image_history[c][i]); 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++) { for (c = 0; c < CDROM_NUM; c++) {

View File

@@ -10,9 +10,12 @@
# #
# Authors: David Hrdlička, <hrdlickadavid@outlook.com> # Authors: David Hrdlička, <hrdlickadavid@outlook.com>
# Jasmine Iwanek, <jriwanek@gmail.com> # Jasmine Iwanek, <jriwanek@gmail.com>
# Toni Riikonen, <riikonen.toni@gmail.com>
# #
# Copyright 2020-2021 David Hrdlička. # Copyright 2020-2021 David Hrdlička.
# Copyright 2024 Jasmine Iwanek. # Copyright 2024 Jasmine Iwanek.
# Copyright 2025 Toni Riikonen.
# All rights reserved.
# #
add_library(fdd OBJECT add_library(fdd OBJECT
@@ -31,6 +34,7 @@ add_library(fdd OBJECT
fdd_pcjs.c fdd_pcjs.c
fdd_mfm.c fdd_mfm.c
fdd_td0.c fdd_td0.c
fdd_audio.c
) )
add_subdirectory(lzw) add_subdirectory(lzw)

View File

@@ -11,9 +11,11 @@
* *
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/> * Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com> * Miran Grca, <mgrca8@gmail.com>
* Toni Riikonen, <riikonen.toni@gmail.com>
* *
* Copyright 2008-2020 Sarah Walker. * Copyright 2008-2020 Sarah Walker.
* Copyright 2016-2020 Miran Grca. * Copyright 2016-2020 Miran Grca.
* Copyright 2025 Toni Riikonen.
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
@@ -662,6 +664,33 @@ real_drive(fdc_t *fdc, int drive)
return 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 void
fdc_seek(fdc_t *fdc, int drive, int params) fdc_seek(fdc_t *fdc, int drive, int params)
{ {
@@ -1229,7 +1258,7 @@ fdc_write(uint16_t addr, uint8_t val, void *priv)
case 0x0f: /* Seek */ case 0x0f: /* Seek */
fdc->rw_drive = fdc->params[0] & 3; fdc->rw_drive = fdc->params[0] & 3;
fdc->stat = (1 << fdc->drive); fdc->stat = (1 << fdc->drive);
if (!(fdc->flags & FDC_FLAG_PCJR)) if (!(fdc->flags & FDC_FLAG_PCJR))
fdc->stat |= 0x80; fdc->stat |= 0x80;
fdc->head = 0; /* TODO: See if this is correct. */ fdc->head = 0; /* TODO: See if this is correct. */
fdc->st0 = fdc->params[0] & 0x03; fdc->st0 = fdc->params[0] & 0x03;
@@ -1901,14 +1930,7 @@ fdc_callback(void *priv)
case 0x0f: /*Seek*/ case 0x0f: /*Seek*/
fdc->st0 = 0x20 | (fdc->params[0] & 3); fdc->st0 = 0x20 | (fdc->params[0] & 3);
fdc->stat = 0x80 | (1 << fdc->rw_drive); fdc->stat = 0x80 | (1 << fdc->rw_drive);
if (fdc->flags & FDC_FLAG_PCJR) { // Interrupts and callbacks in the fdd callback function
fdc->fintr = 1;
fdc->interrupt = -4;
timer_set_delay_u64(&fdc->timer, 1024 * TIMER_USEC);
} else {
fdc->interrupt = -3;
fdc_callback(fdc);
}
return; return;
case 0x10: /*Version*/ case 0x10: /*Version*/
case 0x18: /*NSC*/ case 0x18: /*NSC*/

View File

@@ -11,16 +11,20 @@
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/> * Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com> * Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com> * Fred N. van Kempen, <decwiz@yahoo.com>
* Toni Riikonen, <riikonen.toni@gmail.com>
* *
* Copyright 2008-2019 Sarah Walker. * Copyright 2008-2019 Sarah Walker.
* Copyright 2016-2019 Miran Grca. * Copyright 2016-2019 Miran Grca.
* Copyright 2018-2019 Fred N. van Kempen. * Copyright 2018-2019 Fred N. van Kempen.
* Copyright 2025 Toni Riikonen.
*/ */
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <wchar.h> #include <wchar.h>
#include <stdlib.h>
#define HAVE_STDARG_H #define HAVE_STDARG_H
#include <86box/86box.h> #include <86box/86box.h>
#include <86box/timer.h> #include <86box/timer.h>
@@ -37,6 +41,7 @@
#include <86box/fdd_mfm.h> #include <86box/fdd_mfm.h>
#include <86box/fdd_td0.h> #include <86box/fdd_td0.h>
#include <86box/fdc.h> #include <86box/fdc.h>
#include <86box/fdd_audio.h>
/* Flags: /* Flags:
Bit 0: 300 rpm supported; Bit 0: 300 rpm supported;
@@ -78,15 +83,17 @@ char floppyfns[FDD_NUM][512];
char *fdd_image_history[FDD_NUM][FLOPPY_IMAGE_HISTORY]; char *fdd_image_history[FDD_NUM][FLOPPY_IMAGE_HISTORY];
pc_timer_t fdd_poll_time[FDD_NUM]; pc_timer_t fdd_poll_time[FDD_NUM];
pc_timer_t fdd_seek_timer[FDD_NUM];
static int fdd_notfound = 0; static int fdd_notfound = 0;
static int driveloaders[FDD_NUM]; static int driveloaders[FDD_NUM];
static int fdd_audio_profile[FDD_NUM] = { 0 };
int writeprot[FDD_NUM]; int writeprot[FDD_NUM];
int fwriteprot[FDD_NUM]; int fwriteprot[FDD_NUM];
int fdd_changed[FDD_NUM]; int fdd_changed[FDD_NUM];
int ui_writeprot[FDD_NUM] = { 0, 0, 0, 0 }; int ui_writeprot[FDD_NUM] = { 0, 0, 0, 0 };
int drive_empty[FDD_NUM] = { 1, 1, 1, 1 }; int drive_empty[FDD_NUM] = { 1, 1, 1, 1 };
DRIVE drives[FDD_NUM]; DRIVE drives[FDD_NUM];
@@ -99,9 +106,9 @@ d86f_handler_t d86f_handler[FDD_NUM];
static const struct static const struct
{ {
const char *ext; const char *ext;
void (*load)(int drive, char *fn); void (*load)(int drive, char *fn);
void (*close)(int drive); void (*close)(int drive);
int size; int size;
} loaders[] = { } loaders[] = {
{ "001", img_load, img_close, -1 }, { "001", img_load, img_close, -1 },
{ "002", img_load, img_close, -1 }, { "002", img_load, img_close, -1 },
@@ -145,35 +152,35 @@ static const struct {
const char *internal_name; const char *internal_name;
} drive_types[] = { } drive_types[] = {
/* None */ /* None */
{ 0, 0, "None", "none" }, { 0, 0, "None", "none" },
/* 5.25" 1DD */ /* 5.25" 1DD */
{ 43, FLAG_RPM_300 | FLAG_525 | FLAG_HOLE0, "5.25\" 180k", "525_1dd" }, { 43, FLAG_RPM_300 | FLAG_525 | FLAG_HOLE0, "5.25\" 180k", "525_1dd" },
/* 5.25" DD */ /* 5.25" DD */
{ 43, FLAG_RPM_300 | FLAG_525 | FLAG_DS | FLAG_HOLE0, "5.25\" 360k", "525_2dd" }, { 43, FLAG_RPM_300 | FLAG_525 | FLAG_DS | FLAG_HOLE0, "5.25\" 360k", "525_2dd" },
/* 5.25" QD */ /* 5.25" QD */
{ 86, FLAG_RPM_300 | FLAG_525 | FLAG_DS | FLAG_HOLE0 | FLAG_DOUBLE_STEP, "5.25\" 720k", "525_2qd" }, { 86, FLAG_RPM_300 | FLAG_525 | FLAG_DS | FLAG_HOLE0 | FLAG_DOUBLE_STEP, "5.25\" 720k", "525_2qd" },
/* 5.25" HD */ /* 5.25" HD */
{ 86, FLAG_RPM_360 | FLAG_525 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP | FLAG_PS2, "5.25\" 1.2M", "525_2hd" }, { 86, FLAG_RPM_360 | FLAG_525 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP | FLAG_PS2, "5.25\" 1.2M", "525_2hd" },
/* 5.25" HD Dual RPM */ /* 5.25" HD Dual RPM */
{ 86, FLAG_RPM_300 | FLAG_RPM_360 | FLAG_525 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP, "5.25\" 1.2M 300/360 RPM", "525_2hd_dualrpm" }, { 86, FLAG_RPM_300 | FLAG_RPM_360 | FLAG_525 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP, "5.25\" 1.2M 300/360 RPM", "525_2hd_dualrpm" },
/* 3.5" 1DD */ /* 3.5" 1DD */
{ 86, FLAG_RPM_300 | FLAG_HOLE0 | FLAG_DOUBLE_STEP, "3.5\" 360k", "35_1dd" }, { 86, FLAG_RPM_300 | FLAG_HOLE0 | FLAG_DOUBLE_STEP, "3.5\" 360k", "35_1dd" },
/* 3.5" DD, Equivalent to TEAC FD-235F */ /* 3.5" DD, Equivalent to TEAC FD-235F */
{ 86, FLAG_RPM_300 | FLAG_DS | FLAG_HOLE0 | FLAG_DOUBLE_STEP, "3.5\" 720k", "35_2dd" }, { 86, FLAG_RPM_300 | FLAG_DS | FLAG_HOLE0 | FLAG_DOUBLE_STEP, "3.5\" 720k", "35_2dd" },
/* 3.5" HD, Equivalent to TEAC FD-235HF */ /* 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" }, { 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 */ /* 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 */ /* 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" }, { 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 */ /* 3.5" HD 3-Mode, Equivalent to TEAC FD-235HG */
{ 86, FLAG_RPM_300 | FLAG_RPM_360 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP, "3.5\" 1.44M 300/360 RPM", "35_2hd_3mode" }, { 86, FLAG_RPM_300 | FLAG_RPM_360 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_DOUBLE_STEP, "3.5\" 1.44M 300/360 RPM", "35_2hd_3mode" },
/* 3.5" ED, Equivalent to TEAC FD-235J */ /* 3.5" ED, Equivalent to TEAC FD-235J */
{ 86, FLAG_RPM_300 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_HOLE2 | FLAG_DOUBLE_STEP, "3.5\" 2.88M", "35_2ed" }, { 86, FLAG_RPM_300 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_HOLE2 | FLAG_DOUBLE_STEP, "3.5\" 2.88M", "35_2ed" },
/* 3.5" ED Dual RPM, Equivalent to TEAC FD-335J */ /* 3.5" ED Dual RPM, Equivalent to TEAC FD-335J */
{ 86, FLAG_RPM_300 | FLAG_RPM_360 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_HOLE2 | FLAG_DOUBLE_STEP, "3.5\" 2.88M 300/360 RPM", "35_2ed_dualrpm" }, { 86, FLAG_RPM_300 | FLAG_RPM_360 | FLAG_DS | FLAG_HOLE0 | FLAG_HOLE1 | FLAG_HOLE2 | FLAG_DOUBLE_STEP, "3.5\" 2.88M 300/360 RPM", "35_2ed_dualrpm" },
/* End of list */ /* End of list */
{ -1, -1, "", "" } { -1, -1, "", "" }
}; };
#ifdef ENABLE_FDD_LOG #ifdef ENABLE_FDD_LOG
@@ -182,11 +189,32 @@ int fdd_do_log = ENABLE_FDD_LOG;
static void static void
fdd_log(const char *fmt, ...) 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) { 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); 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); va_end(ap);
} }
} }
@@ -194,6 +222,24 @@ fdd_log(const char *fmt, ...)
# define fdd_log(fmt, ...) # define fdd_log(fmt, ...)
#endif #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 * char *
fdd_getname(int type) fdd_getname(int type)
{ {
@@ -242,12 +288,26 @@ fdd_forced_seek(int drive, int track_diff)
fdd_do_seek(drive, fdd[drive].track); 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 void
fdd_seek(int drive, int track_diff) fdd_seek(int drive, int track_diff)
{ {
fdd_log("fdd_seek(drive=%d, track_diff=%d)\n", drive, track_diff);
if (!track_diff) if (!track_diff)
return; return;
int old_track = fdd[drive].track;
fdd[drive].track += track_diff; fdd[drive].track += track_diff;
if (fdd[drive].track < 0) if (fdd[drive].track < 0)
@@ -258,12 +318,36 @@ fdd_seek(int drive, int track_diff)
fdd_changed[drive] = 0; fdd_changed[drive] = 0;
fdd_do_seek(drive, fdd[drive].track); /* 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 int
fdd_track0(int drive) fdd_track0(int drive)
{ {
fdd_log("fdd_track0(drive=%d)\n", drive);
/* If drive is disabled, TRK0 never gets set. */ /* If drive is disabled, TRK0 never gets set. */
if (!drive_types[fdd[drive].type].max_track) if (!drive_types[fdd[drive].type].max_track)
return 0; return 0;
@@ -406,6 +490,7 @@ fdd_is_double_sided(int drive)
void void
fdd_set_head(int drive, int head) fdd_set_head(int drive, int head)
{ {
fdd_log("fdd_set_head(%d, %d)\n", drive, head);
if (head && !fdd_is_double_sided(drive)) if (head && !fdd_is_double_sided(drive))
fdd[drive].head = 0; fdd[drive].head = 0;
else else
@@ -453,14 +538,13 @@ fdd_get_densel(int drive)
void void
fdd_load(int drive, char *fn) fdd_load(int drive, char *fn)
{ {
fdd_log("fdd_load(%d, %s)\n", drive, fn);
int c = 0; int c = 0;
int size; int size;
const char *p; const char *p;
FILE *fp; FILE *fp;
int offs = 0; int offs = 0;
fdd_log("FDD: loading drive %d with '%s'\n", drive, fn);
if (!fn) if (!fn)
return; return;
if (strstr(fn, "wp://") == fn) { if (strstr(fn, "wp://") == fn) {
@@ -493,7 +577,6 @@ fdd_load(int drive, char *fn)
c++; c++;
} }
} }
fdd_log("FDD: could not load '%s' %s\n", fn, p);
drive_empty[drive] = 1; drive_empty[drive] = 1;
fdd_set_head(drive, 0); fdd_set_head(drive, 0);
memset(floppyfns[drive], 0, sizeof(floppyfns[drive])); memset(floppyfns[drive], 0, sizeof(floppyfns[drive]));
@@ -503,8 +586,6 @@ fdd_load(int drive, char *fn)
void void
fdd_close(int drive) 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. */ d86f_stop(drive); /* Call this first of all to make sure the 86F poll is back to idle state. */
if (loaders[driveloaders[drive]].close) if (loaders[driveloaders[drive]].close)
loaders[driveloaders[drive]].close(drive); loaders[driveloaders[drive]].close(drive);
@@ -546,11 +627,14 @@ fdd_byteperiod(int drive)
void void
fdd_set_motor_enable(int drive, int motor_enable) fdd_set_motor_enable(int drive, int motor_enable)
{ {
/* I think here is where spin-up and spin-down should be implemented. */ fdd_log("fdd_set_motor_enable(%d, %d)\n", drive, motor_enable);
if (motor_enable && !motoron[drive]) fdd_audio_set_motor_enable(drive, motor_enable);
if (motor_enable && !motoron[drive]) {
timer_set_delay_u64(&fdd_poll_time[drive], fdd_byteperiod(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]); timer_disable(&fdd_poll_time[drive]);
}
motoron[drive] = motor_enable; motoron[drive] = motor_enable;
} }
@@ -615,6 +699,7 @@ fdd_reset(void)
void void
fdd_readsector(int drive, int sector, int track, int side, int density, int sector_size) 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) if (drives[drive].readsector)
drives[drive].readsector(drive, sector, track, side, density, sector_size); drives[drive].readsector(drive, sector, track, side, density, sector_size);
else else
@@ -624,6 +709,7 @@ fdd_readsector(int drive, int sector, int track, int side, int density, int sect
void void
fdd_writesector(int drive, int sector, int track, int side, int density, int sector_size) 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) if (drives[drive].writesector)
drives[drive].writesector(drive, sector, track, side, density, sector_size); drives[drive].writesector(drive, sector, track, side, density, sector_size);
else else
@@ -688,10 +774,14 @@ fdd_init(void)
for (i = 0; i < FDD_NUM; i++) { for (i = 0; i < FDD_NUM; i++) {
fdd_load(i, floppyfns[i]); fdd_load(i, floppyfns[i]);
} }
if (fdd_sounds_enabled) {
fdd_audio_init();
}
} }
void void
fdd_do_writeback(int drive) fdd_do_writeback(int drive)
{ {
d86f_handler[drive].writeback(drive); d86f_handler[drive].writeback(drive);
} }

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 char monitor_edid_path[1024]; /* (C) Path to custom EDID */
extern int color_scheme; /* (C) Color scheme of UI (Windows-only) */ 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 #ifndef USE_NEW_DYNAREC
extern FILE *stdlog; /* file to log output to */ extern FILE *stdlog; /* file to log output to */

View File

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

View File

@@ -11,10 +11,12 @@
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/> * Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com> * Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com> * Fred N. van Kempen, <decwiz@yahoo.com>
* Toni Riikonen, <riikonen.toni@gmail.com>
* *
* Copyright 2008-2025 Sarah Walker. * Copyright 2008-2025 Sarah Walker.
* Copyright 2016-2025 Miran Grca. * Copyright 2016-2025 Miran Grca.
* Copyright 2018-2025 Fred N. van Kempen. * Copyright 2018-2025 Fred N. van Kempen.
* Copyright 2025 Toni Riikonen.
*/ */
#ifndef EMU_FDD_H #ifndef EMU_FDD_H
#define EMU_FDD_H #define EMU_FDD_H
@@ -23,6 +25,13 @@
#define FLOPPY_IMAGE_HISTORY 10 #define FLOPPY_IMAGE_HISTORY 10
#define SEEK_RECALIBRATE -999 #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 #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@@ -53,6 +62,10 @@ extern int fdd_get_check_bpb(int drive);
extern void fdd_set_type(int drive, int type); extern void fdd_set_type(int drive, int type);
extern int fdd_get_type(int drive); 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_flags(int drive);
extern int fdd_get_densel(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_end(void);
extern void sound_cd_thread_reset(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 closeal(void);
extern void inital(void); extern void inital(void);
extern void givealbuffer(const void *buf); extern void givealbuffer(const void *buf);
extern void givealbuffer_music(const void *buf); extern void givealbuffer_music(const void *buf);
extern void givealbuffer_wt(const void *buf); extern void givealbuffer_wt(const void *buf);
extern void givealbuffer_cd(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_vibra16c_onboard_relocate_base sb_vibra16s_onboard_relocate_base
#define sb_vibra16cl_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; ++i;
} }
model = new QStandardItemModel(0, 3, this); model = new QStandardItemModel(0, 4, this);
ui->tableViewFloppy->setModel(model); ui->tableViewFloppy->setModel(model);
model->setHeaderData(0, Qt::Horizontal, tr("Type")); model->setHeaderData(0, Qt::Horizontal, tr("Type"));
model->setHeaderData(1, Qt::Horizontal, tr("Turbo")); model->setHeaderData(1, Qt::Horizontal, tr("Turbo"));
model->setHeaderData(2, Qt::Horizontal, tr("Check BPB")); model->setHeaderData(2, Qt::Horizontal, tr("Check BPB"));
model->setHeaderData(3, Qt::Horizontal, tr("Audio"));
model->insertRows(0, FDD_NUM); model->insertRows(0, FDD_NUM);
/* Floppy drives category */ /* Floppy drives category */
@@ -143,6 +144,26 @@ SettingsFloppyCDROM::SettingsFloppyCDROM(QWidget *parent)
setFloppyType(model, idx, type); setFloppyType(model, idx, type);
model->setData(idx.siblingAtColumn(1), fdd_get_turbo(i) > 0 ? tr("On") : tr("Off")); 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")); 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(); ui->tableViewFloppy->resizeColumnsToContents();
@@ -150,7 +171,22 @@ SettingsFloppyCDROM::SettingsFloppyCDROM(QWidget *parent)
connect(ui->tableViewFloppy->selectionModel(), &QItemSelectionModel::currentRowChanged, connect(ui->tableViewFloppy->selectionModel(), &QItemSelectionModel::currentRowChanged,
this, &SettingsFloppyCDROM::onFloppyRowChanged); 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)); 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_disabled_icon = QIcon(":/settings/qt/icons/cdrom_disabled.ico");
cdrom_icon = QIcon(":/settings/qt/icons/cdrom.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_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_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); 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 */ /* Removable devices category */
@@ -250,6 +289,12 @@ SettingsFloppyCDROM::save()
cdrom[i].speed = model->index(i, 1).data(Qt::UserRole).toUInt(); cdrom[i].speed = model->index(i, 1).data(Qt::UserRole).toUInt();
cdrom_set_type(i, model->index(i, 2).data(Qt::UserRole).toInt()); 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 void
@@ -259,6 +304,10 @@ SettingsFloppyCDROM::onFloppyRowChanged(const QModelIndex &current)
ui->comboBoxFloppyType->setCurrentIndex(type); ui->comboBoxFloppyType->setCurrentIndex(type);
ui->checkBoxTurboTimings->setChecked(current.siblingAtColumn(1).data() == tr("On")); ui->checkBoxTurboTimings->setChecked(current.siblingAtColumn(1).data() == tr("On"));
ui->checkBoxCheckBPB->setChecked(current.siblingAtColumn(2).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 void
@@ -318,7 +367,7 @@ void
SettingsFloppyCDROM::on_checkBoxTurboTimings_stateChanged(int arg1) SettingsFloppyCDROM::on_checkBoxTurboTimings_stateChanged(int arg1)
{ {
auto idx = ui->tableViewFloppy->selectionModel()->currentIndex(); auto idx = ui->tableViewFloppy->selectionModel()->currentIndex();
ui->tableViewFloppy->model()->setData(idx.siblingAtColumn(1), arg1 == Qt::Checked ? ui->tableViewFloppy->model()->setData(idx.siblingAtColumn(1), arg1 == Qt::Checked ?
tr("On") : tr("Off")); tr("On") : tr("Off"));
} }
@@ -326,10 +375,11 @@ void
SettingsFloppyCDROM::on_checkBoxCheckBPB_stateChanged(int arg1) SettingsFloppyCDROM::on_checkBoxCheckBPB_stateChanged(int arg1)
{ {
auto idx = ui->tableViewFloppy->selectionModel()->currentIndex(); auto idx = ui->tableViewFloppy->selectionModel()->currentIndex();
ui->tableViewFloppy->model()->setData(idx.siblingAtColumn(2), arg1 == Qt::Checked ? ui->tableViewFloppy->model()->setData(idx.siblingAtColumn(2), arg1 == Qt::Checked ?
tr("On") : tr("Off")); tr("On") : tr("Off"));
} }
void void
SettingsFloppyCDROM::on_comboBoxFloppyType_activated(int index) SettingsFloppyCDROM::on_comboBoxFloppyType_activated(int index)
{ {
@@ -337,6 +387,34 @@ SettingsFloppyCDROM::on_comboBoxFloppyType_activated(int index)
ui->tableViewFloppy->selectionModel()->currentIndex(), 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() { void SettingsFloppyCDROM::reloadBusChannels() {
auto selected = ui->comboBoxChannel->currentIndex(); auto selected = ui->comboBoxChannel->currentIndex();
Harddrives::populateBusChannels(ui->comboBoxChannel->model(), ui->comboBoxBus->currentData().toInt(), Harddrives::busTrackClass); 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_comboBoxFloppyType_activated(int index);
void on_checkBoxTurboTimings_stateChanged(int arg1); void on_checkBoxTurboTimings_stateChanged(int arg1);
void on_checkBoxCheckBPB_stateChanged(int arg1); void on_checkBoxCheckBPB_stateChanged(int arg1);
void on_comboBoxFloppyAudio_activated(int index);
void onCDROMRowChanged(const QModelIndex &current); void onCDROMRowChanged(const QModelIndex &current);
void on_comboBoxBus_activated(int index); void on_comboBoxBus_activated(int index);

View File

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

View File

@@ -33,18 +33,20 @@
#endif #endif
#define I_NORMAL 0 #define I_NORMAL 0
#define I_MUSIC 1 #define I_MUSIC 1
#define I_WT 2 #define I_WT 2
#define I_CD 3 #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 #ifdef USE_NEW_API
static struct audio_swpar info[5]; static struct audio_swpar info[5];
#else #else
static audio_info_t info[5]; static audio_info_t info[6];
#endif #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 void
closeal(void) closeal(void)
@@ -165,12 +167,18 @@ givealbuffer_cd(const void *buf)
givealbuffer_common(buf, I_CD, CD_BUFLEN << 1); 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 void
givealbuffer_midi(const void *buf, const uint32_t size) givealbuffer_midi(const void *buf, const uint32_t size)
{ {
givealbuffer_common(buf, I_MIDI, (int) size); givealbuffer_common(buf, I_MIDI, (int) size);
} }
void void
al_set_midi(const int freq, UNUSED(const int buf_size)) al_set_midi(const int freq, UNUSED(const int buf_size))
{ {

View File

@@ -39,14 +39,16 @@
#define I_MUSIC 1 #define I_MUSIC 1
#define I_WT 2 #define I_WT 2
#define I_CD 3 #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[4]; /* front and back buffers */
ALuint buffers_music[4]; /* front and back buffers */ ALuint buffers_music[4]; /* front and back buffers */
ALuint buffers_wt[4]; /* front and back buffers */ ALuint buffers_wt[4]; /* front and back buffers */
ALuint buffers_cd[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 */ 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_freq = 44100;
static int midi_buf_size = 4410; static int midi_buf_size = 4410;
@@ -103,8 +105,9 @@ closeal(void)
alSourceStopv(sources, source); alSourceStopv(sources, source);
alDeleteSources(sources, source); alDeleteSources(sources, source);
if (sources == 4) if (sources >= 6)
alDeleteBuffers(4, buffers_midi); alDeleteBuffers(4, buffers_midi);
alDeleteBuffers(4, buffers_fdd);
alDeleteBuffers(4, buffers_cd); alDeleteBuffers(4, buffers_cd);
alDeleteBuffers(4, buffers_music); alDeleteBuffers(4, buffers_music);
alDeleteBuffers(4, buffers); alDeleteBuffers(4, buffers);
@@ -122,11 +125,13 @@ inital(void)
float *wt_buf = NULL; float *wt_buf = NULL;
float *cd_buf = NULL; float *cd_buf = NULL;
float *midi_buf = NULL; float *midi_buf = NULL;
float *fdd_buf = NULL;
int16_t *buf_int16 = NULL; int16_t *buf_int16 = NULL;
int16_t *music_buf_int16 = NULL; int16_t *music_buf_int16 = NULL;
int16_t *wt_buf_int16 = NULL; int16_t *wt_buf_int16 = NULL;
int16_t *cd_buf_int16 = NULL; int16_t *cd_buf_int16 = NULL;
int16_t *midi_buf_int16 = NULL; int16_t *midi_buf_int16 = NULL;
int16_t *fdd_buf_int16 = NULL;
int init_midi = 0; int init_midi = 0;
@@ -140,13 +145,14 @@ inital(void)
if ((strcmp(mdn, "none") != 0) && (strcmp(mdn, SYSTEM_MIDI_INTERNAL_NAME) != 0)) 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 init_midi = 1; /* If the device is neither none, nor system MIDI, initialize the
MIDI buffer and source, otherwise, do not. */ MIDI buffer and source, otherwise, do not. */
sources = 4 + !!init_midi;
sources = 5 + !!init_midi;
if (sound_is_float) { if (sound_is_float) {
buf = (float *) calloc((BUFLEN << 1), sizeof(float)); buf = (float *) calloc((BUFLEN << 1), sizeof(float));
music_buf = (float *) calloc((MUSICBUFLEN << 1), sizeof(float)); music_buf = (float *) calloc((MUSICBUFLEN << 1), sizeof(float));
wt_buf = (float *) calloc((WTBUFLEN << 1), sizeof(float)); wt_buf = (float *) calloc((WTBUFLEN << 1), sizeof(float));
cd_buf = (float *) calloc((CD_BUFLEN << 1), sizeof(float)); cd_buf = (float *) calloc((CD_BUFLEN << 1), sizeof(float));
fdd_buf = (float *) calloc((BUFLEN << 1), sizeof(float));
if (init_midi) if (init_midi)
midi_buf = (float *) calloc(midi_buf_size, sizeof(float)); midi_buf = (float *) calloc(midi_buf_size, sizeof(float));
} else { } else {
@@ -154,17 +160,22 @@ inital(void)
music_buf_int16 = (int16_t *) calloc((MUSICBUFLEN << 1), sizeof(int16_t)); music_buf_int16 = (int16_t *) calloc((MUSICBUFLEN << 1), sizeof(int16_t));
wt_buf_int16 = (int16_t *) calloc((WTBUFLEN << 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)); 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) if (init_midi)
midi_buf_int16 = (int16_t *) calloc(midi_buf_size, sizeof(int16_t)); midi_buf_int16 = (int16_t *) calloc(midi_buf_size, sizeof(int16_t));
} }
alGenBuffers(4, buffers); alGenBuffers(4, buffers);
alGenBuffers(4, buffers_cd); alGenBuffers(4, buffers_cd);
alGenBuffers(4, buffers_fdd);
alGenBuffers(4, buffers_music); alGenBuffers(4, buffers_music);
alGenBuffers(4, buffers_wt); alGenBuffers(4, buffers_wt);
if (init_midi) if (init_midi)
alGenBuffers(4, buffers_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) if (init_midi)
alGenSources(5, source); alGenSources(5, source);
else else
@@ -194,6 +205,12 @@ inital(void)
alSourcef(source[I_CD], AL_ROLLOFF_FACTOR, 0.0f); alSourcef(source[I_CD], AL_ROLLOFF_FACTOR, 0.0f);
alSourcei(source[I_CD], AL_SOURCE_RELATIVE, AL_TRUE); 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) { if (init_midi) {
alSource3f(source[I_MIDI], AL_POSITION, 0.0f, 0.0f, 0.0f); alSource3f(source[I_MIDI], AL_POSITION, 0.0f, 0.0f, 0.0f);
alSource3f(source[I_MIDI], AL_VELOCITY, 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(cd_buf, 0, CD_BUFLEN * 2 * sizeof(float));
memset(music_buf, 0, MUSICBUFLEN * 2 * sizeof(float)); memset(music_buf, 0, MUSICBUFLEN * 2 * sizeof(float));
memset(wt_buf, 0, WTBUFLEN * 2 * sizeof(float)); memset(wt_buf, 0, WTBUFLEN * 2 * sizeof(float));
memset(fdd_buf, 0, BUFLEN * 2 * sizeof(float));
if (init_midi) if (init_midi)
memset(midi_buf, 0, midi_buf_size * sizeof(float)); memset(midi_buf, 0, midi_buf_size * sizeof(float));
} else { } else {
@@ -214,6 +232,7 @@ inital(void)
memset(cd_buf_int16, 0, CD_BUFLEN * 2 * sizeof(int16_t)); memset(cd_buf_int16, 0, CD_BUFLEN * 2 * sizeof(int16_t));
memset(music_buf_int16, 0, MUSICBUFLEN * 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(wt_buf_int16, 0, WTBUFLEN * 2 * sizeof(int16_t));
memset(fdd_buf_int16, 0, BUFLEN * 2 * sizeof(int16_t));
if (init_midi) if (init_midi)
memset(midi_buf_int16, 0, midi_buf_size * sizeof(int16_t)); 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_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_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_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) if (init_midi)
alBufferData(buffers_midi[c], AL_FORMAT_STEREO_FLOAT32, midi_buf, midi_buf_size * (int) sizeof(float), midi_freq); alBufferData(buffers_midi[c], AL_FORMAT_STEREO_FLOAT32, midi_buf, midi_buf_size * (int) sizeof(float), midi_freq);
} else { } 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_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_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_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) if (init_midi)
alBufferData(buffers_midi[c], AL_FORMAT_STEREO16, midi_buf_int16, midi_buf_size * (int) sizeof(int16_t), midi_freq); 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_MUSIC], 4, buffers_music);
alSourceQueueBuffers(source[I_WT], 4, buffers_wt); alSourceQueueBuffers(source[I_WT], 4, buffers_wt);
alSourceQueueBuffers(source[I_CD], 4, buffers_cd); alSourceQueueBuffers(source[I_CD], 4, buffers_cd);
alSourceQueueBuffers(source[I_FDD], 4, buffers_fdd);
if (init_midi) if (init_midi)
alSourceQueueBuffers(source[I_MIDI], 4, buffers_midi); alSourceQueueBuffers(source[I_MIDI], 4, buffers_midi);
alSourcePlay(source[I_NORMAL]); alSourcePlay(source[I_NORMAL]);
alSourcePlay(source[I_MUSIC]); alSourcePlay(source[I_MUSIC]);
alSourcePlay(source[I_WT]); alSourcePlay(source[I_WT]);
alSourcePlay(source[I_CD]); alSourcePlay(source[I_CD]);
alSourcePlay(source[I_FDD]);
if (init_midi) if (init_midi)
alSourcePlay(source[I_MIDI]); alSourcePlay(source[I_MIDI]);
@@ -256,6 +279,7 @@ inital(void)
free(wt_buf); free(wt_buf);
free(music_buf); free(music_buf);
free(buf); free(buf);
free(fdd_buf);
} else { } else {
if (init_midi) if (init_midi)
free(midi_buf_int16); free(midi_buf_int16);
@@ -263,6 +287,7 @@ inital(void)
free(wt_buf_int16); free(wt_buf_int16);
free(music_buf_int16); free(music_buf_int16);
free(buf_int16); free(buf_int16);
free(fdd_buf_int16);
} }
initialized = 1; initialized = 1;
@@ -327,5 +352,11 @@ givealbuffer_cd(const void *buf)
void void
givealbuffer_midi(const void *buf, const uint32_t size) 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 <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <math.h> #include <math.h>
#include <sndio.h> #include <sndio.h>
#include <86box/86box.h> #include <86box/86box.h>
@@ -30,10 +29,11 @@
#define I_WT 2 #define I_WT 2
#define I_CD 3 #define I_CD 3
#define I_MIDI 4 #define I_MIDI 4
#define I_FDD 5
static struct sio_hdl* audio[5] = { NULL, NULL, NULL, NULL, NULL }; static struct sio_hdl* audio[6] = {NULL, NULL, NULL, NULL, NULL, NULL};
static struct sio_par info[5]; static struct sio_par info[6];
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 void
closeal(void) closeal(void)
@@ -147,7 +147,13 @@ givealbuffer_midi(const void *buf, const uint32_t size)
{ {
givealbuffer_common(buf, I_MIDI, (int) 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 void
al_set_midi(const int freq, UNUSED(const int buf_size)) al_set_midi(const int freq, UNUSED(const int buf_size))
{ {

View File

@@ -36,6 +36,7 @@
#include <86box/timer.h> #include <86box/timer.h>
#include <86box/snd_mpu401.h> #include <86box/snd_mpu401.h>
#include <86box/sound.h> #include <86box/sound.h>
#include <86box/fdd_audio.h>
typedef struct { typedef struct {
const device_t *device; const device_t *device;
@@ -89,6 +90,13 @@ static int cd_buf_update = CD_BUFLEN / SOUNDBUFLEN;
static volatile int cdaudioon = 0; static volatile int cdaudioon = 0;
static int cd_thread_enable = 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)(int channel, double *buffer, void *priv) = NULL;
static void *filter_cd_audio_p = 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; sound_pos_global = 0;
} }
} }
@@ -698,17 +709,14 @@ sound_reset(void)
inital(); inital();
timer_add(&sound_poll_timer, sound_poll, NULL, 1); timer_add(&sound_poll_timer, sound_poll, NULL, 1);
sound_handlers_num = 0; sound_handlers_num = 0;
memset(sound_handlers, 0x00, 8 * sizeof(sound_handler_t)); memset(sound_handlers, 0x00, 8 * sizeof(sound_handler_t));
timer_add(&music_poll_timer, music_poll, NULL, 1); timer_add(&music_poll_timer, music_poll, NULL, 1);
music_handlers_num = 0; music_handlers_num = 0;
memset(music_handlers, 0x00, 8 * sizeof(sound_handler_t)); memset(music_handlers, 0x00, 8 * sizeof(sound_handler_t));
timer_add(&wavetable_poll_timer, wavetable_poll, NULL, 1); timer_add(&wavetable_poll_timer, wavetable_poll, NULL, 1);
wavetable_handlers_num = 0; wavetable_handlers_num = 0;
memset(wavetable_handlers, 0x00, 8 * sizeof(sound_handler_t)); 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; 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 *srcvoicewt = NULL;
static IXAudio2SourceVoice *srcvoicemidi = NULL; static IXAudio2SourceVoice *srcvoicemidi = NULL;
static IXAudio2SourceVoice *srcvoicecd = NULL; static IXAudio2SourceVoice *srcvoicecd = NULL;
static IXAudio2SourceVoice *srcvoicefdd = NULL;
#define FREQ SOUND_FREQ #define FREQ SOUND_FREQ
#define BUFLEN SOUNDBUFLEN #define BUFLEN SOUNDBUFLEN
@@ -178,11 +179,18 @@ inital(void)
(void) IXAudio2_CreateSourceVoice(xaudio2, &srcvoicecd, &fmt, 0, 2.0f, &callbacks, NULL, NULL); (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_SetVolume(srcvoice, 1, XAUDIO2_COMMIT_NOW);
(void) IXAudio2SourceVoice_Start(srcvoice, 0, XAUDIO2_COMMIT_NOW); (void) IXAudio2SourceVoice_Start(srcvoice, 0, XAUDIO2_COMMIT_NOW);
(void) IXAudio2SourceVoice_Start(srcvoicecd, 0, XAUDIO2_COMMIT_NOW); (void) IXAudio2SourceVoice_Start(srcvoicecd, 0, XAUDIO2_COMMIT_NOW);
(void) IXAudio2SourceVoice_Start(srcvoicemusic, 0, XAUDIO2_COMMIT_NOW); (void) IXAudio2SourceVoice_Start(srcvoicemusic, 0, XAUDIO2_COMMIT_NOW);
(void) IXAudio2SourceVoice_Start(srcvoicewt, 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); 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_FlushSourceBuffers(srcvoicewt);
(void) IXAudio2SourceVoice_Stop(srcvoicecd, 0, XAUDIO2_COMMIT_NOW); (void) IXAudio2SourceVoice_Stop(srcvoicecd, 0, XAUDIO2_COMMIT_NOW);
(void) IXAudio2SourceVoice_FlushSourceBuffers(srcvoicecd); (void) IXAudio2SourceVoice_FlushSourceBuffers(srcvoicecd);
(void) IXAudio2SourceVoice_Stop(srcvoicefdd, 0, XAUDIO2_COMMIT_NOW);
(void) IXAudio2SourceVoice_FlushSourceBuffers(srcvoicefdd);
if (srcvoicemidi) { if (srcvoicemidi) {
(void) IXAudio2SourceVoice_Stop(srcvoicemidi, 0, XAUDIO2_COMMIT_NOW); (void) IXAudio2SourceVoice_Stop(srcvoicemidi, 0, XAUDIO2_COMMIT_NOW);
(void) IXAudio2SourceVoice_FlushSourceBuffers(srcvoicemidi); (void) IXAudio2SourceVoice_FlushSourceBuffers(srcvoicemidi);
@@ -220,6 +230,7 @@ closeal(void)
} }
IXAudio2SourceVoice_DestroyVoice(srcvoicewt); IXAudio2SourceVoice_DestroyVoice(srcvoicewt);
IXAudio2SourceVoice_DestroyVoice(srcvoicecd); IXAudio2SourceVoice_DestroyVoice(srcvoicecd);
IXAudio2SourceVoice_DestroyVoice(srcvoicefdd);
IXAudio2SourceVoice_DestroyVoice(srcvoicemusic); IXAudio2SourceVoice_DestroyVoice(srcvoicemusic);
IXAudio2SourceVoice_DestroyVoice(srcvoice); IXAudio2SourceVoice_DestroyVoice(srcvoice);
IXAudio2MasteringVoice_DestroyVoice(mastervoice); IXAudio2MasteringVoice_DestroyVoice(mastervoice);
@@ -227,6 +238,7 @@ closeal(void)
srcvoice = NULL; srcvoice = NULL;
srcvoicecd = NULL; srcvoicecd = NULL;
srcvoicemidi = NULL; srcvoicemidi = NULL;
srcvoicefdd = NULL;
mastervoice = NULL; mastervoice = NULL;
xaudio2 = NULL; xaudio2 = NULL;
@@ -288,6 +300,18 @@ givealbuffer_cd(const void *buf)
givealbuffer_common(buf, srcvoicecd, CD_BUFLEN << 1); 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 void
al_set_midi(const int freq, const int buf_size) al_set_midi(const int freq, const int buf_size)
{ {
@@ -321,4 +345,4 @@ void
givealbuffer_midi(const void *buf, const uint32_t size) givealbuffer_midi(const void *buf, const uint32_t size)
{ {
givealbuffer_common(buf, srcvoicemidi, size); givealbuffer_common(buf, srcvoicemidi, size);
} }