Multiple HDD drive sounds support

This commit is contained in:
Domppari
2026-01-04 21:35:59 +02:00
parent 419ee5cbd9
commit 4b86bcc2bf
2 changed files with 525 additions and 299 deletions

View File

@@ -29,15 +29,29 @@
#include <86box/mem.h>
#include <86box/rom.h>
/* Maximum number of simultaneous seek sounds */
#define HDD_MAX_SEEK_VOICES 8
/* Maximum number of simultaneous seek sounds per HDD */
#define HDD_MAX_SEEK_VOICES_PER_HDD 8
/* Maximum number of HDDs with audio emulation */
#define HDD_AUDIO_MAX_DRIVES 8
typedef struct {
int active;
int position;
float volume;
int profile_id; /* Which profile's seek sound to use */
} hdd_seek_voice_t;
/* Per-HDD audio state */
typedef struct {
int hdd_index; /* Index into hdd[] array */
int profile_id; /* Audio profile ID */
hdd_spindle_state_t spindle_state;
int spindle_pos;
int spindle_transition_pos;
hdd_seek_voice_t seek_voices[HDD_MAX_SEEK_VOICES_PER_HDD];
} hdd_audio_drive_state_t;
/* Audio samples structure for a profile */
typedef struct {
int16_t *spindle_start_buffer;
@@ -62,16 +76,11 @@ static int audio_profile_count = 0;
/* Per-profile loaded samples */
static hdd_audio_samples_t profile_samples[HDD_AUDIO_PROFILE_MAX];
/* Active profile for audio playback (first HDD with valid profile) */
static int active_audio_profile = 0;
/* Per-HDD audio states */
static hdd_audio_drive_state_t drive_states[HDD_AUDIO_MAX_DRIVES];
static int active_drive_count = 0;
static hdd_seek_voice_t hdd_seek_voices[HDD_MAX_SEEK_VOICES];
static mutex_t *hdd_audio_mutex = NULL;
/* Spindle motor state */
static hdd_spindle_state_t spindle_state = HDD_SPINDLE_STOPPED;
static int spindle_pos = 0;
static int spindle_transition_pos = 0; /* Position in start/stop sample */
static mutex_t *hdd_audio_mutex = NULL;
/* Load audio profiles from configuration file */
void
@@ -301,45 +310,68 @@ hdd_audio_load_profile_samples(int profile_id)
samples->loaded = 1;
}
/* Find drive state for a given HDD index, or NULL if not tracked */
static hdd_audio_drive_state_t *
hdd_audio_find_drive_state(int hdd_index)
{
for (int i = 0; i < active_drive_count; i++) {
if (drive_states[i].hdd_index == hdd_index)
return &drive_states[i];
}
return NULL;
}
void
hdd_audio_init(void)
{
/* Initialize profile samples */
memset(profile_samples, 0, sizeof(profile_samples));
memset(drive_states, 0, sizeof(drive_states));
active_drive_count = 0;
pclog("HDD Audio Init: audio_profile_count=%d\n", audio_profile_count);
/* Find first HDD with a valid audio profile and load its samples */
active_audio_profile = 0;
for (int i = 0; i < HDD_NUM; i++) {
if (hdd[i].bus_type != HDD_BUS_DISABLED) {
pclog("HDD Audio Init: HDD %d bus_type=%d audio_profile=%d\n",
i, hdd[i].bus_type, hdd[i].audio_profile);
if (hdd[i].audio_profile > 0) {
active_audio_profile = hdd[i].audio_profile;
pclog("HDD Audio: Using profile %d from HDD %d\n", active_audio_profile, i);
break;
}
}
}
pclog("HDD Audio Init: active_audio_profile=%d\n", active_audio_profile);
for (int i = 0; i < HDD_MAX_SEEK_VOICES; i++) {
hdd_seek_voices[i].active = 0;
hdd_seek_voices[i].position = 0;
hdd_seek_voices[i].volume = 1.0f;
}
/* Create mutex BEFORE loading samples or calling spinup */
if (!hdd_audio_mutex)
hdd_audio_mutex = thread_create_mutex();
/* Load samples for the active profile */
if (active_audio_profile > 0 && active_audio_profile < audio_profile_count) {
hdd_audio_load_profile_samples(active_audio_profile);
/* Start spindle motor */
hdd_audio_spinup();
/* Find all HDDs with valid audio profiles and initialize their states */
for (int i = 0; i < HDD_NUM && active_drive_count < HDD_AUDIO_MAX_DRIVES; i++) {
if (hdd[i].bus_type != HDD_BUS_DISABLED && hdd[i].audio_profile > 0) {
pclog("HDD Audio Init: HDD %d bus_type=%d audio_profile=%d\n",
i, hdd[i].bus_type, hdd[i].audio_profile);
hdd_audio_drive_state_t *state = &drive_states[active_drive_count];
state->hdd_index = i;
state->profile_id = hdd[i].audio_profile;
state->spindle_state = HDD_SPINDLE_STOPPED;
state->spindle_pos = 0;
state->spindle_transition_pos = 0;
/* Initialize seek voices for this drive */
for (int v = 0; v < HDD_MAX_SEEK_VOICES_PER_HDD; v++) {
state->seek_voices[v].active = 0;
state->seek_voices[v].position = 0;
state->seek_voices[v].volume = 1.0f;
state->seek_voices[v].profile_id = state->profile_id;
}
/* Load samples for this profile if not already loaded */
hdd_audio_load_profile_samples(state->profile_id);
pclog("HDD Audio: Initialized drive %d with profile %d (%s)\n",
i, state->profile_id,
hdd_audio_get_profile_name(state->profile_id));
active_drive_count++;
}
}
pclog("HDD Audio Init: %d active drives with audio\n", active_drive_count);
/* Start spindle motors for all active drives */
for (int i = 0; i < active_drive_count; i++) {
hdd_audio_spinup_drive(drive_states[i].hdd_index);
}
sound_hdd_thread_init();
@@ -354,20 +386,18 @@ hdd_audio_reset(void)
if (hdd_audio_mutex)
thread_wait_mutex(hdd_audio_mutex);
/* Reset spindle state first to stop audio playback */
spindle_state = HDD_SPINDLE_STOPPED;
spindle_pos = 0;
spindle_transition_pos = 0;
/* Reset seek voices */
for (int i = 0; i < HDD_MAX_SEEK_VOICES; i++) {
hdd_seek_voices[i].active = 0;
hdd_seek_voices[i].position = 0;
hdd_seek_voices[i].volume = 1.0f;
/* Reset all drive states */
for (int i = 0; i < active_drive_count; i++) {
drive_states[i].spindle_state = HDD_SPINDLE_STOPPED;
drive_states[i].spindle_pos = 0;
drive_states[i].spindle_transition_pos = 0;
for (int v = 0; v < HDD_MAX_SEEK_VOICES_PER_HDD; v++) {
drive_states[i].seek_voices[v].active = 0;
drive_states[i].seek_voices[v].position = 0;
drive_states[i].seek_voices[v].volume = 1.0f;
}
}
/* Reset active profile before freeing buffers */
active_audio_profile = 0;
active_drive_count = 0;
/* Free previously loaded samples (but keep profiles) */
for (int i = 0; i < HDD_AUDIO_PROFILE_MAX; i++) {
@@ -393,23 +423,42 @@ hdd_audio_reset(void)
if (hdd_audio_mutex)
thread_release_mutex(hdd_audio_mutex);
/* Find new active profile from current HDD configuration */
for (int i = 0; i < HDD_NUM; i++) {
if (hdd[i].bus_type != HDD_BUS_DISABLED) {
/* Find all HDDs with valid audio profiles and initialize their states */
for (int i = 0; i < HDD_NUM && active_drive_count < HDD_AUDIO_MAX_DRIVES; i++) {
if (hdd[i].bus_type != HDD_BUS_DISABLED && hdd[i].audio_profile > 0) {
pclog("HDD Audio Reset: HDD %d audio_profile=%d\n", i, hdd[i].audio_profile);
if (hdd[i].audio_profile > 0) {
active_audio_profile = hdd[i].audio_profile;
pclog("HDD Audio: Reset with profile %d from HDD %d\n", active_audio_profile, i);
break;
hdd_audio_drive_state_t *state = &drive_states[active_drive_count];
state->hdd_index = i;
state->profile_id = hdd[i].audio_profile;
state->spindle_state = HDD_SPINDLE_STOPPED;
state->spindle_pos = 0;
state->spindle_transition_pos = 0;
/* Initialize seek voices for this drive */
for (int v = 0; v < HDD_MAX_SEEK_VOICES_PER_HDD; v++) {
state->seek_voices[v].active = 0;
state->seek_voices[v].position = 0;
state->seek_voices[v].volume = 1.0f;
state->seek_voices[v].profile_id = state->profile_id;
}
/* Load samples for this profile if not already loaded */
hdd_audio_load_profile_samples(state->profile_id);
pclog("HDD Audio: Reset drive %d with profile %d (%s)\n",
i, state->profile_id,
hdd_audio_get_profile_name(state->profile_id));
active_drive_count++;
}
}
/* Load samples for the active profile */
if (active_audio_profile > 0 && active_audio_profile < audio_profile_count) {
hdd_audio_load_profile_samples(active_audio_profile);
/* Start spindle motor */
hdd_audio_spinup();
pclog("HDD Audio Reset: %d active drives with audio\n", active_drive_count);
/* Start spindle motors for all active drives */
for (int i = 0; i < active_drive_count; i++) {
hdd_audio_spinup_drive(drive_states[i].hdd_index);
}
}
@@ -421,10 +470,20 @@ hdd_audio_seek(hard_disk_t *hdd_drive, uint32_t new_cylinder)
if (cylinder_diff == 0)
return;
/* Use the drive's audio profile, fallback to active profile */
int profile_id = hdd_drive->audio_profile;
if (profile_id == 0)
profile_id = active_audio_profile;
/* Find the drive state for this HDD */
hdd_audio_drive_state_t *drive_state = NULL;
for (int i = 0; i < active_drive_count; i++) {
if (&hdd[drive_states[i].hdd_index] == hdd_drive) {
drive_state = &drive_states[i];
break;
}
}
/* If no drive state found, drive has no audio profile */
if (!drive_state)
return;
int profile_id = drive_state->profile_id;
/* No audio profile selected */
if (profile_id == 0 || profile_id >= audio_profile_count)
@@ -450,9 +509,10 @@ hdd_audio_seek(hard_disk_t *hdd_drive, uint32_t new_cylinder)
thread_wait_mutex(hdd_audio_mutex);
for (int i = 0; i < HDD_MAX_SEEK_VOICES; i++) {
if (hdd_seek_voices[i].active) {
int pos = hdd_seek_voices[i].position;
/* Check if we should skip due to minimum spacing (per-drive) */
for (int v = 0; v < HDD_MAX_SEEK_VOICES_PER_HDD; v++) {
if (drive_state->seek_voices[v].active) {
int pos = drive_state->seek_voices[v].position;
if (pos >= 0 && pos < min_seek_spacing) {
thread_release_mutex(hdd_audio_mutex);
return;
@@ -460,11 +520,13 @@ hdd_audio_seek(hard_disk_t *hdd_drive, uint32_t new_cylinder)
}
}
for (int i = 0; i < HDD_MAX_SEEK_VOICES; i++) {
if (!hdd_seek_voices[i].active) {
hdd_seek_voices[i].active = 1;
hdd_seek_voices[i].position = 0;
hdd_seek_voices[i].volume = samples->seek_volume;
/* Find a free seek voice for this drive */
for (int v = 0; v < HDD_MAX_SEEK_VOICES_PER_HDD; v++) {
if (!drive_state->seek_voices[v].active) {
drive_state->seek_voices[v].active = 1;
drive_state->seek_voices[v].position = 0;
drive_state->seek_voices[v].volume = samples->seek_volume;
drive_state->seek_voices[v].profile_id = profile_id;
thread_release_mutex(hdd_audio_mutex);
return;
}
@@ -473,42 +535,400 @@ hdd_audio_seek(hard_disk_t *hdd_drive, uint32_t new_cylinder)
thread_release_mutex(hdd_audio_mutex);
}
/* Spinup a specific drive by HDD index */
void
hdd_audio_spinup(void)
hdd_audio_spinup_drive(int hdd_index)
{
if (spindle_state == HDD_SPINDLE_RUNNING || spindle_state == HDD_SPINDLE_STARTING)
hdd_audio_drive_state_t *state = hdd_audio_find_drive_state(hdd_index);
if (!state)
return;
pclog("HDD Audio: Spinup requested (current state: %d)\n", spindle_state);
if (state->spindle_state == HDD_SPINDLE_RUNNING || state->spindle_state == HDD_SPINDLE_STARTING)
return;
pclog("HDD Audio: Spinup requested for drive %d (current state: %d)\n", hdd_index, state->spindle_state);
if (hdd_audio_mutex)
thread_wait_mutex(hdd_audio_mutex);
spindle_state = HDD_SPINDLE_STARTING;
spindle_transition_pos = 0;
state->spindle_state = HDD_SPINDLE_STARTING;
state->spindle_transition_pos = 0;
if (hdd_audio_mutex)
thread_release_mutex(hdd_audio_mutex);
}
/* Spindown a specific drive by HDD index */
void
hdd_audio_spindown_drive(int hdd_index)
{
hdd_audio_drive_state_t *state = hdd_audio_find_drive_state(hdd_index);
if (!state)
return;
if (state->spindle_state == HDD_SPINDLE_STOPPED || state->spindle_state == HDD_SPINDLE_STOPPING)
return;
pclog("HDD Audio: Spindown requested for drive %d (current state: %d)\n", hdd_index, state->spindle_state);
if (hdd_audio_mutex)
thread_wait_mutex(hdd_audio_mutex);
state->spindle_state = HDD_SPINDLE_STOPPING;
state->spindle_transition_pos = 0;
if (hdd_audio_mutex)
thread_release_mutex(hdd_audio_mutex);
}
/* Legacy functions for backward compatibility - operate on all drives */
void
hdd_audio_spinup(void)
{
for (int i = 0; i < active_drive_count; i++) {
hdd_audio_spinup_drive(drive_states[i].hdd_index);
}
}
void
hdd_audio_spindown(void)
{
if (spindle_state == HDD_SPINDLE_STOPPED || spindle_state == HDD_SPINDLE_STOPPING)
return;
pclog("HDD Audio: Spindown requested (current state: %d)\n", spindle_state);
if (hdd_audio_mutex)
thread_wait_mutex(hdd_audio_mutex);
spindle_state = HDD_SPINDLE_STOPPING;
spindle_transition_pos = 0;
if (hdd_audio_mutex)
thread_release_mutex(hdd_audio_mutex);
for (int i = 0; i < active_drive_count; i++) {
hdd_audio_spindown_drive(drive_states[i].hdd_index);
}
}
hdd_spindle_state_t
hdd_audio_get_spindle_state(void)
{
return spindle_state;
/* Return running if any drive is running */
for (int i = 0; i < active_drive_count; i++) {
if (drive_states[i].spindle_state == HDD_SPINDLE_RUNNING)
return HDD_SPINDLE_RUNNING;
}
for (int i = 0; i < active_drive_count; i++) {
if (drive_states[i].spindle_state == HDD_SPINDLE_STARTING)
return HDD_SPINDLE_STARTING;
}
for (int i = 0; i < active_drive_count; i++) {
if (drive_states[i].spindle_state == HDD_SPINDLE_STOPPING)
return HDD_SPINDLE_STOPPING;
}
return HDD_SPINDLE_STOPPED;
}
hdd_spindle_state_t
hdd_audio_get_drive_spindle_state(int hdd_index)
{
hdd_audio_drive_state_t *state = hdd_audio_find_drive_state(hdd_index);
if (!state)
return HDD_SPINDLE_STOPPED;
return state->spindle_state;
}
/* Helper: Mix spindle start sound into float buffer */
static void
hdd_audio_mix_spindle_start_float(hdd_audio_drive_state_t *state, hdd_audio_samples_t *samples,
float *float_buffer, int frames_in_buffer)
{
if (!samples->spindle_start_buffer || samples->spindle_start_samples <= 0) {
state->spindle_state = HDD_SPINDLE_RUNNING;
state->spindle_pos = 0;
return;
}
float start_volume = samples->spindle_start_volume;
for (int i = 0; i < frames_in_buffer && state->spindle_transition_pos < samples->spindle_start_samples; i++) {
float left_sample = (float) samples->spindle_start_buffer[state->spindle_transition_pos * 2] / 131072.0f * start_volume;
float right_sample = (float) samples->spindle_start_buffer[state->spindle_transition_pos * 2 + 1] / 131072.0f * start_volume;
float_buffer[i * 2] += left_sample;
float_buffer[i * 2 + 1] += right_sample;
state->spindle_transition_pos++;
}
if (state->spindle_transition_pos >= samples->spindle_start_samples) {
state->spindle_state = HDD_SPINDLE_RUNNING;
state->spindle_pos = 0;
pclog("HDD Audio: Drive %d spinup complete, now running\n", state->hdd_index);
}
}
/* Helper: Mix spindle loop sound into float buffer */
static void
hdd_audio_mix_spindle_loop_float(hdd_audio_drive_state_t *state, hdd_audio_samples_t *samples,
float *float_buffer, int frames_in_buffer)
{
if (!samples->spindle_loop_buffer || samples->spindle_loop_samples <= 0)
return;
float spindle_volume = samples->spindle_loop_volume;
for (int i = 0; i < frames_in_buffer; i++) {
float left_sample = (float) samples->spindle_loop_buffer[state->spindle_pos * 2] / 131072.0f * spindle_volume;
float right_sample = (float) samples->spindle_loop_buffer[state->spindle_pos * 2 + 1] / 131072.0f * spindle_volume;
float_buffer[i * 2] += left_sample;
float_buffer[i * 2 + 1] += right_sample;
state->spindle_pos++;
if (state->spindle_pos >= samples->spindle_loop_samples) {
state->spindle_pos = 0;
}
}
}
/* Helper: Mix spindle stop sound into float buffer */
static void
hdd_audio_mix_spindle_stop_float(hdd_audio_drive_state_t *state, hdd_audio_samples_t *samples,
float *float_buffer, int frames_in_buffer)
{
if (!samples->spindle_stop_buffer || samples->spindle_stop_samples <= 0) {
state->spindle_state = HDD_SPINDLE_STOPPED;
return;
}
float stop_volume = samples->spindle_stop_volume;
for (int i = 0; i < frames_in_buffer && state->spindle_transition_pos < samples->spindle_stop_samples; i++) {
float left_sample = (float) samples->spindle_stop_buffer[state->spindle_transition_pos * 2] / 131072.0f * stop_volume;
float right_sample = (float) samples->spindle_stop_buffer[state->spindle_transition_pos * 2 + 1] / 131072.0f * stop_volume;
float_buffer[i * 2] += left_sample;
float_buffer[i * 2 + 1] += right_sample;
state->spindle_transition_pos++;
}
if (state->spindle_transition_pos >= samples->spindle_stop_samples) {
state->spindle_state = HDD_SPINDLE_STOPPED;
pclog("HDD Audio: Drive %d spindown complete, now stopped\n", state->hdd_index);
}
}
/* Helper: Mix seek sounds into float buffer */
static void
hdd_audio_mix_seek_float(hdd_audio_drive_state_t *state, float *float_buffer, int frames_in_buffer)
{
for (int v = 0; v < HDD_MAX_SEEK_VOICES_PER_HDD; v++) {
if (!state->seek_voices[v].active)
continue;
int seek_profile_id = state->seek_voices[v].profile_id;
hdd_audio_samples_t *seek_samples = &profile_samples[seek_profile_id];
if (!seek_samples->seek_buffer || seek_samples->seek_samples == 0)
continue;
float voice_vol = state->seek_voices[v].volume;
int pos = state->seek_voices[v].position;
if (pos < 0) pos = 0;
for (int i = 0; i < frames_in_buffer && pos < seek_samples->seek_samples; i++, pos++) {
float seek_left = (float) seek_samples->seek_buffer[pos * 2] / 131072.0f * voice_vol;
float seek_right = (float) seek_samples->seek_buffer[pos * 2 + 1] / 131072.0f * voice_vol;
float_buffer[i * 2] += seek_left;
float_buffer[i * 2 + 1] += seek_right;
}
if (pos >= seek_samples->seek_samples) {
state->seek_voices[v].active = 0;
state->seek_voices[v].position = 0;
} else {
state->seek_voices[v].position = pos;
}
}
}
/* Helper: Mix spindle start sound into int16 buffer */
static void
hdd_audio_mix_spindle_start_int16(hdd_audio_drive_state_t *state, hdd_audio_samples_t *samples,
int16_t *buffer, int frames_in_buffer)
{
if (!samples->spindle_start_buffer || samples->spindle_start_samples <= 0) {
state->spindle_state = HDD_SPINDLE_RUNNING;
state->spindle_pos = 0;
return;
}
float start_volume = samples->spindle_start_volume;
for (int i = 0; i < frames_in_buffer && state->spindle_transition_pos < samples->spindle_start_samples; i++) {
int32_t left = buffer[i * 2] + (int32_t)(samples->spindle_start_buffer[state->spindle_transition_pos * 2] * start_volume);
int32_t right = buffer[i * 2 + 1] + (int32_t)(samples->spindle_start_buffer[state->spindle_transition_pos * 2 + 1] * start_volume);
if (left > 32767) left = 32767;
if (left < -32768) left = -32768;
if (right > 32767) right = 32767;
if (right < -32768) right = -32768;
buffer[i * 2] = (int16_t) left;
buffer[i * 2 + 1] = (int16_t) right;
state->spindle_transition_pos++;
}
if (state->spindle_transition_pos >= samples->spindle_start_samples) {
state->spindle_state = HDD_SPINDLE_RUNNING;
state->spindle_pos = 0;
pclog("HDD Audio: Drive %d spinup complete, now running\n", state->hdd_index);
}
}
/* Helper: Mix spindle loop sound into int16 buffer */
static void
hdd_audio_mix_spindle_loop_int16(hdd_audio_drive_state_t *state, hdd_audio_samples_t *samples,
int16_t *buffer, int frames_in_buffer)
{
if (!samples->spindle_loop_buffer || samples->spindle_loop_samples <= 0)
return;
float spindle_volume = samples->spindle_loop_volume;
for (int i = 0; i < frames_in_buffer; i++) {
int32_t left = buffer[i * 2] + (int32_t)(samples->spindle_loop_buffer[state->spindle_pos * 2] * spindle_volume);
int32_t right = buffer[i * 2 + 1] + (int32_t)(samples->spindle_loop_buffer[state->spindle_pos * 2 + 1] * spindle_volume);
if (left > 32767) left = 32767;
if (left < -32768) left = -32768;
if (right > 32767) right = 32767;
if (right < -32768) right = -32768;
buffer[i * 2] = (int16_t) left;
buffer[i * 2 + 1] = (int16_t) right;
state->spindle_pos++;
if (state->spindle_pos >= samples->spindle_loop_samples) {
state->spindle_pos = 0;
}
}
}
/* Helper: Mix spindle stop sound into int16 buffer */
static void
hdd_audio_mix_spindle_stop_int16(hdd_audio_drive_state_t *state, hdd_audio_samples_t *samples,
int16_t *buffer, int frames_in_buffer)
{
if (!samples->spindle_stop_buffer || samples->spindle_stop_samples <= 0) {
state->spindle_state = HDD_SPINDLE_STOPPED;
return;
}
float stop_volume = samples->spindle_stop_volume;
for (int i = 0; i < frames_in_buffer && state->spindle_transition_pos < samples->spindle_stop_samples; i++) {
int32_t left = buffer[i * 2] + (int32_t)(samples->spindle_stop_buffer[state->spindle_transition_pos * 2] * stop_volume);
int32_t right = buffer[i * 2 + 1] + (int32_t)(samples->spindle_stop_buffer[state->spindle_transition_pos * 2 + 1] * stop_volume);
if (left > 32767) left = 32767;
if (left < -32768) left = -32768;
if (right > 32767) right = 32767;
if (right < -32768) right = -32768;
buffer[i * 2] = (int16_t) left;
buffer[i * 2 + 1] = (int16_t) right;
state->spindle_transition_pos++;
}
if (state->spindle_transition_pos >= samples->spindle_stop_samples) {
state->spindle_state = HDD_SPINDLE_STOPPED;
pclog("HDD Audio: Drive %d spindown complete, now stopped\n", state->hdd_index);
}
}
/* Helper: Mix seek sounds into int16 buffer */
static void
hdd_audio_mix_seek_int16(hdd_audio_drive_state_t *state, int16_t *buffer, int frames_in_buffer)
{
for (int v = 0; v < HDD_MAX_SEEK_VOICES_PER_HDD; v++) {
if (!state->seek_voices[v].active)
continue;
int seek_profile_id = state->seek_voices[v].profile_id;
hdd_audio_samples_t *seek_samples = &profile_samples[seek_profile_id];
if (!seek_samples->seek_buffer || seek_samples->seek_samples == 0)
continue;
float voice_vol = state->seek_voices[v].volume;
int pos = state->seek_voices[v].position;
if (pos < 0) pos = 0;
for (int i = 0; i < frames_in_buffer && pos < seek_samples->seek_samples; i++, pos++) {
int32_t left = buffer[i * 2] + (int32_t)(seek_samples->seek_buffer[pos * 2] * voice_vol);
int32_t right = buffer[i * 2 + 1] + (int32_t)(seek_samples->seek_buffer[pos * 2 + 1] * voice_vol);
if (left > 32767) left = 32767;
if (left < -32768) left = -32768;
if (right > 32767) right = 32767;
if (right < -32768) right = -32768;
buffer[i * 2] = (int16_t) left;
buffer[i * 2 + 1] = (int16_t) right;
}
if (pos >= seek_samples->seek_samples) {
state->seek_voices[v].active = 0;
state->seek_voices[v].position = 0;
} else {
state->seek_voices[v].position = pos;
}
}
}
/* Process a single drive's audio in float mode */
static void
hdd_audio_process_drive_float(hdd_audio_drive_state_t *state, float *float_buffer, int frames_in_buffer)
{
int profile_id = state->profile_id;
if (profile_id <= 0 || profile_id >= HDD_AUDIO_PROFILE_MAX)
return;
hdd_audio_samples_t *samples = &profile_samples[profile_id];
if (!samples->loaded)
return;
/* Handle spindle states for this drive */
switch (state->spindle_state) {
case HDD_SPINDLE_STARTING:
hdd_audio_mix_spindle_start_float(state, samples, float_buffer, frames_in_buffer);
break;
case HDD_SPINDLE_RUNNING:
hdd_audio_mix_spindle_loop_float(state, samples, float_buffer, frames_in_buffer);
break;
case HDD_SPINDLE_STOPPING:
hdd_audio_mix_spindle_stop_float(state, samples, float_buffer, frames_in_buffer);
break;
case HDD_SPINDLE_STOPPED:
default:
break;
}
/* Seek sounds - only play when spindle is running */
if (samples->seek_buffer && samples->seek_samples > 0 &&
hdd_audio_mutex && state->spindle_state == HDD_SPINDLE_RUNNING) {
thread_wait_mutex(hdd_audio_mutex);
hdd_audio_mix_seek_float(state, float_buffer, frames_in_buffer);
thread_release_mutex(hdd_audio_mutex);
}
}
/* Process a single drive's audio in int16 mode */
static void
hdd_audio_process_drive_int16(hdd_audio_drive_state_t *state, int16_t *buffer, int frames_in_buffer)
{
int profile_id = state->profile_id;
if (profile_id <= 0 || profile_id >= HDD_AUDIO_PROFILE_MAX)
return;
hdd_audio_samples_t *samples = &profile_samples[profile_id];
if (!samples->loaded)
return;
/* Handle spindle states for this drive */
switch (state->spindle_state) {
case HDD_SPINDLE_STARTING:
hdd_audio_mix_spindle_start_int16(state, samples, buffer, frames_in_buffer);
break;
case HDD_SPINDLE_RUNNING:
hdd_audio_mix_spindle_loop_int16(state, samples, buffer, frames_in_buffer);
break;
case HDD_SPINDLE_STOPPING:
hdd_audio_mix_spindle_stop_int16(state, samples, buffer, frames_in_buffer);
break;
case HDD_SPINDLE_STOPPED:
default:
break;
}
/* Seek sounds - only play when spindle is running */
if (samples->seek_buffer && samples->seek_samples > 0 &&
hdd_audio_mutex && state->spindle_state == HDD_SPINDLE_RUNNING) {
thread_wait_mutex(hdd_audio_mutex);
hdd_audio_mix_seek_int16(state, buffer, frames_in_buffer);
thread_release_mutex(hdd_audio_mutex);
}
}
void
@@ -516,12 +936,6 @@ hdd_audio_callback(int16_t *buffer, int length)
{
int frames_in_buffer = length / 2;
/* Get active profile samples */
hdd_audio_samples_t *samples = NULL;
if (active_audio_profile > 0 && active_audio_profile < HDD_AUDIO_PROFILE_MAX) {
samples = &profile_samples[active_audio_profile];
}
if (sound_is_float) {
float *float_buffer = (float *) buffer;
@@ -530,108 +944,9 @@ hdd_audio_callback(int16_t *buffer, int length)
float_buffer[i] = 0.0f;
}
/* Handle spindle states */
if (samples) {
switch (spindle_state) {
case HDD_SPINDLE_STARTING:
/* Play spinup sound */
if (samples->spindle_start_buffer && samples->spindle_start_samples > 0) {
float start_volume = samples->spindle_start_volume;
for (int i = 0; i < frames_in_buffer && spindle_transition_pos < samples->spindle_start_samples; i++) {
float left_sample = (float) samples->spindle_start_buffer[spindle_transition_pos * 2] / 131072.0f * start_volume;
float right_sample = (float) samples->spindle_start_buffer[spindle_transition_pos * 2 + 1] / 131072.0f * start_volume;
float_buffer[i * 2] = left_sample;
float_buffer[i * 2 + 1] = right_sample;
spindle_transition_pos++;
}
if (spindle_transition_pos >= samples->spindle_start_samples) {
spindle_state = HDD_SPINDLE_RUNNING;
spindle_pos = 0;
pclog("HDD Audio: Spinup complete, now running\n");
}
} else {
/* No start sample, go directly to running */
spindle_state = HDD_SPINDLE_RUNNING;
spindle_pos = 0;
}
break;
case HDD_SPINDLE_RUNNING:
/* Play spindle loop */
if (samples->spindle_loop_buffer && samples->spindle_loop_samples > 0) {
float spindle_volume = samples->spindle_loop_volume;
for (int i = 0; i < frames_in_buffer; i++) {
float left_sample = (float) samples->spindle_loop_buffer[spindle_pos * 2] / 131072.0f * spindle_volume;
float right_sample = (float) samples->spindle_loop_buffer[spindle_pos * 2 + 1] / 131072.0f * spindle_volume;
float_buffer[i * 2] = left_sample;
float_buffer[i * 2 + 1] = right_sample;
spindle_pos++;
if (spindle_pos >= samples->spindle_loop_samples) {
spindle_pos = 0;
}
}
}
break;
case HDD_SPINDLE_STOPPING:
/* Play spindown sound */
if (samples->spindle_stop_buffer && samples->spindle_stop_samples > 0) {
float stop_volume = samples->spindle_stop_volume;
for (int i = 0; i < frames_in_buffer && spindle_transition_pos < samples->spindle_stop_samples; i++) {
float left_sample = (float) samples->spindle_stop_buffer[spindle_transition_pos * 2] / 131072.0f * stop_volume;
float right_sample = (float) samples->spindle_stop_buffer[spindle_transition_pos * 2 + 1] / 131072.0f * stop_volume;
float_buffer[i * 2] = left_sample;
float_buffer[i * 2 + 1] = right_sample;
spindle_transition_pos++;
}
if (spindle_transition_pos >= samples->spindle_stop_samples) {
spindle_state = HDD_SPINDLE_STOPPED;
pclog("HDD Audio: Spindown complete, now stopped\n");
}
} else {
/* No stop sample, go directly to stopped */
spindle_state = HDD_SPINDLE_STOPPED;
}
break;
case HDD_SPINDLE_STOPPED:
default:
/* Silence - buffer already zeroed */
break;
}
}
/* Seek sounds from profile - only play when spindle is running */
if (samples && samples->seek_buffer && samples->seek_samples > 0 &&
hdd_audio_mutex && spindle_state == HDD_SPINDLE_RUNNING) {
thread_wait_mutex(hdd_audio_mutex);
for (int v = 0; v < HDD_MAX_SEEK_VOICES; v++) {
if (!hdd_seek_voices[v].active)
continue;
float voice_vol = hdd_seek_voices[v].volume;
int pos = hdd_seek_voices[v].position;
if (pos < 0) pos = 0;
for (int i = 0; i < frames_in_buffer && pos < samples->seek_samples; i++, pos++) {
float seek_left = (float) samples->seek_buffer[pos * 2] / 131072.0f * voice_vol;
float seek_right = (float) samples->seek_buffer[pos * 2 + 1] / 131072.0f * voice_vol;
float_buffer[i * 2] += seek_left;
float_buffer[i * 2 + 1] += seek_right;
}
if (pos >= samples->seek_samples) {
hdd_seek_voices[v].active = 0;
hdd_seek_voices[v].position = 0;
} else {
hdd_seek_voices[v].position = pos;
}
}
thread_release_mutex(hdd_audio_mutex);
/* Process each active drive */
for (int d = 0; d < active_drive_count; d++) {
hdd_audio_process_drive_float(&drive_states[d], float_buffer, frames_in_buffer);
}
} else {
/* Initialize buffer to silence */
@@ -639,105 +954,9 @@ hdd_audio_callback(int16_t *buffer, int length)
buffer[i] = 0;
}
/* Handle spindle states */
if (samples) {
switch (spindle_state) {
case HDD_SPINDLE_STARTING:
/* Play spinup sound */
if (samples->spindle_start_buffer && samples->spindle_start_samples > 0) {
float start_volume = samples->spindle_start_volume;
for (int i = 0; i < frames_in_buffer && spindle_transition_pos < samples->spindle_start_samples; i++) {
buffer[i * 2] = (int16_t)(samples->spindle_start_buffer[spindle_transition_pos * 2] * start_volume);
buffer[i * 2 + 1] = (int16_t)(samples->spindle_start_buffer[spindle_transition_pos * 2 + 1] * start_volume);
spindle_transition_pos++;
}
if (spindle_transition_pos >= samples->spindle_start_samples) {
spindle_state = HDD_SPINDLE_RUNNING;
spindle_pos = 0;
pclog("HDD Audio: Spinup complete, now running\n");
}
} else {
spindle_state = HDD_SPINDLE_RUNNING;
spindle_pos = 0;
}
break;
case HDD_SPINDLE_RUNNING:
/* Play spindle loop */
if (samples->spindle_loop_buffer && samples->spindle_loop_samples > 0) {
float spindle_volume = samples->spindle_loop_volume;
for (int i = 0; i < frames_in_buffer; i++) {
buffer[i * 2] = (int16_t)(samples->spindle_loop_buffer[spindle_pos * 2] * spindle_volume);
buffer[i * 2 + 1] = (int16_t)(samples->spindle_loop_buffer[spindle_pos * 2 + 1] * spindle_volume);
spindle_pos++;
if (spindle_pos >= samples->spindle_loop_samples) {
spindle_pos = 0;
}
}
}
break;
case HDD_SPINDLE_STOPPING:
/* Play spindown sound */
if (samples->spindle_stop_buffer && samples->spindle_stop_samples > 0) {
float stop_volume = samples->spindle_stop_volume;
for (int i = 0; i < frames_in_buffer && spindle_transition_pos < samples->spindle_stop_samples; i++) {
buffer[i * 2] = (int16_t)(samples->spindle_stop_buffer[spindle_transition_pos * 2] * stop_volume);
buffer[i * 2 + 1] = (int16_t)(samples->spindle_stop_buffer[spindle_transition_pos * 2 + 1] * stop_volume);
spindle_transition_pos++;
}
if (spindle_transition_pos >= samples->spindle_stop_samples) {
spindle_state = HDD_SPINDLE_STOPPED;
pclog("HDD Audio: Spindown complete, now stopped\n");
}
} else {
spindle_state = HDD_SPINDLE_STOPPED;
}
break;
case HDD_SPINDLE_STOPPED:
default:
/* Silence - buffer already zeroed */
break;
}
}
/* Seek sounds from profile - only play when spindle is running */
if (samples && samples->seek_buffer && samples->seek_samples > 0 &&
hdd_audio_mutex && spindle_state == HDD_SPINDLE_RUNNING) {
thread_wait_mutex(hdd_audio_mutex);
for (int v = 0; v < HDD_MAX_SEEK_VOICES; v++) {
if (!hdd_seek_voices[v].active)
continue;
float voice_vol = hdd_seek_voices[v].volume;
int pos = hdd_seek_voices[v].position;
if (pos < 0) pos = 0;
for (int i = 0; i < frames_in_buffer && pos < samples->seek_samples; i++, pos++) {
int32_t left = buffer[i * 2] + (int32_t)(samples->seek_buffer[pos * 2] * voice_vol);
int32_t right = buffer[i * 2 + 1] + (int32_t)(samples->seek_buffer[pos * 2 + 1] * voice_vol);
if (left > 32767) left = 32767;
if (left < -32768) left = -32768;
if (right > 32767) right = 32767;
if (right < -32768) right = -32768;
buffer[i * 2] = (int16_t) left;
buffer[i * 2 + 1] = (int16_t) right;
}
if (pos >= samples->seek_samples) {
hdd_seek_voices[v].active = 0;
hdd_seek_voices[v].position = 0;
} else {
hdd_seek_voices[v].position = pos;
}
}
thread_release_mutex(hdd_audio_mutex);
/* Process each active drive */
for (int d = 0; d < active_drive_count; d++) {
hdd_audio_process_drive_int16(&drive_states[d], buffer, frames_in_buffer);
}
}
}

View File

@@ -65,6 +65,13 @@ extern void hdd_audio_reset(void);
extern void hdd_audio_close(void);
extern void hdd_audio_callback(int16_t *buffer, int length);
extern void hdd_audio_seek(hard_disk_t *hdd, uint32_t new_cylinder);
/* Per-drive spindle control */
extern void hdd_audio_spinup_drive(int hdd_index);
extern void hdd_audio_spindown_drive(int hdd_index);
extern hdd_spindle_state_t hdd_audio_get_drive_spindle_state(int hdd_index);
/* Legacy functions for backward compatibility - operate on all drives */
extern void hdd_audio_spinup(void);
extern void hdd_audio_spindown(void);
extern hdd_spindle_state_t hdd_audio_get_spindle_state(void);