summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/formats/flacfile.cpp
blob: c594a809a8e7aa81697bc3434c72ab263dfa5d9d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
/*********************************************************************

    flacfile.cpp

    Format code for flac files.

*********************************************************************/

#include "flacfile.h"

#include "flac.h"

#include <memory>
#include <new>


namespace {

constexpr int MAX_CHANNELS = 8;


// Copied from cassimg.cpp; put somewhere central?
/*********************************************************************
    helper code
*********************************************************************/
constexpr double map_double(double d, uint64_t low, uint64_t high, uint64_t value)
{
	return d * (value - low) / (high - low);
}

constexpr size_t waveform_bytes_per_sample(int waveform_flags)
{
	return size_t(1 << ((waveform_flags & 0x06) / 2));
}


cassette_image::error flacfile_identify(cassette_image *cassette, cassette_image::Options *opts)
{
	cassette->get_raw_cassette_image()->seek(0, SEEK_SET);
	flac_decoder decoder(*cassette->get_raw_cassette_image());
	const int channels = decoder.channels();
	const int sample_rate = decoder.sample_rate();
	const int bits_per_sample = decoder.bits_per_sample();
	const int total_samples = decoder.total_samples();
	decoder.finish();

	opts->channels = channels;
	opts->sample_frequency = sample_rate;
	opts->bits_per_sample = bits_per_sample;

	if (channels > MAX_CHANNELS)
		return cassette_image::error::INVALID_IMAGE;
	else if (channels > 0 && sample_rate > 0 && total_samples > 0)
		return cassette_image::error::SUCCESS;
	else
		return cassette_image::error::INVALID_IMAGE;
}


cassette_image::error flacfile_load(cassette_image *cassette)
{
	cassette->get_raw_cassette_image()->seek(0, SEEK_SET);
	flac_decoder decoder(*cassette->get_raw_cassette_image());
	const int channels = decoder.channels();
	const int total_samples = decoder.total_samples();

	std::unique_ptr<int16_t []> samples[MAX_CHANNELS];
	int16_t *channel_samples[MAX_CHANNELS];
	for (int channel = 0; channel < channels; channel++)
	{
		samples[channel].reset(new (std::nothrow) int16_t [total_samples]);
		if (!samples[channel])
			return cassette_image::error::OUT_OF_MEMORY;
		channel_samples[channel] = samples[channel].get();
	}
	if (!decoder.decode(channel_samples, decoder.total_samples()))
		return cassette_image::error::INVALID_IMAGE;
	for (int channel = 0; channel < channels; channel++)
	{
		const cassette_image::error err = cassette->put_samples(
				channel, 0.0, (double)total_samples/decoder.sample_rate(), total_samples,
				2, channel_samples[channel], cassette_image::WAVEFORM_16BIT);
		if (err != cassette_image::error::SUCCESS)
			return err;
	}

	return cassette_image::error::SUCCESS;
}


cassette_image::error flacfile_save(cassette_image *cassette, const cassette_image::Info *info)
{
	if (info->channels > MAX_CHANNELS)
		return cassette_image::error::INVALID_IMAGE;

	cassette->get_raw_cassette_image()->seek(0, SEEK_SET);
	flac_encoder encoder;
	encoder.set_num_channels(info->channels);
	encoder.set_sample_rate(info->sample_frequency);
	if (!encoder.reset(*cassette->get_raw_cassette_image()))
		return cassette_image::error::INTERNAL;

	size_t samples_saved = 0;
	int16_t buffer[MAX_CHANNELS][4096];
	int16_t *buffer_ptr[MAX_CHANNELS];
	const size_t bytes_per_sample = waveform_bytes_per_sample(cassette_image::WAVEFORM_16BIT);
	const size_t sample_spacing = bytes_per_sample * info->channels;
	const double sample_period = info->sample_count / (double) info->sample_frequency;

	for (int channel = 0; channel < MAX_CHANNELS; channel++)
	{
		buffer_ptr[channel] = &buffer[channel][0];
	}

	while (samples_saved < info->sample_count)
	{
		size_t chunk_sample_count = std::min(sizeof(buffer) / sample_spacing, (info->sample_count - samples_saved));
		double chunk_sample_period = map_double(sample_period, 0, info->sample_count, chunk_sample_count);
		double chunk_time_index = map_double(sample_period, 0, info->sample_count, samples_saved);

		for (int channel = 0; channel < info->channels; channel++)
		{
			cassette_image::error err = cassette->get_samples(channel, chunk_time_index, chunk_sample_period,
				chunk_sample_count, sample_spacing, &buffer[channel * bytes_per_sample], cassette_image::WAVEFORM_16BIT);

			if (err != cassette_image::error::SUCCESS)
				return err;
		}

		if (!encoder.encode(buffer_ptr, chunk_sample_count))
			return cassette_image::error::INTERNAL;

		samples_saved += chunk_sample_count;
	}

	encoder.finish();

	return cassette_image::error::SUCCESS;
}

} // anonymous namespace


const cassette_image::Format cassette_image::flacfile_format =
{
	"flac",
	flacfile_identify,
	flacfile_load,
	flacfile_save
};