diff options
Diffstat (limited to 'src/devices/imagedev/cassette.cpp')
-rw-r--r-- | src/devices/imagedev/cassette.cpp | 345 |
1 files changed, 199 insertions, 146 deletions
diff --git a/src/devices/imagedev/cassette.cpp b/src/devices/imagedev/cassette.cpp index 3007ea752f5..cef022b57a5 100644 --- a/src/devices/imagedev/cassette.cpp +++ b/src/devices/imagedev/cassette.cpp @@ -9,16 +9,24 @@ *********************************************************************/ #include "emu.h" -#include "formats/imageutl.h" #include "cassette.h" +#include "softlist_dev.h" + +#include "formats/imageutl.h" + +#include "util/ioprocs.h" +#include "util/ioprocsfilter.h" + +#include <regex> -#define LOG_WARN (1U<<1) // Warnings -#define LOG_DETAIL (1U<<2) // Details +#define LOG_WARN (1U << 1) // Warnings +#define LOG_DETAIL (1U << 2) // Details -#define VERBOSE ( LOG_WARN ) +#define VERBOSE (LOG_WARN) #include "logmacro.h" + // device type definition DEFINE_DEVICE_TYPE(CASSETTE, cassette_image_device, "cassette_image", "Cassette") @@ -26,14 +34,14 @@ DEFINE_DEVICE_TYPE(CASSETTE, cassette_image_device, "cassette_image", "Cassette" // cassette_image_device - constructor //------------------------------------------------- -cassette_image_device::cassette_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, CASSETTE, tag, owner, clock), +cassette_image_device::cassette_image_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, CASSETTE, tag, owner, clock), device_image_interface(mconfig, *this), device_sound_interface(mconfig, *this), m_cassette(nullptr), m_state(CASSETTE_STOPPED), - m_position(0), - m_position_time(0), + m_position(0.0), + m_position_time(0.0), m_value(0), m_channel(0), m_speed(0), @@ -63,8 +71,8 @@ cassette_image_device::~cassette_image_device() void cassette_image_device::device_config_complete() { m_extension_list[0] = '\0'; - for (int i = 0; m_formats[i]; i++ ) - image_specify_extension( m_extension_list, 256, m_formats[i]->extensions ); + for (int i = 0; m_formats[i]; i++) + image_specify_extension(m_extension_list, 256, m_formats[i]->extensions); } @@ -72,50 +80,41 @@ void cassette_image_device::device_config_complete() cassette IO *********************************************************************/ -bool cassette_image_device::is_motor_on() -{ - if ((m_state & CASSETTE_MASK_UISTATE) == CASSETTE_STOPPED) - return false; - if ((m_state & CASSETTE_MASK_MOTOR) != CASSETTE_MOTOR_ENABLED) - return false; - else - return true; -} - - - void cassette_image_device::update() { double cur_time = machine().time().as_double(); - if (is_motor_on()) + if (!is_stopped() && motor_on()) { - double new_position = m_position + (cur_time - m_position_time)*m_speed*m_direction; + double new_position = m_position + (cur_time - m_position_time) * m_speed * m_direction; - switch(m_state & CASSETTE_MASK_UISTATE) + switch (m_state & CASSETTE_MASK_UISTATE) { case CASSETTE_RECORD: - cassette_put_sample(m_cassette, m_channel, m_position, new_position - m_position, m_value); + m_cassette->put_sample(m_channel, m_position, new_position - m_position, m_value); break; case CASSETTE_PLAY: - if ( m_cassette ) + if (m_cassette) { - cassette_get_sample(m_cassette, m_channel, new_position, 0.0, &m_value); - /* See if reached end of tape */ + m_cassette->get_sample(m_channel, new_position, 0.0, &m_value); + // See if reached end of tape double length = get_length(); if (new_position > length) { - m_state = (cassette_state)(( m_state & ~CASSETTE_MASK_UISTATE ) | CASSETTE_STOPPED); + m_state = (m_state & ~CASSETTE_MASK_UISTATE) | CASSETTE_STOPPED; new_position = length; } - else if (new_position < 0) + else if (new_position < 0.0) { - m_state = (cassette_state)(( m_state & ~CASSETTE_MASK_UISTATE ) | CASSETTE_STOPPED); - new_position = 0; + m_state = (m_state & ~CASSETTE_MASK_UISTATE) | CASSETTE_STOPPED; + new_position = 0.0; } } break; + + default: + break; } m_position = new_position; } @@ -124,12 +123,17 @@ void cassette_image_device::update() void cassette_image_device::change_state(cassette_state state, cassette_state mask) { - cassette_state new_state = m_state; - new_state = (cassette_state)(new_state & ~mask); - new_state = (cassette_state)(new_state | (state & mask)); - if ((m_state ^ new_state) & (CASSETTE_MASK_UISTATE | CASSETTE_MASK_MOTOR)) - m_position_time = machine().time().as_double(); - m_state = new_state; + cassette_state new_state; + + new_state = m_state; + new_state &= ~mask; + new_state |= (state & mask); + + if (new_state != m_state) + { + update(); + m_state = new_state; + } } @@ -137,12 +141,11 @@ void cassette_image_device::change_state(cassette_state state, cassette_state ma double cassette_image_device::input() { update(); - int32_t sample = m_value; - double double_value = sample / ((double) 0x7FFFFFFF); + double value = m_value / (double(0x7fffffff)); - LOGMASKED(LOG_DETAIL, "cassette_input(): time_index=%g value=%g\n", m_position, double_value); + LOGMASKED(LOG_DETAIL, "cassette_input(): time_index=%g value=%g\n", m_position, value); - return double_value; + return value; } @@ -153,26 +156,26 @@ void cassette_image_device::output(double value) { update(); - value = std::min(value, 1.0); - value = std::max(value, -1.0); - - m_value = (int32_t) (value * 0x7FFFFFFF); + value = std::clamp(value, -1.0, 1.0); + m_value = int32_t(value * 0x7fffffff); } } double cassette_image_device::get_position() { - return m_position; + double position = m_position; + + if (!is_stopped() && motor_on()) + position += (machine().time().as_double() - m_position_time) * m_speed * m_direction; + return position; } double cassette_image_device::get_length() { - struct CassetteInfo info; - - cassette_get_info(m_cassette, &info); + cassette_image::Info info = m_cassette->get_info(); return ((double) info.sample_count) / info.sample_frequency; } @@ -199,9 +202,12 @@ void cassette_image_device::go_reverse() void cassette_image_device::seek(double time, int origin) { + update(); + double length = get_length(); - switch(origin) { + switch (origin) + { case SEEK_SET: break; @@ -214,14 +220,8 @@ void cassette_image_device::seek(double time, int origin) break; } - /* clip position into legal bounds */ - if (time < 0) - time = 0; - else - if (time > length) - time = length; - - m_position = time; + // clip position into legal bounds + m_position = std::clamp(time, 0.0, length); } @@ -230,38 +230,76 @@ void cassette_image_device::seek(double time, int origin) cassette device init/load/unload/specify *********************************************************************/ +// allow save_item on a non-fundamental type +ALLOW_SAVE_TYPE(cassette_state); + void cassette_image_device::device_start() { - /* set to default state */ + // set to default state m_cassette = nullptr; m_state = m_default_state; m_value = 0; - machine().sound().stream_alloc(*this, 0, (m_stereo? 2:1), machine().sample_rate()); + stream_alloc(0, m_stereo ? 2 : 1, machine().sample_rate()); + + save_item(NAME(m_state)); + save_item(NAME(m_position)); + save_item(NAME(m_position_time)); + save_item(NAME(m_value)); + save_item(NAME(m_channel)); + save_item(NAME(m_speed)); + save_item(NAME(m_direction)); +} + +const software_list_loader &cassette_image_device::get_software_list_loader() const +{ + return image_software_list_loader::instance(); } -image_init_result cassette_image_device::call_create(int format_type, util::option_resolution *format_options) +std::pair<std::error_condition, std::string> cassette_image_device::call_create(int format_type, util::option_resolution *format_options) { - return internal_load(true); + return std::make_pair(internal_load(true), std::string()); } -image_init_result cassette_image_device::call_load() +std::pair<std::error_condition, std::string> cassette_image_device::call_load() { - return internal_load(false); + return std::make_pair(internal_load(false), std::string()); +} + +bool cassette_image_device::has_any_extension(std::string_view candidate_extensions) const +{ + const char separator = ','; + std::istringstream extension_stream(std::string{candidate_extensions}); + for (std::string extension; std::getline(extension_stream, extension, separator);) + if (is_filetype(extension)) + return true; + + return false; } -image_init_result cassette_image_device::internal_load(bool is_create) +std::error_condition cassette_image_device::internal_load(bool is_create) { cassette_image::error err; device_image_interface *image = nullptr; interface(image); - if (is_create || (length()==0)) // empty existing images are fine to write over. + check_for_file(); + if (is_create || (length() == 0)) // empty existing images are fine to write over. { - // creating an image - err = cassette_create((void *)image, &image_ioprocs, &wavfile_format, m_create_opts, CASSETTE_FLAG_READWRITE|CASSETTE_FLAG_SAVEONEXIT, &m_cassette); - if (err != cassette_image::error::SUCCESS) - goto error; + auto io = util::random_read_write_fill(image_core_file(), 0x00); + if (io) + { + err = cassette_image::create( + std::move(io), + has_any_extension(cassette_image::flacfile_format.extensions) ? &cassette_image::flacfile_format : &cassette_image::wavfile_format, + m_create_opts, + cassette_image::FLAG_READWRITE|cassette_image::FLAG_SAVEONEXIT, + m_cassette); + } + else + { + err = cassette_image::error::OUT_OF_MEMORY; + } } else { @@ -272,11 +310,24 @@ image_init_result cassette_image_device::internal_load(bool is_create) // we probably don't want to retry... retry = false; - // try opening the cassette - int cassette_flags = is_readonly() - ? CASSETTE_FLAG_READONLY - : (CASSETTE_FLAG_READWRITE | CASSETTE_FLAG_SAVEONEXIT); - err = cassette_open_choices((void *)image, &image_ioprocs, filetype(), m_formats, cassette_flags, &m_cassette); + auto io = util::random_read_write_fill(image_core_file(), 0x00); + if (io) + { + // try opening the cassette + int const cassette_flags = is_readonly() + ? cassette_image::FLAG_READONLY + : (cassette_image::FLAG_READWRITE | cassette_image::FLAG_SAVEONEXIT); + err = cassette_image::open_choices( + std::move(io), + filetype(), + m_formats, + cassette_flags, + m_cassette); + } + else + { + err = cassette_image::error::OUT_OF_MEMORY; + } // special case - if we failed due to readwrite not being supported, make the image be read only and retry if (err == cassette_image::error::READ_WRITE_UNSUPPORTED) @@ -285,64 +336,62 @@ image_init_result cassette_image_device::internal_load(bool is_create) retry = true; } } - while(retry); - - if (err != cassette_image::error::SUCCESS) - goto error; + while (retry); } - /* set to default state, but only change the UI state */ - //change_state(m_default_state, CASSETTE_MASK_UISTATE); - m_state = m_default_state; - - /* reset the position */ - m_position = 0.0; - m_position_time = machine().time().as_double(); + if (err == cassette_image::error::SUCCESS) + { + // set to default state, but only change the UI state + change_state(m_default_state, CASSETTE_MASK_UISTATE); - /* default channel to 0, speed multiplier to 1 */ - m_channel = 0; - m_speed = 1; - m_direction = 1; + // reset the position + m_position = 0.0; + m_position_time = machine().time().as_double(); - return image_init_result::PASS; + // default channel to 0, speed multiplier to 1 + m_channel = 0; + m_speed = 1; + m_direction = 1; -error: - image_error_t imgerr = IMAGE_ERROR_UNSPECIFIED; - switch(err) + return std::error_condition(); + } + else { - case cassette_image::error::INTERNAL: - imgerr = IMAGE_ERROR_INTERNAL; - break; - case cassette_image::error::UNSUPPORTED: - imgerr = IMAGE_ERROR_UNSUPPORTED; - break; - case cassette_image::error::OUT_OF_MEMORY: - imgerr = IMAGE_ERROR_OUTOFMEMORY; - break; - case cassette_image::error::INVALID_IMAGE: - imgerr = IMAGE_ERROR_INVALIDIMAGE; - break; - default: - imgerr = IMAGE_ERROR_UNSPECIFIED; - break; + std::error_condition imgerr = image_error::UNSPECIFIED; + switch(err) + { + case cassette_image::error::INTERNAL: + imgerr = image_error::INTERNAL; + break; + case cassette_image::error::UNSUPPORTED: + imgerr = image_error::UNSUPPORTED; + break; + case cassette_image::error::OUT_OF_MEMORY: + imgerr = std::errc::not_enough_memory; + break; + case cassette_image::error::INVALID_IMAGE: + imgerr = image_error::INVALIDIMAGE; + break; + default: + imgerr = image_error::UNSPECIFIED; + break; + } + return imgerr; } - image->seterror(imgerr, "" ); - return image_init_result::FAIL; } void cassette_image_device::call_unload() { - /* if we are recording, write the value to the image */ + // if we are recording, write the value to the image if ((m_state & CASSETTE_MASK_UISTATE) == CASSETTE_RECORD) update(); - /* close out the cassette */ - cassette_close(m_cassette); - m_cassette = nullptr; + // close out the cassette + m_cassette.reset(); - /* set to default state, but only change the UI state */ + // set to default state, but only change the UI state change_state(CASSETTE_STOPPED, CASSETTE_MASK_UISTATE); } @@ -359,24 +408,24 @@ std::string cassette_image_device::call_display() std::string result; // only show the image when a cassette is loaded and the motor is on - if (exists() && is_motor_on()) + if (exists() && !is_stopped() && motor_on()) { static char const *const shapes[] = { u8"\u2500", u8"\u2572", u8"\u2502", u8"\u2571" }; // figure out where we are in the cassette double position = get_position(); double length = get_length(); - cassette_state uistate = cassette_state(get_state() & CASSETTE_MASK_UISTATE); + cassette_state uistate = get_state() & CASSETTE_MASK_UISTATE; // choose which frame of the animation we are at - int n = (int(position) / ANIMATION_FPS) % ARRAY_LENGTH(shapes); + int n = (int(position) / ANIMATION_FPS) % std::size(shapes); // play or record const char *status_icon = (uistate == CASSETTE_PLAY) - ? u8"\u25BA" - : u8"\u25CF"; + ? u8"\u25ba" + : u8"\u25cf"; - // Since you can have anything in a BDF file, we will use crude ascii characters instead + // create information string result = string_format("%s %s %02d:%02d (%04d) [%02d:%02d (%04d)]", shapes[n], // animation status_icon, // play or record @@ -386,6 +435,18 @@ std::string cassette_image_device::call_display() ((int)length / 60), ((int)length % 60), (int)length); + + // make sure tape stops at end when playing + if ((m_state & CASSETTE_MASK_UISTATE) == CASSETTE_PLAY) + { + if (m_cassette) + { + if (get_position() > get_length()) + { + m_state = ((m_state & ~CASSETTE_MASK_UISTATE) | CASSETTE_STOPPED); + } + } + } } return result; } @@ -394,37 +455,29 @@ std::string cassette_image_device::call_display() // Cassette sound //------------------------------------------------- -void cassette_image_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +void cassette_image_device::sound_stream_update(sound_stream &stream) { - stream_sample_t *left_buffer = outputs[0]; - stream_sample_t *right_buffer = nullptr; - - if (m_stereo) - right_buffer = outputs[1]; - - cassette_state state = get_state(); + cassette_state state = get_state() & (CASSETTE_MASK_UISTATE | CASSETTE_MASK_MOTOR | CASSETTE_MASK_SPEAKER); if (exists() && (state == (CASSETTE_PLAY | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED))) { + int samples = stream.samples(); cassette_image *cassette = get_image(); double time_index = get_position(); - double duration = ((double) samples) / machine().sample_rate(); + double duration = ((double) samples) / stream.sample_rate(); - cassette_get_samples(cassette, 0, time_index, duration, samples, 2, left_buffer, CASSETTE_WAVEFORM_16BIT); - if (m_stereo) - cassette_get_samples(cassette, 1, time_index, duration, samples, 2, right_buffer, CASSETTE_WAVEFORM_16BIT); + if (m_samples.size() < samples) + m_samples.resize(samples); - for (int i = samples - 1; i >= 0; i--) + const cassette_image::Info info = cassette->get_info(); + for (int ch = 0; ch < stream.output_count(); ch++) { - left_buffer[i] = ((int16_t *) left_buffer)[i]; - if (m_stereo) - right_buffer[i] = ((int16_t *) right_buffer)[i]; + if (ch < info.channels) + cassette->get_samples(ch, time_index, duration, samples, 2, &m_samples[0], cassette_image::WAVEFORM_16BIT); + else + cassette->get_samples(0, time_index, duration, samples, 2, &m_samples[0], cassette_image::WAVEFORM_16BIT); + for (int sampindex = 0; sampindex < samples; sampindex++) + stream.put_int(ch, sampindex, m_samples[sampindex], 32768); } } - else - { - memset(left_buffer, 0, sizeof(*left_buffer) * samples); - if (m_stereo) - memset(right_buffer, 0, sizeof(*right_buffer) * samples); - } } |