diff options
Diffstat (limited to 'src/devices/sound/samples.cpp')
-rw-r--r-- | src/devices/sound/samples.cpp | 133 |
1 files changed, 61 insertions, 72 deletions
diff --git a/src/devices/sound/samples.cpp b/src/devices/sound/samples.cpp index 40ff619cdce..4a1d527a958 100644 --- a/src/devices/sound/samples.cpp +++ b/src/devices/sound/samples.cpp @@ -2,23 +2,21 @@ // copyright-holders:Aaron Giles /*************************************************************************** - samples.c - Sound device for sample playback. **************************************************************************** - Playback of pre-recorded samples. Used for high-level simulation of - discrete sound circuits where proper low-level simulation isn't - available. Also used for tape loops and similar. - - Current limitations - - Only supports single channel samples! +Playback of pre-recorded samples. Used for high-level simulation of discrete +sound circuits where proper low-level simulation isn't available. Also used +for tape loops and similar. - Considerations - - Maybe this should be part of the presentation layer - (artwork etc.) with samples specified in .lay files instead of - in drivers? +TODO: +- Only supports single channel samples! +- When mame.ini samplerate is close to the loaded sample(s) samplerate, + (eg. 48000, with 44100Hz samples), things can sound quite bad. This is + more an issue in sound.cpp resampler, not this device. +- Maybe this should be part of the presentation layer (artwork etc.) + with samples specified in .lay files instead of in drivers? ***************************************************************************/ @@ -26,6 +24,7 @@ #include "samples.h" #include "emuopts.h" +#include "fileio.h" #include "flac.h" @@ -55,6 +54,7 @@ samples_device::samples_device(const machine_config &mconfig, const char *tag, d samples_device::samples_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, type, tag, owner, clock) , device_sound_interface(mconfig, *this) + , m_samples_start_cb(*this) , m_channels(0) , m_names(nullptr) { @@ -85,12 +85,11 @@ void samples_device::start(uint8_t channel, uint32_t samplenum, bool loop) // update the parameters sample_t &sample = m_sample[samplenum]; chan.source = (sample.data.size() > 0) ? &sample.data[0] : nullptr; - chan.source_length = sample.data.size(); - chan.source_num = (chan.source_length > 0) ? samplenum : -1; + chan.source_num = (chan.source_len > 0) ? samplenum : -1; + chan.source_len = sample.data.size(); chan.pos = 0; - chan.frac = 0; chan.basefreq = sample.frequency; - chan.step = (int64_t(chan.basefreq) << FRAC_BITS) / machine().sample_rate(); + chan.curfreq = sample.frequency; chan.loop = loop; } @@ -110,12 +109,11 @@ void samples_device::start_raw(uint8_t channel, const int16_t *sampledata, uint3 // update the parameters chan.source = sampledata; - chan.source_length = samples; chan.source_num = -1; + chan.source_len = samples; chan.pos = 0; - chan.frac = 0; chan.basefreq = frequency; - chan.step = (int64_t(chan.basefreq) << FRAC_BITS) / machine().sample_rate(); + chan.curfreq = frequency; chan.loop = loop; } @@ -132,7 +130,7 @@ void samples_device::set_frequency(uint8_t channel, uint32_t freq) // force an update before we start channel_t &chan = m_channel[channel]; chan.stream->update(); - chan.step = (int64_t(freq) << FRAC_BITS) / machine().sample_rate(); + chan.curfreq = freq; } @@ -146,8 +144,7 @@ void samples_device::set_volume(uint8_t channel, float volume) assert(channel < m_channels); // force an update before we start - channel_t &chan = m_channel[channel]; - chan.stream->set_output_gain(0, volume); + set_output_gain(channel, volume); } @@ -200,11 +197,7 @@ void samples_device::stop_all() uint32_t samples_device::base_frequency(uint8_t channel) const { assert(channel < m_channels); - - // force an update before we start - const channel_t &chan = m_channel[channel]; - chan.stream->update(); - return chan.basefreq; + return m_channel[channel].basefreq; } @@ -244,25 +237,23 @@ void samples_device::device_start() { // initialize channel channel_t &chan = m_channel[channel]; - chan.stream = stream_alloc(0, 1, machine().sample_rate()); + chan.stream = stream_alloc(0, 1, SAMPLE_RATE_OUTPUT_ADAPTIVE); chan.source = nullptr; chan.source_num = -1; - chan.step = 0; + chan.pos = 0; chan.loop = 0; chan.paused = 0; // register with the save state system - save_item(NAME(chan.source_length), channel); save_item(NAME(chan.source_num), channel); + save_item(NAME(chan.source_len), channel); save_item(NAME(chan.pos), channel); - save_item(NAME(chan.frac), channel); - save_item(NAME(chan.step), channel); save_item(NAME(chan.loop), channel); save_item(NAME(chan.paused), channel); } // initialize any custom handlers - m_samples_start_cb.bind_relative_to(*owner()); + m_samples_start_cb.resolve(); if (!m_samples_start_cb.isnull()) m_samples_start_cb(); @@ -295,16 +286,21 @@ void samples_device::device_post_load() { sample_t &sample = m_sample[chan.source_num]; chan.source = &sample.data[0]; - chan.source_length = sample.data.size(); + chan.source_len = sample.data.size(); if (sample.data.empty()) chan.source_num = -1; } // validate the position against the length in case the sample is smaller - if (chan.source != nullptr && chan.pos >= chan.source_length) + double endpos = chan.source_len; + if (chan.source != nullptr && chan.pos >= endpos) { if (chan.loop) - chan.pos %= chan.source_length; + { + double posfloor = floor(chan.pos); + chan.pos -= posfloor; + chan.pos += double(int32_t(posfloor) % chan.source_len); + } else { chan.source = nullptr; @@ -319,60 +315,51 @@ void samples_device::device_post_load() // sound_stream_update - update a sound stream //------------------------------------------------- -void samples_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +void samples_device::sound_stream_update(sound_stream &stream) { // find the channel with this stream + constexpr sound_stream::sample_t sample_scale = 1.0 / 32768.0; for (int channel = 0; channel < m_channels; channel++) if (&stream == m_channel[channel].stream) { channel_t &chan = m_channel[channel]; - stream_sample_t *buffer = outputs[0]; // process if we still have a source and we're not paused if (chan.source != nullptr && !chan.paused) { // load some info locally - uint32_t pos = chan.pos; - uint32_t frac = chan.frac; - uint32_t step = chan.step; + double step = double(chan.curfreq) / double(stream.sample_rate()); + double endpos = chan.source_len; const int16_t *sample = chan.source; - uint32_t sample_length = chan.source_length; - while (samples--) + for (int sampindex = 0; sampindex < stream.samples(); sampindex++) { // do a linear interp on the sample - int32_t sample1 = sample[pos]; - int32_t sample2 = sample[(pos + 1) % sample_length]; - int32_t fracmult = frac >> (FRAC_BITS - 14); - *buffer++ = ((0x4000 - fracmult) * sample1 + fracmult * sample2) >> 14; + double pos_floor = floor(chan.pos); + double frac = chan.pos - pos_floor; + int32_t ipos = int32_t(pos_floor); + + sound_stream::sample_t sample1 = sound_stream::sample_t(sample[ipos++]); + sound_stream::sample_t sample2 = sound_stream::sample_t(sample[(ipos + 1) % chan.source_len]); + stream.put(0, sampindex, sample_scale * ((1.0 - frac) * sample1 + frac * sample2)); // advance - frac += step; - pos += frac >> FRAC_BITS; - frac = frac & ((1 << FRAC_BITS) - 1); + chan.pos += step; // handle looping/ending - if (pos >= sample_length) + if (chan.pos >= endpos) { if (chan.loop) - pos %= sample_length; + chan.pos -= endpos; else { chan.source = nullptr; chan.source_num = -1; - if (samples > 0) - memset(buffer, 0, samples * sizeof(*buffer)); break; } } } - - // push position back out - chan.pos = pos; - chan.frac = frac; } - else - memset(buffer, 0, samples * sizeof(*buffer)); break; } } @@ -604,7 +591,7 @@ bool samples_device::load_samples() return false; // iterate over ourself - const char *basename = machine().basename(); + const std::string &basename = machine().basename(); samples_iterator iter(*this); const char *altbasename = iter.altbasename(); @@ -613,26 +600,28 @@ bool samples_device::load_samples() // load the samples int index = 0; - for (const char *samplename = iter.first(); samplename != nullptr; index++, samplename = iter.next()) + for (const char *samplename = iter.first(); samplename; index++, samplename = iter.next()) { // attempt to open as FLAC first emu_file file(machine().options().sample_path(), OPEN_FLAG_READ); - osd_file::error filerr = file.open(basename, PATH_SEPARATOR, samplename, ".flac"); - if (filerr != osd_file::error::NONE && altbasename != nullptr) - filerr = file.open(altbasename, PATH_SEPARATOR, samplename, ".flac"); + std::error_condition filerr = file.open(util::string_format("%s" PATH_SEPARATOR "%s.flac", basename, samplename)); + if (filerr && altbasename) + filerr = file.open(util::string_format("%s" PATH_SEPARATOR "%s.flac", altbasename, samplename)); // if not, try as WAV - if (filerr != osd_file::error::NONE) - filerr = file.open(basename, PATH_SEPARATOR, samplename, ".wav"); - if (filerr != osd_file::error::NONE && altbasename != nullptr) - filerr = file.open(altbasename, PATH_SEPARATOR, samplename, ".wav"); + if (filerr) + filerr = file.open(util::string_format("%s" PATH_SEPARATOR "%s.wav", basename, samplename)); + if (filerr && altbasename) + filerr = file.open(util::string_format("%s" PATH_SEPARATOR "%s.wav", altbasename, samplename)); // if opened, read it - if (filerr == osd_file::error::NONE) + if (!filerr) + { read_sample(file, m_sample[index]); - else if (filerr == osd_file::error::NOT_FOUND) + } + else { - logerror("%s: Sample '%s' NOT FOUND\n", tag(), samplename); + logerror("Error opening sample '%s' (%s:%d %s)\n", samplename, filerr.category().name(), filerr.value(), filerr.message()); ok = false; } } |