diff options
author | 2011-01-05 15:59:06 +0000 | |
---|---|---|
committer | 2011-01-05 15:59:06 +0000 | |
commit | 12a5370d85b9176b7b8849354432d40f8f8531b0 (patch) | |
tree | 674c3441a416e1e37e75b438f989acbb3d80907e /src/emu/imagedev/cassimg.c | |
parent | 56b43ee6871a3a4b01e2d23a5bb3e6e0b0c8f32b (diff) |
Moved image devices implementation and related UI functions to emu section from MESS [Miodrag Milanovic]
Diffstat (limited to 'src/emu/imagedev/cassimg.c')
-rw-r--r-- | src/emu/imagedev/cassimg.c | 1059 |
1 files changed, 1059 insertions, 0 deletions
diff --git a/src/emu/imagedev/cassimg.c b/src/emu/imagedev/cassimg.c new file mode 100644 index 00000000000..f2e3c9b2eb7 --- /dev/null +++ b/src/emu/imagedev/cassimg.c @@ -0,0 +1,1059 @@ +/********************************************************************* + + cassimg.c + + Cassette tape image abstraction code + +*********************************************************************/ + + +#include <string.h> + +#include "emu.h" +#include "imageutl.h" +#include "pool.h" +#include "cassimg.h" + + +/* debugging parameters */ +#define LOG_PUT_SAMPLES 0 +#define DUMP_CASSETTES 0 + +#define SAMPLES_PER_BLOCK 0x40000 +#define CASSETTE_FLAG_DIRTY 0x10000 + + +CASSETTE_FORMATLIST_START(cassette_default_formats) +CASSETTE_FORMATLIST_END + + + +/********************************************************************* + helper code +*********************************************************************/ + +static double map_double(double d, UINT64 low, UINT64 high, UINT64 value) +{ +#if defined(_MSC_VER) && (_MSC_VER <= 1200) + /* casting unsigned __int64 to double is not supported on VC6 or before */ + return d * (INT64)(value - low) / (INT64)(high - low); +#else + return d * (value - low) / (high - low); +#endif +} + + + +static size_t waveform_bytes_per_sample(int waveform_flags) +{ + return (size_t) (1 << ((waveform_flags & 0x06) / 2)); +} + + + +/********************************************************************* + extrapolation and interpolation +*********************************************************************/ + +static INT32 extrapolate8(INT8 value) +{ + return ((INT32) value) << 24; +} + +static INT32 extrapolate16(INT16 value) +{ + return ((INT32) value) << 16; +} + +static INT8 interpolate8(INT32 value) +{ + return (INT8) (value >> 24); +} + +static INT16 interpolate16(INT32 value) +{ + return (INT16) (value >> 16); +} + + + +/********************************************************************* + initialization and termination +*********************************************************************/ + +static cassette_image *cassette_init(running_machine *machine,const struct CassetteFormat *format, void *file, const struct io_procs *procs, int flags) +{ + cassette_image *cassette; + + cassette = (cassette_image *)malloc(sizeof(struct _cassette_image)); + if (!cassette) + return NULL; + + memset(cassette, 0, sizeof(*cassette)); + cassette->format = format; + cassette->io.file = file; + cassette->io.procs = procs; + cassette->flags = flags; + cassette->pool = pool_alloc_lib(NULL); + cassette->machine = machine; + return cassette; +} + + + +static void cassette_finishinit(casserr_t err, cassette_image *cassette, cassette_image **outcassette) +{ + if (cassette && (err || !outcassette)) + { + cassette_close(cassette); + cassette = NULL; + } + if (outcassette) + *outcassette = cassette; +} + + + +static int good_format(const struct CassetteFormat *format, const char *extension, int flags) +{ + if (extension && !image_find_extension(format->extensions, extension)) + return FALSE; + if (((flags & CASSETTE_FLAG_READONLY) == 0) && !format->save) + return FALSE; + return TRUE; +} + + + +casserr_t cassette_open_choices(running_machine *machine,void *file, const struct io_procs *procs, const char *extension, + const struct CassetteFormat *const *formats, int flags, cassette_image **outcassette) +{ + casserr_t err; + cassette_image *cassette; + const struct CassetteFormat *format; + struct CassetteOptions opts = {0, }; + int i; + + /* if not specified, use the dummy arguments */ + if (!formats) + formats = cassette_default_formats; + + /* create the cassette object */ + cassette = cassette_init(machine, NULL, file, procs, flags); + if (!cassette) + { + err = CASSETTE_ERROR_OUTOFMEMORY; + goto done; + } + + /* identify the image */ + format = NULL; + for (i = 0; !format && formats[i]; i++) + { + if (good_format(formats[i], extension, flags)) + { + format = formats[i]; + memset(&opts, 0, sizeof(opts)); + err = format->identify(cassette, &opts); + if (err == CASSETTE_ERROR_INVALIDIMAGE) + format = NULL; + else if (err) + goto done; + } + } + + /* have we found a proper format */ + if (!format) + { + err = CASSETTE_ERROR_INVALIDIMAGE; + goto done; + } + 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) + goto done; + + /* success */ + cassette->flags &= ~CASSETTE_FLAG_DIRTY; + err = CASSETTE_ERROR_SUCCESS; + +done: + cassette_finishinit(err, cassette, outcassette); + return err; +} + + + +casserr_t cassette_open(running_machine *machine, void *file, const struct io_procs *procs, + const struct CassetteFormat *format, int flags, cassette_image **outcassette) +{ + const struct CassetteFormat *formats[2]; + formats[0] = format; + formats[1] = NULL; + return cassette_open_choices(machine, file, procs, NULL, formats, flags, outcassette); +} + + + +casserr_t cassette_create(running_machine *machine, void *file, const struct io_procs *procs, const struct CassetteFormat *format, + const struct CassetteOptions *opts, int flags, cassette_image **outcassette) +{ + casserr_t err; + cassette_image *cassette; + static const struct CassetteOptions default_options = { 1, 16, 44100 }; + + /* cannot create to a read only image */ + if (flags & CASSETTE_FLAG_READONLY) + return CASSETTE_ERROR_INVALIDIMAGE; + + /* is this a good format? */ + if (!good_format(format, NULL, flags)) + return CASSETTE_ERROR_INVALIDIMAGE; + + /* normalize arguments */ + if (!opts) + opts = &default_options; + + /* create the cassette object */ + cassette = cassette_init(machine, format, file, procs, flags); + if (!cassette) + { + err = CASSETTE_ERROR_OUTOFMEMORY; + goto done; + } + + /* read the options */ + cassette->channels = opts->channels; + cassette->sample_frequency = opts->sample_frequency; + + err = CASSETTE_ERROR_SUCCESS; + +done: + cassette_finishinit(err, cassette, outcassette); + return err; +} + + + +static casserr_t cassette_perform_save(cassette_image *cassette) +{ + struct CassetteInfo info; + cassette_get_info(cassette, &info); + return cassette->format->save(cassette, &info); +} + + + +casserr_t cassette_save(cassette_image *cassette) +{ + casserr_t err; + + if (!cassette->format || !cassette->format->save) + return CASSETTE_ERROR_UNSUPPORTED; + + err = cassette_perform_save(cassette); + if (err) + return err; + + cassette->flags &= ~CASSETTE_FLAG_DIRTY; + return CASSETTE_ERROR_SUCCESS; +} + + + +void cassette_get_info(cassette_image *cassette, struct CassetteInfo *info) +{ + 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; +} + + + +void cassette_close(cassette_image *cassette) +{ + if (cassette) + { + if ((cassette->flags & CASSETTE_FLAG_DIRTY) && (cassette->flags & CASSETTE_FLAG_SAVEONEXIT)) + cassette_save(cassette); + pool_free_lib(cassette->pool); + free(cassette); + } +} + + + +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; +} + + + +/********************************************************************* + calls for accessing the raw cassette image +*********************************************************************/ + +void cassette_image_read(cassette_image *cassette, void *buffer, UINT64 offset, size_t length) +{ + io_generic_read(&cassette->io, buffer, offset, length); +} + + + +void cassette_image_write(cassette_image *cassette, const void *buffer, UINT64 offset, size_t length) +{ + io_generic_write(&cassette->io, buffer, offset, length); +} + + + +UINT64 cassette_image_size(cassette_image *cassette) +{ + return io_generic_size(&cassette->io); +} + + + +/********************************************************************* + waveform accesses +*********************************************************************/ + +struct manipulation_ranges +{ + 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; +} + + + +static casserr_t compute_manipulation_ranges(cassette_image *cassette, int channel, + double time_index, double sample_period, struct manipulation_ranges *ranges) +{ + 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_ERROR_SUCCESS; +} + + + +static casserr_t lookup_sample(cassette_image *cassette, int channel, size_t sample, int allocate, INT32 **ptr) +{ + size_t sample_block; + size_t sample_index; + size_t sample_size; + size_t new_block_count; + size_t new_block_sample_count; + INT32 *new_block; + struct sample_block *new_blocks; + struct sample_block *block; + + *ptr = NULL; + sample_block = (sample / SAMPLES_PER_BLOCK) * cassette->channels + channel; + sample_index = sample % SAMPLES_PER_BLOCK; + sample_size = sizeof(block->block[0]); + + /* is this block beyond the edge of our waveform? */ + if (sample_block >= cassette->block_count) + { + if (!allocate) + return CASSETTE_ERROR_SUCCESS; + + /* allocate new blocks */ + new_block_count = sample_block + 1; + new_blocks = (struct sample_block *)pool_realloc_lib(cassette->pool, cassette->blocks, new_block_count * sizeof(cassette->blocks[0])); + if (!new_blocks) + return CASSETTE_ERROR_OUTOFMEMORY; + + cassette->blocks = new_blocks; + memset(&cassette->blocks[cassette->block_count], 0, (new_block_count - cassette->block_count) * sizeof(cassette->blocks[0])); + cassette->block_count = new_block_count; + } + + block = &cassette->blocks[sample_block]; + + /* is this sample access off the current block? */ + if (sample_index >= block->sample_count) + { + if (!allocate) + return CASSETTE_ERROR_SUCCESS; + + new_block_sample_count = SAMPLES_PER_BLOCK; + + new_block = (INT32*)pool_realloc_lib(cassette->pool, block->block, new_block_sample_count * sample_size); + if (!new_block) + return CASSETTE_ERROR_OUTOFMEMORY; + + block->block = new_block; + memset(&block->block[block->sample_count], 0, (new_block_sample_count - block->sample_count) * sample_size); + block->sample_count = new_block_sample_count; + } + + *ptr = &block->block[sample_index]; + return CASSETTE_ERROR_SUCCESS; +} + + + +/********************************************************************* + waveform accesses +*********************************************************************/ + +casserr_t cassette_get_samples(cassette_image *cassette, int channel, + double time_index, double sample_period, size_t sample_count, size_t sample_bytes, + void *samples, int waveform_flags) +{ + casserr_t err; + struct manipulation_ranges ranges; + size_t sample_index; + size_t cassette_sample_index; + UINT8 *dest_ptr; + const INT32 *source_ptr; + double d; + INT16 word; + INT32 dword; + INT64 sum; + + assert(cassette); + + err = compute_manipulation_ranges(cassette, channel, time_index, sample_period, &ranges); + if (err) + return err; + + for (sample_index = 0; sample_index < sample_count; sample_index++) + { + 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, TRUE, (INT32 **) &source_ptr); + if (err) + return err; + + sum += *source_ptr; + } + + /* average out the samples */ + sum /= (ranges.channel_last + 1 - ranges.channel_first); + + /* and write out the result */ + dest_ptr = (UINT8*)samples; + dest_ptr += waveform_bytes_per_sample(waveform_flags) * sample_index; + switch(waveform_bytes_per_sample(waveform_flags)) + { + case 1: + *((INT8 *) dest_ptr) = interpolate8(sum); + break; + case 2: + word = interpolate16(sum); + if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP) + word = FLIPENDIAN_INT16(word); + *((INT16 *) dest_ptr) = word; + break; + case 4: + dword = sum; + if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP) + dword = FLIPENDIAN_INT32(dword); + *((INT32 *) dest_ptr) = dword; + break; + } + } + return CASSETTE_ERROR_SUCCESS; +} + + + +casserr_t cassette_put_samples(cassette_image *cassette, int channel, + double time_index, double sample_period, size_t sample_count, size_t sample_bytes, + const void *samples, int waveform_flags) +{ + casserr_t err; + struct manipulation_ranges ranges; + size_t sample_index; + INT32 *dest_ptr; + INT32 dest_value; + INT16 word; + INT32 dword; + const UINT8 *source_ptr; + double d; + + if (!cassette) + return CASSETTE_ERROR_SUCCESS; + + if (sample_period == 0) + return CASSETTE_ERROR_SUCCESS; + + err = compute_manipulation_ranges(cassette, channel, time_index, sample_period, &ranges); + if (err) + return err; + + if (cassette->sample_count < ranges.sample_last+1) + cassette->sample_count = ranges.sample_last + 1; + cassette->flags |= CASSETTE_FLAG_DIRTY; + + if (LOG_PUT_SAMPLES) + { + logerror("cassette_put_samples(): Putting samples TIME=[%2.6g..%2.6g] INDEX=[%i..%i]\n", + time_index, time_index + sample_period, + (int)ranges.sample_first, (int)ranges.sample_last); + } + + for (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*)samples; + source_ptr += ((size_t) d) * sample_bytes; + + /* compute the value that we are writing */ + switch(waveform_bytes_per_sample(waveform_flags)) { + case 1: + dest_value = extrapolate8(*((INT8 *) source_ptr)); + break; + case 2: + word = *((INT16 *) source_ptr); + if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP) + word = FLIPENDIAN_INT16(word); + dest_value = extrapolate16(word); + break; + case 4: + dword = *((INT32 *) source_ptr); + if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP) + dword = FLIPENDIAN_INT32(dword); + dest_value = dword; + break; + default: + return CASSETTE_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, TRUE, &dest_ptr); + if (err) + return err; + *dest_ptr = dest_value; + } + } + return CASSETTE_ERROR_SUCCESS; +} + + + +casserr_t cassette_get_sample(cassette_image *cassette, int channel, + double time_index, double sample_period, INT32 *sample) +{ + return cassette_get_samples(cassette, channel, time_index, + sample_period, 1, 0, sample, CASSETTE_WAVEFORM_32BIT); +} + + + +casserr_t cassette_put_sample(cassette_image *cassette, int channel, + double time_index, double sample_period, INT32 sample) +{ + return cassette_put_samples(cassette, channel, time_index, + sample_period, 1, 0, &sample, CASSETTE_WAVEFORM_32BIT); +} + + + +/********************************************************************* + waveform accesses to/from the raw image +*********************************************************************/ + +casserr_t cassette_read_samples(cassette_image *cassette, int channels, double time_index, + double sample_period, size_t sample_count, UINT64 offset, int waveform_flags) +{ + casserr_t err; + size_t chunk_sample_count; + size_t bytes_per_sample; + size_t sample_bytes; + size_t samples_loaded = 0; + double chunk_time_index; + double chunk_sample_period; + int channel; + UINT8 buffer[8192]; + + bytes_per_sample = waveform_bytes_per_sample(waveform_flags); + sample_bytes = bytes_per_sample * channels; + + while(samples_loaded < sample_count) + { + chunk_sample_count = MIN(sizeof(buffer) / sample_bytes, (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); + + cassette_image_read(cassette, buffer, offset, chunk_sample_count * sample_bytes); + + for (channel = 0; channel < channels; channel++) + { + err = cassette_put_samples(cassette, channel, chunk_time_index, chunk_sample_period, + chunk_sample_count, sample_bytes, &buffer[channel * bytes_per_sample], waveform_flags); + if (err) + return err; + } + + offset += chunk_sample_count * sample_bytes; + samples_loaded += chunk_sample_count; + } + return CASSETTE_ERROR_SUCCESS; +} + + + +casserr_t cassette_write_samples(cassette_image *cassette, int channels, double time_index, + double sample_period, size_t sample_count, UINT64 offset, int waveform_flags) +{ + casserr_t err; + size_t chunk_sample_count; + size_t bytes_per_sample; + size_t sample_bytes; + size_t samples_saved = 0; + double chunk_time_index; + double chunk_sample_period; + int channel; + UINT8 buffer[8192]; + + bytes_per_sample = waveform_bytes_per_sample(waveform_flags); + sample_bytes = bytes_per_sample * channels; + + while(samples_saved < sample_count) + { + chunk_sample_count = MIN(sizeof(buffer) / sample_bytes, (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); + + for (channel = 0; channel < channels; channel++) + { + err = cassette_get_samples(cassette, channel, chunk_time_index, chunk_sample_period, + chunk_sample_count, sample_bytes, &buffer[channel * bytes_per_sample], waveform_flags); + if (err) + return err; + } + + cassette_image_write(cassette, buffer, offset, chunk_sample_count * sample_bytes); + + offset += chunk_sample_count * sample_bytes; + samples_saved += chunk_sample_count; + } + return CASSETTE_ERROR_SUCCESS; +} + + + +/********************************************************************* + waveform accesses to/from the raw image +*********************************************************************/ + +static const INT8 *choose_wave(const struct CassetteModulation *modulation, size_t *wave_bytes_length) +{ + static const INT8 square_wave[] = { -128, 127 }; + static const INT8 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; + } +} + + + +casserr_t cassette_modulation_identify(cassette_image *cassette, const struct CassetteModulation *modulation, + struct CassetteOptions *opts) +{ + size_t wave_bytes_length; + choose_wave(modulation, &wave_bytes_length); + opts->bits_per_sample = 8; + opts->channels = 1; + opts->sample_frequency = (UINT32) (MAX(modulation->zero_frequency_high, modulation->one_frequency_high) * wave_bytes_length * 2); + return CASSETTE_ERROR_SUCCESS; +} + + + +casserr_t cassette_put_modulated_data(cassette_image *cassette, int channel, double time_index, + const void *data, size_t data_length, const struct CassetteModulation *modulation, + double *time_displacement) +{ + casserr_t err; + const UINT8 *data_bytes = (const UINT8 *)data; + const INT8 *wave_bytes; + size_t wave_bytes_length; + double total_displacement = 0.0; + double pulse_period; + double pulse_frequency; + UINT8 b; + int i; + + wave_bytes = choose_wave(modulation, &wave_bytes_length); + + while(data_length--) + { + b = *(data_bytes++); + for (i = 0; i < 8; i++) + { + pulse_frequency = (b & (1 << i)) ? modulation->one_frequency_cannonical : modulation->zero_frequency_cannonical; + 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) + goto done; + time_index += pulse_period; + total_displacement += pulse_period; + } + } + err = CASSETTE_ERROR_SUCCESS; + +done: + if (time_displacement) + *time_displacement = total_displacement; + return err; +} + + + +casserr_t cassette_put_modulated_filler(cassette_image *cassette, int channel, double time_index, + UINT8 filler, size_t filler_length, const struct CassetteModulation *modulation, + double *time_displacement) +{ + casserr_t err; + double delta; + double total_displacement = 0.0; + + while(filler_length--) + { + err = cassette_put_modulated_data(cassette, channel, time_index, &filler, 1, modulation, &delta); + if (err) + return err; + total_displacement += delta; + time_index += delta; + } + + if (time_displacement) + *time_displacement = total_displacement; + return CASSETTE_ERROR_SUCCESS; +} + + + +casserr_t cassette_read_modulated_data(cassette_image *cassette, int channel, double time_index, + UINT64 offset, UINT64 length, const struct CassetteModulation *modulation, + double *time_displacement) +{ + casserr_t err; + UINT8 buffer_stack[1024]; + UINT8 *buffer; + UINT8 *alloc_buffer = NULL; + double delta; + double total_displacement = 0.0; + size_t this_length; + size_t buffer_length; + + if (length <= sizeof(buffer_stack)) + { + buffer = buffer_stack; + buffer_length = sizeof(buffer_stack); + } + else + { + buffer_length = MIN(length, 100000); + alloc_buffer = (UINT8*)malloc(buffer_length); + if (!alloc_buffer) + { + err = CASSETTE_ERROR_OUTOFMEMORY; + goto done; + } + buffer = alloc_buffer; + } + + while(length > 0) + { + this_length = (size_t) MIN(length, buffer_length); + cassette_image_read(cassette, buffer, offset, this_length); + + err = cassette_put_modulated_data(cassette, channel, time_index, buffer, this_length, modulation, &delta); + if (err) + goto done; + total_displacement += delta; + time_index += delta; + length -= this_length; + } + + if (time_displacement) + *time_displacement = total_displacement; + err = CASSETTE_ERROR_SUCCESS; + +done: + if (alloc_buffer) + free(alloc_buffer); + return err; +} + + + +casserr_t cassette_put_modulated_data_bit(cassette_image *cassette, int channel, double time_index, + UINT8 data, const struct CassetteModulation *modulation, + double *time_displacement) +{ + casserr_t err; + const INT8 *wave_bytes; + size_t wave_bytes_length; + double total_displacement = 0.0; + double pulse_period; + double pulse_frequency; + + wave_bytes = choose_wave(modulation, &wave_bytes_length); + + pulse_frequency = (data) ? modulation->one_frequency_cannonical : modulation->zero_frequency_cannonical; + 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) + goto done; + time_index += pulse_period; + total_displacement += pulse_period; + + err = CASSETTE_ERROR_SUCCESS; + +done: + if (time_displacement) + *time_displacement = total_displacement; + return err; +} + + + +/********************************************************************* + waveform accesses to/from the raw image +*********************************************************************/ + +casserr_t cassette_legacy_identify(cassette_image *cassette, struct CassetteOptions *opts, + const struct CassetteLegacyWaveFiller *legacy_args) +{ + opts->channels = 1; + opts->bits_per_sample = 16; + opts->sample_frequency = legacy_args->sample_frequency; + return CASSETTE_ERROR_SUCCESS; +} + + + +casserr_t cassette_legacy_construct(cassette_image *cassette, + const struct CassetteLegacyWaveFiller *legacy_args) +{ + casserr_t err; + int length; + int sample_count; + void *bytes = NULL; + void *chunk = NULL; + INT16 *samples = NULL; + int pos = 0; + UINT64 offset = 0; + UINT64 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); + + /* normalize the args */ + 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); + if (args.sample_frequency == 0) + args.sample_frequency = 11025; + + /* allocate a buffer for the binary data */ + chunk = malloc(args.chunk_size); + if (!chunk) + { + err = CASSETTE_ERROR_OUTOFMEMORY; + goto done; + } + + /* determine number of samples */ + if (args.chunk_sample_calc) + { + if (size > 0x7FFFFFFF) + { + err = CASSETTE_ERROR_OUTOFMEMORY; + goto done; + } + + bytes = malloc(size); + if (!bytes) + { + err = CASSETTE_ERROR_OUTOFMEMORY; + goto done; + } + cassette_image_read(cassette, bytes, 0, size); + sample_count = args.chunk_sample_calc((const UINT8*)bytes, (int) size); + + if (args.header_samples < 0) + args.header_samples = sample_count; + } + else + { + sample_count = ((size + args.chunk_size - 1) / args.chunk_size) + * args.chunk_samples; + } + sample_count += args.header_samples + args.trailer_samples; + + /* allocate a buffer for the completed samples */ + samples = (INT16*)malloc(sample_count * sizeof(*samples)); + if (!samples) + { + err = CASSETTE_ERROR_OUTOFMEMORY; + goto done; + } + + /* if there has to be a header */ + if (args.header_samples > 0) + { + length = args.fill_wave(samples + pos, sample_count - pos, CODE_HEADER); + if (length < 0) + { + err = CASSETTE_ERROR_INVALIDIMAGE; + goto done; + } + pos += length; + } + + /* convert the file data to samples */ + while((pos < sample_count) && (offset < size)) + { + cassette_image_read(cassette, chunk, offset, args.chunk_size); + offset += args.chunk_size; + + length = args.fill_wave(samples + pos, sample_count - pos, (UINT8*)chunk); + if (length < 0) + { + err = CASSETTE_ERROR_INVALIDIMAGE; + goto done; + } + pos += length; + if (length == 0) + break; + } + + /* if there has to be a trailer */ + if (args.trailer_samples > 0) + { + length = args.fill_wave(samples + pos, sample_count - pos, CODE_TRAILER); + if (length < 0) + { + err = CASSETTE_ERROR_INVALIDIMAGE; + goto done; + } + pos += length; + } + + /* specify the wave */ + err = cassette_put_samples(cassette, 0, 0.0, ((double) pos) / args.sample_frequency, + pos, 2, samples, CASSETTE_WAVEFORM_16BIT); + if (err) + goto done; + + /* success! */ + err = CASSETTE_ERROR_SUCCESS; + +#if DUMP_CASSETTES + cassette_dump(cassette, "C:\\TEMP\\CASDUMP.WAV"); +#endif + +done: + if (samples) + free(samples); + if (chunk) + free(chunk); + if (bytes) + free(bytes); + return err; +} + + + +/********************************************************************* + cassette_dump + + A debugging call to dump a casette image to a disk based wave file +*********************************************************************/ + +void cassette_dump(cassette_image *image, const char *filename) +{ + FILE *f; + struct io_generic saved_io; + const struct CassetteFormat *saved_format; + + f = fopen(filename, "wb"); + if (!f) + return; + + memcpy(&saved_io, &image->io, sizeof(saved_io)); + saved_format = image->format; + + image->io.file = f; + image->io.procs = &stdio_ioprocs_noclose; + image->format = &wavfile_format; + cassette_perform_save(image); + + memcpy(&image->io, &saved_io, sizeof(saved_io)); + image->format = saved_format; + + fclose(f); +} + + |