1 // license:BSD-3-Clause
2 // copyright-holders:Nathan Woods
3 /*********************************************************************
4
5 cassimg.cpp
6
7 Cassette tape image abstraction code
8
9 *********************************************************************/
10
11 #include "cassimg.h"
12 #include "imageutl.h"
13
14 #include <algorithm>
15 #include <cassert>
16 #include <cstring>
17
18
19 /* debugging parameters */
20 #define LOG_PUT_SAMPLES 0
21 #define DUMP_CASSETTES 0
22
23 #define SAMPLES_PER_BLOCK 0x40000
24 #define CASSETTE_FLAG_DIRTY 0x10000
25
26
27 CASSETTE_FORMATLIST_START(cassette_default_formats)
28 CASSETTE_FORMATLIST_END
29
30
31 namespace {
32
33 /*********************************************************************
34 helper code
35 *********************************************************************/
36
map_double(double d,uint64_t low,uint64_t high,uint64_t value)37 constexpr double map_double(double d, uint64_t low, uint64_t high, uint64_t value)
38 {
39 return d * (value - low) / (high - low);
40 }
41
42
43
waveform_bytes_per_sample(int waveform_flags)44 constexpr size_t waveform_bytes_per_sample(int waveform_flags)
45 {
46 return size_t(1 << ((waveform_flags & 0x06) / 2));
47 }
48
49
50
51 /*********************************************************************
52 extrapolation and interpolation
53 *********************************************************************/
54
extrapolate8(int8_t value)55 constexpr int32_t extrapolate8(int8_t value)
56 {
57 return int32_t(value) << 24;
58 }
59
extrapolate16(int16_t value)60 constexpr int32_t extrapolate16(int16_t value)
61 {
62 return int32_t(value) << 16;
63 }
64
interpolate8(int32_t value)65 constexpr int8_t interpolate8(int32_t value)
66 {
67 return int8_t(value >> 24);
68 }
69
interpolate16(int32_t value)70 constexpr int16_t interpolate16(int32_t value)
71 {
72 return int16_t(value >> 16);
73 }
74
75
76
my_round(double d)77 constexpr size_t my_round(double d)
78 {
79 return size_t(d + 0.5);
80 }
81
82
83
84 /*********************************************************************
85 waveform accesses to/from the raw image
86 *********************************************************************/
87
choose_wave(const cassette_image::Modulation & modulation,size_t & wave_bytes_length)88 const int8_t *choose_wave(const cassette_image::Modulation &modulation, size_t &wave_bytes_length)
89 {
90 static const int8_t square_wave[] = { -128, 127 };
91 static const int8_t sine_wave[] = { 0, 48, 89, 117, 127, 117, 89, 48, 0, -48, -89, -117, -127, -117, -89, -48 };
92
93 if (modulation.flags & cassette_image::MODULATION_SINEWAVE)
94 {
95 wave_bytes_length = ARRAY_LENGTH(sine_wave);
96 return sine_wave;
97 }
98 else
99 {
100 wave_bytes_length = ARRAY_LENGTH(square_wave);
101 return square_wave;
102 }
103 }
104
105 } // anonymous namespace
106
107
108
109 /*********************************************************************
110 initialization and termination
111 *********************************************************************/
112
try_identify_format(const Format & format,const std::string & extension,int flags,Options & opts)113 cassette_image::error cassette_image::try_identify_format(const Format &format, const std::string &extension, int flags, Options &opts)
114 {
115 // is this the right extension?
116 if (!extension.empty() && !image_find_extension(format.extensions, extension.c_str()))
117 return error::INVALID_IMAGE;
118
119 // invoke format->identify
120 opts = Options();
121 error err = format.identify(this, &opts);
122 if (err != error::SUCCESS)
123 return err;
124
125 // is this a read only format, but the cassette was not opened read only?
126 if (((flags & FLAG_READONLY) == 0) && (format.save == nullptr))
127 return error::READ_WRITE_UNSUPPORTED;
128
129 // success!
130 return error::SUCCESS;
131 }
132
133
perform_save()134 cassette_image::error cassette_image::perform_save()
135 {
136 Info info = get_info();
137 return m_format->save(this, &info);
138 }
139
140
141
142 /*********************************************************************
143 waveform accesses
144 *********************************************************************/
145
146 struct cassette_image::manipulation_ranges
147 {
148 int channel_first;
149 int channel_last;
150 size_t sample_first;
151 size_t sample_last;
152 };
153
154
155
compute_manipulation_ranges(int channel,double time_index,double sample_period,manipulation_ranges & ranges) const156 cassette_image::error cassette_image::compute_manipulation_ranges(int channel,
157 double time_index, double sample_period, manipulation_ranges &ranges) const
158 {
159 if (channel < 0)
160 {
161 ranges.channel_first = 0;
162 ranges.channel_last = m_channels - 1;
163 }
164 else
165 {
166 ranges.channel_first = channel;
167 ranges.channel_last = channel;
168 }
169
170 ranges.sample_first = my_round(time_index * m_sample_frequency);
171 ranges.sample_last = my_round((time_index + sample_period) * m_sample_frequency);
172
173 if (ranges.sample_last > ranges.sample_first)
174 ranges.sample_last--;
175
176 return error::SUCCESS;
177 }
178
179
180
lookup_sample(int channel,size_t sample,int32_t * & ptr)181 cassette_image::error cassette_image::lookup_sample(int channel, size_t sample, int32_t *&ptr)
182 {
183 ptr = nullptr;
184 size_t sample_blocknum = (sample / SAMPLES_PER_BLOCK) * m_channels + channel;
185 size_t sample_index = sample % SAMPLES_PER_BLOCK;
186
187 // is this block beyond the edge of our waveform?
188 if (sample_blocknum >= m_blocks.size())
189 m_blocks.resize(sample_blocknum + 1);
190
191 if (!m_blocks[sample_blocknum])
192 m_blocks[sample_blocknum] = make_unique_clear<int32_t []>(SAMPLES_PER_BLOCK);
193
194 ptr = &m_blocks[sample_blocknum][sample_index];
195 return error::SUCCESS;
196 }
197
198
199
cassette_image(const Format * format,void * file,const io_procs * procs,int flags)200 cassette_image::cassette_image(const Format *format, void *file, const io_procs *procs, int flags)
201 {
202 m_format = format;
203 m_io.file = file;
204 m_io.procs = procs;
205 m_flags = flags;
206 }
207
208
209
~cassette_image()210 cassette_image::~cassette_image()
211 {
212 if ((m_flags & CASSETTE_FLAG_DIRTY) && (m_flags & FLAG_SAVEONEXIT))
213 save();
214 }
215
216
217
open(void * file,const io_procs * procs,const Format * format,int flags,ptr & outcassette)218 cassette_image::error cassette_image::open(void *file, const io_procs *procs,
219 const Format *format, int flags, ptr &outcassette)
220 {
221 const Format *const formats[2] = { format, nullptr };
222 return open_choices(file, procs, nullptr, formats, flags, outcassette);
223 }
224
225
226
open_choices(void * file,const io_procs * procs,const std::string & extension,const Format * const * formats,int flags,ptr & outcassette)227 cassette_image::error cassette_image::open_choices(void *file, const io_procs *procs, const std::string &extension,
228 const Format *const *formats, int flags, ptr &outcassette)
229 {
230 // if not specified, use the dummy arguments
231 if (!formats)
232 formats = cassette_default_formats;
233
234 // create the cassette object
235 ptr cassette;
236 try { cassette.reset(new cassette_image(nullptr, file, procs, flags)); }
237 catch (std::bad_alloc const &) { return error::OUT_OF_MEMORY; }
238
239 // identify the image
240 const Format *format = nullptr;
241 Options opts;
242 for (int i = 0; !format && formats[i]; i++)
243 {
244 // try this format
245 error err = cassette->try_identify_format(*formats[i], extension, flags, opts);
246 if (err != error::SUCCESS && err != error::INVALID_IMAGE)
247 return err;
248
249 // did we succeed?
250 if (err == error::SUCCESS)
251 format = formats[i];
252 }
253
254 // have we found a proper format
255 if (!format)
256 return error::INVALID_IMAGE;
257 cassette->m_format = format;
258
259 // read the options
260 cassette->m_channels = opts.channels;
261 cassette->m_sample_frequency = opts.sample_frequency;
262
263 // load the image
264 error err = format->load(cassette.get());
265 if (err != error::SUCCESS)
266 return err;
267
268 /* success */
269 cassette->m_flags &= ~CASSETTE_FLAG_DIRTY;
270 outcassette = std::move(cassette);
271 return error::SUCCESS;
272 }
273
274
275
create(void * file,const io_procs * procs,const Format * format,const Options * opts,int flags,ptr & outcassette)276 cassette_image::error cassette_image::create(void *file, const io_procs *procs, const Format *format,
277 const Options *opts, int flags, ptr &outcassette)
278 {
279 static const Options default_options = { 1, 16, 44100 };
280
281 // cannot create to a read only image
282 if (flags & FLAG_READONLY)
283 return error::INVALID_IMAGE;
284
285 // is this a good format?
286 if (format->save == nullptr)
287 return error::INVALID_IMAGE;
288
289 // normalize arguments
290 if (!opts)
291 opts = &default_options;
292
293 // create the cassette object
294 ptr cassette;
295 try { cassette.reset(new cassette_image(format, file, procs, flags)); }
296 catch (std::bad_alloc const &) { return error::OUT_OF_MEMORY; }
297
298 // read the options
299 cassette->m_channels = opts->channels;
300 cassette->m_sample_frequency = opts->sample_frequency;
301
302 outcassette = std::move(cassette);
303 return error::SUCCESS;
304 }
305
306
307
save()308 cassette_image::error cassette_image::save()
309 {
310 if (!m_format || !m_format->save)
311 return error::UNSUPPORTED;
312
313 error err = perform_save();
314 if (err != error::SUCCESS)
315 return err;
316
317 m_flags &= ~CASSETTE_FLAG_DIRTY;
318 return error::SUCCESS;
319 }
320
321
322
get_info() const323 cassette_image::Info cassette_image::get_info() const
324 {
325 Info result;
326 result.channels = m_channels;
327 result.sample_count = m_sample_count;
328 result.sample_frequency = m_sample_frequency;
329 result.bits_per_sample = (int)waveform_bytes_per_sample(m_flags) * 8;
330 return result;
331 }
332
333
334
change(void * file,const io_procs * procs,const Format * format,int flags)335 void cassette_image::change(void *file, const io_procs *procs, const Format *format, int flags)
336 {
337 if ((flags & FLAG_READONLY) == 0)
338 flags |= CASSETTE_FLAG_DIRTY;
339 m_io.file = file;
340 m_io.procs = procs;
341 m_format = format;
342 m_flags = flags;
343 }
344
345
346
347 /*********************************************************************
348 calls for accessing the raw cassette image
349 *********************************************************************/
350
image_read(void * buffer,uint64_t offset,size_t length)351 void cassette_image::image_read(void *buffer, uint64_t offset, size_t length)
352 {
353 io_generic_read(&m_io, buffer, offset, length);
354 }
355
356
357
image_write(const void * buffer,uint64_t offset,size_t length)358 void cassette_image::image_write(const void *buffer, uint64_t offset, size_t length)
359 {
360 io_generic_write(&m_io, buffer, offset, length);
361 }
362
363
364
image_size()365 uint64_t cassette_image::image_size()
366 {
367 return io_generic_size(&m_io);
368 }
369
370
371
372 /*********************************************************************
373 waveform accesses
374 *********************************************************************/
375
376 // Note: In normal use, the sample_spacing is the same as the sample size (in bytes)
377 // But it can be larger to help do an interleaved write to samples
378 // (see cassette_write_samples)
get_samples(int channel,double time_index,double sample_period,size_t sample_count,size_t sample_spacing,void * samples,int waveform_flags)379 cassette_image::error cassette_image::get_samples(int channel,
380 double time_index, double sample_period, size_t sample_count, size_t sample_spacing,
381 void *samples, int waveform_flags)
382 {
383 error err;
384 manipulation_ranges ranges;
385 int32_t *source_ptr;
386
387 err = compute_manipulation_ranges(channel, time_index, sample_period, ranges);
388 if (err != error::SUCCESS)
389 return err;
390
391 for (size_t sample_index = 0; sample_index < sample_count; sample_index++)
392 {
393 int64_t sum = 0;
394
395 for (channel = ranges.channel_first; channel <= ranges.channel_last; channel++)
396 {
397 /* find the sample that we are putting */
398 double d = map_double(ranges.sample_last + 1 - ranges.sample_first, 0, sample_count, sample_index) + ranges.sample_first;
399 size_t cassette_sample_index = (size_t) d;
400 err = lookup_sample(channel, cassette_sample_index, source_ptr);
401 if (err != error::SUCCESS)
402 return err;
403
404 sum += *source_ptr;
405 }
406
407 /* average out the samples */
408 sum /= (ranges.channel_last + 1 - ranges.channel_first);
409
410 /* and write out the result */
411 uint8_t *dest_ptr = (uint8_t*)samples;
412 dest_ptr += sample_index * sample_spacing;
413 int16_t word;
414 int32_t dword;
415 switch (waveform_bytes_per_sample(waveform_flags))
416 {
417 case 1:
418 *((int8_t *) dest_ptr) = interpolate8(sum);
419 break;
420 case 2:
421 word = interpolate16(sum);
422 if (waveform_flags & WAVEFORM_ENDIAN_FLIP)
423 word = swapendian_int16(word);
424 *((int16_t *) dest_ptr) = word;
425 break;
426 case 4:
427 dword = sum;
428 if (waveform_flags & WAVEFORM_ENDIAN_FLIP)
429 dword = swapendian_int32(dword);
430 *((int32_t *) dest_ptr) = dword;
431 break;
432 }
433 }
434 return error::SUCCESS;
435 }
436
437
438 // Note: In normal use, the sample_spacing is the same as the sample size (in bytes)
439 // But it can be larger to help do an interleaved read from samples
440 // (see cassette_read_samples)
put_samples(int channel,double time_index,double sample_period,size_t sample_count,size_t sample_spacing,const void * samples,int waveform_flags)441 cassette_image::error cassette_image::put_samples(int channel,
442 double time_index, double sample_period, size_t sample_count, size_t sample_spacing,
443 const void *samples, int waveform_flags)
444 {
445 error err;
446 manipulation_ranges ranges;
447 int32_t *dest_ptr;
448
449 if (sample_period == 0)
450 return error::SUCCESS;
451
452 err = compute_manipulation_ranges(channel, time_index, sample_period, ranges);
453 if (err != error::SUCCESS)
454 return err;
455
456 if (m_sample_count < ranges.sample_last+1)
457 m_sample_count = ranges.sample_last + 1;
458 m_flags |= CASSETTE_FLAG_DIRTY;
459
460 if (LOG_PUT_SAMPLES)
461 {
462 LOG_FORMATS("cassette_put_samples(): Putting samples TIME=[%2.6g..%2.6g] INDEX=[%i..%i]\n",
463 time_index, time_index + sample_period,
464 (int)ranges.sample_first, (int)ranges.sample_last);
465 }
466
467 for (size_t sample_index = ranges.sample_first; sample_index <= ranges.sample_last; sample_index++)
468 {
469 /* figure out the source pointer */
470 double d = map_double(sample_count, ranges.sample_first, ranges.sample_last + 1, sample_index);
471 const uint8_t *source_ptr = (const uint8_t*)samples;
472 source_ptr += ((size_t) d) * sample_spacing;
473
474 /* compute the value that we are writing */
475 int32_t dest_value;
476 int16_t word;
477 int32_t dword;
478 switch(waveform_bytes_per_sample(waveform_flags)) {
479 case 1:
480 if (waveform_flags & WAVEFORM_UNSIGNED)
481 dest_value = extrapolate8((int8_t)(*source_ptr - 128));
482 else
483 dest_value = extrapolate8(*((int8_t *) source_ptr));
484 break;
485 case 2:
486 word = *((int16_t *) source_ptr);
487 if (waveform_flags & WAVEFORM_ENDIAN_FLIP)
488 word = swapendian_int16(word);
489 dest_value = extrapolate16(word);
490 break;
491 case 4:
492 dword = *((int32_t *) source_ptr);
493 if (waveform_flags & WAVEFORM_ENDIAN_FLIP)
494 dword = swapendian_int32(dword);
495 dest_value = dword;
496 break;
497 default:
498 return error::INTERNAL;
499 }
500
501 for (channel = ranges.channel_first; channel <= ranges.channel_last; channel++)
502 {
503 /* find the sample that we are putting */
504 err = lookup_sample(channel, sample_index, dest_ptr);
505 if (err != error::SUCCESS)
506 return err;
507 *dest_ptr = dest_value;
508 }
509 }
510 return error::SUCCESS;
511 }
512
513
514
get_sample(int channel,double time_index,double sample_period,int32_t * sample)515 cassette_image::error cassette_image::get_sample(int channel,
516 double time_index, double sample_period, int32_t *sample)
517 {
518 return get_samples(channel, time_index,
519 sample_period, 1, 0, sample, WAVEFORM_32BIT);
520 }
521
522
523
put_sample(int channel,double time_index,double sample_period,int32_t sample)524 cassette_image::error cassette_image::put_sample(int channel,
525 double time_index, double sample_period, int32_t sample)
526 {
527 return put_samples(channel, time_index,
528 sample_period, 1, 0, &sample, WAVEFORM_32BIT);
529 }
530
531
532
533 /*********************************************************************
534 waveform accesses to/from the raw image
535 *********************************************************************/
536
read_samples(int channels,double time_index,double sample_period,size_t sample_count,uint64_t offset,int waveform_flags)537 cassette_image::error cassette_image::read_samples(int channels, double time_index,
538 double sample_period, size_t sample_count, uint64_t offset, int waveform_flags)
539 {
540 error err;
541 size_t samples_loaded = 0;
542 uint8_t buffer[8192];
543
544 size_t bytes_per_sample = waveform_bytes_per_sample(waveform_flags);
545 size_t sample_spacing = bytes_per_sample * channels;
546
547 while (samples_loaded < sample_count)
548 {
549 size_t chunk_sample_count = std::min(sizeof(buffer) / sample_spacing, (sample_count - samples_loaded));
550 double chunk_sample_period = map_double(sample_period, 0, sample_count, chunk_sample_count);
551 double chunk_time_index = time_index + map_double(sample_period, 0, sample_count, samples_loaded);
552
553 image_read(buffer, offset, chunk_sample_count * sample_spacing);
554
555 for (int channel = 0; channel < channels; channel++)
556 {
557 err = put_samples(channel, chunk_time_index, chunk_sample_period,
558 chunk_sample_count, sample_spacing, &buffer[channel * bytes_per_sample], waveform_flags);
559 if (err != error::SUCCESS)
560 return err;
561 }
562
563 offset += chunk_sample_count * sample_spacing;
564 samples_loaded += chunk_sample_count;
565 }
566 return error::SUCCESS;
567 }
568
569
570
write_samples(int channels,double time_index,double sample_period,size_t sample_count,uint64_t offset,int waveform_flags)571 cassette_image::error cassette_image::write_samples(int channels, double time_index,
572 double sample_period, size_t sample_count, uint64_t offset, int waveform_flags)
573 {
574 error err;
575 size_t samples_saved = 0;
576 uint8_t buffer[8192];
577
578 size_t bytes_per_sample = waveform_bytes_per_sample(waveform_flags);
579 size_t sample_spacing = bytes_per_sample * channels;
580
581 while (samples_saved < sample_count)
582 {
583 size_t chunk_sample_count = std::min(sizeof(buffer) / sample_spacing, (sample_count - samples_saved));
584 double chunk_sample_period = map_double(sample_period, 0, sample_count, chunk_sample_count);
585 double chunk_time_index = time_index + map_double(sample_period, 0, sample_count, samples_saved);
586
587 for (int channel = 0; channel < channels; channel++)
588 {
589 err = get_samples(channel, chunk_time_index, chunk_sample_period,
590 chunk_sample_count, sample_spacing, &buffer[channel * bytes_per_sample], waveform_flags);
591 if (err != error::SUCCESS)
592 return err;
593 }
594
595 image_write(buffer, offset, chunk_sample_count * sample_spacing);
596
597 offset += chunk_sample_count * sample_spacing;
598 samples_saved += chunk_sample_count;
599 }
600 return error::SUCCESS;
601 }
602
603
604
modulation_identify(const Modulation & modulation,Options * opts)605 cassette_image::error cassette_image::modulation_identify(const Modulation &modulation, Options *opts)
606 {
607 size_t wave_bytes_length;
608 choose_wave(modulation, wave_bytes_length);
609 opts->bits_per_sample = 8;
610 opts->channels = 1;
611 opts->sample_frequency = uint32_t(std::max(modulation.zero_frequency_high, modulation.one_frequency_high) * wave_bytes_length * 2);
612 return error::SUCCESS;
613 }
614
615
616
put_modulated_data(int channel,double time_index,const void * data,size_t data_length,const Modulation & modulation,double * time_displacement)617 cassette_image::error cassette_image::put_modulated_data(int channel, double time_index,
618 const void *data, size_t data_length, const Modulation &modulation,
619 double *time_displacement)
620 {
621 error err;
622 const uint8_t *data_bytes = (const uint8_t *)data;
623 size_t wave_bytes_length;
624 double total_displacement = 0.0;
625
626 const int8_t *wave_bytes = choose_wave(modulation, wave_bytes_length);
627
628 while (data_length--)
629 {
630 uint8_t b = *(data_bytes++);
631 for (int i = 0; i < 8; i++)
632 {
633 double pulse_frequency = (b & (1 << i)) ? modulation.one_frequency_canonical : modulation.zero_frequency_canonical;
634 double pulse_period = 1 / pulse_frequency;
635 err = put_samples(0, time_index, pulse_period, wave_bytes_length, 1, wave_bytes, WAVEFORM_8BIT);
636 if (err != error::SUCCESS)
637 goto done;
638 time_index += pulse_period;
639 total_displacement += pulse_period;
640 }
641 }
642 err = error::SUCCESS;
643
644 done:
645 if (time_displacement)
646 *time_displacement = total_displacement;
647 return err;
648 }
649
650
651
put_modulated_filler(int channel,double time_index,uint8_t filler,size_t filler_length,const Modulation & modulation,double * time_displacement)652 cassette_image::error cassette_image::put_modulated_filler(int channel, double time_index,
653 uint8_t filler, size_t filler_length, const Modulation &modulation,
654 double *time_displacement)
655 {
656 error err;
657 double delta;
658 double total_displacement = 0.0;
659
660 while (filler_length--)
661 {
662 err = put_modulated_data(channel, time_index, &filler, 1, modulation, &delta);
663 if (err != error::SUCCESS)
664 return err;
665 total_displacement += delta;
666 time_index += delta;
667 }
668
669 if (time_displacement)
670 *time_displacement = total_displacement;
671 return error::SUCCESS;
672 }
673
674
675
read_modulated_data(int channel,double time_index,uint64_t offset,uint64_t length,const Modulation & modulation,double * time_displacement)676 cassette_image::error cassette_image::read_modulated_data(int channel, double time_index,
677 uint64_t offset, uint64_t length, const Modulation &modulation,
678 double *time_displacement)
679 {
680 uint8_t *buffer;
681 uint8_t buffer_stack[1024];
682 std::unique_ptr<uint8_t []> alloc_buffer;
683 size_t buffer_length;
684 if (length <= sizeof(buffer_stack))
685 {
686 buffer = buffer_stack;
687 buffer_length = sizeof(buffer_stack);
688 }
689 else
690 {
691 buffer_length = std::min<uint64_t>(length, 100000);
692 try { alloc_buffer = std::make_unique<uint8_t []>(buffer_length); }
693 catch (std::bad_alloc const &) { return error::OUT_OF_MEMORY; }
694 buffer = alloc_buffer.get();
695 }
696
697 double delta;
698 double total_displacement = 0.0;
699 while (length > 0)
700 {
701 size_t this_length = std::min<uint64_t>(length, buffer_length);
702 image_read(buffer, offset, this_length);
703
704 error err = put_modulated_data(channel, time_index, buffer, this_length, modulation, &delta);
705 if (err != error::SUCCESS)
706 return err;
707 total_displacement += delta;
708 time_index += delta;
709 length -= this_length;
710 }
711
712 if (time_displacement)
713 *time_displacement = total_displacement;
714 return error::SUCCESS;
715 }
716
717
718
put_modulated_data_bit(int channel,double time_index,uint8_t data,const Modulation & modulation,double * time_displacement)719 cassette_image::error cassette_image::put_modulated_data_bit(int channel, double time_index,
720 uint8_t data, const Modulation &modulation,
721 double *time_displacement)
722 {
723 error err;
724 size_t wave_bytes_length;
725 double total_displacement = 0.0;
726
727 const int8_t *wave_bytes = choose_wave(modulation, wave_bytes_length);
728
729 double pulse_frequency = (data) ? modulation.one_frequency_canonical : modulation.zero_frequency_canonical;
730 double pulse_period = 1 / pulse_frequency;
731 err = put_samples(0, time_index, pulse_period, wave_bytes_length, 1, wave_bytes, WAVEFORM_8BIT);
732 if (err != error::SUCCESS)
733 goto done;
734 time_index += pulse_period;
735 total_displacement += pulse_period;
736
737 err = error::SUCCESS;
738
739 done:
740 if (time_displacement)
741 *time_displacement = total_displacement;
742 return err;
743 }
744
745
746
747 /*********************************************************************
748 waveform accesses to/from the raw image
749 *********************************************************************/
750
legacy_identify(Options * opts,const LegacyWaveFiller * legacy_args)751 cassette_image::error cassette_image::legacy_identify(Options *opts,
752 const LegacyWaveFiller *legacy_args)
753 {
754 opts->channels = 1;
755 opts->bits_per_sample = 16;
756 opts->sample_frequency = legacy_args->sample_frequency;
757 return error::SUCCESS;
758 }
759
760
761
legacy_construct(const LegacyWaveFiller * legacy_args)762 cassette_image::error cassette_image::legacy_construct(const LegacyWaveFiller *legacy_args)
763 {
764 error err;
765 int length;
766 int sample_count;
767 std::vector<uint8_t> bytes;
768 std::vector<int16_t> samples;
769 int pos = 0;
770 uint64_t offset = 0;
771
772 /* sanity check the args */
773 assert(legacy_args->header_samples >= -1);
774 assert(legacy_args->trailer_samples >= 0);
775 assert(legacy_args->fill_wave);
776
777 uint64_t size = image_size();
778
779 /* normalize the args */
780 LegacyWaveFiller args = *legacy_args;
781 if (args.chunk_size == 0)
782 args.chunk_size = 1;
783 else if (args.chunk_size < 0)
784 args.chunk_size = image_size();
785 if (args.sample_frequency == 0)
786 args.sample_frequency = 11025;
787
788 /* allocate a buffer for the binary data */
789 std::vector<uint8_t> chunk(args.chunk_size);
790
791 /* determine number of samples */
792 if (args.chunk_sample_calc != nullptr)
793 {
794 if (size > 0x7FFFFFFF)
795 {
796 err = error::OUT_OF_MEMORY;
797 goto done;
798 }
799
800 bytes.resize(size);
801 image_read(&bytes[0], 0, size);
802 sample_count = args.chunk_sample_calc(&bytes[0], (int)size);
803
804 // chunk_sample_calc functions report errors by returning negative numbers
805 if (sample_count < 0)
806 {
807 err = error::INVALID_IMAGE;
808 goto done;
809 }
810
811 if (args.header_samples < 0)
812 args.header_samples = sample_count;
813 }
814 else
815 {
816 sample_count = ((size + args.chunk_size - 1) / args.chunk_size)
817 * args.chunk_samples;
818 }
819 sample_count += args.header_samples + args.trailer_samples;
820
821 /* allocate a buffer for the completed samples */
822 samples.resize(sample_count);
823
824 /* if there has to be a header */
825 if (args.header_samples > 0)
826 {
827 length = args.fill_wave(&samples[pos], sample_count - pos, CODE_HEADER);
828 if (length < 0)
829 {
830 err = error::INVALID_IMAGE;
831 goto done;
832 }
833 pos += length;
834 }
835
836 /* convert the file data to samples */
837 while ((pos < sample_count) && (offset < size))
838 {
839 image_read(&chunk[0], offset, args.chunk_size);
840 offset += args.chunk_size;
841
842 length = args.fill_wave(&samples[pos], sample_count - pos, &chunk[0]);
843 if (length < 0)
844 {
845 err = error::INVALID_IMAGE;
846 goto done;
847 }
848 pos += length;
849 if (length == 0)
850 break;
851 }
852
853 /* if there has to be a trailer */
854 if (args.trailer_samples > 0)
855 {
856 length = args.fill_wave(&samples[pos], sample_count - pos, CODE_TRAILER);
857 if (length < 0)
858 {
859 err = error::INVALID_IMAGE;
860 goto done;
861 }
862 pos += length;
863 }
864
865 /* specify the wave */
866 err = put_samples(0, 0.0, ((double) pos) / args.sample_frequency,
867 pos, 2, &samples[0], WAVEFORM_16BIT);
868 if (err != error::SUCCESS)
869 goto done;
870
871 /* success! */
872 err = error::SUCCESS;
873
874 #if DUMP_CASSETTES
875 dump("C:\\TEMP\\CASDUMP.WAV");
876 #endif
877
878 done:
879 return err;
880 }
881
882
883
884 /*********************************************************************
885 cassette_dump
886
887 A debugging call to dump a cassette image to a disk based wave file
888 *********************************************************************/
889
dump(const char * filename)890 void cassette_image::dump(const char *filename)
891 {
892 FILE *f = fopen(filename, "wb");
893 if (!f)
894 return;
895
896 io_generic saved_io = m_io;
897 const Format *saved_format = m_format;
898
899 m_io.file = f;
900 m_io.procs = &stdio_ioprocs_noclose;
901 m_format = &wavfile_format;
902 perform_save();
903
904 m_io = saved_io;
905 m_format = saved_format;
906
907 fclose(f);
908 }
909