mirror of
https://github.com/86Box/86Box.git
synced 2026-02-22 01:25:33 -07:00
Multiple HDD drive sounds support
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user