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:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -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.
Binary file not shown.
Binary file not shown.
BIN
samples/TeacFD-55GFR_5.25_1.2MB_motor_loop_48000_16_1_PCM.wav
Normal file
BIN
samples/TeacFD-55GFR_5.25_1.2MB_motor_loop_48000_16_1_PCM.wav
Normal file
Binary file not shown.
BIN
samples/TeacFD-55GFR_5.25_1.2MB_motor_start_48000_16_1_PCM.wav
Normal file
BIN
samples/TeacFD-55GFR_5.25_1.2MB_motor_start_48000_16_1_PCM.wav
Normal file
Binary file not shown.
BIN
samples/TeacFD-55GFR_5.25_1.2MB_motor_stop_48000_16_1_PCM.wav
Normal file
BIN
samples/TeacFD-55GFR_5.25_1.2MB_motor_stop_48000_16_1_PCM.wav
Normal file
Binary file not shown.
BIN
samples/TeacFD-55GFR_5.25_1.2MB_track_step_48000_16_1_PCM.wav
Normal file
BIN
samples/TeacFD-55GFR_5.25_1.2MB_track_step_48000_16_1_PCM.wav
Normal file
Binary file not shown.
Binary file not shown.
BIN
samples/mitsumi_offseek_80_tracks_1000ms_48000_16_1_PCM.wav
Normal file
BIN
samples/mitsumi_offseek_80_tracks_1000ms_48000_16_1_PCM.wav
Normal file
Binary file not shown.
BIN
samples/mitsumi_seek_80_tracks_380ms_48000_16_1_PCM.wav
Normal file
BIN
samples/mitsumi_seek_80_tracks_380ms_48000_16_1_PCM.wav
Normal file
Binary file not shown.
BIN
samples/mitsumi_seek_80_tracks_495ms_48000_16_1_PCM.wav
Normal file
BIN
samples/mitsumi_seek_80_tracks_495ms_48000_16_1_PCM.wav
Normal file
Binary file not shown.
BIN
samples/mitsumi_spindle_motor_loop_48000_16_1_PCM.wav
Normal file
BIN
samples/mitsumi_spindle_motor_loop_48000_16_1_PCM.wav
Normal file
Binary file not shown.
BIN
samples/mitsumi_spindle_motor_start_48000_16_1_PCM.wav
Normal file
BIN
samples/mitsumi_spindle_motor_start_48000_16_1_PCM.wav
Normal file
Binary file not shown.
BIN
samples/mitsumi_spindle_motor_stop_48000_16_1_PCM.wav
Normal file
BIN
samples/mitsumi_spindle_motor_stop_48000_16_1_PCM.wav
Normal file
Binary file not shown.
BIN
samples/mitsumi_track_step_48000_16_1_PCM.wav
Normal file
BIN
samples/mitsumi_track_step_48000_16_1_PCM.wav
Normal file
Binary file not shown.
13
samples/readme.txt
Normal file
13
samples/readme.txt
Normal 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.
|
||||
@@ -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];
|
||||
|
||||
23
src/config.c
23
src/config.c
@@ -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++) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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*/
|
||||
|
||||
112
src/floppy/fdd.c
112
src/floppy/fdd.c
@@ -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
667
src/floppy/fdd_audio.c
Normal 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 */
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
86
src/include/86box/fdd_audio.h
Normal file
86
src/include/86box/fdd_audio.h
Normal 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*/
|
||||
@@ -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
|
||||
|
||||
@@ -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 ¤t)
|
||||
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);
|
||||
|
||||
@@ -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 ¤t);
|
||||
void on_comboBoxBus_activated(int index);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user