summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Michael Zapf <Michael.Zapf@mizapf.de>2016-08-25 20:43:12 +0200
committer Michael Zapf <Michael.Zapf@mizapf.de>2016-08-25 20:43:12 +0200
commitac24b9ab303100ca046bc42dacd35fb088e66dbd (patch)
tree450b0f26060b583c790c2013ee0385198c7210be
parent596b04dd5c4d5fa5a09f5b2f04577384f54db14c (diff)
Improved floppy sound emulation.
-rw-r--r--src/devices/imagedev/floppy.cpp433
-rw-r--r--src/devices/imagedev/floppy.h41
2 files changed, 311 insertions, 163 deletions
diff --git a/src/devices/imagedev/floppy.cpp b/src/devices/imagedev/floppy.cpp
index b01f5db11cf..cb07b6654dd 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 5e93da56c69..40320c01e73 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 @@ protected:
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;
};