-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdecode.cpp
363 lines (305 loc) · 9.59 KB
/
decode.cpp
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
/* Copyright (c) 2011, Markus Peloquin <[email protected]>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <algorithm>
#include <iostream>
#include <memory>
#include <FLAC++/decoder.h>
#include <sndfile.h>
#include "decode.hpp"
#include "errors.hpp"
namespace {
const unsigned FRAMES_PER_SEC = 75;
flacsplit::file_format get_file_format(FILE *);
class Flac_decoder :
public FLAC::Decoder::File,
public flacsplit::Basic_decoder {
public:
struct Flac_decode_error : flacsplit::Decode_error {
Flac_decode_error(const std::string &msg) : _msg(msg) {}
const char *what() const noexcept override {
return _msg.c_str();
}
std::string _msg;
};
//! Note that this takes ownership of the file (or rather, the FLAC
//! library takes ownership).
//! \throw Flac_decode_error
Flac_decoder(FILE *);
//! \throw Flac_decode_error
flacsplit::Frame next_frame(bool allow_short) override;
void seek(int64_t sample) override {
seek_absolute(sample);
}
int32_t sample_rate() const override {
return get_sample_rate();
}
int64_t total_samples() const override {
return get_total_samples();
}
protected:
FLAC__StreamDecoderWriteStatus write_callback(
const FLAC__Frame *, const FLAC__int32 *const *) override;
FLAC__StreamDecoderSeekStatus seek_callback(FLAC__uint64) override;
FLAC__StreamDecoderTellStatus tell_callback(FLAC__uint64 *) override;
FLAC__StreamDecoderLengthStatus length_callback(FLAC__uint64 *)
override;
bool eof_callback() override {
return feof(_fp);
}
void error_callback(FLAC__StreamDecoderErrorStatus status) override {
_last_status = FLAC__StreamDecoderErrorStatusString[status];
}
private:
FILE *_fp;
const FLAC__Frame *_last_frame;
std::unique_ptr<const FLAC__int32 *>
_last_buffer;
const char *_last_status;
bool _frame_retrieved;
};
class Wave_decoder : public flacsplit::Basic_decoder {
public:
struct Wave_decode_error : flacsplit::Decode_error {
Wave_decode_error(const char *msg) : msg(msg) {}
const char *what() const noexcept override {
return msg.c_str();
}
std::string msg;
};
//! \throw flacsplit::Sndfile_error
Wave_decoder(FILE *);
virtual ~Wave_decoder() noexcept {
close_quiet(_file);
}
//! \throw Wave_decode_error
flacsplit::Frame next_frame(bool allow_short) override;
//! \throw Wave_decode_error
void seek(int64_t frame) override {
if (sf_seek(_file, frame, SF_SEEK_SET) == -1)
throw_traced(flacsplit::Sndfile_error(
"sf_seek error", sf_error(_file)
));
}
int32_t sample_rate() const override {
return _info.samplerate;
}
int64_t total_samples() const override {
// 31 bits is big enough for 2-channel 48 kHz for 6.21 hours
return _info.frames;
}
private:
static void close_quiet(SNDFILE *file) noexcept;
std::unique_ptr<int32_t> _samples;
std::unique_ptr<int32_t> _transp;
std::unique_ptr<int32_t *> _transp_ptrs;
SNDFILE *_file;
SF_INFO _info;
sf_count_t _samples_len;
};
Flac_decoder::Flac_decoder(FILE *fp) :
FLAC::Decoder::File(),
Basic_decoder(),
_fp(fp),
_last_frame(nullptr),
_last_buffer(),
_last_status(nullptr),
_frame_retrieved(false)
{
FLAC__StreamDecoderInitStatus status;
if ((status = init(fp)) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
throw_traced(Flac_decode_error(
FLAC__StreamDecoderInitStatusString[status]
));
seek(0);
}
flacsplit::Frame
Flac_decoder::next_frame(bool) {
// a seek will trigger a call to write_callback(), so don't process
// the next frame if it hasn't been seen yet here
if (!_last_frame || _frame_retrieved)
if (!process_single())
throw_traced(Flac_decode_error(
get_state().as_cstring()
));
if (_last_status)
throw_traced(Flac_decode_error(_last_status));
flacsplit::Frame frame;
frame.data = _last_buffer.get();
frame.bits_per_sample = _last_frame->header.bits_per_sample;
frame.channels = _last_frame->header.channels;
frame.samples = _last_frame->header.blocksize;
frame.rate = _last_frame->header.sample_rate;
_frame_retrieved = true;
return frame;
}
FLAC__StreamDecoderWriteStatus
Flac_decoder::write_callback(const FLAC__Frame *frame,
const FLAC__int32 *const *buffer) {
if (!_last_buffer.get())
// the number of channels should be constant
_last_buffer.reset(new const FLAC__int32 *[
frame->header.channels]);
/*
* usually, 'buffer' is an array; when there is a short, unaligned,
* read, 'buffer' is a temporary heap-allocated buffer that must be
* copied
*/
_last_frame = frame;
std::copy(buffer, buffer + frame->header.channels,
_last_buffer.get());
_last_status = nullptr;
_frame_retrieved = false;
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
FLAC__StreamDecoderSeekStatus
Flac_decoder::seek_callback(FLAC__uint64 absolute_byte_offset) {
long off = absolute_byte_offset;
if (static_cast<FLAC__uint64>(off) != absolute_byte_offset)
flacsplit::throw_traced(std::runtime_error("bad offset"));
return fseek(_fp, off, SEEK_SET) ?
FLAC__STREAM_DECODER_SEEK_STATUS_ERROR :
FLAC__STREAM_DECODER_SEEK_STATUS_OK;
}
FLAC__StreamDecoderTellStatus
Flac_decoder::tell_callback(FLAC__uint64 *absolute_byte_offset) {
long off = ftell(_fp);
if (off < 0)
return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
*absolute_byte_offset = off;
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
}
FLAC__StreamDecoderLengthStatus
Flac_decoder::length_callback(FLAC__uint64 *stream_length) {
struct stat st;
if (fstat(fileno(_fp), &st))
return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
*stream_length = st.st_size;
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
}
Wave_decoder::Wave_decoder(FILE *fp) :
Basic_decoder(),
_samples(),
_transp()
{
_file = sf_open_fd(fileno(fp), SFM_READ, &_info, false);
if (!_file)
throw_traced(flacsplit::Sndfile_error(
"sf_open_fd failed", sf_error(nullptr)
));
try {
_samples_len = _info.channels * _info.samplerate /
FRAMES_PER_SEC;
_samples.reset(new int32_t[_samples_len]);
_transp.reset(new int32_t[_samples_len]);
_transp_ptrs.reset(new int32_t *[_info.channels]);
} catch (...) {
close_quiet(_file);
throw;
}
}
void
Wave_decoder::close_quiet(SNDFILE *file) noexcept {
int errnum = sf_close(file);
if (errnum) {
// Probably never occurs.
std::cerr << "failed to close: "
<< sf_error_number(errnum) << '\n';
}
}
flacsplit::Frame
Wave_decoder::next_frame(bool allow_short) {
sf_count_t samples;
samples = sf_read_int(_file, _samples.get(), _samples_len);
if (!allow_short && samples < _samples_len) {
// This may only occur on the final track if this is not an
// exact number of frames. Perhaps because the data is not from
// a CD.
std::cerr << "expected " << _samples_len << " but got " << samples << '\n';
throw_traced(flacsplit::Sndfile_error(
"sf_read error", sf_error(_file)
));
}
int32_t bits_per_sample;
int shamt;
switch (_info.format & SF_FORMAT_SUBMASK) {
case SF_FORMAT_PCM_S8: bits_per_sample = 8; shamt = 24; break;
case SF_FORMAT_PCM_16: bits_per_sample = 16; shamt = 16; break;
case SF_FORMAT_PCM_24: bits_per_sample = 24; shamt = 8; break;
case SF_FORMAT_PCM_32: bits_per_sample = 32; shamt = 0; break;
default:
throw flacsplit::throw_traced(std::runtime_error("bad format"));
}
flacsplit::Frame frame;
frame.data = _transp_ptrs.get();
frame.bits_per_sample = bits_per_sample;
frame.channels = _info.channels;
if (samples % frame.channels)
flacsplit::throw_traced(std::runtime_error(
"bad number of samples"
));
frame.samples = samples / frame.channels;
frame.rate = _info.samplerate;
// transpose _samples => _transp
// Also scale down values when not 32bit.
size_t i = 0;
for (int sample = 0; sample < frame.samples; sample++) {
int j = sample;
for (int channel = 0; channel < frame.channels; channel++) {
_transp.get()[j] = _samples.get()[i] >> shamt;
i++;
j += frame.samples;
}
}
// make 2d array to return
_transp_ptrs.get()[0] = _transp.get();
for (int channel = 1; channel < frame.channels; channel++)
_transp_ptrs.get()[channel] = _transp_ptrs.get()[channel-1] +
frame.samples;
return frame;
}
flacsplit::file_format
get_file_format(FILE *fp) {
const char *const RIFF = "RIFF";
const char *const WAVE = "WAVE";
const char *const FLAC = "fLaC";
char buf[12];
if (!fread(buf, sizeof(buf), 1, fp))
return flacsplit::file_format::UNKNOWN;
fseek(fp, -sizeof(buf), SEEK_CUR);
if (std::equal(buf, buf+4, RIFF) && std::equal(buf+8, buf+12, WAVE))
return flacsplit::file_format::WAVE;
if (std::equal(buf, buf+4, FLAC))
return flacsplit::file_format::FLAC;
return flacsplit::file_format::UNKNOWN;
}
} // end anon
flacsplit::Decoder::Decoder(FILE *fp, file_format format) :
Basic_decoder(),
_decoder()
{
if (format == file_format::UNKNOWN)
format = get_file_format(fp);
switch (format) {
case file_format::UNKNOWN:
throw throw_traced(Bad_format());
case file_format::WAVE:
_decoder.reset(new Wave_decoder(fp));
break;
case file_format::FLAC:
_decoder.reset(new Flac_decoder(fp));
}
}