summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/formats/cassimg.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/formats/cassimg.cpp')
-rw-r--r--src/lib/formats/cassimg.cpp848
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);
}