2015-09-06 19:57:33 +02:00
|
|
|
#include "./mpegaudioframe.h"
|
2015-09-06 15:42:18 +02:00
|
|
|
|
2015-09-06 19:57:33 +02:00
|
|
|
#include "../exceptions.h"
|
2015-04-22 19:22:01 +02:00
|
|
|
|
2020-11-14 22:58:40 +01:00
|
|
|
#include <c++utilities/conversion/stringbuilder.h>
|
|
|
|
#include <c++utilities/conversion/stringconversion.h>
|
2015-04-22 19:22:01 +02:00
|
|
|
#include <c++utilities/io/binaryreader.h>
|
|
|
|
|
|
|
|
using namespace std;
|
2019-06-10 22:49:11 +02:00
|
|
|
using namespace CppUtilities;
|
2015-04-22 19:22:01 +02:00
|
|
|
|
2018-03-06 23:09:15 +01:00
|
|
|
namespace TagParser {
|
2015-04-22 19:22:01 +02:00
|
|
|
|
2015-07-31 01:09:41 +02:00
|
|
|
/*!
|
|
|
|
* \brief Returns the string representation for the specified \a channelMode.
|
|
|
|
*/
|
2021-01-30 21:53:06 +01:00
|
|
|
std::string_view mpegChannelModeString(MpegChannelMode channelMode)
|
2015-07-31 01:09:41 +02:00
|
|
|
{
|
2018-03-07 01:17:50 +01:00
|
|
|
switch (channelMode) {
|
|
|
|
case MpegChannelMode::Stereo:
|
|
|
|
return "2 channels: stereo";
|
|
|
|
case MpegChannelMode::JointStereo:
|
|
|
|
return "2 channels: joint stereo";
|
|
|
|
case MpegChannelMode::DualChannel:
|
|
|
|
return "2 channels: dual channel";
|
|
|
|
case MpegChannelMode::SingleChannel:
|
|
|
|
return "1 channel: single channel";
|
|
|
|
default:
|
2021-02-01 17:10:01 +01:00
|
|
|
return std::string_view();
|
2015-07-31 01:09:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-22 19:22:01 +02:00
|
|
|
/*!
|
2018-06-03 20:38:32 +02:00
|
|
|
* \class TagParser::MpegAudioFrame
|
2015-04-22 19:22:01 +02:00
|
|
|
* \brief The MpegAudioFrame class is used to parse MPEG audio frames.
|
|
|
|
*/
|
|
|
|
|
2019-03-13 19:06:42 +01:00
|
|
|
const std::uint16_t MpegAudioFrame::s_bitrateTable[0x2][0x3][0xF] = { { { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 },
|
|
|
|
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 },
|
|
|
|
{ 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 } },
|
2018-03-07 01:17:50 +01:00
|
|
|
{ { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 }, { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 },
|
|
|
|
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 } } };
|
2015-04-22 19:22:01 +02:00
|
|
|
|
|
|
|
/*!
|
2015-07-15 00:10:24 +02:00
|
|
|
* \brief Parses the header read using the specified \a reader.
|
2015-04-22 19:22:01 +02:00
|
|
|
* \throws Throws InvalidDataException if the data read from the stream is
|
|
|
|
* no valid frame header.
|
|
|
|
*/
|
2018-07-28 14:56:00 +02:00
|
|
|
void MpegAudioFrame::parseHeader(BinaryReader &reader, Diagnostics &diag)
|
2015-04-22 19:22:01 +02:00
|
|
|
{
|
2020-11-14 22:58:40 +01:00
|
|
|
// read MPEG audio frame header
|
2015-04-22 19:22:01 +02:00
|
|
|
m_header = reader.readUInt32BE();
|
2018-03-07 01:17:50 +01:00
|
|
|
if (!isValid()) {
|
2020-11-14 22:58:40 +01:00
|
|
|
diag.emplace_back(DiagLevel::Critical,
|
2021-03-20 21:26:25 +01:00
|
|
|
"Frame 0x" % numberToString(m_header, 16u) % " at 0x" % numberToString<std::int64_t>(reader.stream()->tellg() - 4l, 16) + " is invalid.",
|
2020-11-14 22:58:40 +01:00
|
|
|
"parsing MPEG audio frame header");
|
2015-07-31 01:09:41 +02:00
|
|
|
throw InvalidDataException();
|
|
|
|
}
|
2020-11-14 22:58:40 +01:00
|
|
|
|
|
|
|
// read XING header (see https://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header#XINGHeader)
|
|
|
|
if (size() < s_xingHeaderOffset - 4 + 8) {
|
|
|
|
return;
|
|
|
|
}
|
2018-07-28 21:20:02 +02:00
|
|
|
reader.stream()->seekg(s_xingHeaderOffset - 4, ios_base::cur);
|
2015-04-22 19:22:01 +02:00
|
|
|
m_xingHeader = reader.readUInt64BE();
|
2018-03-07 01:17:50 +01:00
|
|
|
if (isXingHeaderAvailable()) {
|
2020-11-14 22:58:40 +01:00
|
|
|
m_xingHeaderFlags = static_cast<XingHeaderFlags>(m_xingHeader & 0xffffffffuL);
|
2018-03-07 01:17:50 +01:00
|
|
|
if (isXingFramefieldPresent()) {
|
2015-04-22 19:22:01 +02:00
|
|
|
m_xingFramefield = reader.readUInt32BE();
|
|
|
|
}
|
2018-03-07 01:17:50 +01:00
|
|
|
if (isXingBytesfieldPresent()) {
|
2015-04-22 19:22:01 +02:00
|
|
|
m_xingBytesfield = reader.readUInt32BE();
|
|
|
|
}
|
2018-03-07 01:17:50 +01:00
|
|
|
if (isXingTocFieldPresent()) {
|
2015-07-15 00:10:24 +02:00
|
|
|
reader.stream()->seekg(64, ios_base::cur);
|
2015-04-22 19:22:01 +02:00
|
|
|
}
|
2018-03-07 01:17:50 +01:00
|
|
|
if (isXingQualityIndicatorFieldPresent()) {
|
2015-04-22 19:22:01 +02:00
|
|
|
m_xingQualityIndicator = reader.readUInt32BE();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns the MPEG version if known (1.0, 2.0 or 2.5); otherwise returns 0.
|
|
|
|
*/
|
|
|
|
double MpegAudioFrame::mpegVersion() const
|
|
|
|
{
|
2018-03-07 01:17:50 +01:00
|
|
|
switch (m_header & 0x180000u) {
|
2015-04-22 19:22:01 +02:00
|
|
|
case 0x180000u:
|
|
|
|
return 1.0;
|
|
|
|
case 0x100000u:
|
|
|
|
return 2.0;
|
|
|
|
case 0x0u:
|
|
|
|
return 2.5;
|
|
|
|
default:
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns the MPEG layer if known (1, 2, or 3); otherwise returns 0.
|
|
|
|
*/
|
|
|
|
int MpegAudioFrame::layer() const
|
|
|
|
{
|
|
|
|
switch (m_header & 0x60000u) {
|
|
|
|
case 0x60000u:
|
|
|
|
return 1;
|
|
|
|
case 0x40000u:
|
|
|
|
return 2;
|
|
|
|
case 0x20000u:
|
|
|
|
return 3;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
2015-07-31 01:09:41 +02:00
|
|
|
* \brief Returns the sampeling frequency of the frame if known; otherwise returns 0.
|
2015-04-22 19:22:01 +02:00
|
|
|
*/
|
2019-03-13 19:06:42 +01:00
|
|
|
std::uint32_t MpegAudioFrame::samplingFrequency() const
|
2015-04-22 19:22:01 +02:00
|
|
|
{
|
|
|
|
switch (m_header & 0xc00u) {
|
|
|
|
case 0xc00u:
|
|
|
|
return 0;
|
|
|
|
case 0x800u:
|
|
|
|
switch (m_header & 0x180000u) {
|
|
|
|
case 0x180000u:
|
|
|
|
return 32000;
|
|
|
|
case 0x100000u:
|
|
|
|
return 16000;
|
|
|
|
case 0x0u:
|
|
|
|
return 8000u;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x400u:
|
|
|
|
switch (m_header & 0x180000u) {
|
|
|
|
case 0x180000u:
|
|
|
|
return 48000;
|
|
|
|
case 0x100000u:
|
|
|
|
return 24000;
|
|
|
|
case 0x0u:
|
|
|
|
return 12000;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x0u:
|
|
|
|
switch (m_header & 0x180000u) {
|
|
|
|
case 0x180000u:
|
|
|
|
return 44100;
|
|
|
|
case 0x100000:
|
|
|
|
return 22050;
|
|
|
|
case 0x0u:
|
|
|
|
return 11025;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns the channel mode if known; otherwise returns MpegChannelMode::Unspecifed.
|
|
|
|
*/
|
|
|
|
MpegChannelMode MpegAudioFrame::channelMode() const
|
|
|
|
{
|
2018-03-07 01:17:50 +01:00
|
|
|
if (isValid()) {
|
2015-04-22 19:22:01 +02:00
|
|
|
switch (m_header & 0xc0u) {
|
|
|
|
case 0xc0u:
|
|
|
|
return MpegChannelMode::SingleChannel;
|
|
|
|
case 0x80u:
|
|
|
|
return MpegChannelMode::DualChannel;
|
|
|
|
case 0x40u:
|
|
|
|
return MpegChannelMode::JointStereo;
|
|
|
|
case 0x00:
|
|
|
|
return MpegChannelMode::Stereo;
|
2018-03-07 01:17:50 +01:00
|
|
|
default:;
|
2015-04-22 19:22:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return MpegChannelMode::Unspecifed;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns the sample count if known; otherwise returns 0.
|
|
|
|
*/
|
2019-03-13 19:06:42 +01:00
|
|
|
std::uint32_t MpegAudioFrame::sampleCount() const
|
2015-04-22 19:22:01 +02:00
|
|
|
{
|
|
|
|
switch (m_header & 0x60000u) {
|
|
|
|
case 0x60000u:
|
|
|
|
return 384u;
|
|
|
|
case 0x40000u:
|
|
|
|
return 1152u;
|
|
|
|
case 0x20000u:
|
|
|
|
switch (m_header & 0x180000u) {
|
|
|
|
case 0x180000u:
|
|
|
|
return 1152u;
|
|
|
|
case 0x100000u:
|
|
|
|
case 0x0u:
|
|
|
|
return 576u;
|
|
|
|
}
|
2018-03-07 01:17:50 +01:00
|
|
|
default:;
|
2015-04-22 19:22:01 +02:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
2021-07-02 03:00:50 +02:00
|
|
|
* \brief Returns the size if known; otherwise returns 0.
|
2015-04-22 19:22:01 +02:00
|
|
|
*/
|
2019-03-13 19:06:42 +01:00
|
|
|
std::uint32_t MpegAudioFrame::size() const
|
2015-04-22 19:22:01 +02:00
|
|
|
{
|
|
|
|
switch (m_header & 0x60000u) {
|
2020-11-14 22:54:33 +01:00
|
|
|
case 0x60000u: // layer 1
|
2019-03-13 19:06:42 +01:00
|
|
|
return static_cast<std::uint32_t>(
|
2020-11-14 22:54:33 +01:00
|
|
|
((static_cast<double>(bitrate()) * 1024.0 / 8.0) / static_cast<double>(samplingFrequency())) * static_cast<double>(sampleCount())
|
|
|
|
+ static_cast<double>(paddingSize()))
|
|
|
|
* 4;
|
|
|
|
case 0x40000u: // layer 2
|
|
|
|
case 0x20000u: // layer 3
|
2019-03-13 19:06:42 +01:00
|
|
|
return static_cast<std::uint32_t>(
|
2018-03-07 01:17:50 +01:00
|
|
|
((static_cast<double>(bitrate()) * 1024.0 / 8.0) / static_cast<double>(samplingFrequency())) * static_cast<double>(sampleCount())
|
|
|
|
+ static_cast<double>(paddingSize()));
|
2015-04-22 19:22:01 +02:00
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-07 01:17:50 +01:00
|
|
|
} // namespace TagParser
|