diff options
Diffstat (limited to 'src/lib/formats/cassimg.cpp')
-rw-r--r-- | src/lib/formats/cassimg.cpp | 848 |
1 files changed, 365 insertions, 483 deletions
diff --git a/src/lib/formats/cassimg.cpp b/src/lib/formats/cassimg.cpp index 4f5d9320754..25b060e0311 100644 --- a/src/lib/formats/cassimg.cpp +++ b/src/lib/formats/cassimg.cpp @@ -8,12 +8,12 @@ *********************************************************************/ -#include <cstring> -#include <cassert> - -#include "imageutl.h" #include "cassimg.h" +#include "imageutl.h" + #include <algorithm> +#include <cassert> +#include <cstring> /* debugging parameters */ @@ -28,21 +28,22 @@ CASSETTE_FORMATLIST_START(cassette_default_formats) CASSETTE_FORMATLIST_END +namespace { /********************************************************************* helper code *********************************************************************/ -static double map_double(double d, uint64_t low, uint64_t high, uint64_t value) +constexpr double map_double(double d, uint64_t low, uint64_t high, uint64_t value) { return d * (value - low) / (high - low); } -static size_t waveform_bytes_per_sample(int waveform_flags) +constexpr size_t waveform_bytes_per_sample(int waveform_flags) { - return (size_t) (1 << ((waveform_flags & 0x06) / 2)); + return size_t(1 << ((waveform_flags & 0x06) / 2)); } @@ -51,361 +52,319 @@ static size_t waveform_bytes_per_sample(int waveform_flags) extrapolation and interpolation *********************************************************************/ -static int32_t extrapolate8(int8_t value) +constexpr int32_t extrapolate8(int8_t value) { - return ((int32_t) value) << 24; + return int32_t(value) << 24; } -static int32_t extrapolate16(int16_t value) +constexpr int32_t extrapolate16(int16_t value) { - return ((int32_t) value) << 16; + return int32_t(value) << 16; } -static int8_t interpolate8(int32_t value) +constexpr int8_t interpolate8(int32_t value) { - return (int8_t) (value >> 24); + return int8_t(value >> 24); } -static int16_t interpolate16(int32_t value) +constexpr int16_t interpolate16(int32_t value) { - return (int16_t) (value >> 16); + return int16_t(value >> 16); } -/********************************************************************* - initialization and termination -*********************************************************************/ - -static cassette_image *cassette_init(const struct CassetteFormat *format, void *file, const struct io_procs *procs, int flags) +constexpr size_t my_round(double d) { - cassette_image *cassette; - - cassette = global_alloc_clear<cassette_image>(); - cassette->format = format; - cassette->io.file = file; - cassette->io.procs = procs; - cassette->flags = flags; - return cassette; + return size_t(d + 0.5); } -static void cassette_finishinit(cassette_image::error err, cassette_image *cassette, cassette_image **outcassette) +/********************************************************************* + waveform accesses to/from the raw image +*********************************************************************/ + +const int8_t *choose_wave(const cassette_image::Modulation &modulation, size_t &wave_bytes_length) { - if (cassette && ((err != cassette_image::error::SUCCESS) || !outcassette)) + static const int8_t square_wave[] = { -128, 127 }; + static const int8_t sine_wave[] = { 0, 48, 89, 117, 127, 117, 89, 48, 0, -48, -89, -117, -127, -117, -89, -48 }; + + if (modulation.flags & cassette_image::MODULATION_SINEWAVE) + { + wave_bytes_length = ARRAY_LENGTH(sine_wave); + return sine_wave; + } + else { - cassette_close(cassette); - cassette = nullptr; + wave_bytes_length = ARRAY_LENGTH(square_wave); + return square_wave; } - if (outcassette) - *outcassette = cassette; } +} // anonymous namespace -static cassette_image::error try_identify_format(const struct CassetteFormat &format, cassette_image *image, const std::string &extension, int flags, struct CassetteOptions &opts) + +/********************************************************************* + initialization and termination +*********************************************************************/ + +cassette_image::error cassette_image::try_identify_format(const Format &format, const std::string &extension, int flags, Options &opts) { // is this the right extension? if (!extension.empty() && !image_find_extension(format.extensions, extension.c_str())) - return cassette_image::error::INVALID_IMAGE; + return error::INVALID_IMAGE; // invoke format->identify - memset(&opts, 0, sizeof(opts)); - cassette_image::error err = format.identify(image, &opts); - if (err != cassette_image::error::SUCCESS) + opts = Options(); + error err = format.identify(this, &opts); + if (err != error::SUCCESS) return err; // is this a read only format, but the cassette was not opened read only? - if (((flags & CASSETTE_FLAG_READONLY) == 0) && (format.save == nullptr)) - return cassette_image::error::READ_WRITE_UNSUPPORTED; + if (((flags & FLAG_READONLY) == 0) && (format.save == nullptr)) + return error::READ_WRITE_UNSUPPORTED; // success! - return cassette_image::error::SUCCESS; + return error::SUCCESS; +} + + +cassette_image::error cassette_image::perform_save() +{ + Info info = get_info(); + return m_format->save(this, &info); } -cassette_image::error cassette_open_choices(void *file, const struct io_procs *procs, const std::string &extension, - const struct CassetteFormat *const *formats, int flags, cassette_image **outcassette) +/********************************************************************* + waveform accesses +*********************************************************************/ + +struct cassette_image::manipulation_ranges { - cassette_image::error err; - cassette_image *cassette; - const struct CassetteFormat *format; - struct CassetteOptions opts = {0, }; - int i; + int channel_first; + int channel_last; + size_t sample_first; + size_t sample_last; +}; - /* if not specified, use the dummy arguments */ - if (!formats) - formats = cassette_default_formats; - /* create the cassette object */ - cassette = cassette_init(nullptr, file, procs, flags); - if (!cassette) - { - err = cassette_image::error::OUT_OF_MEMORY; - goto done; - } - /* identify the image */ - format = nullptr; - for (i = 0; !format && formats[i]; i++) +cassette_image::error cassette_image::compute_manipulation_ranges(int channel, + double time_index, double sample_period, manipulation_ranges &ranges) const +{ + if (channel < 0) { - // try this format - err = try_identify_format(*formats[i], cassette, extension, flags, opts); - if (err != cassette_image::error::SUCCESS && err != cassette_image::error::INVALID_IMAGE) - goto done; - - // did we succeed? - if (err == cassette_image::error::SUCCESS) - format = formats[i]; + ranges.channel_first = 0; + ranges.channel_last = m_channels - 1; } - - /* have we found a proper format */ - if (!format) + else { - err = cassette_image::error::INVALID_IMAGE; - goto done; + ranges.channel_first = channel; + ranges.channel_last = channel; } - cassette->format = format; - - /* read the options */ - cassette->channels = opts.channels; - cassette->sample_frequency = opts.sample_frequency; - /* load the image */ - err = format->load(cassette); - if (err != cassette_image::error::SUCCESS) - goto done; + ranges.sample_first = my_round(time_index * m_sample_frequency); + ranges.sample_last = my_round((time_index + sample_period) * m_sample_frequency); - /* success */ - cassette->flags &= ~CASSETTE_FLAG_DIRTY; - err = cassette_image::error::SUCCESS; + if (ranges.sample_last > ranges.sample_first) + ranges.sample_last--; -done: - cassette_finishinit(err, cassette, outcassette); - return err; + return error::SUCCESS; } -cassette_image::error cassette_open(void *file, const struct io_procs *procs, - const struct CassetteFormat *format, int flags, cassette_image **outcassette) +cassette_image::error cassette_image::lookup_sample(int channel, size_t sample, int32_t *&ptr) { - const struct CassetteFormat *formats[2]; - formats[0] = format; - formats[1] = nullptr; - return cassette_open_choices(file, procs, nullptr, formats, flags, outcassette); -} - + ptr = nullptr; + size_t sample_blocknum = (sample / SAMPLES_PER_BLOCK) * m_channels + channel; + size_t sample_index = sample % SAMPLES_PER_BLOCK; + // is this block beyond the edge of our waveform? + if (sample_blocknum >= m_blocks.size()) + m_blocks.resize(sample_blocknum + 1); -cassette_image::error cassette_create(void *file, const struct io_procs *procs, const struct CassetteFormat *format, - const struct CassetteOptions *opts, int flags, cassette_image **outcassette) -{ - cassette_image::error err; - cassette_image *cassette; - static const struct CassetteOptions default_options = { 1, 16, 44100 }; + if (!m_blocks[sample_blocknum]) + m_blocks[sample_blocknum] = make_unique_clear<int32_t []>(SAMPLES_PER_BLOCK); - /* cannot create to a read only image */ - if (flags & CASSETTE_FLAG_READONLY) - return cassette_image::error::INVALID_IMAGE; + ptr = &m_blocks[sample_blocknum][sample_index]; + return error::SUCCESS; +} - /* is this a good format? */ - if (format->save == nullptr) - return cassette_image::error::INVALID_IMAGE; - /* normalize arguments */ - if (!opts) - opts = &default_options; - /* create the cassette object */ - cassette = cassette_init(format, file, procs, flags); - if (!cassette) - { - err = cassette_image::error::OUT_OF_MEMORY; - goto done; - } +cassette_image::cassette_image(const Format *format, void *file, const io_procs *procs, int flags) +{ + m_format = format; + m_io.file = file; + m_io.procs = procs; + m_flags = flags; +} - /* read the options */ - cassette->channels = opts->channels; - cassette->sample_frequency = opts->sample_frequency; - err = cassette_image::error::SUCCESS; -done: - cassette_finishinit(err, cassette, outcassette); - return err; +cassette_image::~cassette_image() +{ + if ((m_flags & CASSETTE_FLAG_DIRTY) && (m_flags & FLAG_SAVEONEXIT)) + save(); } -static cassette_image::error cassette_perform_save(cassette_image *cassette) +cassette_image::error cassette_image::open(void *file, const io_procs *procs, + const Format *format, int flags, ptr &outcassette) { - struct CassetteInfo info; - cassette_get_info(cassette, &info); - return cassette->format->save(cassette, &info); + const Format *const formats[2] = { format, nullptr }; + return open_choices(file, procs, nullptr, formats, flags, outcassette); } -cassette_image::error cassette_save(cassette_image *cassette) +cassette_image::error cassette_image::open_choices(void *file, const io_procs *procs, const std::string &extension, + const Format *const *formats, int flags, ptr &outcassette) { - cassette_image::error err; + // if not specified, use the dummy arguments + if (!formats) + formats = cassette_default_formats; - if (!cassette->format || !cassette->format->save) - return cassette_image::error::UNSUPPORTED; + // create the cassette object + ptr cassette; + try { cassette.reset(new cassette_image(nullptr, file, procs, flags)); } + catch (std::bad_alloc const &) { return error::OUT_OF_MEMORY; } - err = cassette_perform_save(cassette); - if (err != cassette_image::error::SUCCESS) + // identify the image + const Format *format = nullptr; + Options opts; + for (int i = 0; !format && formats[i]; i++) + { + // try this format + error err = cassette->try_identify_format(*formats[i], extension, flags, opts); + if (err != error::SUCCESS && err != error::INVALID_IMAGE) + return err; + + // did we succeed? + if (err == error::SUCCESS) + format = formats[i]; + } + + // have we found a proper format + if (!format) + return error::INVALID_IMAGE; + cassette->m_format = format; + + // read the options + cassette->m_channels = opts.channels; + cassette->m_sample_frequency = opts.sample_frequency; + + // load the image + error err = format->load(cassette.get()); + if (err != error::SUCCESS) return err; - cassette->flags &= ~CASSETTE_FLAG_DIRTY; - return cassette_image::error::SUCCESS; + /* success */ + cassette->m_flags &= ~CASSETTE_FLAG_DIRTY; + outcassette = std::move(cassette); + return error::SUCCESS; } -void cassette_get_info(cassette_image *cassette, struct CassetteInfo *info) +cassette_image::error cassette_image::create(void *file, const io_procs *procs, const Format *format, + const Options *opts, int flags, ptr &outcassette) { - memset(info, 0, sizeof(*info)); - info->channels = cassette->channels; - info->sample_count = cassette->sample_count; - info->sample_frequency = cassette->sample_frequency; - info->bits_per_sample = (int) waveform_bytes_per_sample(cassette->flags) * 8; -} + static const Options default_options = { 1, 16, 44100 }; + // cannot create to a read only image + if (flags & FLAG_READONLY) + return error::INVALID_IMAGE; + // is this a good format? + if (format->save == nullptr) + return error::INVALID_IMAGE; -void cassette_close(cassette_image *cassette) -{ - if (cassette) - { - if ((cassette->flags & CASSETTE_FLAG_DIRTY) && (cassette->flags & CASSETTE_FLAG_SAVEONEXIT)) - cassette_save(cassette); - for (auto & elem : cassette->blocks) - { - global_free(elem); - } - global_free(cassette); - } -} + // normalize arguments + if (!opts) + opts = &default_options; + // create the cassette object + ptr cassette; + try { cassette.reset(new cassette_image(format, file, procs, flags)); } + catch (std::bad_alloc const &) { return error::OUT_OF_MEMORY; } + // read the options + cassette->m_channels = opts->channels; + cassette->m_sample_frequency = opts->sample_frequency; -void cassette_change(cassette_image *cassette, void *file, const struct io_procs *procs, const struct CassetteFormat *format, int flags) -{ - if ((flags & CASSETTE_FLAG_READONLY) == 0) - flags |= CASSETTE_FLAG_DIRTY; - cassette->io.file = file; - cassette->io.procs = procs; - cassette->format = format; - cassette->flags = flags; + outcassette = std::move(cassette); + return error::SUCCESS; } -/********************************************************************* - calls for accessing the raw cassette image -*********************************************************************/ - -void cassette_image_read(cassette_image *cassette, void *buffer, uint64_t offset, size_t length) +cassette_image::error cassette_image::save() { - io_generic_read(&cassette->io, buffer, offset, length); + if (!m_format || !m_format->save) + return error::UNSUPPORTED; + + error err = perform_save(); + if (err != error::SUCCESS) + return err; + + m_flags &= ~CASSETTE_FLAG_DIRTY; + return error::SUCCESS; } -void cassette_image_write(cassette_image *cassette, const void *buffer, uint64_t offset, size_t length) +cassette_image::Info cassette_image::get_info() const { - io_generic_write(&cassette->io, buffer, offset, length); + Info result; + result.channels = m_channels; + result.sample_count = m_sample_count; + result.sample_frequency = m_sample_frequency; + result.bits_per_sample = (int)waveform_bytes_per_sample(m_flags) * 8; + return result; } -uint64_t cassette_image_size(cassette_image *cassette) +void cassette_image::change(void *file, const io_procs *procs, const Format *format, int flags) { - return io_generic_size(&cassette->io); + if ((flags & FLAG_READONLY) == 0) + flags |= CASSETTE_FLAG_DIRTY; + m_io.file = file; + m_io.procs = procs; + m_format = format; + m_flags = flags; } /********************************************************************* - waveform accesses + calls for accessing the raw cassette image *********************************************************************/ -struct manipulation_ranges +void cassette_image::image_read(void *buffer, uint64_t offset, size_t length) { - int channel_first; - int channel_last; - size_t sample_first; - size_t sample_last; -}; - - - -static size_t my_round(double d) -{ - size_t result; - d += 0.5; - result = (size_t) d; - return result; + io_generic_read(&m_io, buffer, offset, length); } -static cassette_image::error compute_manipulation_ranges(cassette_image *cassette, int channel, - double time_index, double sample_period, struct manipulation_ranges *ranges) +void cassette_image::image_write(const void *buffer, uint64_t offset, size_t length) { - if (channel < 0) - { - ranges->channel_first = 0; - ranges->channel_last = cassette->channels - 1; - } - else - { - ranges->channel_first = channel; - ranges->channel_last = channel; - } - - ranges->sample_first = my_round(time_index * cassette->sample_frequency); - ranges->sample_last = my_round((time_index + sample_period) * cassette->sample_frequency); - - if (ranges->sample_last > ranges->sample_first) - ranges->sample_last--; - - return cassette_image::error::SUCCESS; + io_generic_write(&m_io, buffer, offset, length); } -static cassette_image::error lookup_sample(cassette_image *cassette, int channel, size_t sample, int32_t **ptr) +uint64_t cassette_image::image_size() { - *ptr = nullptr; - size_t sample_blocknum = (sample / SAMPLES_PER_BLOCK) * cassette->channels + channel; - size_t sample_index = sample % SAMPLES_PER_BLOCK; - - /* is this block beyond the edge of our waveform? */ - if (sample_blocknum >= cassette->blocks.size()) { - size_t osize = cassette->blocks.size(); - cassette->blocks.resize(sample_blocknum + 1); - memset(&cassette->blocks[osize], 0, (cassette->blocks.size()-osize)*sizeof(cassette->blocks[0])); - } - - if (cassette->blocks[sample_blocknum] == nullptr) - cassette->blocks[sample_blocknum] = global_alloc(sample_block); - - sample_block &block = *cassette->blocks[sample_blocknum]; - - /* is this sample access off the current block? */ - if (sample_index >= block.size()) { - size_t osize = block.size(); - block.resize(SAMPLES_PER_BLOCK); - memset(&block[osize], 0, (SAMPLES_PER_BLOCK-osize)*sizeof(block[0])); - } - - *ptr = &block[sample_index]; - return cassette_image::error::SUCCESS; + return io_generic_size(&m_io); } @@ -417,38 +376,29 @@ static cassette_image::error lookup_sample(cassette_image *cassette, int channel // Note: In normal use, the sample_spacing is the same as the sample size (in bytes) // But it can be larger to help do an interleaved write to samples // (see cassette_write_samples) -cassette_image::error cassette_get_samples(cassette_image *cassette, int channel, +cassette_image::error cassette_image::get_samples(int channel, double time_index, double sample_period, size_t sample_count, size_t sample_spacing, void *samples, int waveform_flags) { - cassette_image::error err; - struct manipulation_ranges ranges; - size_t sample_index; - size_t cassette_sample_index; - uint8_t *dest_ptr; - const int32_t *source_ptr; - double d; - int16_t word; - int32_t dword; - int64_t sum; - - assert(cassette); - - err = compute_manipulation_ranges(cassette, channel, time_index, sample_period, &ranges); - if (err != cassette_image::error::SUCCESS) + error err; + manipulation_ranges ranges; + int32_t *source_ptr; + + err = compute_manipulation_ranges(channel, time_index, sample_period, ranges); + if (err != error::SUCCESS) return err; - for (sample_index = 0; sample_index < sample_count; sample_index++) + for (size_t sample_index = 0; sample_index < sample_count; sample_index++) { - sum = 0; + int64_t sum = 0; for (channel = ranges.channel_first; channel <= ranges.channel_last; channel++) { /* find the sample that we are putting */ - d = map_double(ranges.sample_last + 1 - ranges.sample_first, 0, sample_count, sample_index) + ranges.sample_first; - cassette_sample_index = (size_t) d; - err = lookup_sample(cassette, channel, cassette_sample_index, (int32_t **) &source_ptr); - if (err != cassette_image::error::SUCCESS) + double d = map_double(ranges.sample_last + 1 - ranges.sample_first, 0, sample_count, sample_index) + ranges.sample_first; + size_t cassette_sample_index = (size_t) d; + err = lookup_sample(channel, cassette_sample_index, source_ptr); + if (err != error::SUCCESS) return err; sum += *source_ptr; @@ -458,61 +408,54 @@ cassette_image::error cassette_get_samples(cassette_image *cassette, int channel sum /= (ranges.channel_last + 1 - ranges.channel_first); /* and write out the result */ - dest_ptr = (uint8_t*)samples; + uint8_t *dest_ptr = (uint8_t*)samples; dest_ptr += sample_index * sample_spacing; - switch(waveform_bytes_per_sample(waveform_flags)) + int16_t word; + int32_t dword; + switch (waveform_bytes_per_sample(waveform_flags)) { - case 1: - *((int8_t *) dest_ptr) = interpolate8(sum); - break; - case 2: - word = interpolate16(sum); - if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP) - word = swapendian_int16(word); - *((int16_t *) dest_ptr) = word; - break; - case 4: - dword = sum; - if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP) - dword = swapendian_int32(dword); - *((int32_t *) dest_ptr) = dword; - break; + case 1: + *((int8_t *) dest_ptr) = interpolate8(sum); + break; + case 2: + word = interpolate16(sum); + if (waveform_flags & WAVEFORM_ENDIAN_FLIP) + word = swapendian_int16(word); + *((int16_t *) dest_ptr) = word; + break; + case 4: + dword = sum; + if (waveform_flags & WAVEFORM_ENDIAN_FLIP) + dword = swapendian_int32(dword); + *((int32_t *) dest_ptr) = dword; + break; } } - return cassette_image::error::SUCCESS; + return error::SUCCESS; } // Note: In normal use, the sample_spacing is the same as the sample size (in bytes) // But it can be larger to help do an interleaved read from samples // (see cassette_read_samples) -cassette_image::error cassette_put_samples(cassette_image *cassette, int channel, +cassette_image::error cassette_image::put_samples(int channel, double time_index, double sample_period, size_t sample_count, size_t sample_spacing, const void *samples, int waveform_flags) { - cassette_image::error err; - struct manipulation_ranges ranges; - size_t sample_index; + error err; + manipulation_ranges ranges; int32_t *dest_ptr; - int32_t dest_value; - int16_t word; - int32_t dword; - const uint8_t *source_ptr; - double d; - - if (!cassette) - return cassette_image::error::SUCCESS; if (sample_period == 0) - return cassette_image::error::SUCCESS; + return error::SUCCESS; - err = compute_manipulation_ranges(cassette, channel, time_index, sample_period, &ranges); - if (err != cassette_image::error::SUCCESS) + err = compute_manipulation_ranges(channel, time_index, sample_period, ranges); + if (err != error::SUCCESS) return err; - if (cassette->sample_count < ranges.sample_last+1) - cassette->sample_count = ranges.sample_last + 1; - cassette->flags |= CASSETTE_FLAG_DIRTY; + if (m_sample_count < ranges.sample_last+1) + m_sample_count = ranges.sample_last + 1; + m_flags |= CASSETTE_FLAG_DIRTY; if (LOG_PUT_SAMPLES) { @@ -521,65 +464,68 @@ cassette_image::error cassette_put_samples(cassette_image *cassette, int channel (int)ranges.sample_first, (int)ranges.sample_last); } - for (sample_index = ranges.sample_first; sample_index <= ranges.sample_last; sample_index++) + for (size_t sample_index = ranges.sample_first; sample_index <= ranges.sample_last; sample_index++) { /* figure out the source pointer */ - d = map_double(sample_count, ranges.sample_first, ranges.sample_last + 1, sample_index); - source_ptr = (const uint8_t*)samples; + double d = map_double(sample_count, ranges.sample_first, ranges.sample_last + 1, sample_index); + const uint8_t *source_ptr = (const uint8_t*)samples; source_ptr += ((size_t) d) * sample_spacing; /* compute the value that we are writing */ + int32_t dest_value; + int16_t word; + int32_t dword; switch(waveform_bytes_per_sample(waveform_flags)) { case 1: - if (waveform_flags & CASSETTE_WAVEFORM_UNSIGNED) + if (waveform_flags & WAVEFORM_UNSIGNED) dest_value = extrapolate8((int8_t)(*source_ptr - 128)); else dest_value = extrapolate8(*((int8_t *) source_ptr)); break; case 2: word = *((int16_t *) source_ptr); - if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP) + if (waveform_flags & WAVEFORM_ENDIAN_FLIP) word = swapendian_int16(word); dest_value = extrapolate16(word); break; case 4: dword = *((int32_t *) source_ptr); - if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP) + if (waveform_flags & WAVEFORM_ENDIAN_FLIP) dword = swapendian_int32(dword); dest_value = dword; break; default: - return cassette_image::error::INTERNAL; + return error::INTERNAL; } for (channel = ranges.channel_first; channel <= ranges.channel_last; channel++) { /* find the sample that we are putting */ - err = lookup_sample(cassette, channel, sample_index, &dest_ptr); - if (err != cassette_image::error::SUCCESS) + err = lookup_sample(channel, sample_index, dest_ptr); + if (err != error::SUCCESS) return err; *dest_ptr = dest_value; } } - return cassette_image::error::SUCCESS; + return error::SUCCESS; } -cassette_image::error cassette_get_sample(cassette_image *cassette, int channel, +cassette_image::error cassette_image::get_sample(int channel, double time_index, double sample_period, int32_t *sample) { - return cassette_get_samples(cassette, channel, time_index, - sample_period, 1, 0, sample, CASSETTE_WAVEFORM_32BIT); + return get_samples(channel, time_index, + sample_period, 1, 0, sample, WAVEFORM_32BIT); } -cassette_image::error cassette_put_sample(cassette_image *cassette, int channel, +cassette_image::error cassette_image::put_sample(int channel, double time_index, double sample_period, int32_t sample) { - return cassette_put_samples(cassette, channel, time_index, - sample_period, 1, 0, &sample, CASSETTE_WAVEFORM_32BIT); + return put_samples(channel, time_index, + sample_period, 1, 0, &sample, WAVEFORM_32BIT); } @@ -588,153 +534,112 @@ cassette_image::error cassette_put_sample(cassette_image *cassette, int channel, waveform accesses to/from the raw image *********************************************************************/ -cassette_image::error cassette_read_samples(cassette_image *cassette, int channels, double time_index, +cassette_image::error cassette_image::read_samples(int channels, double time_index, double sample_period, size_t sample_count, uint64_t offset, int waveform_flags) { - cassette_image::error err; - size_t chunk_sample_count; - size_t bytes_per_sample; - size_t sample_spacing; + error err; size_t samples_loaded = 0; - double chunk_time_index; - double chunk_sample_period; - int channel; uint8_t buffer[8192]; - bytes_per_sample = waveform_bytes_per_sample(waveform_flags); - sample_spacing = bytes_per_sample * channels; + size_t bytes_per_sample = waveform_bytes_per_sample(waveform_flags); + size_t sample_spacing = bytes_per_sample * channels; - while(samples_loaded < sample_count) + while (samples_loaded < sample_count) { - chunk_sample_count = std::min(sizeof(buffer) / sample_spacing, (sample_count - samples_loaded)); - chunk_sample_period = map_double(sample_period, 0, sample_count, chunk_sample_count); - chunk_time_index = time_index + map_double(sample_period, 0, sample_count, samples_loaded); + size_t chunk_sample_count = std::min(sizeof(buffer) / sample_spacing, (sample_count - samples_loaded)); + double chunk_sample_period = map_double(sample_period, 0, sample_count, chunk_sample_count); + double chunk_time_index = time_index + map_double(sample_period, 0, sample_count, samples_loaded); - cassette_image_read(cassette, buffer, offset, chunk_sample_count * sample_spacing); + image_read(buffer, offset, chunk_sample_count * sample_spacing); - for (channel = 0; channel < channels; channel++) + for (int channel = 0; channel < channels; channel++) { - err = cassette_put_samples(cassette, channel, chunk_time_index, chunk_sample_period, + err = put_samples(channel, chunk_time_index, chunk_sample_period, chunk_sample_count, sample_spacing, &buffer[channel * bytes_per_sample], waveform_flags); - if (err != cassette_image::error::SUCCESS) + if (err != error::SUCCESS) return err; } offset += chunk_sample_count * sample_spacing; samples_loaded += chunk_sample_count; } - return cassette_image::error::SUCCESS; + return error::SUCCESS; } -cassette_image::error cassette_write_samples(cassette_image *cassette, int channels, double time_index, +cassette_image::error cassette_image::write_samples(int channels, double time_index, double sample_period, size_t sample_count, uint64_t offset, int waveform_flags) { - cassette_image::error err; - size_t chunk_sample_count; - size_t bytes_per_sample; - size_t sample_spacing; + error err; size_t samples_saved = 0; - double chunk_time_index; - double chunk_sample_period; - int channel; uint8_t buffer[8192]; - bytes_per_sample = waveform_bytes_per_sample(waveform_flags); - sample_spacing = bytes_per_sample * channels; + size_t bytes_per_sample = waveform_bytes_per_sample(waveform_flags); + size_t sample_spacing = bytes_per_sample * channels; - while(samples_saved < sample_count) + while (samples_saved < sample_count) { - chunk_sample_count = std::min(sizeof(buffer) / sample_spacing, (sample_count - samples_saved)); - chunk_sample_period = map_double(sample_period, 0, sample_count, chunk_sample_count); - chunk_time_index = time_index + map_double(sample_period, 0, sample_count, samples_saved); + size_t chunk_sample_count = std::min(sizeof(buffer) / sample_spacing, (sample_count - samples_saved)); + double chunk_sample_period = map_double(sample_period, 0, sample_count, chunk_sample_count); + double chunk_time_index = time_index + map_double(sample_period, 0, sample_count, samples_saved); - for (channel = 0; channel < channels; channel++) + for (int channel = 0; channel < channels; channel++) { - err = cassette_get_samples(cassette, channel, chunk_time_index, chunk_sample_period, + err = get_samples(channel, chunk_time_index, chunk_sample_period, chunk_sample_count, sample_spacing, &buffer[channel * bytes_per_sample], waveform_flags); - if (err != cassette_image::error::SUCCESS) + if (err != error::SUCCESS) return err; } - cassette_image_write(cassette, buffer, offset, chunk_sample_count * sample_spacing); + image_write(buffer, offset, chunk_sample_count * sample_spacing); offset += chunk_sample_count * sample_spacing; samples_saved += chunk_sample_count; } - return cassette_image::error::SUCCESS; + return error::SUCCESS; } -/********************************************************************* - waveform accesses to/from the raw image -*********************************************************************/ - -static const int8_t *choose_wave(const struct CassetteModulation *modulation, size_t *wave_bytes_length) -{ - static const int8_t square_wave[] = { -128, 127 }; - static const int8_t sine_wave[] = { 0, 48, 89, 117, 127, 117, 89, 48, 0, -48, -89, -117, -127, -117, -89, -48 }; - - if (modulation->flags & CASSETTE_MODULATION_SINEWAVE) - { - *wave_bytes_length = ARRAY_LENGTH(sine_wave); - return sine_wave; - } - else - { - *wave_bytes_length = ARRAY_LENGTH(square_wave); - return square_wave; - } -} - - - -cassette_image::error cassette_modulation_identify(cassette_image *cassette, const struct CassetteModulation *modulation, - struct CassetteOptions *opts) +cassette_image::error cassette_image::modulation_identify(const Modulation &modulation, Options *opts) { size_t wave_bytes_length; - choose_wave(modulation, &wave_bytes_length); + choose_wave(modulation, wave_bytes_length); opts->bits_per_sample = 8; opts->channels = 1; - opts->sample_frequency = (uint32_t) (std::max(modulation->zero_frequency_high, modulation->one_frequency_high) * wave_bytes_length * 2); - return cassette_image::error::SUCCESS; + opts->sample_frequency = uint32_t(std::max(modulation.zero_frequency_high, modulation.one_frequency_high) * wave_bytes_length * 2); + return error::SUCCESS; } -cassette_image::error cassette_put_modulated_data(cassette_image *cassette, int channel, double time_index, - const void *data, size_t data_length, const struct CassetteModulation *modulation, +cassette_image::error cassette_image::put_modulated_data(int channel, double time_index, + const void *data, size_t data_length, const Modulation &modulation, double *time_displacement) { - cassette_image::error err; + error err; const uint8_t *data_bytes = (const uint8_t *)data; - const int8_t *wave_bytes; size_t wave_bytes_length; double total_displacement = 0.0; - double pulse_period; - double pulse_frequency; - uint8_t b; - int i; - wave_bytes = choose_wave(modulation, &wave_bytes_length); + const int8_t *wave_bytes = choose_wave(modulation, wave_bytes_length); - while(data_length--) + while (data_length--) { - b = *(data_bytes++); - for (i = 0; i < 8; i++) + uint8_t b = *(data_bytes++); + for (int i = 0; i < 8; i++) { - pulse_frequency = (b & (1 << i)) ? modulation->one_frequency_canonical : modulation->zero_frequency_canonical; - pulse_period = 1 / pulse_frequency; - err = cassette_put_samples(cassette, 0, time_index, pulse_period, wave_bytes_length, 1, wave_bytes, CASSETTE_WAVEFORM_8BIT); - if (err != cassette_image::error::SUCCESS) + double pulse_frequency = (b & (1 << i)) ? modulation.one_frequency_canonical : modulation.zero_frequency_canonical; + double pulse_period = 1 / pulse_frequency; + err = put_samples(0, time_index, pulse_period, wave_bytes_length, 1, wave_bytes, WAVEFORM_8BIT); + if (err != error::SUCCESS) goto done; time_index += pulse_period; total_displacement += pulse_period; } } - err = cassette_image::error::SUCCESS; + err = error::SUCCESS; done: if (time_displacement) @@ -744,18 +649,18 @@ done: -cassette_image::error cassette_put_modulated_filler(cassette_image *cassette, int channel, double time_index, - uint8_t filler, size_t filler_length, const struct CassetteModulation *modulation, +cassette_image::error cassette_image::put_modulated_filler(int channel, double time_index, + uint8_t filler, size_t filler_length, const Modulation &modulation, double *time_displacement) { - cassette_image::error err; + error err; double delta; double total_displacement = 0.0; - while(filler_length--) + while (filler_length--) { - err = cassette_put_modulated_data(cassette, channel, time_index, &filler, 1, modulation, &delta); - if (err != cassette_image::error::SUCCESS) + err = put_modulated_data(channel, time_index, &filler, 1, modulation, &delta); + if (err != error::SUCCESS) return err; total_displacement += delta; time_index += delta; @@ -763,24 +668,19 @@ cassette_image::error cassette_put_modulated_filler(cassette_image *cassette, in if (time_displacement) *time_displacement = total_displacement; - return cassette_image::error::SUCCESS; + return error::SUCCESS; } -cassette_image::error cassette_read_modulated_data(cassette_image *cassette, int channel, double time_index, - uint64_t offset, uint64_t length, const struct CassetteModulation *modulation, +cassette_image::error cassette_image::read_modulated_data(int channel, double time_index, + uint64_t offset, uint64_t length, const Modulation &modulation, double *time_displacement) { - cassette_image::error err; - uint8_t buffer_stack[1024]; uint8_t *buffer; - uint8_t *alloc_buffer = nullptr; - double delta; - double total_displacement = 0.0; - size_t this_length; + uint8_t buffer_stack[1024]; + std::unique_ptr<uint8_t []> alloc_buffer; size_t buffer_length; - if (length <= sizeof(buffer_stack)) { buffer = buffer_stack; @@ -789,23 +689,21 @@ cassette_image::error cassette_read_modulated_data(cassette_image *cassette, int else { buffer_length = std::min<uint64_t>(length, 100000); - alloc_buffer = (uint8_t*)malloc(buffer_length); - if (!alloc_buffer) - { - err = cassette_image::error::OUT_OF_MEMORY; - goto done; - } - buffer = alloc_buffer; + try { alloc_buffer = std::make_unique<uint8_t []>(buffer_length); } + catch (std::bad_alloc const &) { return error::OUT_OF_MEMORY; } + buffer = alloc_buffer.get(); } - while(length > 0) + double delta; + double total_displacement = 0.0; + while (length > 0) { - this_length = (std::min<uint64_t>)(length, buffer_length); - cassette_image_read(cassette, buffer, offset, this_length); + size_t this_length = std::min<uint64_t>(length, buffer_length); + image_read(buffer, offset, this_length); - err = cassette_put_modulated_data(cassette, channel, time_index, buffer, this_length, modulation, &delta); - if (err != cassette_image::error::SUCCESS) - goto done; + error err = put_modulated_data(channel, time_index, buffer, this_length, modulation, &delta); + if (err != error::SUCCESS) + return err; total_displacement += delta; time_index += delta; length -= this_length; @@ -813,38 +711,30 @@ cassette_image::error cassette_read_modulated_data(cassette_image *cassette, int if (time_displacement) *time_displacement = total_displacement; - err = cassette_image::error::SUCCESS; - -done: - if (alloc_buffer) - free(alloc_buffer); - return err; + return error::SUCCESS; } -cassette_image::error cassette_put_modulated_data_bit(cassette_image *cassette, int channel, double time_index, - uint8_t data, const struct CassetteModulation *modulation, +cassette_image::error cassette_image::put_modulated_data_bit(int channel, double time_index, + uint8_t data, const Modulation &modulation, double *time_displacement) { - cassette_image::error err; - const int8_t *wave_bytes; + error err; size_t wave_bytes_length; double total_displacement = 0.0; - double pulse_period; - double pulse_frequency; - wave_bytes = choose_wave(modulation, &wave_bytes_length); + const int8_t *wave_bytes = choose_wave(modulation, wave_bytes_length); - pulse_frequency = (data) ? modulation->one_frequency_canonical : modulation->zero_frequency_canonical; - pulse_period = 1 / pulse_frequency; - err = cassette_put_samples(cassette, 0, time_index, pulse_period, wave_bytes_length, 1, wave_bytes, CASSETTE_WAVEFORM_8BIT); - if (err != cassette_image::error::SUCCESS) + double pulse_frequency = (data) ? modulation.one_frequency_canonical : modulation.zero_frequency_canonical; + double pulse_period = 1 / pulse_frequency; + err = put_samples(0, time_index, pulse_period, wave_bytes_length, 1, wave_bytes, WAVEFORM_8BIT); + if (err != error::SUCCESS) goto done; time_index += pulse_period; total_displacement += pulse_period; - err = cassette_image::error::SUCCESS; + err = error::SUCCESS; done: if (time_displacement) @@ -858,67 +748,63 @@ done: waveform accesses to/from the raw image *********************************************************************/ -cassette_image::error cassette_legacy_identify(cassette_image *cassette, struct CassetteOptions *opts, - const struct CassetteLegacyWaveFiller *legacy_args) +cassette_image::error cassette_image::legacy_identify(Options *opts, + const LegacyWaveFiller *legacy_args) { opts->channels = 1; opts->bits_per_sample = 16; opts->sample_frequency = legacy_args->sample_frequency; - return cassette_image::error::SUCCESS; + return error::SUCCESS; } -cassette_image::error cassette_legacy_construct(cassette_image *cassette, - const struct CassetteLegacyWaveFiller *legacy_args) +cassette_image::error cassette_image::legacy_construct(const LegacyWaveFiller *legacy_args) { - cassette_image::error err; + error err; int length; int sample_count; std::vector<uint8_t> bytes; - std::vector<uint8_t> chunk; std::vector<int16_t> samples; int pos = 0; uint64_t offset = 0; - uint64_t size; - struct CassetteLegacyWaveFiller args; /* sanity check the args */ assert(legacy_args->header_samples >= -1); assert(legacy_args->trailer_samples >= 0); assert(legacy_args->fill_wave); - size = cassette_image_size(cassette); + uint64_t size = image_size(); /* normalize the args */ - args = *legacy_args; + LegacyWaveFiller args = *legacy_args; if (args.chunk_size == 0) args.chunk_size = 1; else if (args.chunk_size < 0) - args.chunk_size = cassette_image_size(cassette); + args.chunk_size = image_size(); if (args.sample_frequency == 0) args.sample_frequency = 11025; /* allocate a buffer for the binary data */ - chunk.resize(args.chunk_size); + std::vector<uint8_t> chunk(args.chunk_size); /* determine number of samples */ if (args.chunk_sample_calc != nullptr) { if (size > 0x7FFFFFFF) { - err = cassette_image::error::OUT_OF_MEMORY; + err = error::OUT_OF_MEMORY; goto done; } bytes.resize(size); - cassette_image_read(cassette, &bytes[0], 0, size); + image_read(&bytes[0], 0, size); sample_count = args.chunk_sample_calc(&bytes[0], (int)size); // chunk_sample_calc functions report errors by returning negative numbers if (sample_count < 0) { - err = cassette_image::error::INVALID_IMAGE; + err = error::INVALID_IMAGE; goto done; } @@ -941,22 +827,22 @@ cassette_image::error cassette_legacy_construct(cassette_image *cassette, length = args.fill_wave(&samples[pos], sample_count - pos, CODE_HEADER); if (length < 0) { - err = cassette_image::error::INVALID_IMAGE; + err = error::INVALID_IMAGE; goto done; } pos += length; } /* convert the file data to samples */ - while((pos < sample_count) && (offset < size)) + while ((pos < sample_count) && (offset < size)) { - cassette_image_read(cassette, &chunk[0], offset, args.chunk_size); + image_read(&chunk[0], offset, args.chunk_size); offset += args.chunk_size; length = args.fill_wave(&samples[pos], sample_count - pos, &chunk[0]); if (length < 0) { - err = cassette_image::error::INVALID_IMAGE; + err = error::INVALID_IMAGE; goto done; } pos += length; @@ -970,23 +856,23 @@ cassette_image::error cassette_legacy_construct(cassette_image *cassette, length = args.fill_wave(&samples[pos], sample_count - pos, CODE_TRAILER); if (length < 0) { - err = cassette_image::error::INVALID_IMAGE; + err = error::INVALID_IMAGE; goto done; } pos += length; } /* specify the wave */ - err = cassette_put_samples(cassette, 0, 0.0, ((double) pos) / args.sample_frequency, - pos, 2, &samples[0], CASSETTE_WAVEFORM_16BIT); - if (err != cassette_image::error::SUCCESS) + err = put_samples(0, 0.0, ((double) pos) / args.sample_frequency, + pos, 2, &samples[0], WAVEFORM_16BIT); + if (err != error::SUCCESS) goto done; /* success! */ - err = cassette_image::error::SUCCESS; + err = error::SUCCESS; #if DUMP_CASSETTES - cassette_dump(cassette, "C:\\TEMP\\CASDUMP.WAV"); + dump("C:\\TEMP\\CASDUMP.WAV"); #endif done: @@ -1001,26 +887,22 @@ done: A debugging call to dump a cassette image to a disk based wave file *********************************************************************/ -void cassette_dump(cassette_image *image, const char *filename) +void cassette_image::dump(const char *filename) { - FILE *f; - struct io_generic saved_io; - const struct CassetteFormat *saved_format; - - f = fopen(filename, "wb"); + FILE *f = fopen(filename, "wb"); if (!f) return; - memcpy(&saved_io, &image->io, sizeof(saved_io)); - saved_format = image->format; + io_generic saved_io = m_io; + const Format *saved_format = m_format; - image->io.file = f; - image->io.procs = &stdio_ioprocs_noclose; - image->format = &wavfile_format; - cassette_perform_save(image); + m_io.file = f; + m_io.procs = &stdio_ioprocs_noclose; + m_format = &wavfile_format; + perform_save(); - memcpy(&image->io, &saved_io, sizeof(saved_io)); - image->format = saved_format; + m_io = saved_io; + m_format = saved_format; fclose(f); } |