Audacity 3.2.0
ImportFFmpeg.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5ImportFFmpeg.cpp
6
7Copyright 2008 LRN
8Based on ImportFLAC.cpp by Sami Liedes and transcode_sample.c by ANYwebcam Pty Ltd
9Licensed under the GNU General Public License v2 or later
10
11*//****************************************************************//****************************************************************//*******************************************************************/
22
23#include "FFmpeg.h"
24#include "FFmpegFunctions.h"
25
26#include <wx/log.h>
27#include <wx/window.h>
28
29#define DESC XO("FFmpeg-compatible files")
30
31//TODO: remove non-audio extensions
32static const auto exts = {
33 wxT("4xm"),
34 wxT("MTV"),
35 wxT("roq"),
36 wxT("aac"),
37 wxT("ac3"),
38 wxT("aif"),
39 wxT("aiff"),
40 wxT("afc"),
41 wxT("aifc"),
42 wxT("al"),
43 wxT("amr"),
44 wxT("apc"),
45 wxT("ape"),
46 wxT("apl"),
47 wxT("mac"),
48 wxT("asf"),
49 wxT("wmv"),
50 wxT("wma"),
51 wxT("au"),
52 wxT("avi"),
53 wxT("avs"),
54 wxT("bethsoftvid"),
55 wxT("c93"),
56 wxT("302"),
57 wxT("daud"),
58 wxT("dsicin"),
59 wxT("dts"),
60 wxT("dv"),
61 wxT("dxa"),
62 wxT("ea"),
63 wxT("cdata"),
64 wxT("ffm"),
65 wxT("film_cpk"),
66 wxT("flac"),
67 wxT("flic"),
68 wxT("flv"),
69 wxT("gif"),
70 wxT("gxf"),
71 wxT("idcin"),
72 wxT("image2"),
73 wxT("image2pipe"),
74 wxT("cgi"),
75 wxT("ipmovie"),
76 wxT("nut"),
77 wxT("lmlm4"),
78 wxT("m4v"),
79 wxT("mkv"),
80 wxT("mm"),
81 wxT("mmf"),
82 wxT("mov"),
83 wxT("mp4"),
84 wxT("m4a"),
85 wxT("m4r"),
86 wxT("3gp"),
87 wxT("3g2"),
88 wxT("mj2"),
89 wxT("mp3"),
90 wxT("mpc"),
91 wxT("mpc8"),
92 wxT("mpg"),
93 wxT("mpeg"),
94 wxT("ts"),
95 wxT("mpegtsraw"),
96 wxT("mpegvideo"),
97 wxT("msnwctcp"),
98 wxT("ul"),
99 wxT("mxf"),
100 wxT("nsv"),
101 wxT("nuv"),
102 wxT("ogg"),
103 wxT("opus"),
104 wxT("psxstr"),
105 wxT("pva"),
106 wxT("redir"),
107 wxT("rl2"),
108 wxT("rm"),
109 wxT("ra"),
110 wxT("rv"),
111 wxT("rtsp"),
112 wxT("s16be"),
113 wxT("sw"),
114 wxT("s8"),
115 wxT("sb"),
116 wxT("sdp"),
117 wxT("shn"),
118 wxT("siff"),
119 wxT("vb"),
120 wxT("son"),
121 wxT("smk"),
122 wxT("sol"),
123 wxT("swf"),
124 wxT("thp"),
125 wxT("tiertexseq"),
126 wxT("tta"),
127 wxT("txd"),
128 wxT("u16be"),
129 wxT("uw"),
130 wxT("ub"),
131 wxT("u8"),
132 wxT("vfwcap"),
133 wxT("vmd"),
134 wxT("voc"),
135 wxT("wav"),
136 wxT("wc3movie"),
137 wxT("wsaud"),
138 wxT("wsvqa"),
139 wxT("wv")
140};
141
142// all the includes live here by default
143#include "Import.h"
144#include "Tags.h"
145#include "WaveTrack.h"
146#include "ImportPlugin.h"
147#include "ImportUtils.h"
149
151
155{
156public:
159 {
160 }
161
163
164 wxString GetPluginStringID() override { return wxT("libav"); }
166
168 {
169 return !FFmpegFunctions::Load()
170 ? XO("Try installing FFmpeg.\n") : TranslatableString{};
171 }
172
174 std::unique_ptr<ImportFileHandle> Open(
175 const FilePath &Filename, AudacityProject*) override;
176};
177
178struct StreamContext final
179{
180 int StreamIndex { -1 };
181
182 std::unique_ptr<AVCodecContextWrapper> CodecContext;
183
186
187 bool Use { true };
188};
189
192{
193
194public:
197
200 bool Init();
203 bool InitCodecs();
204
205
208
209 void Import(
210 ImportProgressListener& progressListener, WaveTrackFactory* trackFactory,
211 TrackHolders& outTracks, Tags* tags,
212 std::optional<LibFileFormats::AcidizerTags>& outAcidTags) override;
213
214 FilePath GetFilename() const override;
215
216 void Cancel() override;
217
218 void Stop() override;
219
222 void WriteData(StreamContext* sc, const AVPacketWrapper* packet);
223
227 void WriteMetadata(Tags *tags);
228
234 void GetMetadata(Tags &tags, const wxChar *tag, const char *name);
235
238 wxInt32 GetStreamCount() override
239 {
240 return static_cast<wxInt32>(mStreamContexts.size());
241 }
242
246 {
247 return mStreamInfo;
248 }
249
253 void SetStreamUsage(wxInt32 StreamID, bool Use) override
254 {
255 if (StreamID < static_cast<wxInt32>(mStreamContexts.size()))
256 mStreamContexts[StreamID].Use = Use;
257 }
258
259private:
260 // Construct this member first, so it is destroyed last, so the functions
261 // remain loaded while other members are destroyed
262 const std::shared_ptr<FFmpegFunctions> mFFmpeg = FFmpegFunctions::Load();
263
264 std::vector<StreamContext> mStreamContexts;
265
266 std::unique_ptr<AVFormatContextWrapper> mAVFormatContext;
267
269
270 wxInt64 mProgressPos = 0;
271 wxInt64 mProgressLen = 1;
272
273 bool mCancelled = false;
274 bool mStopped = false;
276 std::vector<TrackListHolder> mStreams;
277};
278
279
281{
282 return DESC;
283}
284
285std::unique_ptr<ImportFileHandle> FFmpegImportPlugin::Open(
286 const FilePath &filename, AudacityProject*)
287{
288 auto ffmpeg = FFmpegFunctions::Load();
289
290 //Check if we're loading explicitly supported format
291 wxString extension = filename.AfterLast(wxT('.'));
293 {
294 //Audacity is trying to load something that is declared as
295 //officially supported by this plugin.
296 //If we don't have FFmpeg configured - tell the user about it.
297 //Since this will be happening often, use disableable "FFmpeg not found" dialog
298 //insdead of usual AudacityMessageBox()
299 bool newsession = NewImportingSession.Read();
300 if (!ffmpeg)
301 {
302 auto dontShowDlg = FFmpegNotFoundDontShow.Read();
303 if (dontShowDlg == 0 && newsession)
304 {
306 gPrefs->Flush();
307 FFmpegNotFoundDialog{ nullptr }.ShowModal();
308
309 ffmpeg = FFmpegFunctions::Load();
310 }
311 }
312 }
313 if (!ffmpeg)
314 {
315 return nullptr;
316 }
317
318 // Construct the handle only after any reloading of ffmpeg functions
319 auto handle = std::make_unique<FFmpegImportFileHandle>(filename);
320
321 // Open the file for import
322 bool success = handle->Init();
323
324 if (!success) {
325 return nullptr;
326 }
327
328 return handle;
329}
330
332 std::make_unique< FFmpegImportPlugin >()
333};
334
335
337 : mName{ name }
338{
339}
340
342{
343 if (!mFFmpeg)
344 return false;
345
346 mAVFormatContext = mFFmpeg->CreateAVFormatContext();
347
348 const auto err = mAVFormatContext->OpenInputContext(mName, nullptr, AVDictionaryWrapper(*mFFmpeg));
349
351 {
352 wxLogError(wxT("FFmpeg : AVFormatContextWrapper::OpenInputContext() failed for file %s"), mName);
353 return false;
354 }
355
356 if (!InitCodecs())
357 return false;
358
359 return true;
360}
361
363{
364 for (unsigned int i = 0; i < mAVFormatContext->GetStreamsCount(); i++)
365 {
366 const AVStreamWrapper* stream = mAVFormatContext->GetStream(i);
367
368 if (stream->IsAudio())
369 {
370 const AVCodecIDFwd id = mAVFormatContext->GetStream(i)->GetAVCodecID();
371
372 auto codec = mFFmpeg->CreateDecoder(id);
373 auto name = mFFmpeg->avcodec_get_name(id);
374
375 if (codec == NULL)
376 {
377 wxLogError(
378 wxT("FFmpeg : CreateDecoder() failed. Index[%02d], Codec[%02x - %s]"),
379 i, id, name);
380 //FFmpeg can't decode this stream, skip it
381 continue;
382 }
383
384 auto codecContextPtr = stream->GetAVCodecContext();
385
386 if ( codecContextPtr->Open( codecContextPtr->GetCodec() ) < 0 )
387 {
388 wxLogError(wxT("FFmpeg : Open() failed. Index[%02d], Codec[%02x - %s]"),i,id,name);
389 //Can't open decoder - skip this stream
390 continue;
391 }
392
393 const int channels = codecContextPtr->GetChannels();
394 const sampleFormat preferredFormat =
395 codecContextPtr->GetPreferredAudacitySampleFormat();
396
397 auto codecContext = codecContextPtr.get();
398
399 mStreamContexts.emplace_back(
400 StreamContext { stream->GetIndex(), std::move(codecContextPtr),
401 channels, preferredFormat, true });
402
403 // Stream is decodeable and it is audio. Add it and its description to the arrays
404 int duration = 0;
405 if (stream->GetDuration() > 0)
406 duration = stream->GetDuration() * stream->GetTimeBase().num / stream->GetTimeBase().den;
407 else
408 duration = mAVFormatContext->GetDuration() / AUDACITY_AV_TIME_BASE;
409
410 wxString bitrate;
411 if (codecContext->GetBitRate() > 0)
412 bitrate.Printf(wxT("%d"),(int)codecContext->GetBitRate());
413 else
414 bitrate.Printf(wxT("?"));
415
416 AVDictionaryWrapper streamMetadata = stream->GetMetadata();
417
418 auto lang = std::string(streamMetadata.Get("language", {}));
419
420 auto strinfo = XO(
421/* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
422"Index[%02x] Codec[%s], Language[%s], Bitrate[%s], Channels[%d], Duration[%d]")
423 .Format(
424 stream->GetIndex(),
425 name,
426 lang,
427 bitrate,
428 (int)codecContext->GetChannels(),
429 (int)duration);
430
431 mStreamInfo.push_back(strinfo);
432 }
433 //for video and unknown streams do nothing
434 }
435 //It doesn't really returns false, but GetStreamCount() will return 0 if file is composed entirely of unreadable streams
436 return true;
437}
438
440{
441 return DESC;
442}
443
444
446{
447 // TODO: Get Uncompressed byte count.
448 return 0;
449}
450
452 ImportProgressListener& progressListener, WaveTrackFactory* trackFactory,
453 TrackHolders& outTracks, Tags* tags,
454 std::optional<LibFileFormats::AcidizerTags>&)
455{
456 outTracks.clear();
457 mCancelled = false;
458 mStopped = false;
459
461 mStreamContexts.erase (std::remove_if (mStreamContexts.begin (), mStreamContexts.end (), [](const StreamContext& ctx) {
462 return !ctx.Use;
463 }), mStreamContexts.end());
464
465 for(unsigned s = 0; s < mStreamContexts.size(); ++s)
466 {
467 const StreamContext& sc = mStreamContexts[s];
468
470 auto tracks = trackFactory->CreateMany(sc.InitialChannels, format, sc.CodecContext->GetSampleRate());
471
472
473 // Handles the start_time by creating silence. This may or may not be correct.
474 // There is a possibility that we should ignore first N milliseconds of audio instead. I do not know.
476
477 int64_t stream_delay = 0;
478
479 const int64_t streamStartTime =
480 mAVFormatContext->GetStream(sc.StreamIndex)->GetStartTime();
481
483 {
484 stream_delay = streamStartTime;
485
486 wxLogDebug(
487 wxT("Stream %d start_time = %lld, that would be %f milliseconds."),
488 s, (long long)streamStartTime, double(streamStartTime) / 1000);
489 }
490 if (stream_delay > 0)
491 {
492 for (auto track : *tracks)
493 {
494 track->InsertSilence(0, double(stream_delay) / AUDACITY_AV_TIME_BASE);
495 }
496 }
497
498 mStreams.push_back(tracks);
499 }
500
501 // This is the heart of the importing process
502
503 // Read frames.
504 for (std::unique_ptr<AVPacketWrapper> packet;
505 (packet = mAVFormatContext->ReadNextPacket()) != nullptr &&
506 !mCancelled && !mStopped;)
507 {
508 // Find a matching StreamContext
509 auto streamContextIt = std::find_if(
510 mStreamContexts.begin(), mStreamContexts.end(),
511 [index = packet->GetStreamIndex()](const StreamContext& ctx)
512 { return ctx.StreamIndex == index;
513 });
514
515 if (streamContextIt == mStreamContexts.end())
516 continue;
517
518 WriteData(&(*streamContextIt), packet.get());
519 if(mProgressLen > 0)
520 progressListener.OnImportProgress(static_cast<double>(mProgressPos) /
521 static_cast<double>(mProgressLen));
522 }
523
524 // Flush the decoders.
525 if (!mStreamContexts.empty() && !mCancelled)
526 {
527 auto emptyPacket = mFFmpeg->CreateAVPacketWrapper();
528
530 WriteData(&sc, emptyPacket.get());
531 }
532
533 if(mCancelled)
534 {
536 return;
537 }
538
539 // Copy audio from mStreams to newly created tracks (destroying mStreams elements in process)
540 for (auto& stream : mStreams)
541 {
542 ImportUtils::FinalizeImport(outTracks, std::move(*stream));
543 }
544 mStreams.clear();
545
546 // Save metadata
547 WriteMetadata(tags);
548 progressListener.OnImportResult(mStopped
551}
552
554{
555 return mName;
556}
557
559{
560 if(!mStopped)
561 mCancelled = true;
562}
563
565{
566 if(!mCancelled)
567 mStopped = true;
568}
569
571{
572 // Find the stream in mStreamContexts array
573 auto streamIt = std::find_if(
574 mStreamContexts.begin(),
575 mStreamContexts.end(),
576 [&](StreamContext& context) { return sc == &context; }
577 );
578
579 // Stream is not found. This should not really happen
580 if (streamIt == mStreamContexts.end())
581 {
582 //VS: Shouldn't this mean import failure?
583 return;
584 }
585 auto stream = mStreams[std::distance(mStreamContexts.begin(), streamIt)];
586
587 const auto nChannels = std::min(sc->CodecContext->GetChannels(), sc->InitialChannels);
588
589 // Write audio into WaveTracks
590 if (sc->SampleFormat == int16Sample)
591 {
592 auto data = sc->CodecContext->DecodeAudioPacketInt16(packet);
593 const auto channelsCount = sc->CodecContext->GetChannels();
594 const auto samplesPerChannel = data.size() / channelsCount;
595
596 unsigned chn = 0;
597 ImportUtils::ForEachChannel(*stream, [&](auto& channel)
598 {
599 if(chn >= nChannels)
600 return;
601
602 channel.AppendBuffer(
603 reinterpret_cast<samplePtr>(data.data() + chn),
604 sc->SampleFormat,
605 samplesPerChannel,
606 sc->CodecContext->GetChannels(),
607 sc->SampleFormat
608 );
609 ++chn;
610 });
611 }
612 else if (sc->SampleFormat == floatSample)
613 {
614 auto data = sc->CodecContext->DecodeAudioPacketFloat(packet);
615 const auto channelsCount = sc->CodecContext->GetChannels();
616 const auto samplesPerChannel = data.size() / channelsCount;
617
618 auto channelIndex = 0;
619 ImportUtils::ForEachChannel(*stream, [&](auto& channel)
620 {
621 if(channelIndex >= nChannels)
622 return;
623
624 channel.AppendBuffer(
625 reinterpret_cast<samplePtr>(data.data() + channelIndex),
626 sc->SampleFormat,
627 samplesPerChannel,
628 sc->CodecContext->GetChannels(),
629 sc->SampleFormat
630 );
631 ++channelIndex;
632 });
633 }
634 const AVStreamWrapper* avStream = mAVFormatContext->GetStream(sc->StreamIndex);
635
636 int64_t filesize = mFFmpeg->avio_size(mAVFormatContext->GetAVIOContext()->GetWrappedValue());
637 // PTS (presentation time) is the proper way of getting current position
638 if (
641 {
642 auto timeBase = avStream->GetTimeBase();
643
645 packet->GetPresentationTimestamp() * timeBase.num / timeBase.den;
646
648 (mAVFormatContext->GetDuration() > 0 ?
649 mAVFormatContext->GetDuration() / AUDACITY_AV_TIME_BASE :
650 1);
651 }
652 // When PTS is not set, use number of frames and number of current frame
653 else if (
654 avStream->GetFramesCount() > 0 && sc->CodecContext->GetFrameNumber() > 0 &&
655 sc->CodecContext->GetFrameNumber() <= avStream->GetFramesCount())
656 {
657 mProgressPos = sc->CodecContext->GetFrameNumber();
658 mProgressLen = avStream->GetFramesCount();
659 }
660 // When number of frames is unknown, use position in file
661 else if (
662 filesize > 0 && packet->GetPos() > 0 && packet->GetPos() <= filesize)
663 {
664 mProgressPos = packet->GetPos();
665 mProgressLen = filesize;
666 }
667}
668
670{
671 Tags temp;
672
673 GetMetadata(temp, TAG_TITLE, "title");
674 GetMetadata(temp, TAG_COMMENTS, "comment");
675 GetMetadata(temp, TAG_ALBUM, "album");
676 GetMetadata(temp, TAG_TRACK, "track");
677 GetMetadata(temp, TAG_GENRE, "genre");
678
679 if (wxString(mAVFormatContext->GetInputFormat()->GetName()).Contains("m4a"))
680 {
681 GetMetadata(temp, TAG_ARTIST, "artist");
682 GetMetadata(temp, TAG_YEAR, "date");
683 }
684 else if (wxString(mAVFormatContext->GetInputFormat()->GetName()).Contains("asf")) /* wma */
685 {
686 GetMetadata(temp, TAG_ARTIST, "artist");
687 GetMetadata(temp, TAG_YEAR, "year");
688 }
689 else
690 {
691 GetMetadata(temp, TAG_ARTIST, "author");
692 GetMetadata(temp, TAG_YEAR, "year");
693 }
694
695 if (!temp.IsEmpty())
696 {
697 *tags = temp;
698 }
699}
700
701void FFmpegImportFileHandle::GetMetadata(Tags &tags, const wxChar *tag, const char *name)
702{
703 auto metadata = mAVFormatContext->GetMetadata();
704
705 if (metadata.HasValue(name, DICT_IGNORE_SUFFIX))
706 tags.SetTag(tag, wxString::FromUTF8(std::string(metadata.Get(name, {}, DICT_IGNORE_SUFFIX))));
707}
708
709
711{
712
713}
int AVCodecIDFwd
Definition: AVCodecID.h:407
#define DICT_IGNORE_SUFFIX
wxT("CloseDown"))
int min(int a, int b)
BoolSetting FFmpegNotFoundDontShow
Definition: FFmpeg.cpp:355
#define AUDACITY_AV_TIME_BASE
Definition: FFmpegTypes.h:80
#define AUDACITY_AV_NOPTS_VALUE
Definition: FFmpegTypes.h:74
XO("Cut/Copy/Paste")
BoolSetting NewImportingSession
Definition: Import.cpp:860
static const auto exts
#define DESC
static Importer::RegisteredImportPlugin registered
The interface that all file import "plugins" (if you want to call them that) must implement....
std::vector< std::shared_ptr< Track > > TrackHolders
Definition: ImportRaw.h:24
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
wxString FilePath
Definition: Project.h:21
sampleFormat
The ordering of these values with operator < agrees with the order of increasing bit width.
Definition: SampleFormat.h:30
char * samplePtr
Definition: SampleFormat.h:57
#define TAG_TRACK
Definition: Tags.h:61
#define TAG_COMMENTS
Definition: Tags.h:64
#define TAG_GENRE
Definition: Tags.h:63
#define TAG_ALBUM
Definition: Tags.h:60
#define TAG_YEAR
Definition: Tags.h:62
#define TAG_TITLE
Definition: Tags.h:58
#define TAG_ARTIST
Definition: Tags.h:59
wxString name
Definition: TagsEditor.cpp:166
const auto tracks
std::vector< TranslatableString > TranslatableStrings
std::string_view Get(const std::string_view &key, const std::string_view &defaultValue, int flags=0) const
virtual int64_t GetPos() const noexcept=0
virtual int64_t GetPresentationTimestamp() const noexcept=0
virtual int64_t GetDuration() const noexcept=0
virtual int GetIndex() const noexcept=0
virtual bool IsAudio() const noexcept=0
virtual AVDictionaryWrapper GetMetadata() const noexcept=0
virtual AudacityAVRational GetTimeBase() const noexcept=0
virtual int64_t GetFramesCount() const noexcept=0
virtual std::unique_ptr< AVCodecContextWrapper > GetAVCodecContext() const noexcept=0
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
! Does actual import, returned by FFmpegImportPlugin::Open
void WriteData(StreamContext *sc, const AVPacketWrapper *packet)
wxInt64 mProgressPos
Current timestamp, file position or whatever is used as first argument for Update()
std::unique_ptr< AVFormatContextWrapper > mAVFormatContext
FFmpegImportFileHandle(const FilePath &name)
void GetMetadata(Tags &tags, const wxChar *tag, const char *name)
TranslatableStrings mStreamInfo
Array of stream descriptions. After Init() and before Import(), same size as mStreamContexts.
void Import(ImportProgressListener &progressListener, WaveTrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags, std::optional< LibFileFormats::AcidizerTags > &outAcidTags) override
std::vector< TrackListHolder > mStreams
void SetStreamUsage(wxInt32 StreamID, bool Use) override
FilePath GetFilename() const override
void Stop() override
const TranslatableStrings & GetStreamInfo() override
void Cancel() override
const std::shared_ptr< FFmpegFunctions > mFFmpeg
TranslatableString GetFileDescription() override
void WriteMetadata(Tags *tags)
bool mStopped
True if importing was stopped by user.
ByteCount GetFileUncompressedBytes() override
bool mCancelled
True if importing was canceled by user.
wxInt32 GetStreamCount() override
std::vector< StreamContext > mStreamContexts
wxInt64 mProgressLen
Duration, total length or whatever is used as second argument for Update()
An ImportPlugin for FFmpeg data.
TranslatableString FailureHint() const override
User visible message suggesting what to do when a file type isn't recognized; default empty string.
wxString GetPluginStringID() override
std::unique_ptr< ImportFileHandle > Open(const FilePath &Filename, AudacityProject *) override
! Probes the file and opens it if appropriate
TranslatableString GetPluginFormatDescription() override
Base class for FlacImportFileHandle, LOFImportFileHandle, MP3ImportFileHandle, OggImportFileHandle an...
Definition: ImportPlugin.h:111
unsigned long long ByteCount
Definition: ImportPlugin.h:114
Base class for FlacImportPlugin, LOFImportPlugin, MP3ImportPlugin, OggImportPlugin and PCMImportPlugi...
Definition: ImportPlugin.h:67
bool SupportsExtension(const FileExtension &extension)
Interface used to report on import state and progress.
virtual void OnImportResult(ImportResult result)=0
Used to report on import result for file handle passed as argument to OnImportFileOpened.
virtual void OnImportProgress(double progress)=0
static sampleFormat ChooseFormat(sampleFormat effectiveFormat)
Choose appropriate format, which will not be narrower than the specified one.
Definition: ImportUtils.cpp:19
static void ForEachChannel(TrackList &trackList, const std::function< void(WaveChannel &)> &op)
Iterates over channels in each wave track from the list.
Definition: ImportUtils.cpp:73
static void FinalizeImport(TrackHolders &outTracks, const std::vector< std::shared_ptr< WaveTrack > > &importedStreams)
Flushes the given channels and moves them to outTracks.
Definition: ImportUtils.cpp:49
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:259
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:207
ID3 Tags (for MP3)
Definition: Tags.h:73
bool IsEmpty()
Definition: Tags.cpp:282
void SetTag(const wxString &name, const wxString &value, const bool bSpecialTag=false)
Definition: Tags.cpp:431
Holds a msgid for the translation catalog; may also bind format arguments.
Used to create or clone a WaveTrack, with appropriate context from the project that will own the trac...
Definition: WaveTrack.h:871
TrackListHolder CreateMany(size_t nChannels)
Creates tracks with project's default rate and format and the given number of channels.
Definition: WaveTrack.cpp:423
virtual bool Flush() noexcept=0
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
const char * begin(const char *str) noexcept
Definition: StringUtils.h:101
int den
denominator
Definition: FFmpegTypes.h:139
int num
numerator
Definition: FFmpegTypes.h:138
static std::shared_ptr< FFmpegFunctions > Load(bool fromUserPathOnly=false)
std::unique_ptr< AVCodecContextWrapper > CodecContext
sampleFormat SampleFormat