410 {
411 Mp3Info info;
412
413
415 if (error_message) {
416 *error_message = "Empty MP3 data";
417 }
418 return info;
419 }
420
421
422 if (data.
size() < 128) {
423 if (error_message) {
424 *error_message = "MP3 data too small";
425 }
426 return info;
427 }
428
429
430 bool foundSync = false;
431 fl::size syncOffset = 0;
432
433 for (fl::size i = 0; i <= data.
size() - 4; i++) {
434 if (data[i] == 0xFF && (data[i + 1] & 0xE0) == 0xE0) {
435 foundSync = true;
436 syncOffset = i;
437 break;
438 }
439 }
440
441 if (!foundSync) {
442 if (error_message) {
443 *error_message = "Invalid MP3 stream - no sync word found";
444 }
445 return info;
446 }
447
448
449 third_party::Mp3HelixDecoder decoder;
450 if (!decoder.init()) {
451 if (error_message) {
452 *error_message = "Failed to initialize MP3 decoder";
453 }
454 return info;
455 }
456
457 const fl::u8* inptr = data.
data() + syncOffset;
458 fl::size bytes_left = data.
size() - syncOffset;
459
460
461 int offset = decoder.findSyncWord(inptr, bytes_left);
465
466 int result = decoder.decodeFrame(&inptr, &bytes_left);
468
469 info.sampleRate = decoder.mFrameInfo.samprate;
470 info.channels =
static_cast<fl::u8>(decoder.mFrameInfo.nChans);
471 info.bitrate = decoder.mFrameInfo.bitrate;
472 info.version =
static_cast<fl::u8>(decoder.mFrameInfo.version);
473 info.layer =
static_cast<fl::u8>(decoder.mFrameInfo.layer);
474 info.isValid = true;
475 } else if (error_message) {
476 *error_message = "Failed to decode MP3 frame header";
477 }
478 } else if (error_message) {
479 *error_message = "Failed to find MP3 sync word";
480 }
481
482 return info;
483}
constexpr bool empty() const FL_NOEXCEPT
const T * data() const FL_NOEXCEPT
constexpr fl::size size() const FL_NOEXCEPT
fl::UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
expected< T, E > result
Alias for expected (Rust-style naming)