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