Audacity 3.2.0
ExportOpus.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 SPDX-License-Identifier: GPL-2.0-or-later
4
5 Audacity: A Digital Audio Editor
6
7 ExportOpus.cpp
8
9 Dmitry Vedenko
10
11**********************************************************************/
12
13#include "Export.h"
14
15#include <random>
16#include <string_view>
17
18#include <ogg/ogg.h>
19#include <opus/opus.h>
20#include <opus/opus_multistream.h>
21
22#include "wxFileNameWrapper.h"
23#include "Mix.h"
24
25#include "MemoryX.h"
26
27#include "Track.h"
28#include "Tags.h"
29
30#include "ExportPluginHelpers.h"
31#include "ExportOptionsEditor.h"
33
35
36#include "CodeConversions.h"
37
38
39namespace
40{
41
43{
44 switch (error)
45 {
46 case OPUS_OK:
47 return XO("no error");
48 case OPUS_BAD_ARG:
49 return XO("invalid argument");
50 case OPUS_BUFFER_TOO_SMALL:
51 return XO("buffer too small");
52 case OPUS_INTERNAL_ERROR:
53 return XO("internal error");
54 case OPUS_INVALID_PACKET:
55 return XO("invalid packet");
56 case OPUS_UNIMPLEMENTED:
57 return XO("not implemented");
58 case OPUS_INVALID_STATE:
59 return XO("invalid state");
60 case OPUS_ALLOC_FAIL:
61 return XO("memory allocation has failed");
62 default:
63 return XO("Unknown error");
64 }
65}
66
67[[noreturn]] void FailExport(const TranslatableString& title, int errorCode = 0)
68{
69 if (errorCode != 0)
70 {
71 throw ExportException(Verbatim("%s: %s")
73 .Translation());
74 }
75
76 throw ExportException(title.Translation());
77}
78
79/* i18n-hint: kbps abbreviates "thousands of bits per second" */
81{
82 return XO("%d kbps").Format(n);
83}
84
85enum : int
86{
93};
94
95namespace VBRMode
96{
97enum : int
98{
101 CVBR
103}
104
105const std::initializer_list<PlainExportOptionsEditor::OptionDesc> OPUSOptions {
106 {
107 {
108 OPUSOptionIDBitRate, XO("Bit Rate"),
109 OPUS_AUTO,
111 {
112 6000,
113 8000,
114 16000,
115 24000,
116 32000,
117 40000,
118 48000,
119 64000,
120 80000,
121 96000,
122 128000,
123 160000,
124 192000,
125 256000,
126 OPUS_AUTO,
127 OPUS_BITRATE_MAX,
128 },
129 {
130 n_kbps( 6 ),
131 n_kbps( 8 ),
132 n_kbps( 16 ),
133 n_kbps( 24 ),
134 n_kbps( 32 ),
135 n_kbps( 40 ),
136 n_kbps( 48 ),
137 n_kbps( 64 ),
138 n_kbps( 80 ),
139 n_kbps( 96 ),
140 n_kbps( 128 ),
141 n_kbps( 160 ),
142 n_kbps( 192 ),
143 n_kbps( 256 ),
144 XO("Auto"),
145 XO("Maximum")
146 }
147 }, wxT("/FileFormats/OPUS/Bitrate")
148 },
149 {
150 {
151 OPUSOptionIDQuality, XO("Quality"),
152 10,
154 { 0, 10 }
155 }, wxT("/FileFormats/OPUS/Quality")
156 },
157 {
158 {
159 OPUSOptionIDFrameDuration, XO("Frame Duration"),
160 200,
162 {
163 25,
164 50,
165 100,
166 200,
167 400,
168 600,
169 },
170 {
171 XO("2.5 ms"),
172 XO("5 ms"),
173 XO("10 ms"),
174 XO("20 ms"),
175 XO("40 ms"),
176 XO("60 ms"),
177 }
178 }, wxT("/FileFormats/OPUS/FrameDuration")
179 },
180 {
181 {
182 OPUSOptionIDVBRMode, XO("VBR Mode"),
186 { XO("Off"), XO("On"), XO("Constrained") }
187 }, wxT("/FileFormats/OPUS/VbrMode")
188 },
189 {
190 {
191 OPUSOptionIDApplication, XO("Optimize for"),
192 OPUS_APPLICATION_AUDIO,
194 { OPUS_APPLICATION_VOIP, OPUS_APPLICATION_AUDIO, OPUS_APPLICATION_RESTRICTED_LOWDELAY },
195 { XO("Speech"), XO("Audio"), XO("Low Delay") }
196 }, wxT("/FileFormats/OPUS/Application")
197 },
198 {
199 {
200 OPUSOptionIDCutoff, XO("Cutoff"),
201 OPUS_AUTO,
203 {
204 OPUS_AUTO,
205 OPUS_BANDWIDTH_NARROWBAND,
206 OPUS_BANDWIDTH_MEDIUMBAND,
207 OPUS_BANDWIDTH_WIDEBAND,
208 OPUS_BANDWIDTH_SUPERWIDEBAND,
209 OPUS_BANDWIDTH_FULLBAND,
210 },
211 {
212 XO("Auto"),
213 XO("Narrowband"),
214 XO("Mediumband"),
215 XO("Wideband"),
216 XO("Super Wideband"),
217 XO("Fullband")
218 }
219 }, wxT("/FileFormats/OPUS/Cutoff")
220 },
221};
222
223constexpr int supportedSampleRates[] = { 8000, 12000, 16000, 24000, 48000 };
224
226{
227 for (auto sr : supportedSampleRates)
228 if (sr == sampleRate)
229 return true;
230 return false;
231}
232}
233
235{
236 struct OggPacket final
237 {
238 using byte_type = std::remove_pointer_t<decltype(ogg_packet::packet)>;
239 static_assert(sizeof(byte_type) == 1);
240
241 OggPacket(int64_t packetNo, bool resizable)
242 : resizable { resizable }
243 {
244 packet.packetno = packetNo;
245 }
246
247 explicit OggPacket(int64_t packetNo)
248 : OggPacket { packetNo, false }
249 {
250 }
251
252 OggPacket(int64_t packetNo, long size, bool resizable)
253 : OggPacket { packetNo, resizable }
254 {
255 Resize(size);
256 }
257
258 void Resize(long size)
259 {
260 buffer.resize(size);
261 packet.packet = buffer.data();
262 }
263
264 void Reset() noexcept
265 {
266 packet.bytes = 0;
267 }
268
269 void MarkBOS() noexcept
270 {
271 packet.b_o_s = 1;
272 }
273
274 void MarkEOS() noexcept
275 {
276 packet.e_o_s = 1;
277 }
278
279 void Write(const void* data, const long length)
280 {
281 const auto nextPos = packet.bytes + length;
282
283 if (nextPos > buffer.size())
284 {
285 if (resizable)
286 Resize(std::max<size_t>(1024, buffer.size() * 2));
287 else
289 XO("Buffer overflow in OGG packet"), OPUS_BUFFER_TOO_SMALL);
290 }
291
292 std::copy(
293 reinterpret_cast<const byte_type*>(data),
294 reinterpret_cast<const byte_type*>(data) + length,
295 buffer.data() + packet.bytes);
296
297 packet.bytes = nextPos;
298 }
299
300 template<typename IntType>
301 void Write(IntType value)
302 {
303 static_assert(std::is_integral_v<IntType>);
304 if constexpr (sizeof (IntType) == 1)
305 {
306 Write(&value, 1);
307 }
308 else
309 {
310 if (IsLittleEndian ())
311 {
312 Write(&value, sizeof (IntType));
313 }
314 else
315 {
316 IntType swapped = SwapIntBytes(value);
317 Write(&swapped, sizeof (IntType));
318 }
319 }
320 }
321
323 {
324 return buffer.data();
325 }
326
327 size_t GetBufferSize() const
328 {
329 return buffer.size();
330 }
331
332 ogg_packet packet {};
333
334 private:
335 std::vector<byte_type> buffer;
336 bool resizable { false };
337 };
338
339 struct
340 {
342 int32_t sampleRate {};
343
344 double t0 {};
345 double t1 {};
346 unsigned numChannels {};
348 wxFile outFile;
349 std::unique_ptr<Mixer> mixer;
350 std::unique_ptr<Tags> metadata;
351
352 // Encoder properties
353 struct OpusState final
354 {
355 ~OpusState()
356 {
357 if (encoder != nullptr)
358 opus_multistream_encoder_destroy(encoder);
359 }
360
361 OpusMSEncoder* encoder {};
362
363 int32_t frameSize {};
365 uint16_t preskip {};
366 uint8_t channelMapping {};
367 uint8_t nbStreams {};
368 uint8_t nbCoupled {};
369 uint8_t streamMap[255] {};
371
372 // Bitstream properties
373 struct OggState final
374 {
375 OggState()
376 // Audio always starts in the packet #3.
377 // The first one is for opus header, the second one is for tags,
378 // both are mandatory
380 {
381 // As per OGG docs - stream serialno should be a random
382 // number. It is used to stitch the streams, this doesn't
383 // matter much for Audacity
384 std::mt19937 gen(std::time(nullptr));
385 ogg_stream_init(&stream, gen());
386 }
387
388 void PacketIn(const OggPacket& packet)
389 {
390 ogg_stream_packetin(&stream,
391 // C libraries are not always const-correct
392 const_cast<ogg_packet*>(&packet.packet));
393 }
394
395 void WriteOut(wxFile& outputStream)
396 {
397 ogg_page page {};
398
399 while (ogg_stream_pageout(&stream, &page))
400 WritePage(outputStream, page);
401 }
402
403 void Flush(wxFile& outputStream)
404 {
405 ogg_page page {};
406
407 while (ogg_stream_flush(&stream, &page))
408 WritePage(outputStream, page);
409 }
410
411 ogg_stream_state stream;
412
414
415 private:
416 void WritePage(wxFile& outputStream, const ogg_page& page)
417 {
418 if (
419 outputStream.Write(page.header, page.header_len) !=
420 page.header_len)
421 FailExport(XO("Unable to write OGG page header"));
422
423 if (outputStream.Write(page.body, page.body_len) != page.body_len)
424 FailExport(XO("Unable to write OGG page"));
425 }
427
428 std::vector<float> encodeBuffer;
430
431 void WriteOpusHeader();
432 void WriteTags();
433
434 int32_t GetBestFrameSize(int32_t samplesCount) const noexcept
435 {
436 static const int32_t multipliers[] = {
437 25, 50, 100, 200, 400, 600,
438 };
439
440 const auto sampleRate = context.sampleRate;
441
442 for (auto multiplier : multipliers)
443 {
444 const auto frameSize = multiplier * sampleRate / 10000;
445
446 if (samplesCount <= frameSize)
447 return frameSize;
448 }
449
450 return 60 * sampleRate / 1000;
451 }
452
453public:
454
456
458 const Parameters& parameters,
459 const wxFileNameWrapper& filename,
460 double t0, double t1, bool selectedOnly,
461 double sampleRate, unsigned channels,
462 MixerOptions::Downmix* mixerSpec,
463 const Tags* tags) override;
464
465 ExportResult Process(ExportProcessorDelegate& delegate) override;
466
467};
468
469class ExportOpus final : public ExportPlugin
470{
471public:
472
474
475 int GetFormatCount() const override;
476 FormatInfo GetFormatInfo(int) const override;
477
478 std::vector<std::string> GetMimeTypes(int) const override;
479
480 std::unique_ptr<ExportOptionsEditor>
482
483 std::unique_ptr<ExportProcessor> CreateProcessor(int format) const override;
484};
485
486ExportOpus::ExportOpus() = default;
487
489{
490 return 1;
491}
492
494{
495 return {
496 wxT("Opus"), XO("Opus Files"), { wxT("opus") }, 255, true
497 };
498}
499
500std::vector<std::string> ExportOpus::GetMimeTypes(int) const
501{
502 return { "audio/opus" };
503}
504
505std::unique_ptr<ExportOptionsEditor>
507{
508 return std::make_unique<PlainExportOptionsEditor>(
510 ExportOptionsEditor::SampleRateList { 8000, 12000, 16000, 24000, 48000 },
511 listener);
512}
513
514std::unique_ptr<ExportProcessor> ExportOpus::CreateProcessor(int) const
515{
516 return std::make_unique<OpusExportProcessor>();
517}
518
519
521{
522 const auto headerSize =
523 // "OpusHead"
524 8 +
525 // Version number (always 1)
526 1 +
527 // Channels count
528 1 +
529 // Preskip
530 2 +
531 // Input sample rate
532 4 +
533 // Output gain (always 0)
534 2 +
535 // Channel mapping
536 1 +
537 (context.opus.channelMapping == 0 ? 0 :
538 (
539 // Stream count
540 1 +
541 // Two channel stream count
542 1 +
543 // Channel mapping
544 context.numChannels));
545
546 OggPacket headerPacket(0, headerSize, false);
547 // Header must have beginning-of-stream marker
548 headerPacket.MarkBOS();
549
550 headerPacket.Write("OpusHead", 8);
551 headerPacket.Write<uint8_t>(1);
552 headerPacket.Write<uint8_t>(context.numChannels);
553 headerPacket.Write(context.opus.preskip);
554 // Should we put the project sample rate here?
555 headerPacket.Write(context.sampleRate);
556 // Opus docs recommend encoders to use 0 as a gain
557 headerPacket.Write<uint16_t>(0);
558 headerPacket.Write(context.opus.channelMapping);
559
560 if (context.opus.channelMapping > 0)
561 {
562 headerPacket.Write(context.opus.nbStreams);
563 headerPacket.Write(context.opus.nbCoupled);
564
565 for (int i = 0; i < context.numChannels; ++i)
566 headerPacket.Write<uint8_t>(context.opus.streamMap[i]);
567 }
568
569 // This is guaranteed by the way we calculate the header size
570 assert(headerPacket.packet.bytes == headerSize);
571
572 context.ogg.PacketIn(headerPacket);
573 context.ogg.Flush(context.outFile);
574}
575
577{
578 OggPacket commentsPacket { 1, true };
579
580 commentsPacket.Write("OpusTags", 8);
581
582 const std::string_view vendor { opus_get_version_string() };
583
584 commentsPacket.Write<uint32_t>(vendor.size());
585 commentsPacket.Write(vendor.data(), vendor.size());
586
587 commentsPacket.Write<uint32_t>(context.metadata->Count());
588
589 for (const auto& pair : context.metadata->GetRange())
590 {
591 const auto key = pair.first == TAG_YEAR ? std::string("DATE") :
592 audacity::ToUTF8(pair.first);
593
594 const auto value = audacity::ToUTF8(pair.second);
595
596 commentsPacket.Write<uint32_t>(key.size() + value.size() + 1);
597 commentsPacket.Write(key.data(), key.size());
598 commentsPacket.Write("=", 1);
599 commentsPacket.Write(value.data(), value.size());
600 }
601
602 context.ogg.PacketIn(commentsPacket);
603 context.ogg.Flush(context.outFile);
604}
605
607{
608
609}
610
612 AudacityProject& project, const Parameters& parameters,
613 const wxFileNameWrapper& fName, double t0, double t1, bool selectionOnly,
614 double sampleRate, unsigned numChannels, MixerOptions::Downmix* mixerSpec,
615 const Tags* metadata)
616{
617 context.sampleRate = int32_t(sampleRate);
618
619 if (!IsValidSampleRate(context.sampleRate))
620 throw ExportException(XO("Unsupported sample rate").Translation());
621
622 context.t0 = t0;
623 context.t1 = t1;
624 context.numChannels = numChannels;
625 context.fName = fName;
626
627 // Internally the Opus is always in 48k, find out the multiplier for
628 // values, that expect 48k sample rate
629 context.opus.sampleRateFactor = 48000 / context.sampleRate;
630
631 const auto bitRate = ExportPluginHelpers::GetParameterValue<int>(
632 parameters, OPUSOptionIDBitRate, OPUS_AUTO);
633 const auto vbrMode = ExportPluginHelpers::GetParameterValue<int>(
634 parameters, OPUSOptionIDVBRMode, VBRMode::VBR);
635 const int complexity = ExportPluginHelpers::GetParameterValue<int>(
636 parameters, OPUSOptionIDQuality, 10);
637 const int frameMultiplier = ExportPluginHelpers::GetParameterValue<int>(
638 parameters, OPUSOptionIDFrameDuration, 200);
639 const int application = ExportPluginHelpers::GetParameterValue<int>(
640 parameters, OPUSOptionIDApplication, OPUS_APPLICATION_AUDIO);
641 const int cutoff = ExportPluginHelpers::GetParameterValue<int>(
642 parameters, OPUSOptionIDCutoff, OPUS_AUTO);
643
644 // Number of samples per frame per channel
645 context.opus.frameSize = frameMultiplier * context.sampleRate / 10000;
646
647 context.status = selectionOnly ? XO("Exporting selected audio as Opus") :
648 XO("Exporting the audio as Opus");
649
650 // Create opus encoder
651 int error;
652
653 if (numChannels <= 2)
654 {
655 context.opus.channelMapping = 0;
656 context.opus.nbStreams = 1;
657 context.opus.nbCoupled = numChannels - 1;
658 context.opus.streamMap[0] = 0;
659 context.opus.streamMap[1] = 1;
660
661 context.opus.encoder = opus_multistream_encoder_create(
662 sampleRate, numChannels, context.opus.nbStreams,
663 context.opus.nbCoupled, context.opus.streamMap, application, &error);
664 }
665 else
666 {
667 context.opus.channelMapping = numChannels <= 8 ? 1 : 255;
668
669 int nbStreams {}, nbCoupled {};
670
671 context.opus.encoder = opus_multistream_surround_encoder_create(
672 sampleRate, numChannels, context.opus.channelMapping,
674 context.opus.streamMap, application, &error);
675
676 // opus_multistream_surround_encoder_create is expected to fill
677 // stream count with values in [0, 255]
678 context.opus.nbStreams = uint8_t(nbStreams);
679 context.opus.nbCoupled = uint8_t(nbCoupled);
680 }
681
682 if (error != OPUS_OK)
683 FailExport(XO("Unable to create Opus encoder"), error);
684
685 error = opus_multistream_encoder_ctl(
686 context.opus.encoder, OPUS_SET_BITRATE(bitRate));
687
688 if (error != OPUS_OK)
689 FailExport(XO("Unable to set bitrate"), error);
690
691 error = opus_multistream_encoder_ctl(
692 context.opus.encoder, OPUS_SET_COMPLEXITY(complexity));
693
694 if (error != OPUS_OK)
695 FailExport(XO("Unable to set complexity"), error);
696
697 error = opus_multistream_encoder_ctl(
698 context.opus.encoder, OPUS_SET_BANDWIDTH(cutoff));
699
700 if (error != OPUS_OK)
701 FailExport(XO("Unable to set bandwidth"), error);
702
703 error = opus_multistream_encoder_ctl(
704 context.opus.encoder, OPUS_SET_VBR(vbrMode == VBRMode::CBR ? 0 : 1));
705
706 if (error != OPUS_OK)
707 FailExport(XO("Unable to set VBR mode"), error);
708
709 if (vbrMode == VBRMode::CVBR)
710 {
711 error = opus_multistream_encoder_ctl(
712 context.opus.encoder, OPUS_SET_VBR_CONSTRAINT(1));
713
714 if (error != OPUS_OK)
715 FailExport(XO("Unable to set CVBR mode"), error);
716 }
717
718 // Calculate the encoder latency. This value is needed in header
719 // and to flush the encoder
720 int lookahead {};
721 error = opus_multistream_encoder_ctl(
722 context.opus.encoder, OPUS_GET_LOOKAHEAD(&lookahead));
723
724 if (error != OPUS_OK)
725 FailExport(XO("Unable to get lookahead"), error);
726
727 // Latency is always in 48k encoded samples
728 const auto calculatedPreskip = lookahead * context.opus.sampleRateFactor;
729 if (
730 calculatedPreskip < 0 ||
731 calculatedPreskip >= std::numeric_limits<uint16_t>::max())
732 FailExport(XO("Failed to calculate correct preskip"), OPUS_BAD_ARG);
733 // It is safe to cast to uint16_t here
734 context.opus.preskip = uint16_t(calculatedPreskip);
735
736 // Resize the audio packet so it can contain all the raw data.
737 // This is overkill, but should be enough to hold all the data from
738 // the encode float
739 context.ogg.audioStreamPacket.Resize(
740 context.opus.frameSize * sizeof(float) * numChannels);
741
742
743 // Try to open the file for writing
744 if (
745 !context.outFile.Create(fName.GetFullPath(), true) ||
746 !context.outFile.IsOpened())
747 {
748 throw ExportException(_("Unable to open target file for writing"));
749 }
750
752
753 context.metadata = std::make_unique<Tags>(
754 metadata == nullptr ? Tags::Get(project) : *metadata);
755
756 WriteTags();
757
759 project, selectionOnly, t0, t1, numChannels, context.opus.frameSize, true,
760 sampleRate, floatSample, mixerSpec);
761
762 return true;
763}
764
766{
767 delegate.SetStatusString(context.status);
768
769 auto exportResult = ExportResult::Success;
770
771 int64_t granulePos = 0;
772
773 int32_t latencyLeft = context.opus.preskip;
774
775 while (exportResult == ExportResult::Success)
776 {
777 auto samplesThisRun = context.mixer->Process();
778
779 if (samplesThisRun == 0)
780 break;
781
782 auto mixedAudioBuffer =
783 reinterpret_cast<const float*>(context.mixer->GetBuffer());
784
785 // bestFrameSize <= context.opus.frameSize by design
786 auto bestFrameSize = GetBestFrameSize(samplesThisRun);
787
788 if (samplesThisRun < bestFrameSize)
789 {
790 // Opus expects that the full frame is passed to the encoder, fill missing data with zeroes
791 context.encodeBuffer.resize(bestFrameSize * context.numChannels);
792
793 std::copy(
794 mixedAudioBuffer, mixedAudioBuffer + samplesThisRun * context.numChannels,
795 context.encodeBuffer.begin());
796
797 std::fill(
798 context.encodeBuffer.begin() + samplesThisRun * context.numChannels,
799 context.encodeBuffer.begin() + bestFrameSize * context.numChannels,
800 0);
801
802 mixedAudioBuffer = context.encodeBuffer.data();
803
804 auto zeroesCount = bestFrameSize - int32_t(samplesThisRun);
805
806 if (zeroesCount < latencyLeft)
807 samplesThisRun += zeroesCount;
808 else
809 samplesThisRun += latencyLeft;
810
811 // Reduce the latency by the number of zeroes pushed (potentially
812 // removing the need to flush the encoder)
813 latencyLeft = std::max(0, latencyLeft - zeroesCount);
814 }
815
816 auto result = opus_multistream_encode_float(
817 context.opus.encoder, mixedAudioBuffer, bestFrameSize,
818 context.ogg.audioStreamPacket.GetBuffer(),
819 context.ogg.audioStreamPacket.GetBufferSize());
820
821 if (result < 0)
822 FailExport(XO("Failed to encode input buffer"), result);
823
824 // granulePos is the index of the last real sample in the packet at 48k rate
825 granulePos += samplesThisRun * context.opus.sampleRateFactor;
826
827 context.ogg.audioStreamPacket.packet.bytes = result;
828 context.ogg.audioStreamPacket.packet.granulepos = granulePos;
829
830 if (latencyLeft == 0)
831 context.ogg.audioStreamPacket.MarkEOS();
832
833 context.ogg.PacketIn(context.ogg.audioStreamPacket);
834 context.ogg.WriteOut(context.outFile);
835
836 context.ogg.audioStreamPacket.packet.packetno++;
837
839 delegate, *context.mixer, context.t0, context.t1);
840 }
841
842 // Flush the encoder
843
844 while (latencyLeft > 0)
845 {
846 auto frameSize = GetBestFrameSize(latencyLeft);
847
848 context.encodeBuffer.resize(frameSize * context.numChannels);
849
850 std::fill(
851 context.encodeBuffer.begin(),
852 context.encodeBuffer.begin() + frameSize * context.numChannels, 0);
853
854 auto samplesOut = std::min(latencyLeft, frameSize);
855
856 auto result = opus_multistream_encode_float(
857 context.opus.encoder, context.encodeBuffer.data(),
858 frameSize, context.ogg.audioStreamPacket.GetBuffer(),
859 context.ogg.audioStreamPacket.GetBufferSize());
860
861 if (result < 0)
862 FailExport(XO("Failed to encode input buffer"), result);
863
864 granulePos += samplesOut * context.opus.sampleRateFactor;
865
866 context.ogg.audioStreamPacket.packet.bytes = result;
867 context.ogg.audioStreamPacket.packet.granulepos = granulePos;
868
869 if (latencyLeft == samplesOut)
870 context.ogg.audioStreamPacket.MarkEOS();
871
872 context.ogg.PacketIn(context.ogg.audioStreamPacket);
873 context.ogg.WriteOut(context.outFile);
874
875 context.ogg.audioStreamPacket.packet.packetno++;
876
877 latencyLeft -= samplesOut;
878 }
879
880 context.ogg.Flush(context.outFile);
881
882 if (!context.outFile.Close())
883 return ExportResult::Error;
884
885 return exportResult;
886}
887
888
890 []{ return std::make_unique< ExportOpus >(); }
891};
wxT("CloseDown"))
Declare functions to perform UTF-8 to std::wstring conversions.
int min(int a, int b)
TranslatableString n_kbps(int n)
Definition: ExportMP3.cpp:121
static ExportPluginRegistry::RegisteredPlugin sRegisteredPlugin
Definition: ExportOpus.cpp:889
ExportResult
Definition: ExportTypes.h:24
XO("Cut/Copy/Paste")
#define _(s)
Definition: Internat.h:73
constexpr IntType SwapIntBytes(IntType value) noexcept
Swap bytes in an integer.
Definition: MemoryX.h:377
bool IsLittleEndian() noexcept
Check that machine is little-endian.
Definition: MemoryX.h:368
static const AudacityProject::AttachedObjects::RegisteredFactory key
static const auto title
#define TAG_YEAR
Definition: Tags.h:62
const auto project
declares abstract base class Track, TrackList, and iterators over TrackList
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
Listener object that is used to report on option changes.
std::vector< int > SampleRateList
FormatInfo GetFormatInfo(int) const override
Returns FormatInfo structure for given index if it's valid, or a default one. FormatInfo::format isn'...
Definition: ExportOpus.cpp:493
std::vector< std::string > GetMimeTypes(int) const override
Definition: ExportOpus.cpp:500
std::unique_ptr< ExportOptionsEditor > CreateOptionsEditor(int, ExportOptionsEditor::Listener *) const override
Creates format-dependent options editor, that is used to create a valid set of parameters to be used ...
Definition: ExportOpus.cpp:506
std::unique_ptr< ExportProcessor > CreateProcessor(int format) const override
Definition: ExportOpus.cpp:514
int GetFormatCount() const override
Definition: ExportOpus.cpp:488
static ExportResult UpdateProgress(ExportProcessorDelegate &delegate, Mixer &mixer, double t0, double t1)
Sends progress update to delegate and retrieves state update from it. Typically used inside each expo...
static std::unique_ptr< Mixer > CreateMixer(const AudacityProject &project, bool selectionOnly, double startTime, double stopTime, unsigned numOutChannels, size_t outBufferSize, bool outInterleaved, double outRate, sampleFormat outFormat, MixerOptions::Downmix *mixerSpec)
virtual void SetStatusString(const TranslatableString &str)=0
std::vector< std::tuple< ExportOptionID, ExportValue > > Parameters
Definition: ExportPlugin.h:93
Abstract base class used in importing a file.
A matrix of booleans, one row per input channel, column per output.
Definition: MixerOptions.h:32
OggPacket audioStreamPacket
Definition: ExportOpus.cpp:413
OpusMSEncoder * encoder
Definition: ExportOpus.cpp:361
std::unique_ptr< Tags > metadata
Definition: ExportOpus.cpp:350
ogg_stream_state stream
Definition: ExportOpus.cpp:411
struct OpusExportProcessor::@180::OggState ogg
bool Initialize(AudacityProject &project, const Parameters &parameters, const wxFileNameWrapper &filename, double t0, double t1, bool selectedOnly, double sampleRate, unsigned channels, MixerOptions::Downmix *mixerSpec, const Tags *tags) override
Called before start processing.
Definition: ExportOpus.cpp:611
TranslatableString status
Definition: ExportOpus.cpp:341
std::unique_ptr< Mixer > mixer
Definition: ExportOpus.cpp:349
struct OpusExportProcessor::@180::OpusState opus
std::vector< float > encodeBuffer
Definition: ExportOpus.cpp:428
int32_t GetBestFrameSize(int32_t samplesCount) const noexcept
Definition: ExportOpus.cpp:434
uint8_t streamMap[255]
Definition: ExportOpus.cpp:369
ExportResult Process(ExportProcessorDelegate &delegate) override
Definition: ExportOpus.cpp:765
struct OpusExportProcessor::@180 context
wxFileNameWrapper fName
Definition: ExportOpus.cpp:347
ID3 Tags (for MP3)
Definition: Tags.h:73
static Tags & Get(AudacityProject &project)
Definition: Tags.cpp:214
Holds a msgid for the translation catalog; may also bind format arguments.
void FailExport(const TranslatableString &title, int errorCode=0)
Definition: ExportOpus.cpp:67
TranslatableString GetOpusEncErrorString(int error)
Definition: ExportOpus.cpp:42
const std::initializer_list< PlainExportOptionsEditor::OptionDesc > OPUSOptions
Definition: ExportOpus.cpp:105
bool IsValidSampleRate(int sampleRate) noexcept
Definition: ExportOpus.cpp:225
std::string ToUTF8(const std::wstring &wstr)
void copy(const T *src, T *dst, int32_t n)
Definition: VectorOps.h:40
@ TypeEnum
List/enum option. values holds items, and names text to be displayed.
Definition: ExportTypes.h:48
@ TypeRange
Range option. values holds [min, max].
Definition: ExportTypes.h:47
OggPacket(int64_t packetNo, bool resizable)
Definition: ExportOpus.cpp:241
std::remove_pointer_t< decltype(ogg_packet::packet)> byte_type
Definition: ExportOpus.cpp:238
void Write(const void *data, const long length)
Definition: ExportOpus.cpp:279
std::vector< byte_type > buffer
Definition: ExportOpus.cpp:335
void Write(IntType value)
Definition: ExportOpus.cpp:301
OggPacket(int64_t packetNo, long size, bool resizable)
Definition: ExportOpus.cpp:252