Floppy disk audio now gets settings from fdd_audio_profiles.cfg stored in roms/floppy -directory. Floppy settings audio seledtions are also populated by the settings from this config.

This commit is contained in:
Toni Riikonen
2025-10-01 15:49:49 +03:00
parent af11163c04
commit 9ff459f1f5
7 changed files with 619 additions and 326 deletions

View File

@@ -79,6 +79,7 @@
#include <86box/mouse.h>
#include <86box/gameport.h>
#include <86box/fdd.h>
#include <86box/fdd_audio.h>
#include <86box/fdc.h>
#include <86box/fdc_ext.h>
#include <86box/hdd.h>
@@ -1380,6 +1381,11 @@ pc_init_modules(void)
video_init();
fdd_init();
if (fdd_sounds_enabled) {
fdd_audio_load_profiles();
fdd_audio_init();
}
sound_init();

View File

@@ -54,6 +54,7 @@
#include <86box/hdc.h>
#include <86box/hdc_ide.h>
#include <86box/fdd.h>
#include <86box/fdd_audio.h>
#include <86box/fdc_ext.h>
#include <86box/gameport.h>
#include <86box/keyboard.h>
@@ -1379,10 +1380,15 @@ load_floppy_and_cdrom_drives(void)
int c;
int d;
int count = cdrom_get_type_count();
#ifndef DISABLE_FDD_AUDIO
fdd_audio_load_profiles();
#endif
memset(temp, 0x00, sizeof(temp));
for (c = 0; c < FDD_NUM; c++) {
sprintf(temp, "fdd_%02i_type", c + 1);
p = ini_section_get_string(cat, temp, (c < 2) ? "525_2dd" : "none");
if (!strcmp(p, "525_2hd_ps2"))
d = fdd_get_from_internal_name("525_2hd");
@@ -1437,14 +1443,14 @@ 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;
sprintf(temp, "fdd_%02i_audio", c + 1);
#ifndef DISABLE_FDD_AUDIO
p = ini_section_get_string(cat, temp, "none");
int prof = fdd_audio_get_profile_by_internal_name(p);
fdd_set_audio_profile(c, prof);
if (prof == def_prof)
ini_section_delete_var(cat, temp);
#else
fdd_set_audio_profile(c, 0);
#endif
for (int i = 0; i < MAX_PREV_IMAGES; i++) {
fdd_image_history[c][i] = (char *) calloc((MAX_IMAGE_PATH_LEN + 1) << 1, sizeof(char));
@@ -3433,12 +3439,17 @@ save_floppy_and_cdrom_drives(void)
}
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)
#ifndef DISABLE_FDD_AUDIO
int prof = fdd_get_audio_profile(c);
const char *internal_name = fdd_audio_get_profile_internal_name(prof);
if (internal_name && strcmp(internal_name, "none") != 0) {
ini_section_set_string(cat, temp, internal_name);
} else {
ini_section_delete_var(cat, temp);
else
ini_section_set_int(cat, temp, prof);
}
#else
ini_section_delete_var(cat, temp);
#endif
}
for (c = 0; c < CDROM_NUM; c++) {

View File

@@ -409,8 +409,10 @@ fdd_seek(int drive, int track_diff)
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;
/* Get seek timings from audio profile configuration */
double initial_seek_time = fdd_audio_get_seek_time(drive, 1, actual_track_diff);
double track_seek_time = fdd_audio_get_seek_time(drive, 0, actual_track_diff);
fdd_log("Seek timing for drive %d: initial %.2f ms, per track %.2f ms\n", drive, initial_seek_time, track_seek_time);
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);
}

View File

@@ -24,23 +24,28 @@
#include <86box/timer.h>
#include <86box/fdd.h>
#include <86box/fdd_audio.h>
#include <86box/fdc.h>
#include <86box/mem.h>
#include <86box/rom.h>
#include <86box/sound.h>
#include <86box/plat.h>
#include <86box/path.h>
#include <86box/ini.h>
#ifndef DISABLE_FDD_AUDIO
/* Global audio profile configurations */
static fdd_audio_profile_config_t audio_profiles[FDD_AUDIO_PROFILE_MAX];
static int audio_profile_count = 0;
/* Audio sample structure */
typedef struct {
const char *filename;
int16_t *buffer;
int samples;
float volume;
char filename[512];
int16_t *buffer;
int samples;
float volume;
} audio_sample_t;
/* Single step audio state */
typedef struct {
int position;
int active;
@@ -64,77 +69,8 @@ typedef struct {
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 = "roms/floppy/samples/TeacFD-55GFR_5.25_1.2MB_motor_start_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 3.0f
},
.spindlemotor_loop = {
.filename = "roms/floppy/samples/TeacFD-55GFR_5.25_1.2MB_motor_loop_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 3.0f
},
.spindlemotor_stop = {
.filename = "roms/floppy/samples/TeacFD-55GFR_5.25_1.2MB_motor_stop_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 3.0f
},
.single_track_step = {
.filename = "roms/floppy/samples/TeacFD-55GFR_5.25_1.2MB_track_step_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 2.0f
},
.multi_track_seek = {
.filename = "roms/floppy/samples/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 = "roms/floppy/samples/mitsumi_spindle_motor_start_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 0.2f
},
.spindlemotor_loop = {
.filename = "roms/floppy/samples/mitsumi_spindle_motor_loop_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 0.2f
},
.spindlemotor_stop = {
.filename = "roms/floppy/samples/mitsumi_spindle_motor_stop_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 0.2f
},
.single_track_step = {
.filename = "roms/floppy/samples/mitsumi_track_step_48000_16_1_PCM.wav",
.buffer = NULL, .samples = 0, .volume = 1.0f
},
.multi_track_seek = {
.filename = "roms/floppy/samples/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 = "roms/floppy/samples/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 = "roms/floppy/samples/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 = "roms/floppy/samples/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 = "roms/floppy/samples/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 = "roms/floppy/samples/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
}
};
/* Dynamic sample storage for each profile */
static drive_audio_samples_t profile_samples[FDD_AUDIO_PROFILE_MAX];
/* Audio state for each drive */
static int spindlemotor_pos[FDD_NUM] = {};
@@ -149,133 +85,357 @@ static single_step_state_t single_step_state[FDD_NUM] = {};
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 char exe_path[2048];
extern int fdd_get_audio_profile(int drive); /* from fdd.h */
extern int fdd_get_audio_profile(int drive);
/* Forward declaration */
static int16_t *load_wav(const char *filename, int *sample_count);
#ifdef ENABLE_FDD_LOG
int fdc_do_log = ENABLE_FDD_LOG;
static void
fdd_log(const char *fmt, ...)
{
va_list ap;
if (fdc_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
# else
# define fdd_log(fmt, ...)
# endif
/* Logging function for audio profile parameters */
static void
fdd_audio_log_profile_params(int drive, const fdd_audio_profile_config_t* profile)
{
if (!profile) {
fdd_log("FDD Audio Drive %d: No profile assigned\n", drive);
return;
}
fdd_log("FDD Audio Drive %d Profile Parameters:\n", drive);
fdd_log(" Profile ID: %d\n", profile->id);
fdd_log(" Profile Name: %s\n", profile->name);
fdd_log(" Internal Name: %s\n", profile->internal_name);
fdd_log(" Sample Files:\n");
fdd_log(" Spindle Start: %s (volume: %.2f)\n",
profile->spindlemotor_start.filename, profile->spindlemotor_start.volume);
fdd_log(" Spindle Loop: %s (volume: %.2f)\n",
profile->spindlemotor_loop.filename, profile->spindlemotor_loop.volume);
fdd_log(" Spindle Stop: %s (volume: %.2f)\n",
profile->spindlemotor_stop.filename, profile->spindlemotor_stop.volume);
fdd_log(" Single Step: %s (volume: %.2f)\n",
profile->single_track_step.filename, profile->single_track_step.volume);
fdd_log(" Multi Seek: %s (volume: %.2f)\n",
profile->multi_track_seek.filename, profile->multi_track_seek.volume);
fdd_log(" Timing Parameters:\n");
fdd_log(" Seek Duration Per Track: %d samples\n", profile->seek_duration_per_track);
fdd_log(" Total Tracks: %d\n", profile->total_tracks);
fdd_log(" Initial Seek Time: %.1f µs\n", profile->initial_seek_time);
fdd_log(" Initial Seek Time (PCjr): %.1f µs\n", profile->initial_seek_time_pcjr);
fdd_log(" Track Seek Time: %.1f µs\n", profile->track_seek_time);
fdd_log(" Track Seek Time (PCjr): %.1f µs\n", profile->track_seek_time_pcjr);
}
/* Log audio profile parameters for a specific drive */
void
fdd_audio_log_drive_profile(int drive)
{
if (drive < 0 || drive >= FDD_NUM) {
fdd_log("FDD Audio: Invalid drive number %d\n", drive);
return;
}
int profile_id = fdd_get_audio_profile(drive);
const fdd_audio_profile_config_t* profile = fdd_audio_get_profile(profile_id);
fdd_log("FDD Audio Drive %d: Using profile %d\n", drive, profile_id);
fdd_audio_log_profile_params(drive, profile);
}
/* Log only the audio profiles that are actually used by configured drives */
static void
fdd_audio_log_active_profiles(void)
{
fdd_log("FDD Audio: Checking active drive configurations...\n");
int active_drive_count = 0;
for (int drive = 0; drive < FDD_NUM; drive++) {
if (fdd_get_type(drive) == 0)
continue;
active_drive_count++;
int profile_id = fdd_get_audio_profile(drive);
if (profile_id >= 0 && profile_id < audio_profile_count) {
fdd_log("FDD Audio: Drive %d (configured) uses profile %d\n", drive, profile_id);
fdd_audio_log_profile_params(drive, &audio_profiles[profile_id]);
}
}
if (active_drive_count == 0) {
fdd_log("FDD Audio: No drives configured - no audio profiles to log\n");
return;
}
fdd_log("FDD Audio: Active audio profiles for %d configured drive(s):\n", active_drive_count);
}
void
fdd_audio_load_profiles(void)
{
char config_path[2048];
ini_t profiles_ini;
path_append_filename(config_path, exe_path, "roms/floppy/fdd_audio_profiles.cfg");
profiles_ini = ini_read(config_path);
if (profiles_ini == NULL) {
fdd_log("FDD Audio: Could not load profiles from %s\n", config_path);
return;
}
audio_profile_count = 0;
/* Load profiles by trying known profile section names */
for (int i = 0; i < FDD_AUDIO_PROFILE_MAX && audio_profile_count < FDD_AUDIO_PROFILE_MAX; i++) {
char section_name[64];
snprintf(section_name, sizeof(section_name), "Profile \"%d\"", i);
ini_section_t section = ini_find_section(profiles_ini, section_name);
if (section) {
fdd_audio_profile_config_t *profile = &audio_profiles[audio_profile_count];
/* Load profile configuration */
profile->id = ini_section_get_int(section, "id", audio_profile_count);
const char *name = ini_section_get_string(section, "name", "Unknown");
strncpy(profile->name, name, sizeof(profile->name) - 1);
profile->name[sizeof(profile->name) - 1] = '\0';
const char *internal_name = ini_section_get_string(section, "internal_name", "unknown");
strncpy(profile->internal_name, internal_name, sizeof(profile->internal_name) - 1);
profile->internal_name[sizeof(profile->internal_name) - 1] = '\0';
/* Load sample configurations */
const char *filename = ini_section_get_string(section, "spindlemotor_start_file", "");
strncpy(profile->spindlemotor_start.filename, filename, sizeof(profile->spindlemotor_start.filename) - 1);
profile->spindlemotor_start.filename[sizeof(profile->spindlemotor_start.filename) - 1] = '\0';
profile->spindlemotor_start.volume = ini_section_get_double(section, "spindlemotor_start_volume", 1.0);
filename = ini_section_get_string(section, "spindlemotor_loop_file", "");
strncpy(profile->spindlemotor_loop.filename, filename, sizeof(profile->spindlemotor_loop.filename) - 1);
profile->spindlemotor_loop.filename[sizeof(profile->spindlemotor_loop.filename) - 1] = '\0';
profile->spindlemotor_loop.volume = ini_section_get_double(section, "spindlemotor_loop_volume", 1.0);
filename = ini_section_get_string(section, "spindlemotor_stop_file", "");
strncpy(profile->spindlemotor_stop.filename, filename, sizeof(profile->spindlemotor_stop.filename) - 1);
profile->spindlemotor_stop.filename[sizeof(profile->spindlemotor_stop.filename) - 1] = '\0';
profile->spindlemotor_stop.volume = ini_section_get_double(section, "spindlemotor_stop_volume", 1.0);
filename = ini_section_get_string(section, "single_track_step_file", "");
strncpy(profile->single_track_step.filename, filename, sizeof(profile->single_track_step.filename) - 1);
profile->single_track_step.filename[sizeof(profile->single_track_step.filename) - 1] = '\0';
profile->single_track_step.volume = ini_section_get_double(section, "single_track_step_volume", 1.0);
filename = ini_section_get_string(section, "multi_track_seek_file", "");
strncpy(profile->multi_track_seek.filename, filename, sizeof(profile->multi_track_seek.filename) - 1);
profile->multi_track_seek.filename[sizeof(profile->multi_track_seek.filename) - 1] = '\0';
profile->multi_track_seek.volume = ini_section_get_double(section, "multi_track_seek_volume", 1.0);
/* Load timing configurations */
profile->seek_duration_per_track = ini_section_get_int(section, "seek_duration_per_track", 297);
profile->total_tracks = ini_section_get_int(section, "total_tracks", 80);
profile->initial_seek_time = ini_section_get_double(section, "initial_seek_time", 15000.0);
profile->initial_seek_time_pcjr = ini_section_get_double(section, "initial_seek_time_pcjr", 40000.0);
profile->track_seek_time = ini_section_get_double(section, "track_seek_time", 6000.0);
profile->track_seek_time_pcjr = ini_section_get_double(section, "track_seek_time_pcjr", 10000.0);
audio_profile_count++;
}
}
ini_close(profiles_ini);
fdd_log("FDD Audio: Loaded %d audio profiles from %s\n", audio_profile_count, config_path);
}
static void load_profile_samples(int profile_id) {
if (profile_id <= 0 || profile_id >= audio_profile_count)
return;
fdd_audio_profile_config_t *config = &audio_profiles[profile_id];
drive_audio_samples_t *samples = &profile_samples[profile_id];
fdd_log("FDD Audio: Loading samples for profile %d (%s)\n",
profile_id, config->name);
/* Load samples if not already loaded */
if (samples->spindlemotor_start.buffer == NULL && config->spindlemotor_start.filename[0]) {
strcpy(samples->spindlemotor_start.filename, config->spindlemotor_start.filename);
samples->spindlemotor_start.volume = config->spindlemotor_start.volume;
samples->spindlemotor_start.buffer = load_wav(config->spindlemotor_start.filename,
&samples->spindlemotor_start.samples);
if (samples->spindlemotor_start.buffer) {
fdd_log(" Loaded spindlemotor_start: %s (%d samples, volume %.2f)\n",
config->spindlemotor_start.filename,
samples->spindlemotor_start.samples,
config->spindlemotor_start.volume);
} else {
fdd_log(" Failed to load spindlemotor_start: %s\n",
config->spindlemotor_start.filename);
}
}
if (samples->spindlemotor_loop.buffer == NULL && config->spindlemotor_loop.filename[0]) {
strcpy(samples->spindlemotor_loop.filename, config->spindlemotor_loop.filename);
samples->spindlemotor_loop.volume = config->spindlemotor_loop.volume;
samples->spindlemotor_loop.buffer = load_wav(config->spindlemotor_loop.filename,
&samples->spindlemotor_loop.samples);
if (samples->spindlemotor_loop.buffer) {
fdd_log(" Loaded spindlemotor_loop: %s (%d samples, volume %.2f)\n",
config->spindlemotor_loop.filename,
samples->spindlemotor_loop.samples,
config->spindlemotor_loop.volume);
} else {
fdd_log(" Failed to load spindlemotor_loop: %s\n",
config->spindlemotor_loop.filename);
}
}
if (samples->spindlemotor_stop.buffer == NULL && config->spindlemotor_stop.filename[0]) {
strcpy(samples->spindlemotor_stop.filename, config->spindlemotor_stop.filename);
samples->spindlemotor_stop.volume = config->spindlemotor_stop.volume;
samples->spindlemotor_stop.buffer = load_wav(config->spindlemotor_stop.filename,
&samples->spindlemotor_stop.samples);
if (samples->spindlemotor_stop.buffer) {
fdd_log(" Loaded spindlemotor_stop: %s (%d samples, volume %.2f)\n",
config->spindlemotor_stop.filename,
samples->spindlemotor_stop.samples,
config->spindlemotor_stop.volume);
} else {
fdd_log(" Failed to load spindlemotor_stop: %s\n",
config->spindlemotor_stop.filename);
}
}
if (samples->single_track_step.buffer == NULL && config->single_track_step.filename[0]) {
strcpy(samples->single_track_step.filename, config->single_track_step.filename);
samples->single_track_step.volume = config->single_track_step.volume;
samples->single_track_step.buffer = load_wav(config->single_track_step.filename,
&samples->single_track_step.samples);
if (samples->single_track_step.buffer) {
fdd_log(" Loaded single_track_step: %s (%d samples, volume %.2f)\n",
config->single_track_step.filename,
samples->single_track_step.samples,
config->single_track_step.volume);
} else {
fdd_log(" Failed to load single_track_step: %s\n",
config->single_track_step.filename);
}
}
if (samples->multi_track_seek.buffer == NULL && config->multi_track_seek.filename[0]) {
strcpy(samples->multi_track_seek.filename, config->multi_track_seek.filename);
samples->multi_track_seek.volume = config->multi_track_seek.volume;
samples->multi_track_seek.buffer = load_wav(config->multi_track_seek.filename,
&samples->multi_track_seek.samples);
if (samples->multi_track_seek.buffer) {
fdd_log(" Loaded multi_track_seek: %s (%d samples, volume %.2f)\n",
config->multi_track_seek.filename,
samples->multi_track_seek.samples,
config->multi_track_seek.volume);
} else {
fdd_log(" Failed to load multi_track_seek: %s\n",
config->multi_track_seek.filename);
}
}
}
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;
}
int profile_id = fdd_get_audio_profile(drive);
if (profile_id <= 0 || profile_id >= audio_profile_count)
return NULL;
/* Samples are preloaded during fdd_audio_init */
return &profile_samples[profile_id];
}
static int16_t *
load_wav(const char *filename, int *sample_count)
/* Public API functions */
int fdd_audio_get_profile_count(void) {
return audio_profile_count;
}
const fdd_audio_profile_config_t* fdd_audio_get_profile(int id) {
if (id < 0 || id >= audio_profile_count)
return NULL;
return &audio_profiles[id];
}
const char* fdd_audio_get_profile_name(int id) {
if (id < 0 || id >= audio_profile_count)
return NULL;
return audio_profiles[id].name;
}
const char* fdd_audio_get_profile_internal_name(int id) {
if (id < 0 || id >= audio_profile_count)
return NULL;
return audio_profiles[id].internal_name;
}
int
fdd_audio_get_profile_by_internal_name(const char *internal_name)
{
FILE *f = NULL;
char full_path[2048];
if (!internal_name || !*internal_name)
return 0;
if ((filename == NULL) || (strlen(filename) == 0))
return NULL;
if (audio_profile_count == 0)
fdd_audio_load_profiles();
if (strstr(filename, "..") != NULL)
return NULL;
f = rom_fopen(filename, "rb");
if (f == NULL)
return NULL;
wav_header_t hdr;
if (fread(&hdr, sizeof(hdr), 1, f) != 1) {
fclose(f);
return NULL;
for (int i = 0; i < audio_profile_count; i++) {
if (!strcmp(audio_profiles[i].internal_name, internal_name))
return i;
}
return 0;
}
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;
double fdd_audio_get_seek_time(int drive, int is_initial, int track_count) {
int profile_id = fdd_get_audio_profile(drive);
if (profile_id <= 0 || profile_id >= audio_profile_count) {
/* Return default values */
return is_initial ? 15000.0 : 6000.0;
}
/* 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);
fdd_audio_profile_config_t *profile = &audio_profiles[profile_id];
/* Check if using PCjr timing */
extern fdc_t *fdd_fdc;
int is_pcjr = (fdd_fdc && (fdd_fdc->flags & FDC_FLAG_PCJR));
if (is_initial) {
return is_pcjr ? profile->initial_seek_time_pcjr : profile->initial_seek_time;
} else {
/* Already stereo */
output_data = input_data;
output_samples = input_samples / 2; /* Number of stereo sample pairs */
return is_pcjr ? profile->track_seek_time_pcjr : profile->track_seek_time;
}
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);
/* Load audio profiles configuration */
fdd_audio_load_profiles();
/* Initialize audio state for all drives */
for (i = 0; i < FDD_NUM; i++) {
for (int i = 0; i < FDD_NUM; i++) {
spindlemotor_pos[i] = 0;
spindlemotor_state[i] = MOTOR_STATE_STOPPED;
spindlemotor_fade_volume[i] = 1.0f;
@@ -293,95 +453,62 @@ fdd_audio_init(void)
multi_seek_state[i].to_track = -1;
}
/* Preload audio samples for each drive's selected profile */
for (int drive = 0; drive < FDD_NUM; drive++) {
int profile_id = fdd_get_audio_profile(drive);
if (profile_id > 0 && profile_id < audio_profile_count) {
load_profile_samples(profile_id);
}
}
/* Log only the active profiles used by configured drives */
fdd_audio_log_active_profiles();
/* Initialize sound thread */
sound_fdd_thread_init();
fdd_log("FDD Audio: Initialization complete\n");
}
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;
fdd_log("FDD Audio: Shutting down audio system\n");
/* Free loaded profile samples */
for (int profile_id = 0; profile_id < audio_profile_count; profile_id++) {
drive_audio_samples_t *samples = &profile_samples[profile_id];
if (samples->spindlemotor_start.buffer) {
free(samples->spindlemotor_start.buffer);
samples->spindlemotor_start.buffer = NULL;
samples->spindlemotor_start.samples = 0;
}
if (samples->spindlemotor_loop.buffer) {
free(samples->spindlemotor_loop.buffer);
samples->spindlemotor_loop.buffer = NULL;
samples->spindlemotor_loop.samples = 0;
}
if (samples->spindlemotor_stop.buffer) {
free(samples->spindlemotor_stop.buffer);
samples->spindlemotor_stop.buffer = NULL;
samples->spindlemotor_stop.samples = 0;
}
if (samples->single_track_step.buffer) {
free(samples->single_track_step.buffer);
samples->single_track_step.buffer = NULL;
samples->single_track_step.samples = 0;
}
if (samples->multi_track_seek.buffer) {
free(samples->multi_track_seek.buffer);
samples->multi_track_seek.buffer = NULL;
samples->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();
fdd_log("FDD Audio: Shutdown complete\n");
}
void
@@ -394,16 +521,20 @@ fdd_audio_set_motor_enable(int drive, int motor_enable)
if (!samples)
return;
fdd_log("FDD Audio Drive %d: Motor %s\n", drive, motor_enable ? "ON" : "OFF");
if (motor_enable && !motoron[drive]) {
/* Motor starting up */
if (spindlemotor_state[drive] == MOTOR_STATE_STOPPING) {
/* Interrupt stop sequence and transition back to loop */
fdd_log("FDD Audio Drive %d: Interrupting stop sequence, returning to loop\n", drive);
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 */
fdd_log("FDD Audio Drive %d: Starting motor (normal startup)\n", drive);
spindlemotor_state[drive] = MOTOR_STATE_STARTING;
spindlemotor_pos[drive] = 0;
spindlemotor_fade_volume[drive] = 1.0f;
@@ -411,6 +542,7 @@ fdd_audio_set_motor_enable(int drive, int motor_enable)
}
} else if (!motor_enable && motoron[drive]) {
/* Motor stopping */
fdd_log("FDD Audio Drive %d: Stopping motor\n", drive);
spindlemotor_state[drive] = MOTOR_STATE_STOPPING;
spindlemotor_pos[drive] = 0;
spindlemotor_fade_volume[drive] = 1.0f;
@@ -429,6 +561,8 @@ fdd_audio_play_single_track_step(int drive, int from_track, int to_track)
if (abs(from_track - to_track) != 1)
return; /* Only single track movements */
fdd_log("FDD Audio Drive %d: Single track step %d -> %d\n", drive, from_track, to_track);
single_step_state[drive].position = 0;
single_step_state[drive].active = 1;
}
@@ -457,18 +591,20 @@ fdd_audio_play_multi_track_seek(int drive, int from_track, int 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;
}
fdd_log("FDD Audio Drive %d: Multi-track seek %d -> %d (%d tracks)\n",
drive, from_track, to_track, track_diff);
/* Get timing from configuration */
int profile_id = fdd_get_audio_profile(drive);
int duration_samples;
if (profile_id < 1 || profile_id >= audio_profile_count)
return;
/* Use configured timing */
duration_samples = track_diff * audio_profiles[profile_id].seek_duration_per_track;
fdd_log("FDD Audio Drive %d: Seek duration %d samples (%d tracks * %d samples/track)\n",
drive, duration_samples, track_diff, audio_profiles[profile_id].seek_duration_per_track);
/* Clamp to maximum available sample length */
if (duration_samples > samples->multi_track_seek.samples)
duration_samples = samples->multi_track_seek.samples;
@@ -481,6 +617,92 @@ fdd_audio_play_multi_track_seek(int drive, int from_track, int to_track)
multi_seek_state[drive].to_track = to_track;
}
static int16_t *
load_wav(const char *filename, int *sample_count)
{
if ((filename == NULL) || (strlen(filename) == 0))
return NULL;
if (strstr(filename, "..") != NULL)
return NULL;
FILE *f = rom_fopen(filename, "rb");
if (f == NULL) {
fdd_log("FDD Audio: Failed to open WAV file: %s\n", filename);
return NULL;
}
wav_header_t hdr;
if (fread(&hdr, sizeof(hdr), 1, f) != 1) {
fdd_log("FDD Audio: Failed to read WAV header from: %s\n", filename);
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)) {
fdd_log("FDD Audio: Invalid WAV format in file: %s\n", filename);
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)) {
fdd_log("FDD Audio: Unsupported WAV format in %s (format: %d, bits: %d, channels: %d)\n",
filename, hdr.audio_format, hdr.bits_per_sample, hdr.num_channels);
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) {
fdd_log("FDD Audio: Failed to allocate memory for WAV data: %s\n", filename);
fclose(f);
return NULL;
}
if (fread(input_data, 1, hdr.data_size, f) != hdr.data_size) {
fdd_log("FDD Audio: Failed to read WAV data from: %s\n", filename);
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) {
fdd_log("FDD Audio: Failed to allocate stereo conversion buffer for: %s\n", filename);
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);
fdd_log("FDD Audio: Loaded %s (mono->stereo, %d samples)\n", filename, output_samples);
} else {
/* Already stereo */
output_data = input_data;
output_samples = input_samples / 2; /* Number of stereo sample pairs */
fdd_log("FDD Audio: Loaded %s (stereo, %d samples)\n", filename, output_samples);
}
if (sample_count)
*sample_count = output_samples;
return output_data;
}
void
fdd_audio_callback(int16_t *buffer, int length)
{
@@ -777,11 +999,24 @@ fdd_audio_callback(int16_t *buffer, int length)
}
#else
/* Stub implementations when audio is disabled */
void fdd_audio_load_profiles(void) {}
int fdd_audio_get_profile_count(void) { return 1; }
const fdd_audio_profile_config_t* fdd_audio_get_profile(int id) {
static fdd_audio_profile_config_t none_profile = {0, "None", "none"};
return (id == 0) ? &none_profile : NULL;
}
const char* fdd_audio_get_profile_name(int id) { return (id == 0) ? "None" : NULL; }
const char* fdd_audio_get_profile_internal_name(int id) { return (id == 0) ? "none" : NULL; }
int fdd_audio_get_profile_by_internal_name(const char* internal_name) { return 0; }
double fdd_audio_get_seek_time(int drive, int is_initial, int track_count) {
return is_initial ? 15000.0 : 6000.0;
}
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_set_motor_enable(int drive, int motor_enable) {}
void fdd_audio_play_single_track_step(int drive, int from_track, int to_track) {}
void fdd_audio_play_multi_track_seek(int drive, int from_track, int to_track) {}
void fdd_audio_callback(int16_t *buffer, int length) { memset(buffer, 0, length * sizeof(int16_t)); }
#endif /* DISABLE_FDD_AUDIO */

View File

@@ -26,11 +26,11 @@
#define SEEK_RECALIBRATE -999
/* Per-drive audio profiles */
#define FDD_AUDIO_PROFILE_NONE 0
/* #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
#define FDD_AUDIO_PROFILE_MAX 4*/
#ifdef __cplusplus
extern "C" {

View File

@@ -23,6 +23,32 @@ extern "C" {
#ifndef DISABLE_FDD_AUDIO
/* Audio sample configuration structure */
typedef struct {
char filename[512];
float volume;
} audio_sample_config_t;
/* Drive type specific audio configuration */
typedef struct {
int id;
char name[128];
char internal_name[64];
audio_sample_config_t spindlemotor_start;
audio_sample_config_t spindlemotor_loop;
audio_sample_config_t spindlemotor_stop;
audio_sample_config_t single_track_step;
audio_sample_config_t multi_track_seek;
int seek_duration_per_track;
int total_tracks;
double initial_seek_time;
double initial_seek_time_pcjr;
double track_seek_time;
double track_seek_time_pcjr;
} fdd_audio_profile_config_t;
#define FDD_AUDIO_PROFILE_MAX 64
/* Motor sound states */
typedef enum {
MOTOR_STATE_STOPPED = 0,
@@ -52,12 +78,29 @@ typedef struct {
#define FADE_DURATION_MS 75
#define FADE_SAMPLES (48000 * FADE_DURATION_MS / 1000)
/* Functions for configuration management */
extern void fdd_audio_load_profiles(void);
extern int fdd_audio_get_profile_count(void);
extern const fdd_audio_profile_config_t* fdd_audio_get_profile(int id);
extern const char* fdd_audio_get_profile_name(int id);
extern const char* fdd_audio_get_profile_internal_name(int id);
extern int fdd_audio_get_profile_by_internal_name(const char* internal_name);
extern double fdd_audio_get_seek_time(int drive, int is_initial, int track_count);
#else
typedef enum {
MOTOR_STATE_STOPPED = 0
} motor_state_t;
typedef struct {
int id;
char name[128];
char internal_name[64];
} fdd_audio_profile_config_t;
#define FDD_AUDIO_PROFILE_MAX 1
#endif /* DISABLE_FDD_AUDIO */
/* FDD audio initialization and cleanup */
@@ -76,9 +119,6 @@ extern void fdd_audio_play_multi_track_seek(int drive, int from_track, int to_tr
/* 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

View File

@@ -32,6 +32,7 @@ extern "C" {
#include <86box/timer.h>
#include <86box/fdd.h>
#include <86box/cdrom.h>
#include <86box/fdd_audio.h>
}
#include "qt_models_common.hpp"
@@ -136,7 +137,7 @@ SettingsFloppyCDROM::SettingsFloppyCDROM(QWidget *parent)
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 */
for (int i = 0; i < FDD_NUM; i++) {
auto idx = model->index(i, 0);
@@ -145,22 +146,21 @@ SettingsFloppyCDROM::SettingsFloppyCDROM(QWidget *parent)
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);
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;
#ifndef DISABLE_FDD_AUDIO
// Get the profile name from the configuration system
const char *name = fdd_audio_get_profile_internal_name(prof);
if (name) {
profName = QString(name);
} else {
profName = tr("None");
}
#else
profName = tr("None");
#endif
auto audioIdx = model->index(i, 3);
model->setData(audioIdx, profName);
model->setData(audioIdx, prof, Qt::UserRole);
@@ -174,10 +174,13 @@ SettingsFloppyCDROM::SettingsFloppyCDROM(QWidget *parent)
#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);
int profile_count = fdd_audio_get_profile_count();
for (int i = 0; i < profile_count; i++) {
const char *name = fdd_audio_get_profile_name(i);
if (name) {
ui->comboBoxFloppyAudio->addItem(name, i);
}
}
ui->comboBoxFloppyAudio->setSizeAdjustPolicy(QComboBox::AdjustToContents);
#else
ui->comboBoxFloppyAudio->setVisible(false);
@@ -390,26 +393,22 @@ SettingsFloppyCDROM::on_comboBoxFloppyType_activated(int index)
void
SettingsFloppyCDROM::on_comboBoxFloppyAudio_activated(int)
{
auto idx = ui->tableViewFloppy->selectionModel()->currentIndex();
int prof = ui->comboBoxFloppyAudio->currentData().toInt();
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;
#ifndef DISABLE_FDD_AUDIO
// Get the profile name from the configuration system
const char *name = fdd_audio_get_profile_internal_name(prof);
if (name) {
profName = name;
} else {
profName = tr("None");
}
#else
profName = tr("None");
#endif
auto audioIdx = idx.siblingAtColumn(3);
ui->tableViewFloppy->model()->setData(audioIdx, profName);
ui->tableViewFloppy->model()->setData(audioIdx, prof, Qt::UserRole);