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<WaveTrack::Holder> 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
469 auto stream = ImportUtils::NewWaveTrack(
470 *trackFactory,
472 sc.SampleFormat,
473 sc.CodecContext->GetSampleRate()
474 );
475
476 // Handles the start_time by creating silence. This may or may not be correct.
477 // There is a possibility that we should ignore first N milliseconds of audio instead. I do not know.
479
480 int64_t stream_delay = 0;
481
482 const int64_t streamStartTime =
483 mAVFormatContext->GetStream(sc.StreamIndex)->GetStartTime();
484
486 {
487 stream_delay = streamStartTime;
488
489 wxLogDebug(
490 wxT("Stream %d start_time = %lld, that would be %f milliseconds."),
491 s, (long long)streamStartTime, double(streamStartTime) / 1000);
492 }
493
494 if (stream_delay > 0) {
495 stream->InsertSilence(0, double(stream_delay) / AUDACITY_AV_TIME_BASE);
496 }
497
498 mStreams.push_back(stream);
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 mChannels to newly created tracks (destroying mChannels elements in process)
541
542 // Save metadata
543 WriteMetadata(tags);
544 progressListener.OnImportResult(mStopped
547}
548
550{
551 return mName;
552}
553
555{
556 if(!mStopped)
557 mCancelled = true;
558}
559
561{
562 if(!mCancelled)
563 mStopped = true;
564}
565
567{
568 // Find the stream in mStreamContexts array
569 auto streamIt = std::find_if(
570 mStreamContexts.begin(),
571 mStreamContexts.end(),
572 [&](StreamContext& context) { return sc == &context; }
573 );
574
575 // Stream is not found. This should not really happen
576 if (streamIt == mStreamContexts.end())
577 {
578 //VS: Shouldn't this mean import failure?
579 return;
580 }
581 auto stream = mStreams[std::distance(mStreamContexts.begin(), streamIt)];
582
583 const auto nChannels = std::min(sc->CodecContext->GetChannels(), sc->InitialChannels);
584
585 // Write audio into WaveTracks
586 if (sc->SampleFormat == int16Sample)
587 {
588 auto data = sc->CodecContext->DecodeAudioPacketInt16(packet);
589 const auto channelsCount = sc->CodecContext->GetChannels();
590 const auto samplesPerChannel = data.size() / channelsCount;
591
592 unsigned chn = 0;
593 ImportUtils::ForEachChannel(*stream, [&](auto& channel)
594 {
595 if(chn >= nChannels)
596 return;
597
598 channel.AppendBuffer(
599 reinterpret_cast<samplePtr>(data.data() + chn),
600 sc->SampleFormat,
601 samplesPerChannel,
602 sc->CodecContext->GetChannels(),
603 sc->SampleFormat
604 );
605 ++chn;
606 });
607 }
608 else if (sc->SampleFormat == floatSample)
609 {
610 auto data = sc->CodecContext->DecodeAudioPacketFloat(packet);
611 const auto channelsCount = sc->CodecContext->GetChannels();
612 const auto samplesPerChannel = data.size() / channelsCount;
613
614 auto channelIndex = 0;
615 ImportUtils::ForEachChannel(*stream, [&](auto& channel)
616 {
617 if(channelIndex >= nChannels)
618 return;
619
620 channel.AppendBuffer(
621 reinterpret_cast<samplePtr>(data.data() + channelIndex),
622 sc->SampleFormat,
623 samplesPerChannel,
624 sc->CodecContext->GetChannels(),
625 sc->SampleFormat
626 );
627 ++channelIndex;
628 });
629 }
630 const AVStreamWrapper* avStream = mAVFormatContext->GetStream(sc->StreamIndex);
631
632 int64_t filesize = mFFmpeg->avio_size(mAVFormatContext->GetAVIOContext()->GetWrappedValue());
633 // PTS (presentation time) is the proper way of getting current position
634 if (
637 {
638 auto timeBase = avStream->GetTimeBase();
639
641 packet->GetPresentationTimestamp() * timeBase.num / timeBase.den;
642
644 (mAVFormatContext->GetDuration() > 0 ?
645 mAVFormatContext->GetDuration() / AUDACITY_AV_TIME_BASE :
646 1);
647 }
648 // When PTS is not set, use number of frames and number of current frame
649 else if (
650 avStream->GetFramesCount() > 0 && sc->CodecContext->GetFrameNumber() > 0 &&
651 sc->CodecContext->GetFrameNumber() <= avStream->GetFramesCount())
652 {
653 mProgressPos = sc->CodecContext->GetFrameNumber();
654 mProgressLen = avStream->GetFramesCount();
655 }
656 // When number of frames is unknown, use position in file
657 else if (
658 filesize > 0 && packet->GetPos() > 0 && packet->GetPos() <= filesize)
659 {
660 mProgressPos = packet->GetPos();
661 mProgressLen = filesize;
662 }
663}
664
666{
667 Tags temp;
668
669 GetMetadata(temp, TAG_TITLE, "title");
670 GetMetadata(temp, TAG_COMMENTS, "comment");
671 GetMetadata(temp, TAG_ALBUM, "album");
672 GetMetadata(temp, TAG_TRACK, "track");
673 GetMetadata(temp, TAG_GENRE, "genre");
674
675 if (wxString(mAVFormatContext->GetInputFormat()->GetName()).Contains("m4a"))
676 {
677 GetMetadata(temp, TAG_ARTIST, "artist");
678 GetMetadata(temp, TAG_YEAR, "date");
679 }
680 else if (wxString(mAVFormatContext->GetInputFormat()->GetName()).Contains("asf")) /* wma */
681 {
682 GetMetadata(temp, TAG_ARTIST, "artist");
683 GetMetadata(temp, TAG_YEAR, "year");
684 }
685 else
686 {
687 GetMetadata(temp, TAG_ARTIST, "author");
688 GetMetadata(temp, TAG_YEAR, "year");
689 }
690
691 if (!temp.IsEmpty())
692 {
693 *tags = temp;
694 }
695}
696
697void FFmpegImportFileHandle::GetMetadata(Tags &tags, const wxChar *tag, const char *name)
698{
699 auto metadata = mAVFormatContext->GetMetadata();
700
701 if (metadata.HasValue(name, DICT_IGNORE_SUFFIX))
702 tags.SetTag(tag, wxString::FromUTF8(std::string(metadata.Get(name, {}, DICT_IGNORE_SUFFIX))));
703}
704
705
707{
708
709}
int AVCodecIDFwd
Definition: AVCodecID.h:407
#define DICT_IGNORE_SUFFIX
wxT("CloseDown"))
int min(int a, int b)
const TranslatableString name
Definition: Distortion.cpp:76
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:837
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
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
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
std::vector< WaveTrack::Holder > mStreams
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 std::shared_ptr< WaveTrack > NewWaveTrack(WaveTrackFactory &trackFactory, unsigned nChannels, sampleFormat effectiveFormat, double rate)
Definition: ImportUtils.cpp:35
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:870
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