diff options
| author | 2025-11-21 00:20:49 +0100 | |
|---|---|---|
| committer | 2025-11-21 00:20:49 +0100 | |
| commit | 7fece3b95204171023fd5200fd92abb8d3e14d4b (patch) | |
| tree | f9eee1401068ace22a7c1f42606d315fbf18c414 | |
| parent | 6d2f26dc7ed83728875af1e1ffaeabf8c8b1a2e3 (diff) | |
floppy: Revised floppy sound system, allows definition of custom sample lists and fixes unrealistic pitch changes.
| -rw-r--r-- | src/devices/imagedev/floppy.cpp | 651 | ||||
| -rw-r--r-- | src/devices/imagedev/floppy.h | 201 |
2 files changed, 584 insertions, 268 deletions
diff --git a/src/devices/imagedev/floppy.cpp b/src/devices/imagedev/floppy.cpp index 0d90b65eb89..c19a45ef7f2 100644 --- a/src/devices/imagedev/floppy.cpp +++ b/src/devices/imagedev/floppy.cpp @@ -36,13 +36,15 @@ #include <algorithm> -/* - Debugging flags. Set to 0 or 1. -*/ +// Some debug output + +#define LOG_STEP (1U << 1) +#define LOG_TRACK (1U << 2) +#define LOG_SOUND (1U << 3) +#define LOG_MACDRIVE (1U << 4) +#define VERBOSE 0 -// Show step operation -#define TRACE_STEP 0 -#define TRACE_AUDIO 0 +#include "logmacro.h" #define PITCH_SEEK_SAMPLES 1 @@ -215,7 +217,6 @@ floppy_connector::floppy_connector(const machine_config &mconfig, const char *ta device_t(mconfig, FLOPPY_CONNECTOR, tag, owner, clock), device_slot_interface(mconfig, *this), formats(nullptr), - m_enable_sound(false), m_sectoring_type(floppy_image::SOFT) { } @@ -224,6 +225,42 @@ floppy_connector::~floppy_connector() { } +//------------------------------------------- +// Activate floppy sounds with default lists +//------------------------------------------- + +void floppy_connector::enable_sound(bool doit) +{ + if (doit) + enable_sound(nullptr, floppy35_samples, floppy525_samples, nullptr); + else + enable_sound(nullptr); +} + +//------------------------------------------- +// Activate floppy sounds with single custom list +//------------------------------------------- + +void floppy_connector::enable_sound(const floppy_sound_device::floppysoundentry *samples) +{ + enable_sound(nullptr, nullptr, samples, nullptr); +} + +//------------------------------------------------------ +// Activate floppy sounds with lists for various formats +//------------------------------------------------------ + +void floppy_connector::enable_sound(const floppy_sound_device::floppysoundentry *samples3, + const floppy_sound_device::floppysoundentry *samples35, + const floppy_sound_device::floppysoundentry *samples525, + const floppy_sound_device::floppysoundentry *samples8 ) +{ + m_samples3 = samples3; + m_samples35 = samples35; + m_samples525 = samples525; + m_samples8 = samples8; +} + void floppy_connector::device_start() { } @@ -234,7 +271,7 @@ void floppy_connector::device_config_complete() if(dev) { dev->set_formats(formats); - dev->enable_sound(m_enable_sound); + dev->enable_sound(m_samples3, m_samples35, m_samples525, m_samples8); dev->set_sectoring_type(m_sectoring_type); } } @@ -512,6 +549,19 @@ const software_list_loader &floppy_image_device::get_software_list_loader() cons } +void floppy_image_device::enable_sound(const floppy_sound_device::floppysoundentry *samples3, + const floppy_sound_device::floppysoundentry *samples35, + const floppy_sound_device::floppysoundentry *samples525, + const floppy_sound_device::floppysoundentry *samples8 ) +{ + m_samples3 = samples3; + m_samples35 = samples35; + m_samples525 = samples525; + m_samples8 = samples8; + m_make_sound = (samples3 || samples35 || samples525 || samples8); +} + + //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- @@ -540,7 +590,33 @@ void floppy_image_device::device_start() m_ready_counter = 0; m_phases = 0; - if (m_make_sound) m_sound_out = subdevice<floppy_sound_device>(FLOPSND_TAG); + const floppy_sound_device::floppysoundentry *samples = nullptr; + + // Determine a suitable sample set if available + // If there is no matching set, try another one or give up if nothing is left. + if (m_make_sound) + { + if (m_form_factor == floppy_image::FF_3) + samples = m_samples3; + + // Use this if there is no 3" sample set or if the drive is a 3.5" + if (samples == nullptr || m_form_factor == floppy_image::FF_35) + samples = m_samples35; + + if (m_form_factor == floppy_image::FF_8) + samples = m_samples8; + + // Use this as default + if (samples == nullptr || m_form_factor == floppy_image::FF_525) + samples = m_samples525; + } + + // If this is also missing, there will be no sound output + if (samples != nullptr) + { + m_sound_out = subdevice<floppy_sound_device>(FLOPSND_TAG); + m_sound_out->set_samples(samples); + } save_item(NAME(m_dir)); save_item(NAME(m_stp)); @@ -715,6 +791,8 @@ void floppy_image_device::call_unload() mon_w(1); } + if (m_make_sound) m_sound_out->unload(); + set_ready(true); } @@ -858,7 +936,6 @@ TIMER_CALLBACK_MEMBER(floppy_image_device::index_resync) if(m_idx && m_ready) { m_ready_counter--; if(!m_ready_counter) { - // logerror("Drive spun up\n"); set_ready(false); } } @@ -920,11 +997,6 @@ void floppy_image_device::track_changed() void floppy_image_device::stp_w(int state) { - // Before spin-up is done, ignore step pulses - // TODO: There are reports about drives supporting step operation with - // stopped spindle. Need to check that on real drives. - // if (m_ready_counter > 0) return; - if ( m_stp != state ) { cache_clear(); m_stp = state; @@ -937,7 +1009,7 @@ void floppy_image_device::stp_w(int state) } if(ocyl != m_cyl) { - if (TRACE_STEP) logerror("track %d\n", m_cyl); + LOGMASKED(LOG_TRACK, "track %d\n", m_cyl); // Do we want a stepper sound? // We plan for 5 zones with possibly specific sounds if (m_make_sound) m_sound_out->step(m_cyl*5/m_tracks); @@ -991,7 +1063,7 @@ void floppy_image_device::seek_phase_w(int _phases) cache_clear(); if(next_pos != cur_pos) { - if (TRACE_STEP) logerror("track %d.%d\n", m_cyl, m_subcyl); + LOGMASKED(LOG_STEP, "track %d.%d\n", m_cyl, m_subcyl); if (m_make_sound) m_sound_out->step(m_subcyl); } @@ -1334,145 +1406,162 @@ uint32_t floppy_image_device::get_variant() const //=================================================================== // Floppy sound // -// In order to enable floppy sound you must call -// enable_sound(true) -// and you must put audio samples (44100Hz, mono) with names as -// shown in floppy_sample_names into the directory samples/floppy -// Sound will be disabled when these samples are missing. +// MZ, August 2015 +// Updated November 2025 +// +// In order to activate floppy sounds (spinning, head steps, seeks) +// you need to call enable_sound on the floppy_connector. The default is +// no sound. The volume may be set in the OSD menu. If the sample files +// cannot be loaded, sound is disabled. +// +// void enable_sound(bool): +// Enables/disables sounds with default sound definitions (floppy.h). +// +// void enable_sound(floppy_sound_device::floppy_sound_entry *): +// Enables sounds with the provided sound definition (same for every form factor) +// +// void enable_sound(floppy_sound_device::floppy_sound_entry *,... (4 times)) +// Enables sounds with the provided sound definitions for 3", 3.5", +// 5.25", and 8" drives. Unused options may be set to nullptr. The +// form factor determines which sample set is used. +// +// Some macros are defined to simplify the sound sample setup. +// The default lists for 3.5" and 5.25" are contained in floppy.h. +// +// FLOPPY_SOUND(name) +// SAMPLES_DIR(subdirectory in samples dir; must be first entry) +// SPIN_SAMPLE(spin_kind, file name without extension) +// SEEK_SAMPLE(actual_rate, max_rate, file name w/o ext) +// STEP_SAMPLE(file name w/o ext) +// STEP_SAMPLE_RANGE(first_track, last_track, file name w/o ext) +// FLOPPY_SOUND_END +// +// The spin_kind is one of +// START_EMPTY start spinning without a disk in the drive +// SPIN_EMPTY spinning w/o disk +// END_EMPTY stop spinning w/o disk +// START_LOADED_INITIAL start spinning with a disk, first time after insertion +// START_LOADED start spinning with a disk +// SPIN_LOADED spinning with a disk +// END_LOADED stop spinning with a disk +// +// Not all spin_kinds need to be defined. See the array replace_sample for +// the rules by which samples are used when the proper ones are not available. +// The ultimate default is SPIN_LOADED. +// +// The seek samples are chosen by the detected step rate n (in milliseconds). +// The seek sample whose max_rate is minimally higher than n is used, and the +// playback is pitched up or down related to the sample's actual rate. +// If the detected rate varies (as with the Amiga), a different sample is only +// picked when the newly detected rate is more than 10% off the current +// one to avoid stuttering sound output and unrealistic changes in pitch. +// +// Step sounds are played when the interval between them is long enough, so no +// seek sound would be produced. If only one step sample shall be used, +// the range should be set as (0,99), which is done by the STEP_SAMPLE macro. +// Otherwise, the step sample is played whose range contains the current +// track. // -// 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_t clock) : samples_device(mconfig, FLOPPYSOUND, tag, owner, clock), m_sound(nullptr), - m_step_base(0), - m_spin_samples(0), - m_step_samples(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_samplelist(nullptr), m_motor_on(false), m_with_disk(false), - m_loaded(false), + m_spin_kind(QUIET), + m_spin_sample(QUIET), + m_spin_samplepos(0), + m_step_sample(QUIET), + m_step_samplepos(0), + m_seek_sample(QUIET), + m_seek_samplepos(0.0), m_seek_pitch(1.0), - m_seek_samplepos(0.0) + m_seek_sound_timeout(0), + m_last_step_time(), + m_firstturn(true), + m_samples_available(false), + m_in_seek(false), + m_step_rate(0.0) { } void floppy_sound_device::register_for_save_states() { - save_item(NAME(m_step_base)); - save_item(NAME(m_spin_samples)); - save_item(NAME(m_step_samples)); + save_item(NAME(m_spin_kind)); + save_item(NAME(m_spin_sample)); save_item(NAME(m_spin_samplepos)); + save_item(NAME(m_step_sample)); save_item(NAME(m_step_samplepos)); + save_item(NAME(m_seek_sample)); 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)); + save_item(NAME(m_firstturn)); + save_item(NAME(m_in_seek)); + save_item(NAME(m_step_rate)); } void floppy_sound_device::device_start() { - // What kind of drive do we have? - bool is525 = strstr(tag(), "525") != nullptr; - set_samples_names(is525 ? floppy525_sample_names : floppy35_sample_names); + // Set up floppy sound samples + // We need to prepare a samples list for the samples_device + // First entry is the subdirectory in the samples path (marked with a + // leading asterisk) + floppysoundentry flop = m_samplelist[0]; - m_motor_on = false; + if (flop.type != DIR) + throw emu_fatalerror("Missing DIR entry in floppy samples list."); + + // Create the asterisked first entry for the subdirectory + m_dir = '*'; + m_dir += flop.filename; + m_samplenames.push_back(m_dir.c_str()); + + // Max 100 entries so we don't loop forever when END is missing + for (int i=1; i < 100 && flop.type != END; i++) + { + flop = m_samplelist[i]; + if (flop.type == DIR) + throw emu_fatalerror("Duplicate DIR entry in floppy samples list."); + m_samplenames.push_back(flop.filename); + } + + if (flop.type != END) + throw emu_fatalerror("Missing END entry in floppy samples list."); - // 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 + // Put an end to the list + m_samplenames.push_back(nullptr); - m_spin_samplepos = m_step_samplepos = m_seek_samplepos = 0; - m_spin_playback_sample = m_step_playback_sample = QUIET; + set_samples_names(&m_samplenames[0]); + + m_motor_on = false; + m_spin_kind = QUIET; + m_spin_sample = QUIET; + m_step_sample = QUIET; + m_spin_samplepos = 0; + m_step_samplepos = 0; + m_seek_samplepos = 0; // Read audio samples. The samples are stored in the list m_samples. - m_loaded = load_samples(); + m_samples_available = load_samples(); // If we don't have all samples, don't allocate a stream or access sample data. - if (m_loaded) + if (m_samples_available) { m_sound = stream_alloc(0, 1, clock()); // per-floppy stream } register_for_save_states(); + + m_last_step_time = attotime::zero; + m_in_seek = false; + m_step_rate = 0; + m_firstturn = true; +} + +void floppy_sound_device::set_samples(const floppysoundentry *samples) +{ + m_samplelist = samples; } /* @@ -1486,20 +1575,29 @@ void floppy_sound_device::motor(bool running, bool withdisk) { 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 + if ((m_spin_kind==QUIET || m_spin_kind==END_EMPTY || m_spin_kind==END_LOADED ) && 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 + // 3.5" floppy disks have a special first turn sound when the + // spindle motor latch meets the central metal hub hole. + m_spin_kind = withdisk? (m_firstturn? START_LOADED_INITIAL : START_LOADED) : START_EMPTY; + m_firstturn = false; } else { // Motor has been running and is turned off now - if ((m_spin_playback_sample == SPIN_EMPTY || m_spin_playback_sample == SPIN_LOADED) && !running) + if ((m_spin_kind == SPIN_EMPTY || m_spin_kind == SPIN_LOADED) && !running) { m_spin_samplepos = 0; - m_spin_playback_sample = SPIN_END; // go to spin down sound when loop is finished + m_spin_kind = withdisk? END_LOADED : END_EMPTY; // go to spin down sound when loop is finished } } + + m_spin_sample = (m_spin_kind==QUIET)? QUIET : find_spin(m_spin_kind); + if (m_spin_sample != QUIET) + LOGMASKED(LOG_SOUND, "Spin sample file = %s (%d)\n", m_samplenames[m_spin_sample], m_spin_sample); + else + LOGMASKED(LOG_SOUND, "Spin off\n"); } m_motor_on = running; m_with_disk = withdisk; @@ -1507,93 +1605,183 @@ void floppy_sound_device::motor(bool running, bool withdisk) /* 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) +void floppy_sound_device::step(int track) { if (samples_loaded()) { 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); + m_step_sample = find_step(track); + + attotime now = machine().time(); + double rate = 0; + rate = (m_last_step_time == attotime::zero)? 0 : (now - m_last_step_time).as_double() * 1000; + m_last_step_time = now; + + bool recalc = false; - if (m_step_samplepos > 0) + // Cases: + // step, previous step sample completed (step_samplepos == 0) -> new step output + // step, previous step sample not completed (step_samplepos > 0) -> + // not in seek -> determine seek sample, freeze step output + // in seek -> continue with seek sample + // (seek sample timeout is set to twice the step rate) + + // If the step rate changed by more than 10%, we have to change the + // seek sample + if (m_step_rate == 0) { - if (m_seek_playback_sample == QUIET) + recalc = true; + m_step_rate = rate; + } + else + { + if (rate > 0 && rate < 1000) // safe values { - // 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 + double raterel = (m_step_rate - rate) / m_step_rate; + if (raterel < 0) raterel = -raterel; + if (raterel > 0.1 && m_in_seek) { - 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; - } - } + recalc = true; + LOGMASKED(LOG_SOUND, "Step rate has changed from %.1f to %.1f\n", m_step_rate, rate); } - - // Start the new seek sound from the beginning. - m_seek_samplepos = 0; + m_step_rate = rate; } + } - // Changing the pitch does not always sound convincing - if (!PITCH_SEEK_SAMPLES) m_seek_pitch = 1; + if (m_step_samplepos > 0 && m_step_rate < 100) // in seek, or transitioning into seek + { + LOGMASKED(LOG_SOUND, "Step sample not completed ... doing seek. Rate = %.1f, recalc = %d, seek_pos = %d\n", m_step_rate, recalc, m_seek_samplepos); + + if (recalc || !m_in_seek) + { + int newseek = find_seek(m_step_rate, track, m_seek_pitch); + + // Start the new seek sound from the beginning (but only if + // we changed it, or we will get ugly sounds in the output) + if (newseek != m_seek_sample) m_seek_samplepos = 0; + + m_seek_sample = newseek; - if (TRACE_AUDIO) logerror("Seek sample = %d, pitch = %f\n", m_seek_playback_sample, m_seek_pitch); + // Changing the pitch does not always sound convincing + if (!PITCH_SEEK_SAMPLES) m_seek_pitch = 1.0; + + LOGMASKED(LOG_SOUND, "Step rate = %.1fms, seek sample = %s, pitch = %f\n", m_step_rate, m_samplenames[m_seek_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; + m_seek_sound_timeout = (int)(m_step_rate * 2 * 44.1); } else { + LOGMASKED(LOG_SOUND, "Step sample completed\n"); + m_in_seek = false; // Last step sample was completed, this is not a seek process - m_seek_playback_sample = QUIET; + m_seek_sample = QUIET; + m_seek_samplepos = 0; } // 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) + if (m_seek_sample == QUIET) + { m_step_samplepos = 0; + m_in_seek = false; + } + else + { + m_in_seek = true; + } + } +} + +/* + Find a suitable spinning sound in the list. The possible kinds are + enumerated in spinsound_t. +*/ +int floppy_sound_device::find_spin(int kind) { + + // If a sample is not available (left), take the one on the right. + // Simple index replacement. + int replace_sample[7] = + { + /* START_EMPTY -> */ SPIN_EMPTY, + /* SPIN_EMPTY -> */ SPIN_LOADED, + /* END_EMPTY -> */ END_LOADED, + /* START_LOADED_INITIAL -> */ START_LOADED, + /* START_LOADED -> */ SPIN_LOADED, + /* SPIN_LOADED -> */ QUIET, + /* END_LOADED -> */ SPIN_LOADED + }; + + while (kind != QUIET) + { + for (int i=1; m_samplelist[i].type != END; i++) + { + if ((m_samplelist[i].type == SPIN) && (m_samplelist[i].use == kind)) + return i; // found it + } + // Not found, take another kind (maybe try several times) + kind = replace_sample[kind]; + } + return QUIET; // not found +} + +/* + Find a suitable step sample. The samples may be different by track. + In the definition, the range must be specified, where 0, 99 is used for + all tracks (all emulated drives have less than 99 tracks). +*/ +int floppy_sound_device::find_step(int track) { + + for (int i=1; m_samplelist[i].type != END; i++) + { + if ((m_samplelist[i].type == STEP) && (m_samplelist[i].mintrack <= track && m_samplelist[i].maxtrack >= track)) + { + LOGMASKED(LOG_SOUND, "Found step sample %d, name %s\n", i, m_samplelist[i].filename); + return i; + } + } + return QUIET; +} + +/* + Find a suitable seek sample. We allow for a given sample to be played + for a rate that is in some range around that sample, defined in the list. + That is, each seek sample defines its actual rate (e.g. 6 ms) and the + slowest rate that it may be used for (e.g. 8 ms). If the determined rate + is 7 ms, the 6 ms sample will be chosen, and playback will be pitched down + by 6/7 = 0.86. If the rate is 5 ms, playback will be pitched up by 6/5 = 1.2, + unless there is a sample for a faster rate that covers 5 ms. + + If the determined rate is slower than the maximum rate (here, 8 ms), the + next sample will be used for a slower rate (e.g. 10 ms) if available. If + there is no slower rate, -1 is returned. The caller should then use single + step sounds. +*/ +int floppy_sound_device::find_seek(double rate, int track, double& pitch) { + int maxrate = 100; + int index = QUIET; + + for (int i=1; m_samplelist[i].type != END; i++) + { + if ((m_samplelist[i].type == SEEK) && (m_samplelist[i].mintrack <= track && m_samplelist[i].maxtrack >= track)) + { + if ((m_samplelist[i].use < maxrate) && (m_samplelist[i].use >= rate)) + { + index = i; + maxrate = m_samplelist[i].use; + } + } } + if (index != QUIET) + pitch = m_samplelist[index].rate / (double)rate; + + return index; } //------------------------------------------------- @@ -1607,7 +1795,6 @@ void floppy_sound_device::sound_stream_update(sound_stream &stream) // one sample rate of 44100 for all samples int16_t out; - int m_idx = 0; int sampleend = 0; for (int sampindex = 0; sampindex < stream.samples(); sampindex++) @@ -1615,67 +1802,86 @@ void floppy_sound_device::sound_stream_update(sound_stream &stream) out = 0; // Motor sound - if (m_spin_playback_sample != QUIET) + if (m_spin_sample != QUIET) { - m_idx = m_spin_playback_sample; - sampleend = m_sample[m_idx].data.size(); - out = m_sample[m_idx].data[m_spin_samplepos++]; + // The samples list starts at 0 with the first entry after DIR, + // so we adjust by -1 + sampleend = m_sample[m_spin_sample-1].data.size(); + out = m_sample[m_spin_sample-1].data[m_spin_samplepos++]; if (m_spin_samplepos >= sampleend) { + LOGMASKED(LOG_SOUND, "Spin sample %d completed\n", m_spin_sample); // Motor sample has completed - switch (m_spin_playback_sample) + switch (m_spin_kind) { - case SPIN_START_EMPTY: + case START_EMPTY: // After start, switch to the continued spinning sound - m_spin_playback_sample = SPIN_EMPTY; // move to looping sound + m_spin_kind = SPIN_EMPTY; // move to looping sound break; - case SPIN_START_LOADED: + case START_LOADED: + case START_LOADED_INITIAL: // After start, switch to the continued spinning sound - m_spin_playback_sample = SPIN_LOADED; // move to looping sound + m_spin_kind = 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 + if (!m_motor_on) m_spin_kind = END_EMPTY; // 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 + if (!m_motor_on) m_spin_kind = END_LOADED; // motor was turned off already (during spin-up maybe) -> spin down break; - case SPIN_END: + case END_EMPTY: + case END_LOADED: // 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; + { + LOGMASKED(LOG_SOUND, "Restart spinning sound\n"); + m_spin_kind = m_with_disk ? START_LOADED : START_EMPTY; + } else - m_spin_playback_sample = QUIET; + m_spin_kind = QUIET; + break; + + default: break; } + + m_spin_sample = (m_spin_kind==QUIET)? QUIET : find_spin(m_spin_kind); + if (m_spin_sample != QUIET) + LOGMASKED(LOG_SOUND, "Spin sample file = %s (%d)\n", m_samplenames[m_spin_sample], m_spin_sample); + else + LOGMASKED(LOG_SOUND, "Spin off\n"); + // Restart the selected sample m_spin_samplepos = 0; } } // Seek sound - // As long as we have a seek sound, there is no step sound + // As long as we have a seek sound, there is a pending step sound if (m_seek_sound_timeout == 1) { + LOGMASKED(LOG_SOUND, "Finish step sound\n"); // Not retriggered; switch back to the last step sound - m_seek_playback_sample = QUIET; + m_seek_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) + if (m_seek_sample != QUIET) { m_seek_sound_timeout--; - m_idx = m_step_base + m_seek_playback_sample; - sampleend = m_sample[m_idx].data.size(); + sampleend = m_sample[m_seek_sample-1].data.size(); + // Mix it into the stream value - out += m_sample[m_idx].data[(int)m_seek_samplepos]; + out += m_sample[m_seek_sample-1].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; @@ -1687,18 +1893,17 @@ void floppy_sound_device::sound_stream_update(sound_stream &stream) else { // Stepper sound - if (m_step_playback_sample != QUIET) + if (m_step_sample != QUIET) { - m_idx = m_step_base + m_step_playback_sample; - sampleend = m_sample[m_idx].data.size(); + sampleend = m_sample[m_step_sample-1].data.size(); // Mix it into the stream value - out += m_sample[m_idx].data[m_step_samplepos++]; + out += m_sample[m_step_sample-1].data[m_step_samplepos++]; if (m_step_samplepos >= sampleend) { // Step sample done m_step_samplepos = 0; - m_step_playback_sample = QUIET; + m_step_sample = QUIET; } } } @@ -2866,7 +3071,7 @@ bool mac_floppy_device::wpt_r() m_reg = (m_reg & 7) | (m_actual_ss ? 8 : 0); if(0 && (m_reg != 4 && m_reg != 12 && m_reg != 5 && m_reg != 13)) - logerror("fdc disk sense reg %x %s %p\n", m_reg, regnames[m_reg], m_image.get()); + LOGMASKED(LOG_MACDRIVE, "fdc disk sense reg %x %s %p\n", m_reg, regnames[m_reg], m_image.get()); switch(m_reg) { case 0x0: // Step direction @@ -2945,43 +3150,43 @@ void mac_floppy_device::seek_phase_w(int phases) if(m_strb && !prev_strb) { switch(m_reg) { case 0x0: // Step to cylinder + 1 - logerror("cmd step dir +1\n"); + LOGMASKED(LOG_MACDRIVE, "cmd step dir +1\n"); dir_w(0); break; case 0x1: // Step on - logerror("cmd step on\n"); + LOGMASKED(LOG_MACDRIVE, "cmd step on\n"); stp_w(0); // There should be a delay, but it's not necessary stp_w(1); break; case 0x2: // Motor on - logerror("cmd motor on\n"); + LOGMASKED(LOG_MACDRIVE, "cmd motor on\n"); floppy_image_device::mon_w(0); break; case 0x3: // End eject - logerror("cmd end eject\n"); + LOGMASKED(LOG_MACDRIVE, "cmd end eject\n"); break; case 0x4: // Step to cylinder - 1 - logerror("cmd step dir -1\n"); + LOGMASKED(LOG_MACDRIVE, "cmd step dir -1\n"); dir_w(1); break; case 0x6: // Motor off - logerror("cmd motor off\n"); + LOGMASKED(LOG_MACDRIVE, "cmd motor off\n"); floppy_image_device::mon_w(1); break; case 0x7: // Start eject - logerror("cmd start eject\n"); + LOGMASKED(LOG_MACDRIVE, "cmd start eject\n"); unload(); break; case 0x9: // MFM mode on - logerror("cmd mfm on\n"); + LOGMASKED(LOG_MACDRIVE, "cmd mfm on\n"); if(m_has_mfm) { m_mfm = true; track_changed(); @@ -2989,18 +3194,18 @@ void mac_floppy_device::seek_phase_w(int phases) break; case 0xc: // Clear m_dskchg - logerror("cmd clear m_dskchg\n"); + LOGMASKED(LOG_MACDRIVE, "cmd clear m_dskchg\n"); m_dskchg = 1; break; case 0xd: // GCR mode on - logerror("cmd gcr on\n"); + LOGMASKED(LOG_MACDRIVE, "cmd gcr on\n"); m_mfm = false; track_changed(); break; default: - logerror("cmd reg %x %s\n", m_reg, regnames[m_reg]); + LOGMASKED(LOG_MACDRIVE, "cmd reg %x %s\n", m_reg, regnames[m_reg]); break; } } diff --git a/src/devices/imagedev/floppy.h b/src/devices/imagedev/floppy.h index 664e2970e17..5d3d099a505 100644 --- a/src/devices/imagedev/floppy.h +++ b/src/devices/imagedev/floppy.h @@ -22,7 +22,137 @@ namespace fs { class meta_data; }; -class floppy_sound_device; +/* + Floppy drive sound +*/ + +class floppy_sound_device : public samples_device +{ +public: + floppy_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + void motor(bool on, bool withdisk); + void step(int track); + void unload() { m_firstturn = true; } + bool samples_loaded() { return m_samples_available; } + void register_for_save_states(); + + enum + { + QUIET=-1, // also used for the sample index when there is no sound + START_EMPTY=0, + SPIN_EMPTY, + END_EMPTY, + START_LOADED_INITIAL, + START_LOADED, + SPIN_LOADED, // mandatory; this sample is a fallback for all other spin samples + END_LOADED + }; + + enum { + END=0, + DIR, + SPIN, + STEP, + SEEK + }; + + struct floppysoundentry { + int type; // end of list, spin, step, or seek + int mintrack; // valid from here (including), meaningless for spin entries + int maxtrack; // to here (including), meaningless for spin entries + int rate; // actual rate of the seek sample + int use; // max rate for seek entries, type for spin entries, meaningless for step + const char *filename; + }; + + void set_samples(const floppysoundentry *samples); + +protected: + void device_start() override ATTR_COLD; + +private: + // device_sound_interface overrides + virtual void sound_stream_update(sound_stream &stream) override; + sound_stream* m_sound; + + const floppysoundentry *m_samplelist; + std::vector<const char*> m_samplenames; // Constructed list for samples_device + + std::string m_dir; // Subdirectory which contains the samples + bool m_motor_on; + bool m_with_disk; + int m_spin_kind; + int m_spin_sample; + int m_spin_samplepos; + int m_step_sample; + int m_step_samplepos; + int m_seek_sample; + double m_seek_samplepos; // we may using a non-integer pitch + double m_seek_pitch; + int m_seek_sound_timeout; + attotime m_last_step_time; + + // Used to pick a different sound sample when the disk is spun the first + // time, in particular with 3.5" drives + bool m_firstturn; + bool m_samples_available; + bool m_in_seek; + double m_step_rate; + + int find_spin(int kind); + int find_step(int track); + int find_seek(double rate, int track, double& pitch); +}; + +#define SAMPLES_DIR(dir) { floppy_sound_device::DIR, 0, 0, 0, 0, dir }, +#define SAMPLES_END { floppy_sound_device::END, 0, 0, 0, 0, nullptr }, +#define SPIN_SAMPLE(type, file) { floppy_sound_device::SPIN, 0, 99, 0, type, file }, +#define STEP_SAMPLE(file) { floppy_sound_device::STEP, 0, 99, 0, 0, file }, +#define STEP_SAMPLE_RANGE(from, to, file) { floppy_sound_device::STEP, from, to, 0, 0, file }, +#define SEEK_SAMPLE(nomrate, maxrate, file) { floppy_sound_device::SEEK, 0, 99, nomrate, maxrate, file }, + +#define FLOPPY_SOUND(name) static const floppy_sound_device::floppysoundentry name[] = { +#define FLOPPY_SOUND_END SAMPLES_END }; + +/* + 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. +*/ + +FLOPPY_SOUND(floppy525_samples) + SAMPLES_DIR("floppy") + + SPIN_SAMPLE( floppy_sound_device::START_EMPTY, "525_spin_start_empty" ) + SPIN_SAMPLE( floppy_sound_device::START_LOADED, "525_spin_start_loaded" ) + SPIN_SAMPLE( floppy_sound_device::SPIN_EMPTY, "525_spin_empty" ) + SPIN_SAMPLE( floppy_sound_device::SPIN_LOADED, "525_spin_loaded" ) + SPIN_SAMPLE( floppy_sound_device::END_LOADED, "525_spin_end" ) + + SEEK_SAMPLE( 6, 9, "525_seek_6ms" ) + SEEK_SAMPLE( 12, 15, "525_seek_12ms" ) + SEEK_SAMPLE( 20, 50, "525_seek_20ms" ) + + STEP_SAMPLE( "525_step_1_1" ) + +FLOPPY_SOUND_END + +FLOPPY_SOUND(floppy35_samples) + SAMPLES_DIR("floppy") + + SPIN_SAMPLE( floppy_sound_device::START_EMPTY, "35_spin_start_empty" ) + SPIN_SAMPLE( floppy_sound_device::START_LOADED, "35_spin_start_loaded" ) + SPIN_SAMPLE( floppy_sound_device::SPIN_EMPTY, "35_spin_empty" ) + SPIN_SAMPLE( floppy_sound_device::SPIN_LOADED, "35_spin_loaded" ) + SPIN_SAMPLE( floppy_sound_device::END_LOADED, "35_spin_end" ) + + STEP_SAMPLE( "35_step_1_1" ) + + SEEK_SAMPLE( 2, 3, "35_seek_2ms" ) // constructed + SEEK_SAMPLE( 6, 9, "35_seek_6ms" ) + SEEK_SAMPLE( 12, 15, "35_seek_12ms" ) + SEEK_SAMPLE( 20, 50, "35_seek_20ms" ) +FLOPPY_SOUND_END /*************************************************************************** TYPE DEFINITIONS @@ -158,7 +288,11 @@ public: static void default_pc_floppy_formats(format_registration &fr); // Enable sound - void enable_sound(bool doit) { m_make_sound = doit; } + // Settings are forwarded from the connector + void enable_sound(const floppy_sound_device::floppysoundentry *samples3, + const floppy_sound_device::floppysoundentry *samples35, + const floppy_sound_device::floppysoundentry *samples525, + const floppy_sound_device::floppysoundentry *samples8 ); protected: struct fs_enum; @@ -274,7 +408,12 @@ protected: void cache_weakness_setup(); // Sound - bool m_make_sound; + bool m_make_sound; + const floppy_sound_device::floppysoundentry *m_samples3; + const floppy_sound_device::floppysoundentry *m_samples35; + const floppy_sound_device::floppysoundentry *m_samples525; + const floppy_sound_device::floppysoundentry *m_samples8; + floppy_sound_device* m_sound_out; }; @@ -395,46 +534,6 @@ DECLARE_DEVICE_TYPE(OAD34V, oa_d34v_device) DECLARE_DEVICE_TYPE(MFD51W, mfd51w_device) DECLARE_DEVICE_TYPE(MFD75W, mfd75w_device) - -/* - Floppy drive sound -*/ - -class floppy_sound_device : public samples_device -{ -public: - floppy_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - void motor(bool on, bool withdisk); - void step(int track); - bool samples_loaded() { return m_loaded; } - void register_for_save_states(); - -protected: - void device_start() override ATTR_COLD; - -private: - // device_sound_interface overrides - virtual void sound_stream_update(sound_stream &stream) override; - sound_stream* m_sound; - - 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; -}; - - class floppy_connector: public device_t, public device_slot_interface { @@ -467,7 +566,14 @@ public: virtual ~floppy_connector(); template <typename T> void set_formats(T &&_formats) { formats = std::forward<T>(_formats); } - void enable_sound(bool doit) { m_enable_sound = doit; } + void enable_sound(bool doit); + void enable_sound(const floppy_sound_device::floppysoundentry *samples); + + void enable_sound( const floppy_sound_device::floppysoundentry *samples3, + const floppy_sound_device::floppysoundentry *samples35, + const floppy_sound_device::floppysoundentry *samples525, + const floppy_sound_device::floppysoundentry *samples8 ); + void set_sectoring_type(uint32_t sectoring_type) { m_sectoring_type = sectoring_type; } floppy_image_device *get_device(); @@ -478,7 +584,12 @@ protected: private: std::function<void (format_registration &fr)> formats; - bool m_enable_sound; + + const floppy_sound_device::floppysoundentry *m_samples3; + const floppy_sound_device::floppysoundentry *m_samples35; + const floppy_sound_device::floppysoundentry *m_samples525; + const floppy_sound_device::floppysoundentry *m_samples8; + uint32_t m_sectoring_type; }; |
