mirror of
https://github.com/holub/mame
synced 2025-04-22 08:22:15 +03:00
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.
This commit is contained in:
parent
b4549cfd62
commit
b7b56f24f4
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user