23#define DESC XO("MP3 files")
61 auto end = it + std::strlen(it);
69 if (result.ec != std::errc {})
72 const auto parsedGenre = tags.
GetGenre(tagValue);
74 if (!parsedGenre.empty())
103#if MPG123_API_VERSION < 46
112 return wxT(
"libmpg123");
130 ByteCount GetFileUncompressedBytes()
override;
134 std::optional<LibFileFormats::AcidizerTags>& outAcidTags)
override;
136 bool SetupOutputFormat();
138 void ReadTags(
Tags* tags);
140 wxInt32 GetStreamCount()
override;
142 void SetStreamUsage(wxInt32 StreamID,
bool Use)
override;
148 static ptrdiff_t ReadCallback(
void* handle,
void* buffer,
size_t size);
149 static off_t SeekCallback(
void* handle, off_t offset,
int whence);
152 wxFileOffset mFileLen { 0 };
156 unsigned mNumChannels { 0 };
158 mpg123_handle* mHandle {
nullptr };
160 bool mFloat64Output {};
165std::unique_ptr<ImportFileHandle> MP3ImportPlugin::Open(
168 auto handle = std::make_unique<MP3ImportFileHandle>(Filename);
179 std::make_unique<MP3ImportPlugin>()
186MP3ImportFileHandle::MP3ImportFileHandle(
const FilePath& filename)
189 int errorCode = MPG123_OK;
190 mHandle = mpg123_new(
nullptr, &errorCode);
192 if (errorCode != MPG123_OK)
195 "Failed to create MPG123 handle: %s",
196 mpg123_plain_strerror(errorCode));
203 errorCode = mpg123_replace_reader_handle(
206 if (errorCode != MPG123_OK)
209 "Failed to set reader on the MPG123 handle: %s",
210 mpg123_plain_strerror(errorCode));
217 mpg123_param(
mHandle, MPG123_FLAGS, MPG123_GAPLESS | MPG123_FORCE_FLOAT, 0.0);
219 if (errorCode != MPG123_OK)
222 "Failed to set options on the MPG123 handle",
223 mpg123_plain_strerror(errorCode));
266 std::optional<LibFileFormats::AcidizerTags>&)
270 auto finalAction =
finally([handle =
mHandle]() { mpg123_close(handle); });
275 long long framesCount = mpg123_framelength(
mHandle);
283 off_t frameIndex { 0 };
284 unsigned char* data {
nullptr };
285 size_t dataSize { 0 };
287 std::vector<float> conversionBuffer;
291 while ((ret = mpg123_decode_frame(
mHandle, &frameIndex, &data, &dataSize)) ==
295 progressListener.
OnImportProgress(
static_cast<double>(frameIndex) /
static_cast<double>(framesCount));
305 const size_t samplesCount = dataSize /
sizeof(float) /
mNumChannels;
313 for (
size_t sampleIndex = 0; sampleIndex < conversionBuffer.size();
316 conversionBuffer[sampleIndex] =
static_cast<float>(
317 reinterpret_cast<const double*
>(data)[sampleIndex]);
320 samples =
reinterpret_cast<constSamplePtr>(conversionBuffer.data());
326 channel.AppendBuffer(
327 samples +
sizeof(
float) * chn,
335 if (ret != MPG123_DONE)
338 "Failed to decode MP3 file: %s", mpg123_plain_strerror(ret));
355 int encoding = MPG123_ENC_FLOAT_32;
356 mpg123_getformat(
mHandle, &rate, &channels, &encoding);
360 if (encoding != MPG123_ENC_FLOAT_32 && encoding != MPG123_ENC_FLOAT_64)
362 wxLogError(
"MPG123 returned unexpected encoding");
384 meta = mpg123_meta_check(
mHandle);
386 if (meta & MPG123_ID3 && mpg123_id3(
mHandle, &v1, &v2) == MPG123_OK)
388 if (v2 !=
nullptr && v2->title !=
nullptr && v2->title->fill > 0)
390 else if (v1 !=
nullptr && v1->title[0] !=
'\0')
393 if (v2 !=
nullptr && v2->artist !=
nullptr && v2->artist->fill > 0)
395 else if (v1 !=
nullptr && v1->artist[0] !=
'\0')
398 if (v2 !=
nullptr && v2->album !=
nullptr && v2->album->fill > 0)
400 else if (v1 !=
nullptr && v1->album[0] !=
'\0')
403 if (v2 !=
nullptr && v2->year !=
nullptr && v2->year->fill > 0)
405 else if (v1 !=
nullptr && v1->year[0] !=
'\0')
408 if (v2 !=
nullptr && v2->genre !=
nullptr && v2->genre->fill > 0)
410 else if (v1 !=
nullptr)
413 if (v2 !=
nullptr && v2->comment !=
nullptr && v2->comment->fill > 0)
415 else if (v1 !=
nullptr && v1->comment[0] !=
'\0')
420 for (
size_t i = 0; i < v2->comments; ++i)
422 if (v2->comment_list[i].text.fill == 0)
430 for (
size_t i = 0; i < v2->extras; ++i)
432 if (v2->extra[i].text.fill == 0)
441 for (
size_t i = 0; i < v2->texts; ++i)
443 if (memcmp(v2->text[i].id,
"TRCK", 4) == 0)
473 if (
mFile.Seek(0, wxFromStart) == wxInvalidOffset ||
mFile.Error())
480 auto errorCode = mpg123_open_handle(
mHandle,
this);
482 if (errorCode != MPG123_OK)
486 errorCode = mpg123_scan(
mHandle);
488 if (errorCode != MPG123_OK)
492 errorCode = mpg123_decode_frame(
mHandle,
nullptr,
nullptr,
nullptr);
495 if (errorCode != MPG123_NEW_FORMAT)
502 void* handle,
void* buffer,
size_t size)
514 return wxFromCurrent;
526 void* handle, off_t offset,
int whence)
Toolkit-neutral facade for basic user interface services.
Declare functions to perform UTF-8 to std::wstring conversions.
FromCharsResult FromChars(const char *buffer, const char *last, float &value) noexcept
Parse a string into a single precision floating point value, always uses the dot as decimal.
Declare functions to convert numeric types to string representation.
The interface that all file import "plugins" (if you want to call them that) must implement....
std::vector< std::shared_ptr< Track > > TrackHolders
std::vector< TranslatableString > TranslatableStrings
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
FilePath GetFilename() const override
bool IsCancelled() const noexcept
unsigned long long ByteCount
Base class for FlacImportPlugin, LOFImportPlugin, MP3ImportPlugin, OggImportPlugin and PCMImportPlugi...
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)
static void ForEachChannel(TrackList &trackList, const std::function< void(WaveChannel &)> &op)
Iterates over channels in each wave track from the list.
static void FinalizeImport(TrackHolders &outTracks, const std::vector< std::shared_ptr< WaveTrack > > &importedStreams)
Flushes the given channels and moves them to outTracks.
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...
std::shared_ptr< WaveTrack > Holder
WaveTrackFactory * mTrackFactory
wxInt32 GetStreamCount() override
void Import(ImportProgressListener &progressListener, WaveTrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags, std::optional< LibFileFormats::AcidizerTags > &outAcidTags) override
static off_t SeekCallback(void *handle, off_t offset, int whence)
static ptrdiff_t ReadCallback(void *handle, void *buffer, size_t size)
ByteCount GetFileUncompressedBytes() override
TranslatableString GetFileDescription() override
const TranslatableStrings & GetStreamInfo() override
void ReadTags(Tags *tags)
void SetStreamUsage(wxInt32 StreamID, bool Use) override
TranslatableString GetPluginFormatDescription() override
wxString GetPluginStringID() override
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
wxSeekMode GetWXSeekMode(int whence)
wxString GetId3v2Genre(Tags &tags, const char *genre)
static Importer::RegisteredImportPlugin registered
wxString ToWXString(const std::string &str)
const char * end(const char *str) noexcept
const char * begin(const char *str) noexcept