diff --git a/src/devices/imagedev/floppy.cpp b/src/devices/imagedev/floppy.cpp index b01f5db11cfc4..cb07b6654dd07 100644 --- a/src/devices/imagedev/floppy.cpp +++ b/src/devices/imagedev/floppy.cpp @@ -17,6 +17,9 @@ // Show step operation #define TRACE_STEP 0 +#define TRACE_AUDIO 0 + +#define PITCH_SEEK_SAMPLES 1 #define FLOPSND_TAG "floppysound" @@ -545,7 +548,8 @@ void floppy_image_device::mon_w(int state) } } - if (m_make_sound) m_sound_out->motor(state==0); + // Create a motor sound (loaded or empty) + if (m_make_sound) m_sound_out->motor(state==0, exists()); } attotime floppy_image_device::time_next_index() @@ -632,7 +636,8 @@ void floppy_image_device::stp_w(int state) { if (TRACE_STEP) logerror("%s: track %d\n", tag(), cyl); // Do we want a stepper sound? - if (m_make_sound) m_sound_out->step(); + // We plan for 5 zones with possibly specific sounds + if (m_make_sound) m_sound_out->step(cyl*5/tracks); } /* Update disk detection if applicable */ if (exists()) @@ -949,76 +954,126 @@ UINT32 floppy_image_device::get_variant() const // MZ, Aug 2015 //=================================================================== +enum +{ + QUIET=-1, + SPIN_START_EMPTY=0, + SPIN_START_LOADED, + SPIN_EMPTY, + SPIN_LOADED, + SPIN_END +}; + +enum +{ + STEP_SINGLE=0, + STEP_SEEK2, + STEP_SEEK6, + STEP_SEEK12, + STEP_SEEK20 +}; + +/* + Unless labeled "constructed", all samples were recorded from real floppy drives. + The 3.5" floppy drive is a Sony MPF420-1. + The 5.25" floppy drive is a Chinon FZ502. +*/ +static const char *const floppy35_sample_names[] = +{ +// Subdirectory + "*floppy", +// Spinning sounds + "35_spin_start_empty", + "35_spin_start_loaded", + "35_spin_empty", + "35_spin_loaded", + "35_spin_end", +// Stepping sounds + "35_step_1_1", +// Seeking sounds + "35_seek_2ms", // constructed + "35_seek_6ms", + "35_seek_12ms", + "35_seek_20ms", + nullptr +}; + +static const char *const floppy525_sample_names[] = +{ +// Subdirectory + "*floppy", +// Spinning sounds + "525_spin_start_empty", + "525_spin_start_loaded", + "525_spin_empty", + "525_spin_loaded", + "525_spin_end", +// Stepping sounds + "525_step_1_1", +// Seeking sounds + "525_seek_2ms", // unrealistically fast, but needed for 3.5 (constructed) + "525_seek_6ms", + "525_seek_12ms", + "525_seek_20ms", + nullptr +}; + floppy_sound_device::floppy_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : samples_device(mconfig, FLOPPYSOUND, "Floppy sound", tag, owner, clock, "flopsnd", __FILE__), m_sound(nullptr), - m_is525(false), - m_sampleindex_motor_start(0), - m_sampleindex_motor_loop(0), - m_sampleindex_motor_end(0), - m_samplesize_motor_start(0), - m_samplesize_motor_loop(0), - m_samplesize_motor_end(0), - m_samplepos_motor(0), - m_motor_playback_state(0), - m_motor_on(false), + m_step_base(0), + m_spin_samples(0), m_step_samples(0), - m_sampleindex_step1(0), - m_samplepos_step(0), - m_step_playback_state(0) + m_spin_samplepos(0), + m_step_samplepos(0), + m_seek_sound_timeout(0), + m_zones(0), + m_spin_playback_sample(QUIET), + m_step_playback_sample(QUIET), + m_seek_playback_sample(QUIET), + m_motor_on(false), + m_with_disk(false), + m_loaded(false), + m_seek_pitch(1.0), + m_seek_samplepos(0.0) { - m_loaded = false; } void floppy_sound_device::register_for_save_states() { - save_item(NAME(m_is525)); - save_item(NAME(m_sampleindex_motor_start)); - save_item(NAME(m_sampleindex_motor_loop)); - save_item(NAME(m_sampleindex_motor_end)); - save_item(NAME(m_samplesize_motor_start)); - save_item(NAME(m_samplesize_motor_loop)); - save_item(NAME(m_samplesize_motor_end)); - save_item(NAME(m_samplepos_motor)); - save_item(NAME(m_motor_playback_state)); - save_item(NAME(m_motor_on)); + save_item(NAME(m_step_base)); + save_item(NAME(m_spin_samples)); save_item(NAME(m_step_samples)); - save_item(NAME(m_sampleindex_step1)); - save_item(NAME(m_samplesize_step)); - save_item(NAME(m_samplepos_step)); - save_item(NAME(m_step_playback_state)); + save_item(NAME(m_spin_samplepos)); + save_item(NAME(m_step_samplepos)); + save_item(NAME(m_seek_samplepos)); + save_item(NAME(m_seek_sound_timeout)); + save_item(NAME(m_zones)); + save_item(NAME(m_spin_playback_sample)); + save_item(NAME(m_step_playback_sample)); + save_item(NAME(m_seek_playback_sample)); + save_item(NAME(m_motor_on)); + save_item(NAME(m_with_disk)); + save_item(NAME(m_loaded)); + save_item(NAME(m_seek_pitch)); } void floppy_sound_device::device_start() { - // Sample playback phases - m_motor_playback_state = 0; // 0 == currently not playing, 1 == playing startup sample, 2 == playing the loop, 3 == playing loop for the last time, 4 == playing the spin down sample - m_step_playback_state = 0; // 0 == currently not playing, > 0 = playing one of the step samples from start to end + // What kind of drive do we have? + bool is525 = strstr(tag(), "525") != nullptr; + static_set_samples_names(*this, is525? floppy525_sample_names : floppy35_sample_names); m_motor_on = false; - m_is525 = strstr(tag(), "525") != nullptr; - if (m_is525) - { - m_sampleindex_motor_start = 2; - m_sampleindex_motor_loop = 3; - m_sampleindex_motor_end = 4; - m_step_samples = 5; - m_sampleindex_step1 = 5; - } - else - { - m_sampleindex_motor_start = 0; - m_sampleindex_motor_loop = 0; - m_sampleindex_motor_end = 0; - m_step_samples = 1; - m_sampleindex_step1 = 1; - } - m_samplepos_motor = 0; - m_samplepos_step = 0; - m_samplesize_motor_start = 0; - m_samplesize_motor_loop = 0; - m_samplesize_motor_end = 0; - for (auto & elem : m_samplesize_step) elem = 0; + + // Offsets in the sample collection + m_spin_samples = 5; + m_step_base = 5; + m_step_samples = 1; + m_zones = 1; // > 1 needs more than one step sample + + m_spin_samplepos = m_step_samplepos = m_seek_samplepos = 0; + m_spin_playback_sample = m_step_playback_sample = QUIET; // Read audio samples. The samples are stored in the list m_samples. m_loaded = load_samples(); @@ -1027,39 +1082,122 @@ void floppy_sound_device::device_start() if (m_loaded) { m_sound = machine().sound().stream_alloc(*this, 0, 1, clock()); // per-floppy stream - - // Get the sample lengths from the sample_t - m_samplesize_motor_start = m_sample[m_sampleindex_motor_start].data.size(); - m_samplesize_motor_loop = m_sample[m_sampleindex_motor_loop].data.size(); - m_samplesize_motor_end = m_sample[m_sampleindex_motor_end].data.size(); - for (int i = 0; i < m_step_samples; ++i) m_samplesize_step[i] = m_sample[m_sampleindex_step1 + i].data.size(); } - register_for_save_states(); } -void floppy_sound_device::motor(bool state) +/* + Motor sound. Select appropriate sound sample, depending on whether the + motor is started or keeps running. Motor samples are always fully + played. +*/ +void floppy_sound_device::motor(bool running, bool withdisk) { - if (!m_loaded) return; - m_sound->update(); // required - if ((!m_motor_playback_state || m_motor_playback_state > 2) && state) // motor was either off or already spinning down + if (samples_loaded()) { - m_samplepos_motor = 0; - m_motor_playback_state = 1; // (re)start the motor sound + m_sound->update(); // required + + if ((m_spin_playback_sample==QUIET || m_spin_playback_sample==SPIN_END) && running) // motor was either off or already spinning down + { + m_spin_samplepos = 0; + m_spin_playback_sample = withdisk? SPIN_START_LOADED : SPIN_START_EMPTY; // (re)start the motor sound + } + else + { + // Motor has been running and is turned off now + if ((m_spin_playback_sample == SPIN_EMPTY || m_spin_playback_sample == SPIN_LOADED) && !running) + m_spin_playback_sample = SPIN_END; // go to spin down sound when loop is finished + } } - else if (m_motor_playback_state == 2 && !state) m_motor_playback_state = 3; // go to spin down sound when loop is finished - m_motor_on = state; + m_motor_on = running; + m_with_disk = withdisk; } -// Activate the step sound. -void floppy_sound_device::step() +/* + Activate the step sound. + The zone parameter should be used to select specific samples for the + current head position (if available). Its value should range from 0 to 4. +*/ +void floppy_sound_device::step(int zone) { - if (!m_loaded) return; - m_sound->update(); // required - if (m_step_playback_state == 0) + if (samples_loaded()) { - m_step_playback_state = machine().rand() % m_step_samples + 1; // play one of the floppy drive step samples - m_samplepos_step = 0; // restart step sound + m_sound->update(); // required + + // Pick one of the step samples + // TODO: This is only preliminary, need to complete that. + if (zone >= m_zones) zone = m_zones-1; + m_step_playback_sample = (zone * m_step_samples) + (machine().rand() % m_step_samples); + + if (m_step_samplepos > 0) + { + if (m_seek_playback_sample == QUIET) + { + // The last step sample was not completed; + // we need to find out the step rate + // With a sample rate of 44100 Hz we can calculate the + // rate from the sample position + // 2ms = 88 + // 6ms = 265 + // 12ms = 529 + // 20ms = 882 + + if (m_step_samplepos < 100) + { + // Should only used for 3.5 drives + m_seek_playback_sample = STEP_SEEK2; + m_seek_pitch = 1.0; // don't use a pitch + } + else + { + if (m_step_samplepos < 400) // Use this for 8 ms also + { + m_seek_playback_sample = STEP_SEEK6; + m_seek_pitch = 265.0 / m_step_samplepos; + } + else + { + if (m_step_samplepos < 600) + { + m_seek_playback_sample = STEP_SEEK12; + m_seek_pitch = 529.0 / m_step_samplepos; + } + else + { + if (m_step_samplepos < 1200) + { + m_seek_playback_sample = STEP_SEEK20; + m_seek_pitch = 882.0 / m_step_samplepos; + } + else + // For 30ms and longer we replay the step sound + m_seek_playback_sample = QUIET; + } + } + } + } + + // Changing the pitch does not always sound convincing + if (!PITCH_SEEK_SAMPLES) m_seek_pitch = 1; + + if (TRACE_AUDIO) logerror("Seek sample = %d, pitch = %f\n", m_seek_playback_sample, m_seek_pitch); + + // Set the timeout for the seek sound. When it expires, + // we assume that the seek process is over, and we'll play the + // rest of the step sound. + // This will be retriggered with each step pulse. + m_seek_sound_timeout = m_step_samplepos * 2; + } + else + { + // Last step sample was completed, this is not a seek process + m_seek_playback_sample = QUIET; + } + + // If we switch to the seek sample, let's keep the position of the + // step sample; else reset the step sample position. + if (m_seek_playback_sample == QUIET) + m_step_samplepos = 0; } } @@ -1075,67 +1213,99 @@ void floppy_sound_device::sound_stream_update(sound_stream &stream, stream_sampl INT16 out; stream_sample_t *samplebuffer = outputs[0]; + int idx = 0; + int sampleend = 0; while (samples-- > 0) { out = 0; - // make sure we are spinning the motor if stepping is going on! - if (m_motor_playback_state == 0 && m_step_playback_state > 0) - { - m_samplepos_motor = 0; - m_motor_playback_state = 2; - } - // Motor sound - if (m_motor_playback_state > 0) + if (m_spin_playback_sample != QUIET) { - switch (m_motor_playback_state) + idx = m_spin_playback_sample; + sampleend = m_sample[idx].data.size(); + out = m_sample[idx].data[m_spin_samplepos++]; + + if (m_spin_samplepos >= sampleend) { - case 1: - out = m_sample[m_sampleindex_motor_start].data[m_samplepos_motor++]; - if (m_samplepos_motor >= m_samplesize_motor_start) + // Motor sample has completed + switch (m_spin_playback_sample) { - m_samplepos_motor = 0; - m_motor_playback_state = 2; // move to looping sound + case SPIN_START_EMPTY: + // After start, switch to the continued spinning sound + m_spin_playback_sample = SPIN_EMPTY; // move to looping sound + break; + case SPIN_START_LOADED: + // After start, switch to the continued spinning sound + m_spin_playback_sample = SPIN_LOADED; // move to looping sound + break; + case SPIN_EMPTY: + // As long as the motor pin is asserted, restart the sample + // play the spindown sample + if (!m_motor_on) m_spin_playback_sample = SPIN_END; // motor was turned off already (during spin-up maybe) -> spin down + break; + case SPIN_LOADED: + if (!m_motor_on) m_spin_playback_sample = SPIN_END; // motor was turned off already (during spin-up maybe) -> spin down + break; + case SPIN_END: + // Spindown sample over, be quiet or restart if the + // motor has been restarted + if (m_motor_on) + m_spin_playback_sample = m_with_disk? SPIN_START_LOADED : SPIN_START_EMPTY; + else + m_spin_playback_sample = QUIET; + break; } - break; - case 2: - out = m_sample[m_sampleindex_motor_loop].data[m_samplepos_motor++]; - if (m_samplepos_motor >= m_samplesize_motor_loop) - { - m_samplepos_motor = 0; // continue loop, unless... - if (!m_motor_on) m_motor_playback_state = 4; // motor was turned off already (during spin-up maybe) -> spin down - } - break; - case 3: - out = m_sample[m_sampleindex_motor_loop].data[m_samplepos_motor++]; - if (m_samplepos_motor >= m_samplesize_motor_loop) - { - m_samplepos_motor = 0; - m_motor_playback_state = 4; // move to spin down sound - } - break; - case 4: - out = m_sample[m_sampleindex_motor_end].data[m_samplepos_motor++]; - if (m_samplepos_motor >= m_samplesize_motor_end) - { - m_samplepos_motor = 0; - m_motor_playback_state = !m_motor_on ? 0 : 1; // stop or restart motor sound - } - break; + // Restart the selected sample + m_spin_samplepos = 0; } } - // Stepper sound - if (m_step_playback_state > 0) + // Seek sound + // As long as we have a seek sound, there is no step sound + if (m_seek_sound_timeout == 1) { + // Not retriggered; switch back to the last step sound + m_seek_playback_sample = QUIET; + m_seek_sound_timeout = 0; + // Skip 1/100 sec to dampen the loudest pulse + // yep, a somewhat dirty trick; we don't have to record yet another sample + m_step_samplepos += 441; + } + + if (m_seek_playback_sample != QUIET) + { + m_seek_sound_timeout--; + + idx = m_step_base + m_seek_playback_sample; + sampleend = m_sample[idx].data.size(); // Mix it into the stream value - out += m_sample[m_sampleindex_step1 + m_step_playback_state - 1].data[m_samplepos_step++]; - if (m_samplepos_step >= m_samplesize_step[m_step_playback_state - 1]) + out += m_sample[idx].data[(int)m_seek_samplepos]; + // By adding different values than 1, we can change the playback speed + // This will be used to adjust the seek sound + m_seek_samplepos += m_seek_pitch; + + // The seek sample will be replayed without interrupt + if (m_seek_samplepos >= sampleend) + m_seek_samplepos = 0; + } + else + { + // Stepper sound + if (m_step_playback_sample != QUIET) { - m_samplepos_step = 0; - m_step_playback_state = 0; + idx = m_step_base + m_step_playback_sample; + sampleend = m_sample[idx].data.size(); + + // Mix it into the stream value + out += m_sample[idx].data[m_step_samplepos++]; + if (m_step_samplepos >= sampleend) + { + // Step sample done + m_step_samplepos = 0; + m_step_playback_sample = QUIET; + } } } @@ -1144,28 +1314,11 @@ void floppy_sound_device::sound_stream_update(sound_stream &stream, stream_sampl } } -static const char *const floppy_sample_names[] = -{ - "*floppy", - "floppy_35_motor", - "floppy_35_step", - "floppy_525_motor_start", - "floppy_525_motor_loop", - "floppy_525_motor_end", - "floppy_525_step1", - "floppy_525_step2", - "floppy_525_step3", - "floppy_525_step4", - "floppy_525_step5", - nullptr -}; - #define FLOPSPK "flopsndout" MACHINE_CONFIG_FRAGMENT( floppy_img ) MCFG_SPEAKER_STANDARD_MONO(FLOPSPK) MCFG_SOUND_ADD(FLOPSND_TAG, FLOPPYSOUND, 44100) - MCFG_SAMPLES_NAMES(floppy_sample_names) MCFG_SOUND_ROUTE(ALL_OUTPUTS, FLOPSPK, 0.5) MACHINE_CONFIG_END diff --git a/src/devices/imagedev/floppy.h b/src/devices/imagedev/floppy.h index 5e93da56c698e..40320c01e7339 100644 --- a/src/devices/imagedev/floppy.h +++ b/src/devices/imagedev/floppy.h @@ -251,14 +251,12 @@ extern const device_type FLOPPYSOUND; Floppy drive sound */ -#define MAX_STEP_SAMPLES 5 - class floppy_sound_device : public samples_device { public: floppy_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); - void motor(bool on); - void step(); + void motor(bool on, bool withdisk); + void step(int track); bool samples_loaded() { return m_loaded; } void register_for_save_states(); @@ -268,26 +266,23 @@ class floppy_sound_device : public samples_device private: // device_sound_interface overrides void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; - sound_stream* m_sound; - bool m_loaded; - bool m_is525; // true if this is a 5.25" floppy drive - - int m_sampleindex_motor_start; - int m_sampleindex_motor_loop; - int m_sampleindex_motor_end; - int m_samplesize_motor_start; - int m_samplesize_motor_loop; - int m_samplesize_motor_end; - int m_samplepos_motor; - int m_motor_playback_state; - bool m_motor_on; - - int m_step_samples; - int m_sampleindex_step1; - int m_samplesize_step[MAX_STEP_SAMPLES]; - int m_samplepos_step; - int m_step_playback_state; + + int m_step_base; + int m_spin_samples; + int m_step_samples; + int m_spin_samplepos; + int m_step_samplepos; + int m_seek_sound_timeout; + int m_zones; + int m_spin_playback_sample; + int m_step_playback_sample; + int m_seek_playback_sample; + bool m_motor_on; + bool m_with_disk; + bool m_loaded; + double m_seek_pitch; + double m_seek_samplepos; };