Anklang 0.3.0-460-gc4ef46ba
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
wave.cc
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#include "wave.hh"
3#include "datautils.hh"
4#include "atquit.hh"
5#include "platform.hh"
6#include "randomhash.hh"
7#include "internal.hh"
8#include <cstring>
9#include <sys/types.h>
10#include <sys/stat.h>
11#include <sys/types.h>
12#include <fcntl.h>
13#include <unistd.h>
14#include <opus.h>
15#include <ogg/ogg.h>
16#include <FLAC/all.h>
17
18namespace Ase {
19
20// == WaveWriter ==
21WaveWriter::~WaveWriter ()
22{}
23
24// == WAV ==
26wav_header (const uint8_t n_bits, const uint32_t n_channels, const uint32_t sample_freq, const uint32_t n_samples)
27{
28 union ByteStream {
29 void *v; char *c; uint8_t *u8;
30 uint16_t *u16;
31 uint32_t *u32;
32 char*
33 puts (const char *str)
34 {
35 strncpy (c, str, 1024);
36 c += strlen (str);
37 return c;
38 }
39 };
40 const uint32_t byte_per_sample = n_channels * n_bits / 8;
41 const uint32_t byte_per_second = byte_per_sample * sample_freq;
42 const uint32_t n_data_bytes = (n_samples * byte_per_sample + 1) / 2 * 2; // round odd
44 buffer.resize (1024);
45 ByteStream b { &buffer[0] };
46 b.puts ("RIFF"); // main chunk
47 const ByteStream lpos = b;
48 b.u32++; // skip file length
49 const ByteStream cpos = b; // save chunk pos
50 b.puts ("WAVE"); // chunk type
51 b.puts ("fmt "); // sub chunk
52 const uint16_t fmt = n_bits == 32 ? 3 : 1;
53 const bool extensible = n_channels > 2 || fmt == 3;
54 const uint32_t fmtsz = extensible ? 18 : 16;
55 *b.u32++ = htole32 (fmtsz); // sub chunk length
56 *b.u16++ = htole16 (fmt); // format, 1=PCM, 3=FLOAT
57 *b.u16++ = htole16 (n_channels);
58 *b.u32++ = htole32 (sample_freq);
59 *b.u32++ = htole32 (byte_per_second);
60 *b.u16++ = htole16 (byte_per_sample); // block align
61 *b.u16++ = htole16 (n_bits);
62 if (extensible)
63 *b.u16++ = htole16 (0); // extension size
64 if (fmt == 3)
65 {
66 b.puts ("fact"); // sub chunk
67 *b.u32++ = htole32 (4); // sub chunk length
68 *b.u32++ = htole32 (n_samples); // frames
69 }
70 b.puts ("data"); // data chunk
71 *b.u32++ = htole32 (n_data_bytes);
72 const uint32_t length = b.c - cpos.c + n_data_bytes;
73 *lpos.u32 = htole32 (length); // fix file length
74 buffer.resize (b.u8 - &buffer[0]);
75 return buffer;
76}
77
78static int
79wav_write (int fd, uint8_t n_bits, uint32_t n_channels, uint32_t sample_freq, const float *samples, size_t n_frames)
80{
81 const size_t n_samples = n_channels * n_frames;
82 if (n_bits == 8) // unsigned WAV
83 {
84 uint8_t buffer[16384], *const e = buffer + sizeof (buffer) / sizeof (buffer[0]), *b = buffer;
85 for (size_t n = 0; n < n_samples; )
86 {
87 const uint8_t u8 = 127.5 + 127.5 * samples[n++];
88 *b++ = u8;
89 if (b + 1 >= e || n >= n_samples)
90 {
91 if (n >= n_samples && (n_channels & 1) && (n_samples & 1)) // && (byte_per_sample & 1)
92 *b++ = 0; // final pad byte
93 if (write (fd, buffer, (b - buffer) * sizeof (buffer[0])) < 0)
94 return -errno;
95 b = buffer;
96 }
97 }
98 }
99 if (n_bits == 24)
100 {
101 uint8_t buffer[16384], *const e = buffer + sizeof (buffer) / sizeof (buffer[0]), *b = buffer;
102 for (size_t n = 0; n < n_samples; )
103 {
104 const int32_t i24 = samples[n++] * 8388607.5 - 0.5;
105 *b++ = i24;
106 *b++ = i24 >> 8;
107 *b++ = i24 >> 16;
108 if (b + 4 >= e || n >= n_samples)
109 {
110 if (n >= n_samples && (n_channels & 1) && (n_samples & 1)) // && (byte_per_sample & 1)
111 *b++ = 0; // final pad byte
112 if (write (fd, buffer, (b - buffer) * sizeof (buffer[0])) < 0)
113 return -errno;
114 b = buffer;
115 }
116 }
117 }
118 if (n_bits == 16)
119 {
120 uint16_t buffer[16384], *const e = buffer + sizeof (buffer) / sizeof (buffer[0]), *b = buffer;
121 for (size_t n = 0; n < n_samples; )
122 {
123 const int16_t i16 = samples[n++] * 32767.5 - 0.5;
124 *b++ = htole16 (i16);
125 if (b >= e || n >= n_samples)
126 {
127 if (write (fd, buffer, (b - buffer) * sizeof (buffer[0])) < 0)
128 return -errno;
129 b = buffer;
130 }
131 }
132 }
133 if (n_bits == 32)
134 {
135 uint32_t buffer[16384], *const e = buffer + sizeof (buffer) / sizeof (buffer[0]), *b = buffer;
136 for (size_t n = 0; n < n_samples; )
137 {
138 union { float f; uint32_t u32; } u { samples[n++] };
139 *b++ = htole32 (u.u32);
140 if (b >= e || n >= n_samples)
141 {
142 if (write (fd, buffer, (b - buffer) * sizeof (buffer[0])) < 0)
143 return -errno;
144 b = buffer;
145 }
146 }
147 }
148 return 0;
149}
150
151class WavWriterImpl final : public WaveWriter {
152 String filename_;
153 uint32_t n_channels_ = 0;
154 uint32_t sample_freq_ = 0;
155 uint8_t n_bits_ = 0;
156 int fd_ = -1;
157 size_t n_samples_ = 0;
158 std::function<void()> flush_atquit;
159public:
161 {
162 flush_atquit = [this] () { close(); };
163 atquit_add (&flush_atquit);
164 }
166 {
167 atquit_del (&flush_atquit);
168 close();
169 }
170 bool
171 open (const String &filename, uint8_t n_bits, uint32_t n_channels, uint32_t sample_freq)
172 {
173 assert_return (fd_ == -1, false);
174 assert_return (!filename.empty(), false);
175 assert_return (n_bits == 8 || n_bits == 16 || n_bits == 24 || n_bits == 32, false);
176 assert_return (n_channels > 0, false);
177 assert_return (sample_freq > 0, false);
178 // open, must be seekable
179 fd_ = ::open (filename.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644);
180 if (fd_ < 0)
181 return false;
182 if (lseek (fd_, 0, SEEK_SET) < 0)
183 {
184 ::close (fd_);
185 fd_ = -1;
186 return false;
187 }
188 // setup fields
189 filename_ = filename;
190 n_bits_ = n_bits;
191 n_channels_ = n_channels;
192 sample_freq_ = sample_freq;
193 n_samples_ = 0;
194 // write header
195 std::vector<unsigned char> header = wav_header (n_bits_, n_channels_, sample_freq_, 4294967168);
196 if (::write (fd_, header.data(), header.size()) < 0)
197 {
198 ::close (fd_);
199 fd_ = -1;
200 return false;
201 }
202 return true;
203 }
204 String
205 name () const override
206 {
207 return filename_;
208 }
209 ssize_t
210 write (const float *frames, size_t n_frames) override
211 {
212 assert_return (n_bits_ == 32, false);
213 assert_return (fd_ >= 0, false);
214 return_unless (n_frames, false);
215
216 n_samples_ += n_frames * n_channels_;
217
218 return wav_write (fd_, n_bits_, n_channels_, sample_freq_, frames, n_frames) >= 0;
219 }
220 bool
221 close () override
222 {
223 return_unless (fd_ >= 0, false);
224 std::vector<unsigned char> header = wav_header (n_bits_, n_channels_, sample_freq_, n_samples_ / n_channels_);
225 bool ok = true;
226 if (lseek (fd_, 0, SEEK_SET) >= 0)
227 ok &= ::write (fd_, header.data(), header.size()) >= 0;
228 ok &=::close (fd_) >= 0;
229 fd_ = -1;
230 return ok;
231 }
232};
233
235wave_writer_create_wav (int rate, int channels, const String &filename, int mode, uint8_t n_bits)
236{
238 if (wavw->open (filename, n_bits, channels, rate) == false)
239 return nullptr;
240 return wavw;
241}
242
243// == OpusWriter ==
244String
245wave_writer_opus_version()
246{
247 return opus_get_version_string();
248}
249
250class OpusWriter final : public WaveWriter {
251 String name_;
252 OpusEncoder *enc_ = nullptr;
253 int fd_ = -1;
254 float *bmark_ = nullptr;
255 std::vector<float> buffer_;
256 ogg_stream_state ostream_ = { 0, };
257 uint32_t rate_ = 0;
258 uint8_t n_channels_ = 0;
259 bool eos_ = false;
260 uint packetno_ = 0;
261 int64_t granulepos_ = 0;
262 std::function<void()> flush_atquit;
263public:
264 OpusWriter (const String &filename)
265 {
266 name_ = filename;
267 flush_atquit = [this] () { finish_and_close (true); };
268 atquit_add (&flush_atquit);
269 }
271 {
272 atquit_del (&flush_atquit);
273 close();
274 opus_encoder_destroy (enc_);
275 enc_ = nullptr;
276 ogg_stream_clear (&ostream_);
277 }
278 String
279 name() const override
280 {
281 return name_;
282 }
283 bool
284 create (int mode)
285 {
286 errno = EBUSY;
287 assert_return (fd_ < 0, false);
288 fd_ = open (name_.c_str(), O_CREAT | O_TRUNC | O_WRONLY, mode);
289 return fd_ >= 0;
290 }
291 bool
292 setup_encoder (int rate, int channels, int complexity, float bitrate)
293 {
294 assert_return (fd_ >= 0, false);
295 errno = EINVAL;
296 assert_return (!enc_, false);
297 assert_return (channels == 1 || channels == 2, false);
298 assert_return (rate > 24000, false); // lets opus operate at 48000
299 rate_ = rate;
300 int error = 0;
301 n_channels_ = channels;
302 enc_ = opus_encoder_create (rate_, n_channels_, OPUS_APPLICATION_AUDIO, &error);
303 if (error == OPUS_OK)
304 {
305 bitrate = n_channels_ * bitrate * 1000;
306 const int serialno = random_int64();
307 if (OPUS_OK == opus_encoder_ctl (enc_, OPUS_SET_BITRATE (MIN (256000 * n_channels_, bitrate))) &&
308 OPUS_OK == opus_encoder_ctl (enc_, OPUS_SET_VBR (1)) &&
309 OPUS_OK == opus_encoder_ctl (enc_, OPUS_SET_VBR_CONSTRAINT (0)) &&
310 OPUS_OK == opus_encoder_ctl (enc_, OPUS_SET_FORCE_CHANNELS (n_channels_)) &&
311 OPUS_OK == opus_encoder_ctl (enc_, OPUS_SET_COMPLEXITY (complexity)) &&
312 ogg_stream_init (&ostream_, serialno) >= 0)
313 {
314 const int std_fragment_size = 20 * 48000 / 1000; // 960
315 int fragment_size = std_fragment_size * rate_ / 48000;
316 buffer_.resize (fragment_size * n_channels_);
317 bmark_ = buffer_.data();
318 if (write_header() > 0)
319 return true;
320 }
321 }
322 finish_and_close (false);
323 errno = EINVAL;
324 return false;
325 }
326 ssize_t
327 write_packet (ogg_packet *op, bool force_flush)
328 {
329 ogg_stream_packetin (&ostream_, op);
330 ogg_page opage;
331 auto stream_pageout = force_flush ? ogg_stream_flush : ogg_stream_pageout;
332 while (stream_pageout (&ostream_, &opage))
333 {
334 ssize_t n;
335 do
336 n = ::write (fd_, opage.header, opage.header_len);
337 while (n == -1 && errno == EINTR);
338 if (n != opage.header_len) return -1;
339 do
340 n = ::write (fd_, opage.body, opage.body_len);
341 while (n == -1 && errno == EINTR);
342 if (n != opage.body_len) return -1;
343 }
344 return 1;
345 }
346 ssize_t
347 write_header ()
348 {
349 int lookahead = 0;
350 if (OPUS_OK != opus_encoder_ctl (enc_, OPUS_GET_LOOKAHEAD (&lookahead)))
351 lookahead = 0;
352 // header packet
353 struct OpusHeader {
354 // https://www.rfc-editor.org/rfc/rfc7845.html#section-5
355 char magic[8] = { 'O', 'p', 'u', 's', 'H', 'e', 'a', 'd' };
356 uint8_t version = 1;
357 uint8_t channels = 0;
358 uint16_t pre_skip_le = 0;
359 uint32_t rate_le = 0;
360 int16_t gain_le = 0; // in dB, stored as 20*log10(gainfactor) * 256; sample *= pow (10, gain_le/256./20)
361 uint8_t chmapping = 0;
362 // uint8_t mappings...
363 } __attribute__ ((packed));
364 OpusHeader oh = {
365 .channels = n_channels_,
366 .pre_skip_le = htole16 (lookahead),
367 .rate_le = htole32 (rate_),
368 };
369 static_assert (sizeof (oh) == 19);
370 ogg_packet op0 = {
371 .packet = (uint8_t*) &oh,
372 .bytes = sizeof (oh),
373 .b_o_s = 0, .e_o_s = 0,
374 .granulepos = 0,
375 .packetno = packetno_++
376 };
377 if (write_packet (&op0, true) < 0)
378 return -1;
379 // comment packet
380 uint32_t u32_le;
381 std::string cmtheader;
382 cmtheader += "OpusTags"; // 8, magic
383 const String opus_version = opus_get_version_string();
384 u32_le = htole32 (opus_version.size());
385 cmtheader += String ((char*) &u32_le, 4); // 4, vendor string length
386 cmtheader += opus_version; // vendor string
387 StringS tags;
388 tags.insert (tags.begin(), "ENCODER=Anklang-" + String (ase_version_short));
389 // R128_TRACK_GAIN, R128_ALBUM_GAIN, BPM, ARTIST, TITLE, DATE, ALBUM
390 u32_le = htole32 (tags.size());
391 cmtheader += String ((char*) &u32_le, 4); // 4, number of tags
392 for (const auto &tag : tags)
393 {
394 u32_le = htole32 (tag.size());
395 cmtheader += String ((char*) &u32_le, 4); // 4, tag length
396 cmtheader += tag; // tag string
397 }
398 ogg_packet op1 = {
399 .packet = (uint8_t*) cmtheader.data(),
400 .bytes = long (cmtheader.size()),
401 .b_o_s = 0, .e_o_s = 0,
402 .granulepos = 0,
403 .packetno = packetno_++
404 };
405 if (write_packet (&op1, true) < 0)
406 return -1;
407 return 2;
408 }
409 ssize_t
410 write_ogg (uint8_t *data, long l, bool force_flush)
411 {
412 ogg_packet op = {
413 .packet = data,
414 .bytes = l,
415 .b_o_s = 0, .e_o_s = eos_,
416 .granulepos = granulepos_,
417 .packetno = packetno_++
418 };
419 if (write_packet (&op, force_flush) < 0)
420 return -1;
421 return 1;
422 }
423 ssize_t
424 write (const float *frames, size_t n_frames) override
425 {
426 return write_opus (frames, n_frames, false);
427 }
428 ssize_t
429 write_opus (const float *frames, size_t n_frames, bool force_flush)
430 {
431 errno = EINVAL;
432 return_unless (fd_ >= 0, -1);
433 return_unless (n_frames, 0);
434 assert_return (enc_, -1);
435 assert_return (frames, -1);
436 const float *fend = frames + n_frames * n_channels_;
437 const float *bmax = buffer_.data() + buffer_.size();
438 while (frames < fend)
439 {
440 ssize_t l = MIN (bmax - bmark_, fend - frames);
441 fast_copy (l, bmark_, frames);
442 frames += l;
443 bmark_ += l;
444 if (bmark_ == bmax)
445 {
446 uint8_t pmem[16384];
447 bmark_ = buffer_.data();
448 const uint n_frames = buffer_.size() / n_channels_;
449 granulepos_ += n_frames;
450 l = opus_encode_float (enc_, bmark_, n_frames, pmem, sizeof (pmem));
451 if (l < 0)
452 {
453 printerr ("%s: OpusWriter encoding error: %s\n", name_, opus_strerror (l));
454 finish_and_close (false);
455 errno = EIO;
456 return -1;
457 }
458 if (l > 0 && write_ogg (pmem, l, force_flush) < 0)
459 {
460 const int serrno = errno;
461 finish_and_close (false);
462 errno = serrno;
463 return -1;
464 }
465 }
466 }
467 return n_frames;
468 }
469 bool
470 finish_and_close (bool flush)
471 {
472 return_unless (fd_ >= 0, false);
473 bool err = false;
474 if (flush && enc_)
475 {
476 const size_t n_filled = bmark_ - buffer_.data();
477 // flush fragment and ogg stream
478 const size_t n_floats = buffer_.size() - n_filled;
479 float zeros[n_floats];
480 floatfill (zeros, 0, n_floats);
481 eos_ = true;
482 err &= write_opus (zeros, n_floats / n_channels_, true) < 0;
483 }
484 // flush file descroiptor
485 err &= ::close (fd_) < 0;
486 fd_ = -1;
487 if (err)
488 printerr ("%s: OpusWriter close error: %s\n", name_, strerror (errno ? errno : EIO));
489 return !err;
490 }
491 bool
492 close() override
493 {
494 return finish_and_close (true);
495 }
496};
497
499wave_writer_create_opus (int rate, int channels, const String &filename, int mode, int complexity, float bitrate)
500{
501 auto ow = std::make_shared<OpusWriter> (filename);
502 if (ow->create (mode) == false)
503 return nullptr;
504 if (!ow->setup_encoder (rate, channels, complexity, bitrate))
505 return nullptr;
506 return ow;
507}
508
509// == FlacWriter ==
510String
511wave_writer_flac_version()
512{
513 return FLAC__VERSION_STRING;
514}
515
516class FlacWriter final : public WaveWriter {
517 String name_;
518 FLAC__StreamEncoder *enc_ = nullptr;
519 FLAC__StreamMetadata *metadata_[2] = { nullptr, nullptr };
520 uint32_t rate_ = 0;
521 uint8_t n_channels_ = 0;
522 std::vector<int32_t> ibuffer_;
523 std::function<void()> flush_atquit;
524public:
525 FlacWriter (const String &filename)
526 {
527 name_ = filename;
528 ibuffer_.reserve (65536);
529 flush_atquit = [this] () { if (enc_) close(); };
530 atquit_add (&flush_atquit);
531 }
533 {
534 atquit_del (&flush_atquit);
535 close();
536 }
537 void
538 cleanup()
539 {
540 if (enc_)
541 FLAC__stream_encoder_delete (enc_);
542 enc_ = nullptr;
543 if (metadata_[0])
544 FLAC__metadata_object_delete (metadata_[0]);
545 metadata_[0] = nullptr;
546 }
547 bool
548 close() override
549 {
550 errno = EINVAL;
551 assert_return (enc_, false);
552 const bool ok = FLAC__stream_encoder_finish (enc_);
553 const int saved_errno = errno;
554 // const FLAC__StreamEncoderState state = FLAC__stream_encoder_get_state (enc_);
555 cleanup();
556 if (!ok)
557 printerr ("%s: FlacWriter error: %s\n", name_, strerror (saved_errno ? saved_errno : EIO));
558 return ok;
559 }
560 bool
561 create (int mode, int rate, int channels, int compresion)
562 {
563 errno = EINVAL;
564 assert_return (channels == 1 || channels == 2, false);
565 assert_return (rate > 24000, false); // lets flac operate at 48000
566 errno = EBUSY;
567 assert_return (enc_ == nullptr, false);
568 const int fd = open (name_.c_str(), O_CREAT | O_TRUNC | O_RDWR, mode);
569 return_unless (fd >= 0, false);
570 FILE *file = fdopen (fd, "w+b");
571 if (!file)
572 {
573 ::close (fd);
574 return false;
575 }
576 enc_ = FLAC__stream_encoder_new();
577 if (!enc_)
578 {
579 fclose (file); // closes fd
580 return false;
581 }
582 rate_ = rate;
583 n_channels_ = channels;
584 bool setup_ok = true;
585 setup_ok &= FLAC__stream_encoder_set_channels (enc_, n_channels_);
586 setup_ok &= FLAC__stream_encoder_set_bits_per_sample (enc_, 24);
587 setup_ok &= FLAC__stream_encoder_set_sample_rate (enc_, rate_);
588 setup_ok &= FLAC__stream_encoder_set_compression_level (enc_, compresion);
589 StringS tags;
590 tags.insert (tags.begin(), "ENCODER=Anklang-" + String (ase_version_short));
591 metadata_[0] = FLAC__metadata_object_new (FLAC__METADATA_TYPE_VORBIS_COMMENT);
592 for (const auto &tag : tags)
593 {
594 FLAC__StreamMetadata_VorbisComment_Entry entry = { uint32_t (tag.size()), (uint8_t*) tag.data() };
595 setup_ok &= FLAC__metadata_object_vorbiscomment_append_comment (metadata_[0], entry, true);
596 }
597 setup_ok &= FLAC__stream_encoder_set_metadata (enc_, metadata_, 1);
598 printerr ("%s:%d: ok=%d\n", __FILE__, __LINE__, setup_ok);
599 setup_ok &= FLAC__STREAM_ENCODER_INIT_STATUS_OK == FLAC__stream_encoder_init_FILE (enc_, file, nullptr, nullptr);
600 printerr ("%s:%d: ok=%d\n", __FILE__, __LINE__, setup_ok);
601 if (setup_ok)
602 return true;
603 cleanup(); // deletes enc_ which closes file
604 errno = EINVAL;
605 return false;
606 }
607 ssize_t
608 write (const float *frames, size_t n_frames) override
609 {
610 errno = EINVAL;
611 return_unless (enc_ != nullptr, -1);
612 return_unless (n_frames, 0);
613 assert_return (frames, -1);
614 const size_t n_samples = n_frames * n_channels_;
615 if (ibuffer_.size() < n_samples)
616 ibuffer_.resize (n_samples);
617 for (size_t i = 0; i < n_samples; i++)
618 {
619 int32_t v = frames[i] * 8388607.5 - 0.5;
620 ibuffer_[i] = CLAMP (v, -8388608, +8388607);
621 }
622 const bool ok = FLAC__stream_encoder_process_interleaved (enc_, ibuffer_.data(), n_frames);
623 return ok;
624 }
625 String
626 name() const override
627 {
628 return name_;
629 }
630};
631
633wave_writer_create_flac (int rate, int channels, const String &filename, int mode, int compresion)
634{
635 auto ow = std::make_shared<FlacWriter> (filename);
636 if (ow->create (mode, rate, channels, compresion) == false)
637 return nullptr;
638 return ow;
639}
640
641} // Ase
#define EBUSY
T begin(T... args)
T c_str(T... args)
T data(T... args)
T empty(T... args)
errno
fclose
fdopen
T insert(T... args)
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
Definition internal.hh:29
#define MIN(a, b)
Yield minimum of a and b.
Definition internal.hh:55
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
Definition internal.hh:71
#define CLAMP(v, mi, ma)
Yield v clamped to [mi … ma].
Definition internal.hh:58
lseek
The Anklang C++ API namespace.
Definition api.hh:9
void floatfill(float *dst, float f, size_t n)
Fill n values of dst with f.
Definition datautils.hh:29
std::string String
Convenience alias for std::string.
Definition cxxaux.hh:35
uint32_t uint
Provide 'uint' as convenience type.
Definition cxxaux.hh:18
void fast_copy(size_t n, float *d, const float *s)
Copy a block of floats.
Definition datautils.hh:37
uint64_t random_int64()
open
puts
T size(T... args)
typedef uint8_t
strncpy
strerror
strlen
typedef ssize_t