Audacity 3.2.0
Public Member Functions | Private Member Functions | Static Private Member Functions | Private Attributes | List of all members
MP3ImportFileHandle Class Referencefinal

An ImportFileHandle for MP3 data. More...

Inheritance diagram for MP3ImportFileHandle:
[legend]
Collaboration diagram for MP3ImportFileHandle:
[legend]

Public Member Functions

 MP3ImportFileHandle (const FilePath &filename)
 
 ~MP3ImportFileHandle ()
 
TranslatableString GetFileDescription () override
 
ByteCount GetFileUncompressedBytes () override
 
ProgressResult Import (WaveTrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags) override
 
wxInt32 GetStreamCount () override
 
const TranslatableStringsGetStreamInfo () override
 
void SetStreamUsage (wxInt32 StreamID, bool Use) override
 
- Public Member Functions inherited from ImportFileHandle
 ImportFileHandle (const FilePath &filename)
 
virtual ~ImportFileHandle ()
 
void CreateProgress ()
 
virtual TranslatableString GetFileDescription ()=0
 
virtual ByteCount GetFileUncompressedBytes ()=0
 
virtual wxInt32 GetStreamCount ()=0
 
virtual const TranslatableStringsGetStreamInfo ()=0
 
virtual void SetStreamUsage (wxInt32 StreamID, bool Use)=0
 
virtual ProgressResult Import (WaveTrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags)=0
 

Private Member Functions

bool Open ()
 
void CheckTags ()
 
void CheckAPETags (bool atEnd)
 
void CheckID3V1Tags ()
 
void CheckID3V2Tags (bool atEnd)
 
void CheckLyrics ()
 
bool CheckMP3 ()
 
bool FillBuffer ()
 
void LoadID3 (Tags *tags)
 
mad_flow InputCB (struct mad_stream *stream)
 
mad_flow FilterCB (struct mad_stream const *stream, struct mad_frame *frame)
 
mad_flow OutputCB (struct mad_header const *header, struct mad_pcm *pcm)
 
mad_flow ErrorCB (struct mad_stream *stream, struct mad_frame *frame)
 

Static Private Member Functions

static mad_flow input_cb (void *that, struct mad_stream *stream)
 
static mad_flow filter_cb (void *that, struct mad_stream const *stream, struct mad_frame *frame)
 
static mad_flow output_cb (void *that, struct mad_header const *header, struct mad_pcm *pcm)
 
static mad_flow error_cb (void *that, struct mad_stream *stream, struct mad_frame *frame)
 

Private Attributes

mad_decoder mDecoder
 
wxFile mFile
 
wxFileOffset mFilePos
 
wxFileOffset mFileLen
 
unsigned char mInputBuffer [INPUT_BUFFER_SIZE+MAD_BUFFER_GUARD]
 
int mInputBufferLen
 
WaveTrackFactorymTrackFactory
 
NewChannelGroup mChannels
 
unsigned mNumChannels
 
ProgressResult mUpdateResult
 
int mDelay
 
int mPadding
 
bool mHaveID3
 
friend MP3ImportPlugin
 

Additional Inherited Members

- Public Types inherited from ImportFileHandle
using ProgressResult = BasicUI::ProgressResult
 
using ByteCount = unsigned long long
 
- Static Public Member Functions inherited from ImportFileHandle
static sampleFormat ChooseFormat (sampleFormat effectiveFormat)
 Choose appropriate format, which will not be narrower than the specified one. More...
 
- Protected Member Functions inherited from ImportFileHandle
std::shared_ptr< WaveTrackNewWaveTrack (WaveTrackFactory &trackFactory, sampleFormat effectiveFormat, double rate)
 Build a wave track with appropriate format, which will not be narrower than the specified one. More...
 
- Protected Attributes inherited from ImportFileHandle
FilePath mFilename
 
std::unique_ptr< ProgressDialogmProgress
 

Detailed Description

An ImportFileHandle for MP3 data.

Audacity has finally moved to using a single mp3 library on all platforms! It is the high performance, beautifully written libmad (mpeg audio decoder). Finally there is harmony in the mp3 universe.

Much of this source code is based on 'minimad.c' as distributed with libmad.

Definition at line 104 of file ImportMP3_MAD.cpp.

Constructor & Destructor Documentation

◆ MP3ImportFileHandle()

MP3ImportFileHandle::MP3ImportFileHandle ( const FilePath filename)

Definition at line 219 of file ImportMP3_MAD.cpp.

220: ImportFileHandle(filename)
221{
222}
ImportFileHandle(const FilePath &filename)

◆ ~MP3ImportFileHandle()

MP3ImportFileHandle::~MP3ImportFileHandle ( )

Definition at line 224 of file ImportMP3_MAD.cpp.

225{
226}

Member Function Documentation

◆ CheckAPETags()

void MP3ImportFileHandle::CheckAPETags ( bool  atEnd)
private

Definition at line 384 of file ImportMP3_MAD.cpp.

385{
386 int offset = atEnd ? mFileLen - 32 : mFilePos;
387
388 // Ensure file is positioned to start of (possible) tags
389 if (mFile.Seek(offset, wxFromStart) == wxInvalidOffset || mFile.Error())
390 {
391 return;
392 }
393
394 // An APE tag header is 32 bytes
395 if (mFile.Read(mInputBuffer, 32) != 32 || mFile.Error())
396 {
397 return;
398 }
399
400 // Do we have an APE preamble?
401 if (memcmp(mInputBuffer, "APETAGEX", 8) != 0)
402 {
403 return;
404 }
405
406 // Get the (little endian) length
407 wxFileOffset len = (mInputBuffer[12] & 0xff) |
408 ((mInputBuffer[13] & 0xff) << 8) |
409 ((mInputBuffer[14] & 0xff) << 16) |
410 ((mInputBuffer[15] & 0xff) << 24);
411
412 // Get needed flags
413 bool hasHeader = mInputBuffer[23] & 0x80;
414
415 // Skip the tags
416 if (!atEnd)
417 {
418 mFilePos += (32 + len);
419 }
420 else
421 {
422 mFileLen -= ((hasHeader ? 32 : 0) + len);
423 }
424}
unsigned char mInputBuffer[INPUT_BUFFER_SIZE+MAD_BUFFER_GUARD]

References mFile, mFileLen, mFilePos, and mInputBuffer.

Referenced by CheckTags().

Here is the caller graph for this function:

◆ CheckID3V1Tags()

void MP3ImportFileHandle::CheckID3V1Tags ( )
private

Definition at line 426 of file ImportMP3_MAD.cpp.

427{
428 // Ensure file is positioned to start of (possible) tags
429 if (mFile.Seek(mFileLen - 128, wxFromStart) == wxInvalidOffset || mFile.Error())
430 {
431 return;
432 }
433
434 // An ID3v1 tag header is 3 bytes
435 if (mFile.Read(mInputBuffer, 3) != 3 || mFile.Error())
436 {
437 return;
438 }
439
440 // Do we have ID3v1 tags?
441 if (memcmp(mInputBuffer, "TAG", 3) != 0)
442 {
443 return;
444 }
445
446 // Adjust file length
447 mFileLen -= 128;
448
449 // Remember that we have tags
450 mHaveID3 = true;
451}

References mFile, mFileLen, mHaveID3, and mInputBuffer.

Referenced by CheckTags().

Here is the caller graph for this function:

◆ CheckID3V2Tags()

void MP3ImportFileHandle::CheckID3V2Tags ( bool  atEnd)
private

Definition at line 519 of file ImportMP3_MAD.cpp.

520{
521 int offset = atEnd ? mFileLen - 10 : mFilePos;
522
523 // Ensure file is positioned to start of (possible) tags
524 if (mFile.Seek(offset, wxFromStart) == wxInvalidOffset || mFile.Error())
525 {
526 return;
527 }
528
529 // An ID3v2 tag header is 10 bytes
530 if (mFile.Read(mInputBuffer, 10) != 10 || mFile.Error())
531 {
532 return;
533 }
534
535 // Do we have an ID3v2 header or footer?
536 if (memcmp(mInputBuffer, atEnd ? "3DI" : "ID3", 3) != 0)
537 {
538 return;
539 }
540
541 // Get and decode the length
542 wxFileOffset len = (mInputBuffer[6] & 0x7f);
543 len = (len << 7) | (mInputBuffer[7] & 0x7f);
544 len = (len << 7) | (mInputBuffer[8] & 0x7f);
545 len = (len << 7) | (mInputBuffer[9] & 0x7f);
546
547 // Skip the tags
548 if (!atEnd)
549 {
550 mFilePos += (10 + len);
551 }
552 else
553 {
554 mFileLen -= (10 + len + 10);
555 }
556
557 // Remember that we have tags
558 mHaveID3 = true;
559}

References mFile, mFileLen, mFilePos, mHaveID3, and mInputBuffer.

Referenced by CheckTags().

Here is the caller graph for this function:

◆ CheckLyrics()

void MP3ImportFileHandle::CheckLyrics ( )
private

Definition at line 453 of file ImportMP3_MAD.cpp.

454{
455 int offset = mFileLen - 9;
456
457 // Ensure file is positioned to start of (possible) lyrics
458 if (mFile.Seek(offset, wxFromStart) == wxInvalidOffset || mFile.Error())
459 {
460 return;
461 }
462
463 // An Lyrics3 footeris 9 bytes
464 if (mFile.Read(mInputBuffer, 9) != 9 || mFile.Error())
465 {
466 return;
467 }
468
469 // Found a v1 Lyrics footer?
470 if (memcmp(mInputBuffer, "LYRICSEND", 9) == 0)
471 {
472 wxFileOffset pos = wxMax(offset - 5100, 0);
473 size_t len = offset - pos;
474
475 // Ensure file is positioned to start of (possible) lyrics
476 if (mFile.Seek(pos, wxFromStart) == wxInvalidOffset || mFile.Error())
477 {
478 return;
479 }
480
481 // Read the lyrics
482 if (mFile.Read(mInputBuffer, len) != len || mFile.Error())
483 {
484 return;
485 }
486
487 // Search forward to find the beginning of the lyrics
488 for (size_t i = 0; i < len; ++i)
489 {
490 if (memcmp(&mInputBuffer[i], "LYRICSBEGIN", 11) == 0)
491 {
492 // Adjust the file length to exclude the lyrics
493 mFileLen = pos + i;
494 break;
495 }
496 }
497 }
498 // Found a v2 Lyrics footer?
499 else if (memcmp(mInputBuffer, "LYRICS200", 9) == 0)
500 {
501 // Ensure file is positioned to start of (possible) lyrics
502 if (mFile.Seek(-15, wxFromCurrent) == wxInvalidOffset || mFile.Error())
503 {
504 return;
505 }
506
507 // An Lyrics3v2 length is 6 bytes
508 if (mFile.Read(mInputBuffer, 6) != 6 || mFile.Error())
509 {
510 return;
511 }
512
513 // Adjust the file length to exclude the lyrics
514 mInputBuffer[6] = 0;
515 mFileLen -= (wxAtoi((char *) mInputBuffer) + 15);
516 }
517}

References mFile, mFileLen, and mInputBuffer.

Referenced by CheckTags().

Here is the caller graph for this function:

◆ CheckMP3()

bool MP3ImportFileHandle::CheckMP3 ( )
private

Definition at line 561 of file ImportMP3_MAD.cpp.

562{
563 wxFileOffset savedPos = mFilePos;
564
565 // Ensure file is positioned to start of 1st mp3 frame
566 if (mFile.Seek(mFilePos, wxFromStart) == wxInvalidOffset || mFile.Error())
567 {
568 return false;
569 }
570
571 // Load as much as will fit into the buffer
572 if (!FillBuffer())
573 {
574 return false;
575 }
576
577 // Initialize mad stream
578 mad_stream stream;
579 mad_stream_init(&stream);
580 mad_stream_buffer(&stream, mInputBuffer, mInputBufferLen);
581
582 // And header
583 mad_header header;
584 mad_header_init(&header);
585
586 // Scan the input buffer for 2 consecutive MP3 frames. When the header
587 // decoder finds a frame, it decodes it and ensures it is followed by
588 // another frame or EOF...thus 2 (or 1) consecutive frame(s) are detected.
589 int consecutive = 1;
590 while (consecutive > 0)
591 {
592 // Decode the header at the current stream position.
593 if (mad_header_decode(&header, &stream))
594 {
595 // End of buffer.
596 if (stream.error != MAD_ERROR_NONE)
597 {
598 break;
599 }
600 }
601
602 consecutive -= 1;
603 }
604
605 // Remember how many bytes were processed
606 int used = stream.this_frame - stream.buffer;
607
608 // Cleanup
609 mad_header_finish(&header);
610 mad_stream_finish(&stream);
611
612 // Did we find all that we wanted?
613 if (consecutive)
614 {
615 return false;
616 }
617
618 // Reset file controls
619 mInputBufferLen = 0;
620
621 // Reposition file to start of mp3 frames to prepare for the Import.
622 mFilePos = savedPos + used;
623 if (mFile.Seek(mFilePos, wxFromStart) == wxInvalidOffset || mFile.Error())
624 {
625 return false;
626 }
627
628 // Looks like an MP3...
629 return true;
630}
struct in the MPEG library, used for MP3 compression by MP3Exporter
struct in the MPEG library, used for MP3 compression by MP3Exporter

References FillBuffer(), mFile, mFilePos, mInputBuffer, and mInputBufferLen.

Referenced by Open().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ CheckTags()

void MP3ImportFileHandle::CheckTags ( )
private

Definition at line 362 of file ImportMP3_MAD.cpp.

363{
364 // We do this twice to allow them to be in any order
365 for (int i = 0; i < 2; ++i)
366 {
367 CheckAPETags(false);
368 CheckID3V2Tags(false);
369 }
370
371 // We do this twice to allow them to be in any order. Even though ID3v1 is
372 // supposed to at the end, some apps put the v2 tags after the v1 tags.
373 for (int i = 0; i < 2; ++i)
374 {
375 CheckAPETags(true);
377 CheckLyrics();
378 CheckID3V2Tags(true);
379 }
380
381 return;
382}
void CheckID3V2Tags(bool atEnd)
void CheckAPETags(bool atEnd)

References CheckAPETags(), CheckID3V1Tags(), CheckID3V2Tags(), and CheckLyrics().

Referenced by Open().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ error_cb()

mad_flow MP3ImportFileHandle::error_cb ( void *  that,
struct mad_stream stream,
struct mad_frame frame 
)
staticprivate

Definition at line 1077 of file ImportMP3_MAD.cpp.

1080{
1081 auto cb = [&]()
1082 {
1083 return ((MP3ImportFileHandle *) that)->ErrorCB(stream, frame);
1084 };
1085
1086 return GuardedCall<mad_flow>(cb, MakeSimpleGuard(MAD_FLOW_BREAK));
1087}
SimpleGuard< R > MakeSimpleGuard(R value) noexcept(noexcept(SimpleGuard< R >{ value }))
Convert a value to a handler function returning that value, suitable for GuardedCall<R>
An ImportFileHandle for MP3 data.
mad_flow ErrorCB(struct mad_stream *stream, struct mad_frame *frame)

References ErrorCB(), and MakeSimpleGuard().

Referenced by Import().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ErrorCB()

enum mad_flow MP3ImportFileHandle::ErrorCB ( struct mad_stream stream,
struct mad_frame frame 
)
private

Definition at line 1089 of file ImportMP3_MAD.cpp.

1091{
1092 // You always get a LOSTSYNC error at EOF, so just ignore it
1093 if (stream->error == MAD_ERROR_LOSTSYNC && mFilePos == mFileLen)
1094 {
1095 return MAD_FLOW_CONTINUE;
1096 }
1097
1098 // This can happen when parsing the first frame. We can use the number of channels
1099 // to test for this since it hasn't been determined yet.
1100 if (stream->error == MAD_ERROR_BADDATAPTR && mNumChannels == 0)
1101 {
1102 return MAD_FLOW_CONTINUE;
1103 }
1104
1105 // Let the user know about the error
1106 using namespace BasicUI;
1107 ShowErrorDialog( {},
1109 XO("Import failed\n\nThis is likely caused by a malformed MP3.\n\n"),
1110 "Opening_malformed_MP3_files");
1111 return MAD_FLOW_BREAK;
1112}
XO("Cut/Copy/Paste")
TranslatableString DefaultCaption()
"Message", suitably translated
Definition: BasicUI.cpp:253
void ShowErrorDialog(const WindowPlacement &placement, const TranslatableString &dlogTitle, const TranslatableString &message, const ManualPageID &helpPage, const ErrorDialogOptions &options={})
Show an error dialog with a link to the manual for further help.
Definition: BasicUI.h:259

References BasicUI::DefaultCaption(), mFileLen, mFilePos, mNumChannels, BasicUI::ShowErrorDialog(), and XO().

Referenced by error_cb().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ FillBuffer()

bool MP3ImportFileHandle::FillBuffer ( )
private

Definition at line 632 of file ImportMP3_MAD.cpp.

633{
634 // We either want enough to fill the input buffer or what's left in the file
635 auto want = wxMin(INPUT_BUFFER_SIZE - mInputBufferLen, mFileLen - mFilePos);
636 if (want > 0)
637 {
638 // We should always get what we ask for
639 auto got = mFile.Read(&mInputBuffer[mInputBufferLen], want);
640 if (got != want || mFile.Error())
641 {
642 return false;
643 }
644
645 // Adjust input control
646 mInputBufferLen += got;
647 mFilePos += got;
648 }
649
650 // MAD requires that we add MAD_BUFFER_GUARD extra bytes when we've processed
651 // all of the MP3 frames. Otherwise, we will drop the last frame.
652 if (mFilePos == mFileLen)
653 {
654 memset(&mInputBuffer[mInputBufferLen], 0, MAD_BUFFER_GUARD);
655 mInputBufferLen += MAD_BUFFER_GUARD;
656 }
657
658 return true;
659}
#define INPUT_BUFFER_SIZE

References INPUT_BUFFER_SIZE, mFile, mFileLen, mFilePos, mInputBuffer, and mInputBufferLen.

Referenced by CheckMP3(), and InputCB().

Here is the caller graph for this function:

◆ filter_cb()

mad_flow MP3ImportFileHandle::filter_cb ( void *  that,
struct mad_stream const *  stream,
struct mad_frame frame 
)
staticprivate

Definition at line 904 of file ImportMP3_MAD.cpp.

907{
908 auto cb = [&]()
909 {
910 return ((MP3ImportFileHandle *) that)->FilterCB(stream, frame);
911 };
912
913 return GuardedCall<mad_flow>(cb, MakeSimpleGuard(MAD_FLOW_BREAK));
914}
mad_flow FilterCB(struct mad_stream const *stream, struct mad_frame *frame)

References FilterCB(), and MakeSimpleGuard().

Referenced by Import().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ FilterCB()

mad_flow MP3ImportFileHandle::FilterCB ( struct mad_stream const *  stream,
struct mad_frame frame 
)
private

Definition at line 916 of file ImportMP3_MAD.cpp.

918{
919 // We only want to jinspect the first frame, so disable future calls
920 mDecoder.filter_func = nullptr;
921
922 // Is it a VBRI info frame?
923 if (memcmp(&stream->this_frame[4 + 32], "VBRI", 4) == 0)
924 {
925 mDelay = (stream->this_frame[4 + 32 + 6] & 0xff) << 8 |
926 (stream->this_frame[4 + 32 + 7] & 0xff);
927
928 return MAD_FLOW_CONTINUE;
929 }
930
931 // Look for Xing/Info information
932
933 // Get the ancillary data ptr and length. If the frame has CRC protection, we make
934 // a small adjustment to get around an apparent bug in libmad.
935 auto ptr = stream->anc_ptr.byte - (frame->header.flags & MAD_FLAG_PROTECTION ? 2 : 0);
936 int len = stream->anc_bitlen / 8;
937
938 // Ensure it's something we can understand
939 if (len < 4 || (memcmp(ptr, "Xing", 4) != 0 && memcmp(ptr, "Info", 4) != 0))
940 {
941 return MAD_FLOW_CONTINUE;
942 }
943
944 // Skip the tag
945 ptr += 4;
946 len -= 4;
947
948 enum flagBits
949 {
950 hasFrames = 0x0001,
951 hasBytes = 0x0002,
952 hasToc = 0x0004,
953 hasScale = 0x0008
954 };
955
956 // Extract the flags
957 unsigned int flags = (((((ptr[0] << 8) + ptr[1]) << 8) + ptr[2]) << 8) + ptr[3];
958 ptr += 4;
959 len -= 4;
960
961 // Skip the number of frames
962 if (len >= 4 && flags & hasFrames)
963 {
964 ptr += 4;
965 len -= 4;
966 }
967
968 // Skip the number of bytes
969 if (len >= 4 && flags & hasBytes)
970 {
971 ptr += 4;
972 len -= 4;
973 }
974
975 // Skip the TOC
976 if (len >= 100 && flags & hasToc)
977 {
978 ptr += 100;
979 len -= 100;
980 }
981
982 // Skip the VBR Scale
983 if (len >= 4 && flags & hasScale)
984 {
985 ptr += 4;
986 len -= 4;
987 }
988
989 // Bail if LAME wasn't the encoder or we don't have enough ancillary data left
990 if (len < 24 || memcmp(ptr, "LAME", 4) != 0)
991 {
992 return MAD_FLOW_IGNORE;
993 }
994
995 // Skip down to the delay and padding
996 ptr += 21;
997 len -= 21;
998
999 // Extract the delay and padding and adjust for decoder delay
1000 mDelay = (ptr[0] << 4) + (ptr[1] >> 4) + MAD_DELAY;
1001 mPadding = ((ptr[1] & 0x0f) << 8) + ptr[2] - MAD_DELAY;
1002 if (mPadding < 0)
1003 {
1004 mPadding = 0;
1005 }
1006
1007 return MAD_FLOW_IGNORE;
1008}
#define MAD_DELAY

References MAD_DELAY, mDecoder, mDelay, and mPadding.

Referenced by filter_cb().

Here is the caller graph for this function:

◆ GetFileDescription()

TranslatableString MP3ImportFileHandle::GetFileDescription ( )
overridevirtual

Implements ImportFileHandle.

Definition at line 228 of file ImportMP3_MAD.cpp.

229{
230 return DESC;
231}
#define DESC

References DESC.

◆ GetFileUncompressedBytes()

auto MP3ImportFileHandle::GetFileUncompressedBytes ( )
overridevirtual

Implements ImportFileHandle.

Definition at line 233 of file ImportMP3_MAD.cpp.

234{
235 // TODO
236 return 0;
237}

◆ GetStreamCount()

wxInt32 MP3ImportFileHandle::GetStreamCount ( )
overridevirtual

Implements ImportFileHandle.

Definition at line 239 of file ImportMP3_MAD.cpp.

240{
241 return 1;
242}

◆ GetStreamInfo()

const TranslatableStrings & MP3ImportFileHandle::GetStreamInfo ( )
overridevirtual

Implements ImportFileHandle.

Definition at line 244 of file ImportMP3_MAD.cpp.

245{
246 static TranslatableStrings empty;
247 return empty;
248}
std::vector< TranslatableString > TranslatableStrings

◆ Import()

ProgressResult MP3ImportFileHandle::Import ( WaveTrackFactory trackFactory,
TrackHolders outTracks,
Tags tags 
)
overridevirtual

Implements ImportFileHandle.

Definition at line 254 of file ImportMP3_MAD.cpp.

257{
258 outTracks.clear();
259
261
262 mTrackFactory = trackFactory;
264 mNumChannels = 0;
265
266 // Set delay and padding to best possible in case the LAME tag is not present
268 mPadding = 0;
269
270 // Initialize decoder
271 mad_decoder_init(&mDecoder, this, input_cb, 0, filter_cb, output_cb, error_cb, 0);
272
273 // Send the decoder on its way!
274 auto res = mad_decoder_run(&mDecoder, MAD_DECODER_MODE_SYNC);
275
276 // Terminate decoder
277 mad_decoder_finish(&mDecoder);
278
279 // Decoding failed, so pass it on
280 if (res != 0)
281 {
282 return ProgressResult::Failed;
283 }
284
285 // The user canceled the decoding, so bail without saving tracks or tags
287 {
288 return mUpdateResult;
289 }
290
291 // Flush and trim the channels
292 for (const auto &channel : mChannels)
293 {
294 channel->Flush();
295
296 // Trim any padding
297 if (mPadding)
298 {
299 double et = channel->GetEndTime();
300 double t1 = et - channel->LongSamplesToTime(mPadding);
301 channel->Clear(t1, et);
302 }
303
304 // And delay
305 if (mDelay)
306 {
307 double st = channel->GetStartTime();
308 double t0 = st + channel->LongSamplesToTime(mDelay);
309 channel->Clear(st, t0);
310 }
311 }
312
313 // Copy the WaveTrack pointers into the Track pointer list that
314 // we are expected to fill
315 outTracks.push_back(std::move(mChannels));
316
317 // Load ID3 tags from the file
318 LoadID3(tags);
319
320 return mUpdateResult;
321}
void LoadID3(Tags *tags)
static mad_flow output_cb(void *that, struct mad_header const *header, struct mad_pcm *pcm)
static mad_flow filter_cb(void *that, struct mad_stream const *stream, struct mad_frame *frame)
static mad_flow error_cb(void *that, struct mad_stream *stream, struct mad_frame *frame)
static mad_flow input_cb(void *that, struct mad_stream *stream)
ProgressResult mUpdateResult
NewChannelGroup mChannels
WaveTrackFactory * mTrackFactory

References RefreshCode::Cancelled, ImportFileHandle::CreateProgress(), error_cb(), filter_cb(), input_cb(), LoadID3(), MAD_DELAY, mChannels, mDecoder, mDelay, mNumChannels, mPadding, mTrackFactory, mUpdateResult, output_cb(), and BasicUI::Success.

Here is the call graph for this function:

◆ input_cb()

mad_flow MP3ImportFileHandle::input_cb ( void *  that,
struct mad_stream stream 
)
staticprivate

Definition at line 851 of file ImportMP3_MAD.cpp.

853{
854 auto cb = [&]()
855 {
856 return ((MP3ImportFileHandle *) that)->InputCB(stream);
857 };
858
859 return GuardedCall<mad_flow>(cb, MakeSimpleGuard(MAD_FLOW_BREAK));
860}
mad_flow InputCB(struct mad_stream *stream)

References InputCB(), and MakeSimpleGuard().

Referenced by Import().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ InputCB()

mad_flow MP3ImportFileHandle::InputCB ( struct mad_stream stream)
private

Definition at line 862 of file ImportMP3_MAD.cpp.

863{
864 // Update the progress
865 mUpdateResult = mProgress->Update((wxLongLong_t) mFilePos, (wxLongLong_t) mFileLen);
867 {
868 return MAD_FLOW_STOP;
869 }
870
871 // Stop if we've consumed all of the MP3 data
872 if (mFilePos == mFileLen)
873 {
874 return MAD_FLOW_STOP;
875 }
876
877 // "Each time you refill your buffer, you need to preserve the data in
878 // your existing buffer from stream.next_frame to the end.
879 //
880 // This usually amounts to calling memmove() on this unconsumed portion
881 // of the buffer and appending NEW data after it, before calling
882 // mad_stream_buffer()
883 // -- Rob Leslie, on the mad-dev mailing list
884 if (stream->next_frame)
885 {
886 mInputBufferLen -= (stream->next_frame - mInputBuffer);
887 memmove(mInputBuffer, stream->next_frame, mInputBufferLen);
888 }
889
890 // Refill the buffer
891 if (!FillBuffer())
892 {
893 return MAD_FLOW_BREAK;
894 }
895
896 // And give it back to MAD
897 mad_stream_buffer(stream, mInputBuffer, mInputBufferLen);
898
899 return MAD_FLOW_CONTINUE;
900}
std::unique_ptr< ProgressDialog > mProgress
Definition: ImportPlugin.h:164

References FillBuffer(), mFileLen, mFilePos, mInputBuffer, mInputBufferLen, ImportFileHandle::mProgress, mUpdateResult, and BasicUI::Success.

Referenced by input_cb().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ LoadID3()

void MP3ImportFileHandle::LoadID3 ( Tags tags)
private

Definition at line 661 of file ImportMP3_MAD.cpp.

662{
663#ifdef USE_LIBID3TAG
664 struct id3_file *id3file = NULL;
665 auto cleanup = finally([&]
666 {
667 if (id3file)
668 {
669 id3_file_close(id3file);
670 }
671 });
672
673 // Use id3_file_fdopen() instead of id3_file_open since wxWidgets can open a
674 // file with a Unicode name and id3_file_open() can't (under Windows).
675 id3file = id3_file_fdopen(mFile.fd(), ID3_FILE_MODE_READONLY);
676 if (!id3file)
677 {
678 return;
679 }
680
681 // The file descriptor is now owned by "id3file", so we must tell "mFile" to forget
682 // about it.
683 mFile.Detach();
684
685 // Load the tags
686 struct id3_tag *id3tags = id3_file_tag(id3file);
687 if (!id3tags || id3tags->nframes == 0)
688 {
689 return;
690 }
691
692 // Convert from libid3tag's ucs4 type to wxString.
693 //
694 // The ucs4 type is unsigned long which can be 8 bytes instead
695 // of the expected 4 bytes for a UTF-32 character, so we have
696 // to convert to unsigned int and then to wxString.
697 wxMBConvUTF32 converter;
698 auto toString = [=](const id3_ucs4_t *in)
699 {
700 // Count the number of characters
701 size_t len = 0;
702 for (const id3_ucs4_t *p = in; *p; p++)
703 {
704 len++;
705 }
706
707 // Would like to use std::dynarray or runtime-sized array,
708 // but VS doesn't support either.
709 wxUint32 *buf = (wxUint32 *) alloca((len + 1) * sizeof(wxUint32));
710
711 // Copy and convert to unsigned int
712 wxUint32 *out;
713 for (out = buf; *in; in++, out++)
714 {
715 *out = (wxUint32) (*in);
716 }
717 *out = 0;
718
719 // Finally convert to and return wxString
720 return wxString((char *) buf, converter);
721 };
722
723 tags->Clear();
724
725 // Extract tags from ID3 frames and add to our tags
726 bool have_year = false;
727 for (unsigned int i = 0; i < id3tags->nframes; ++i)
728 {
729 struct id3_frame *frame = id3tags->frames[i];
730
731#if 0
732 wxLogDebug("ID: %08x '%4s'", (int) *(int *)frame->id, frame->id);
733 wxLogDebug("Desc: %s", frame->description);
734 wxLogDebug("Num fields: %d", frame->nfields);
735
736 for (unsigned int j = 0; j < frame->nfields; ++j)
737 {
738 wxLogDebug("field %d type %d", j, frame->fields[j].type);
739 if (frame->fields[j].type == ID3_FIELD_TYPE_STRINGLIST)
740 {
741 wxLogDebug("num strings %d", frame->fields[j].stringlist.nstrings);
742 }
743 }
744#endif
745
746 wxString n;
747 wxString v;
748
749 // Determine the tag name
750 if (strcmp(frame->id, ID3_FRAME_TITLE) == 0)
751 {
752 n = TAG_TITLE;
753 }
754 else if (strcmp(frame->id, ID3_FRAME_ARTIST) == 0)
755 {
756 n = TAG_ARTIST;
757 }
758 else if (strcmp(frame->id, ID3_FRAME_ALBUM) == 0)
759 {
760 n = TAG_ALBUM;
761 }
762 else if (strcmp(frame->id, ID3_FRAME_TRACK) == 0)
763 {
764 n = TAG_TRACK;
765 }
766 else if (strcmp(frame->id, ID3_FRAME_YEAR) == 0)
767 {
768 // LLL: When libid3tag encounters the "TYER" tag, it converts it to a
769 // "ZOBS" (obsolete) tag and adds a "TDRC" tag at the end of the
770 // list of tags using the first 4 characters of the "TYER" tag.
771 // Since we write both the "TDRC" and "TYER" tags, the "TDRC" tag
772 // will always be encountered first in the list. We want to use
773 // it since the converted "TYER" tag may have been truncated.
774 if (have_year)
775 {
776 continue;
777 }
778 n = TAG_YEAR;
779 have_year = true;
780 }
781 else if (strcmp(frame->id, ID3_FRAME_COMMENT) == 0)
782 {
783 n = TAG_COMMENTS;
784 }
785 else if (strcmp(frame->id, ID3_FRAME_GENRE) == 0)
786 {
787 n = TAG_GENRE;
788 }
789 else
790 {
791 // Use frame description as default tag name. The descriptions
792 // may include several "meanings" separated by "/" characters, so
793 // we just use the first meaning
794 n = UTF8CTOWX(frame->description).BeforeFirst(wxT('/'));
795 }
796
797 // Now get the tag value
798 const id3_ucs4_t *ustr = NULL;
799
800 if (n == TAG_COMMENTS)
801 {
802 ustr = id3_field_getfullstring(&frame->fields[3]);
803 }
804 else if (frame->nfields == 3)
805 {
806 ustr = id3_field_getstring(&frame->fields[1]);
807 if (ustr)
808 {
809 n = toString(ustr);
810 }
811
812 ustr = id3_field_getstring(&frame->fields[2]);
813 }
814 else if (frame->nfields >= 2)
815 {
816 ustr = id3_field_getstrings(&frame->fields[1], 0);
817 }
818
819 // Convert the value
820 if (ustr)
821 {
822 v = toString(ustr);
823 }
824
825 // And add it to the list of tags
826 if (!n.empty() && !v.empty())
827 {
828 tags->SetTag(n, v);
829 }
830 }
831
832 // Convert v1 genre to name
833 if (tags->HasTag(TAG_GENRE))
834 {
835 long g = -1;
836 if (tags->GetTag(TAG_GENRE).ToLong(&g))
837 {
838 tags->SetTag(TAG_GENRE, tags->GetGenre(g));
839 }
840 }
841#else
842 (void) tags;
843#endif
844}
wxT("CloseDown"))
#define UTF8CTOWX(X)
Definition: Internat.h:157
#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
void Clear()
Definition: Tags.cpp:308
bool HasTag(const wxString &name) const
Definition: Tags.cpp:407
wxString GetGenre(int value)
Definition: Tags.cpp:383
void SetTag(const wxString &name, const wxString &value, const bool bSpecialTag=false)
Definition: Tags.cpp:441
wxString GetTag(const wxString &name) const
Definition: Tags.cpp:416

References Tags::Clear(), Tags::GetGenre(), Tags::GetTag(), Tags::HasTag(), mFile, Tags::SetTag(), TAG_ALBUM, TAG_ARTIST, TAG_COMMENTS, TAG_GENRE, TAG_TITLE, TAG_TRACK, TAG_YEAR, UTF8CTOWX, and wxT().

Referenced by Import().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Open()

bool MP3ImportFileHandle::Open ( )
private

Definition at line 323 of file ImportMP3_MAD.cpp.

324{
325 mInputBufferLen = 0;
326 mFilePos = 0;
327 mHaveID3 = false;
328
329 // Open the file
330 if (!mFile.Open(mFilename))
331 {
332 return false;
333 }
334
335 // Get the length of the file
336 mFileLen = mFile.Seek(0, wxFromEnd);
337 if (mFileLen == wxInvalidOffset || mFile.Error())
338 {
339 mFile.Close();
340 return false;
341 }
342
343 if (mFile.Seek(0, wxFromStart) == wxInvalidOffset || mFile.Error())
344 {
345 mFile.Close();
346 return false;
347 }
348
349 // Check for ID3 tags
350 CheckTags();
351
352 // Scan for the first MP3 frame
353 if (!CheckMP3())
354 {
355 mFile.Close();
356 return false;
357 }
358
359 return true;
360}
FilePath mFilename
Definition: ImportPlugin.h:163

References CheckMP3(), CheckTags(), mFile, mFileLen, ImportFileHandle::mFilename, mFilePos, mHaveID3, and mInputBufferLen.

Here is the call graph for this function:

◆ output_cb()

mad_flow MP3ImportFileHandle::output_cb ( void *  that,
struct mad_header const *  header,
struct mad_pcm pcm 
)
staticprivate

Definition at line 1012 of file ImportMP3_MAD.cpp.

1015{
1016 auto cb = [&]()
1017 {
1018 return ((MP3ImportFileHandle *) that)->OutputCB(header, pcm);
1019 };
1020
1021 return GuardedCall<mad_flow>(cb, MakeSimpleGuard(MAD_FLOW_BREAK));
1022}
mad_flow OutputCB(struct mad_header const *header, struct mad_pcm *pcm)

References MakeSimpleGuard(), and OutputCB().

Referenced by Import().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ OutputCB()

enum mad_flow MP3ImportFileHandle::OutputCB ( struct mad_header const *  header,
struct mad_pcm pcm 
)
private

Definition at line 1024 of file ImportMP3_MAD.cpp.

1026{
1027 // If this is the first run, we need to create the WaveTracks that
1028 // will hold the data. We do this now because now is the first
1029 // moment when we know how many channels there are.
1030 if (mChannels.empty())
1031 {
1032 mNumChannels = pcm->channels;
1033
1034 mChannels.resize(mNumChannels);
1035
1036 for (auto &channel: mChannels)
1037 {
1038 // Mad library header explains the 32 bit fixed point format with
1039 // 28 fractional bits. Effective sample format must therefore be
1040 // more than 24, and this is our only choice now.
1041 channel = NewWaveTrack(*mTrackFactory, floatSample, pcm->samplerate);
1042 }
1043 }
1044
1045 // Get the number of samples in each channel
1046 auto samples = pcm->length;
1047
1048 // Convert libmad samples to float and append to WaveTracks
1049 for (int chn = 0; chn < mNumChannels; ++chn)
1050 {
1051 // Number of samples will never be more than 1152
1052 float sampleBuf[1152];
1053 wxASSERT(samples <= 1152);
1054
1055 // Copy over the samples
1056 for (int sample = 0; sample < samples; ++sample)
1057 {
1058 // Convert libmad's fixed point representation to float
1059 sampleBuf[sample] = ((float) pcm->samples[chn][sample] / (1L << MAD_F_FRACBITS));
1060 }
1061
1062 // And append to the channel
1063 mChannels[chn]->Append(
1064 (samplePtr) sampleBuf, floatSample, samples, 1,
1065 // Samples were 28 bit fixed-point converted to float:
1066 // see explanation above MAD_F_FRACBITS in mad.h
1067 // Effective sample format must therefore be
1068 // more than 24 bits, so this is our only choice.
1069 floatSample);
1070 }
1071
1072 return MAD_FLOW_CONTINUE;
1073}
char * samplePtr
Definition: SampleFormat.h:55
std::shared_ptr< WaveTrack > NewWaveTrack(WaveTrackFactory &trackFactory, sampleFormat effectiveFormat, double rate)
Build a wave track with appropriate format, which will not be narrower than the specified one.

References floatSample, mChannels, mNumChannels, mTrackFactory, and ImportFileHandle::NewWaveTrack().

Referenced by output_cb().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ SetStreamUsage()

void MP3ImportFileHandle::SetStreamUsage ( wxInt32  StreamID,
bool  Use 
)
overridevirtual

Implements ImportFileHandle.

Member Data Documentation

◆ mChannels

NewChannelGroup MP3ImportFileHandle::mChannels
private

Definition at line 160 of file ImportMP3_MAD.cpp.

Referenced by Import(), and OutputCB().

◆ mDecoder

mad_decoder MP3ImportFileHandle::mDecoder
private

Definition at line 150 of file ImportMP3_MAD.cpp.

Referenced by FilterCB(), and Import().

◆ mDelay

int MP3ImportFileHandle::mDelay
private

Definition at line 165 of file ImportMP3_MAD.cpp.

Referenced by FilterCB(), and Import().

◆ mFile

wxFile MP3ImportFileHandle::mFile
private

◆ mFileLen

wxFileOffset MP3ImportFileHandle::mFileLen
private

◆ mFilePos

wxFileOffset MP3ImportFileHandle::mFilePos
private

◆ mHaveID3

bool MP3ImportFileHandle::mHaveID3
private

Definition at line 168 of file ImportMP3_MAD.cpp.

Referenced by CheckID3V1Tags(), CheckID3V2Tags(), and Open().

◆ mInputBuffer

unsigned char MP3ImportFileHandle::mInputBuffer[INPUT_BUFFER_SIZE+MAD_BUFFER_GUARD]
private

◆ mInputBufferLen

int MP3ImportFileHandle::mInputBufferLen
private

Definition at line 157 of file ImportMP3_MAD.cpp.

Referenced by CheckMP3(), FillBuffer(), InputCB(), and Open().

◆ mNumChannels

unsigned MP3ImportFileHandle::mNumChannels
private

Definition at line 161 of file ImportMP3_MAD.cpp.

Referenced by ErrorCB(), Import(), and OutputCB().

◆ MP3ImportPlugin

friend MP3ImportFileHandle::MP3ImportPlugin
private

Definition at line 170 of file ImportMP3_MAD.cpp.

◆ mPadding

int MP3ImportFileHandle::mPadding
private

Definition at line 166 of file ImportMP3_MAD.cpp.

Referenced by FilterCB(), and Import().

◆ mTrackFactory

WaveTrackFactory* MP3ImportFileHandle::mTrackFactory
private

Definition at line 159 of file ImportMP3_MAD.cpp.

Referenced by Import(), and OutputCB().

◆ mUpdateResult

ProgressResult MP3ImportFileHandle::mUpdateResult
private

Definition at line 163 of file ImportMP3_MAD.cpp.

Referenced by Import(), and InputCB().


The documentation for this class was generated from the following file: