diff options
| author | 2024-03-21 20:35:11 +0000 | |
|---|---|---|
| committer | 2024-03-22 07:35:11 +1100 | |
| commit | b7b56f24f41d3311574b2a9ed43923d0435c2f9f (patch) | |
| tree | 8123962e013f6a2df78640c9d4ff51675059e2b7 | |
| parent | b4549cfd624841594278ca41515f861c1240111a (diff) | |
imagedev/cassette.cpp, formats/flacfile.cpp: Added support for saving cassette images in FLAC format. (#12115)
util/flac.cpp: Implemented seek/tell callbacks for FLAC library.
| -rw-r--r-- | src/devices/imagedev/cassette.cpp | 15 | ||||
| -rw-r--r-- | src/devices/imagedev/cassette.h | 1 | ||||
| -rw-r--r-- | src/lib/formats/flacfile.cpp | 78 | ||||
| -rw-r--r-- | src/lib/util/flac.cpp | 34 | ||||
| -rw-r--r-- | src/lib/util/flac.h | 4 |
5 files changed, 126 insertions, 6 deletions
diff --git a/src/devices/imagedev/cassette.cpp b/src/devices/imagedev/cassette.cpp index 002eaaf912c..21cb7e8bd10 100644 --- a/src/devices/imagedev/cassette.cpp +++ b/src/devices/imagedev/cassette.cpp @@ -17,6 +17,8 @@ #include "util/ioprocs.h" #include "util/ioprocsfilter.h" +#include <regex> + #define LOG_WARN (1U << 1) // Warnings #define LOG_DETAIL (1U << 2) // Details @@ -258,6 +260,16 @@ std::pair<std::error_condition, std::string> cassette_image_device::call_load() 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; +} + std::error_condition cassette_image_device::internal_load(bool is_create) { cassette_image::error err; @@ -270,10 +282,9 @@ std::error_condition cassette_image_device::internal_load(bool is_create) auto io = util::random_read_write_fill(image_core_file(), 0x00); if (io) { - // creating an image err = cassette_image::create( std::move(io), - &cassette_image::wavfile_format, + 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); diff --git a/src/devices/imagedev/cassette.h b/src/devices/imagedev/cassette.h index 486e322b1a1..650b45f1739 100644 --- a/src/devices/imagedev/cassette.h +++ b/src/devices/imagedev/cassette.h @@ -131,6 +131,7 @@ private: const char * m_interface; std::error_condition internal_load(bool is_create); + bool has_any_extension(std::string_view candidate_extensions) const; bool m_stereo; std::vector<s16> m_samples; }; diff --git a/src/lib/formats/flacfile.cpp b/src/lib/formats/flacfile.cpp index f3ccb676e52..c594a809a8e 100644 --- a/src/lib/formats/flacfile.cpp +++ b/src/lib/formats/flacfile.cpp @@ -16,10 +16,27 @@ #include <new> -static constexpr int MAX_CHANNELS = 8; +namespace { +constexpr int MAX_CHANNELS = 8; -static cassette_image::error flacfile_identify(cassette_image *cassette, cassette_image::Options *opts) + +// Copied from cassimg.cpp; put somewhere central? +/********************************************************************* + helper code +*********************************************************************/ +constexpr double map_double(double d, uint64_t low, uint64_t high, uint64_t value) +{ + return d * (value - low) / (high - low); +} + +constexpr size_t waveform_bytes_per_sample(int waveform_flags) +{ + return size_t(1 << ((waveform_flags & 0x06) / 2)); +} + + +cassette_image::error flacfile_identify(cassette_image *cassette, cassette_image::Options *opts) { cassette->get_raw_cassette_image()->seek(0, SEEK_SET); flac_decoder decoder(*cassette->get_raw_cassette_image()); @@ -42,7 +59,7 @@ static cassette_image::error flacfile_identify(cassette_image *cassette, cassett } -static cassette_image::error flacfile_load(cassette_image *cassette) +cassette_image::error flacfile_load(cassette_image *cassette) { cassette->get_raw_cassette_image()->seek(0, SEEK_SET); flac_decoder decoder(*cassette->get_raw_cassette_image()); @@ -73,10 +90,63 @@ static cassette_image::error flacfile_load(cassette_image *cassette) } +cassette_image::error flacfile_save(cassette_image *cassette, const cassette_image::Info *info) +{ + if (info->channels > MAX_CHANNELS) + return cassette_image::error::INVALID_IMAGE; + + cassette->get_raw_cassette_image()->seek(0, SEEK_SET); + flac_encoder encoder; + encoder.set_num_channels(info->channels); + encoder.set_sample_rate(info->sample_frequency); + if (!encoder.reset(*cassette->get_raw_cassette_image())) + return cassette_image::error::INTERNAL; + + size_t samples_saved = 0; + int16_t buffer[MAX_CHANNELS][4096]; + int16_t *buffer_ptr[MAX_CHANNELS]; + const size_t bytes_per_sample = waveform_bytes_per_sample(cassette_image::WAVEFORM_16BIT); + const size_t sample_spacing = bytes_per_sample * info->channels; + const double sample_period = info->sample_count / (double) info->sample_frequency; + + for (int channel = 0; channel < MAX_CHANNELS; channel++) + { + buffer_ptr[channel] = &buffer[channel][0]; + } + + while (samples_saved < info->sample_count) + { + size_t chunk_sample_count = std::min(sizeof(buffer) / sample_spacing, (info->sample_count - samples_saved)); + double chunk_sample_period = map_double(sample_period, 0, info->sample_count, chunk_sample_count); + double chunk_time_index = map_double(sample_period, 0, info->sample_count, samples_saved); + + for (int channel = 0; channel < info->channels; channel++) + { + cassette_image::error err = cassette->get_samples(channel, chunk_time_index, chunk_sample_period, + chunk_sample_count, sample_spacing, &buffer[channel * bytes_per_sample], cassette_image::WAVEFORM_16BIT); + + if (err != cassette_image::error::SUCCESS) + return err; + } + + if (!encoder.encode(buffer_ptr, chunk_sample_count)) + return cassette_image::error::INTERNAL; + + samples_saved += chunk_sample_count; + } + + encoder.finish(); + + return cassette_image::error::SUCCESS; +} + +} // anonymous namespace + + const cassette_image::Format cassette_image::flacfile_format = { "flac", flacfile_identify, flacfile_load, - nullptr + flacfile_save }; diff --git a/src/lib/util/flac.cpp b/src/lib/util/flac.cpp index 3b76bb14303..d3110ca0d38 100644 --- a/src/lib/util/flac.cpp +++ b/src/lib/util/flac.cpp @@ -85,6 +85,8 @@ bool flac_encoder::reset() FLAC__stream_encoder_set_blocksize(m_encoder, m_block_size); // re-start processing + if (m_file) + return (FLAC__stream_encoder_init_stream(m_encoder, write_callback_static, seek_callback_static, tell_callback_static, nullptr, this) == FLAC__STREAM_ENCODER_INIT_STATUS_OK); return (FLAC__stream_encoder_init_stream(m_encoder, write_callback_static, nullptr, nullptr, nullptr, this) == FLAC__STREAM_ENCODER_INIT_STATUS_OK); } @@ -281,6 +283,38 @@ FLAC__StreamEncoderWriteStatus flac_encoder::write_callback(const FLAC__byte buf return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; } +FLAC__StreamEncoderSeekStatus flac_encoder::seek_callback_static(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data) +{ + return reinterpret_cast<flac_encoder *>(client_data)->seek_callback(absolute_byte_offset); +} + +FLAC__StreamEncoderSeekStatus flac_encoder::seek_callback(FLAC__uint64 absolute_byte_offset) +{ + if (m_file) + { + if (!m_file->seek(absolute_byte_offset, SEEK_SET)) + return FLAC__STREAM_ENCODER_SEEK_STATUS_OK; + return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR; + } + return FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED; +} + +FLAC__StreamEncoderTellStatus flac_encoder::tell_callback_static(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data) +{ + return reinterpret_cast<flac_encoder *>(client_data)->tell_callback(absolute_byte_offset); +} + +FLAC__StreamEncoderTellStatus flac_encoder::tell_callback(FLAC__uint64 *absolute_byte_offset) +{ + if (m_file) + { + if (!m_file->tell(*absolute_byte_offset)) + return FLAC__STREAM_ENCODER_TELL_STATUS_OK; + return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR; + } + return FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED; +} + //************************************************************************** diff --git a/src/lib/util/flac.h b/src/lib/util/flac.h index 7170ea96230..be3a0a8ac96 100644 --- a/src/lib/util/flac.h +++ b/src/lib/util/flac.h @@ -62,6 +62,10 @@ private: void init_common(); static FLAC__StreamEncoderWriteStatus write_callback_static(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data); FLAC__StreamEncoderWriteStatus write_callback(const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame); + static FLAC__StreamEncoderSeekStatus seek_callback_static(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data); + FLAC__StreamEncoderSeekStatus seek_callback(FLAC__uint64 absolute_byte_offset); + static FLAC__StreamEncoderTellStatus tell_callback_static(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data); + FLAC__StreamEncoderTellStatus tell_callback(FLAC__uint64 *absolute_byte_offset); // internal state FLAC__StreamEncoder * m_encoder; // actual encoder |
