344 lines
9.9 KiB
C++
344 lines
9.9 KiB
C++
#include "bitfields.h"
|
|
#include "defines.h"
|
|
#include "encode.h"
|
|
#include "flac.h"
|
|
#include <sstream>
|
|
|
|
namespace FLAC{
|
|
|
|
/// Checks the first 4 bytes for the string "flaC". Implementing a basic FLAC header check,
|
|
/// returning true if it is, false if not.
|
|
bool is_header(const char *header){
|
|
if (header[0] != 'f') return false;
|
|
if (header[1] != 'L') return false;
|
|
if (header[2] != 'a') return false;
|
|
if (header[3] != 'C') return false;
|
|
return true;
|
|
}// FLAC::is_header
|
|
|
|
size_t utfBytes(char p){
|
|
if ((p & 0x80) == 0x00){return 1;}
|
|
if ((p & 0xE0) == 0xC0){return 2;}
|
|
if ((p & 0xF0) == 0xE0){return 3;}
|
|
if ((p & 0xF8) == 0xF0){return 4;}
|
|
if ((p & 0xFC) == 0xF8){return 5;}
|
|
if ((p & 0xFE) == 0xFC){return 6;}
|
|
if ((p & 0xFF) == 0xFE){return 7;}
|
|
return 9;
|
|
}
|
|
|
|
uint32_t utfVal(char *p){
|
|
size_t bytes = utfBytes(*p);
|
|
uint32_t ret = 0;
|
|
|
|
if (bytes == 1){
|
|
ret = (uint32_t)*p;
|
|
}else if (bytes == 2){
|
|
ret = (uint32_t)(*p & 0x1F) << 6;
|
|
ret = ret | (*(p + 1) & 0x3f);
|
|
}else if (bytes == 3){
|
|
ret = (uint32_t)(*p & 0x1F) << 6;
|
|
ret = (ret | (*(p + 1) & 0x3f)) << 6;
|
|
ret = ret | (*(p + 2) & 0x3f);
|
|
}else if (bytes == 4){
|
|
ret = (uint32_t)(*p & 0x1F) << 6;
|
|
ret = (ret | (*(p + 1) & 0x3f)) << 6;
|
|
ret = (ret | (*(p + 2) & 0x3f)) << 6;
|
|
ret = ret | (*(p + 3) & 0x3f);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
Frame::Frame(char *pkt){
|
|
data = pkt;
|
|
if (data[0] != 0xFF || (data[1] & 0xFC) != 0xF8){
|
|
WARN_MSG("Sync code incorrect! Ignoring FLAC frame");
|
|
FAIL_MSG("%x %x", data[0], data[1]);
|
|
data = 0;
|
|
}
|
|
}
|
|
|
|
uint16_t Frame::samples(){
|
|
if (!data){return 0;}
|
|
switch ((data[2] & 0xF0) >> 4){
|
|
case 0: return 0; // reserved
|
|
case 1: return 192;
|
|
case 2: return 576;
|
|
case 3: return 1152;
|
|
case 4: return 2304;
|
|
case 5: return 4608;
|
|
case 6: return 1; // 1b at end
|
|
case 7: return 2; // 2b at end
|
|
default: return 256 << (((data[2] & 0xf0) >> 4) - 8);
|
|
}
|
|
}
|
|
uint32_t Frame::rate(){
|
|
if (!data){return 0;}
|
|
switch (data[2] & 0x0F){
|
|
case 0: return 0; // get from STREAMINFO
|
|
case 1: return 88200;
|
|
case 2: return 176400;
|
|
case 3: return 192000;
|
|
case 4: return 8000;
|
|
case 5: return 16000;
|
|
case 6: return 22050;
|
|
case 7: return 24000;
|
|
case 8: return 32000;
|
|
case 9: return 44100;
|
|
case 10: return 48000;
|
|
case 11: return 96000;
|
|
case 12: return 1; // 1b at end, *1000
|
|
case 13: return 2; // 2b at end
|
|
case 14: return 3; // 2b at end, *10
|
|
case 15: return 0; // invalid, get from STREAMINFO
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
uint8_t Frame::channels(){
|
|
if (!data){return 0;}
|
|
uint8_t ret = ((data[3] & 0xF0) >> 4) + 1;
|
|
if (ret > 8 && ret < 12){return 2;}// special stereo
|
|
return ret;
|
|
}
|
|
uint8_t Frame::size(){
|
|
if (!data){return 0;}
|
|
switch (data[3] & 0x0E){
|
|
case 0: return 0; // get from STREAMINFO
|
|
case 1: return 8;
|
|
case 2: return 12;
|
|
case 3: return 0; // reserved
|
|
case 4: return 16;
|
|
case 5: return 20;
|
|
case 6: return 24;
|
|
case 7: return 0; // reserved
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
uint32_t Frame::utfVal(){
|
|
return ::FLAC::utfVal(data + 4);
|
|
}
|
|
|
|
std::string Frame::toPrettyString(){
|
|
if (!data){return "Invalid frame";}
|
|
std::stringstream r;
|
|
r << "FLAC frame, " << ((data[1] & 0x1) ? "variable" : "fixed") << " block size, " << samples() << " samples, "
|
|
<< rate() << " Hz, " << (int)channels() << " Ch, " << (int)size() << "-bit" << std::endl;
|
|
return r.str();
|
|
}
|
|
|
|
MetaBlock::MetaBlock() : ptr(0), len(0){}
|
|
MetaBlock::MetaBlock(const char *_ptr, size_t _len) : ptr(_ptr), len(_len){}
|
|
|
|
std::string MetaBlock::getType(){
|
|
if (!ptr || !len){return "";}
|
|
switch (ptr[0] & 0x7F){
|
|
case 0: return "STREAMINFO";
|
|
case 1: return "PADDING";
|
|
case 2: return "APPLICATION";
|
|
case 3: return "SEEKTABLE";
|
|
case 4: return "VORBIS_COMMENT";
|
|
case 5: return "CUESHEET";
|
|
case 6: return "PICTURE";
|
|
case 127: return "INVALID";
|
|
default: return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
size_t MetaBlock::getSize(){
|
|
if (!ptr || len < 4){return 0;}
|
|
return Bit::btoh24(ptr + 1);
|
|
}
|
|
|
|
bool MetaBlock::getLast(){
|
|
if (!ptr || !len){return false;}
|
|
return ptr[0] & 0x80;
|
|
}
|
|
|
|
void MetaBlock::toPrettyString(std::ostream &out){
|
|
if (len < 4){return;}
|
|
out << getType() << " metadata block (" << getSize() << "b, "
|
|
<< (getLast() ? "last" : "non-last") << ")" << std::endl;
|
|
}
|
|
|
|
/// Helper function that calls the other toPrettyString function and returns the output as a string
|
|
std::string MetaBlock::toPrettyString(){
|
|
std::stringstream str;
|
|
toPrettyString(str);
|
|
return str.str();
|
|
}
|
|
|
|
uint16_t StreamInfo::getMinBlockSize(){
|
|
return Bit::btohs(ptr + 4);
|
|
}
|
|
|
|
uint16_t StreamInfo::getMaxBlockSize(){
|
|
return Bit::btohs(ptr + 6);
|
|
}
|
|
|
|
uint32_t StreamInfo::getMinFrameSize(){
|
|
return Bit::btoh24(ptr + 8);
|
|
}
|
|
|
|
uint32_t StreamInfo::getMaxFrameSize(){
|
|
return Bit::btoh24(ptr + 11);
|
|
}
|
|
|
|
uint32_t StreamInfo::getSampleRate(){
|
|
return ((uint64_t)ptr[14] << 12) + ((uint64_t)ptr[15] << 4) + ((ptr[16] & 0xf0) >> 4);
|
|
}
|
|
|
|
uint8_t StreamInfo::getChannels(){
|
|
return ((ptr[16] & 0x0e) >> 1) + 1;
|
|
}
|
|
|
|
uint8_t StreamInfo::getBits(){
|
|
return ((ptr[17] & 0xf0) >> 4) + (ptr[16] & 1) * 16 + 1;
|
|
}
|
|
|
|
uint64_t StreamInfo::getSamples(){
|
|
return ((uint64_t)(ptr[17] & 0x0f) << 32) + ((uint64_t)ptr[18] << 24) +
|
|
((uint64_t)ptr[19] << 16) + ((uint64_t)ptr[20] << 8) + ptr[21];
|
|
}
|
|
|
|
std::string StreamInfo::getMD5(){
|
|
return Encodings::Hex::encode(std::string(ptr + 19, 16));
|
|
}
|
|
|
|
void StreamInfo::toPrettyString(std::ostream &out){
|
|
if (len < 4){return;}
|
|
out << getType() << " metadata block (" << getSize() << "b, "
|
|
<< (getLast() ? "last" : "non-last") << "):" << std::endl;
|
|
out << " Min block size: " << getMinBlockSize() << std::endl;
|
|
out << " Max block size: " << getMaxBlockSize() << std::endl;
|
|
out << " Min frame size: " << getMinFrameSize() << std::endl;
|
|
out << " Max frame size: " << getMaxFrameSize() << std::endl;
|
|
out << " Sample rate: " << getSampleRate() << std::endl;
|
|
out << " Channels: " << (size_t)getChannels() << std::endl;
|
|
out << " Bits: " << (size_t)getBits() << std::endl;
|
|
out << " Samples: " << getSamples() << std::endl;
|
|
out << " Checksum: " << getMD5() << std::endl;
|
|
}
|
|
|
|
std::string Picture::getPicType(){
|
|
uint32_t t = Bit::btohl(ptr + 4);
|
|
switch (t){
|
|
case 0: return "Other";
|
|
case 1: return "File icon";
|
|
case 2: return "Other file icon";
|
|
case 3: return "Cover (front)";
|
|
case 4: return "Cover (back)";
|
|
case 5: return "Leaflet";
|
|
case 6: return "Media";
|
|
case 7: return "Lead artist";
|
|
case 8: return "Artist";
|
|
case 9: return "Conductor";
|
|
case 10: return "Band";
|
|
case 11: return "Composer";
|
|
case 12: return "Text writer";
|
|
case 13: return "Recording location";
|
|
case 14: return "During recording";
|
|
case 15: return "During performance";
|
|
case 16: return "Movie capture";
|
|
case 17: return "A bright coloured fish";
|
|
case 18: return "Illustration";
|
|
case 19: return "Band logo";
|
|
case 20: return "Publisher logo";
|
|
}
|
|
return "Unknown";
|
|
}
|
|
|
|
std::string Picture::getMime(){
|
|
return std::string(ptr + 12, getMimeLen());
|
|
}
|
|
|
|
uint32_t Picture::getMimeLen(){
|
|
return Bit::btohl(ptr + 8);
|
|
}
|
|
|
|
std::string Picture::getDesc(){
|
|
return std::string(ptr + 16 + getMimeLen(), getDescLen());
|
|
}
|
|
|
|
uint32_t Picture::getDescLen(){
|
|
return Bit::btohl(ptr + 12 + getMimeLen());
|
|
}
|
|
|
|
uint32_t Picture::getWidth(){
|
|
return Bit::btohl(ptr + 16 + getMimeLen() + getDescLen());
|
|
}
|
|
|
|
uint32_t Picture::getHeight(){
|
|
return Bit::btohl(ptr + 20 + getMimeLen() + getDescLen());
|
|
}
|
|
|
|
uint32_t Picture::getDepth(){
|
|
return Bit::btohl(ptr + 24 + getMimeLen() + getDescLen());
|
|
}
|
|
|
|
uint32_t Picture::getColors(){
|
|
return Bit::btohl(ptr + 28 + getMimeLen() + getDescLen());
|
|
}
|
|
|
|
uint32_t Picture::getDataLen(){
|
|
return Bit::btohl(ptr + 32 + getMimeLen() + getDescLen());
|
|
}
|
|
|
|
const char *Picture::getData(){
|
|
return ptr + 36 + getMimeLen() + getDescLen();
|
|
}
|
|
|
|
void Picture::toPrettyString(std::ostream &out){
|
|
if (len < 4){return;}
|
|
out << getType() << " metadata block (" << getSize() << "b, "
|
|
<< (getLast() ? "last" : "non-last") << "):" << std::endl;
|
|
out << " Picture type: " << getPicType() << std::endl;
|
|
out << " Mime type: " << getMime() << std::endl;
|
|
out << " Description: " << getDesc() << std::endl;
|
|
out << " Dimensions: " << getWidth() << "x" << getHeight() << std::endl;
|
|
out << " Color depth: " << getDepth() << std::endl;
|
|
out << " Color count: " << getColors() << std::endl;
|
|
out << " Picture data size: " << getDataLen() << "b" << std::endl;
|
|
}
|
|
|
|
uint32_t VorbisComment::getVendorSize(){
|
|
return Bit::btohl_le(ptr + 4);
|
|
}
|
|
|
|
std::string VorbisComment::getVendor(){
|
|
return std::string(ptr + 8, getVendorSize());
|
|
}
|
|
|
|
std::string VorbisComment::getComment(uint32_t _num){
|
|
size_t offset = 12 + getVendorSize();
|
|
size_t i = 0;
|
|
while (offset < getSize() - 4){
|
|
size_t len = Bit::btohl_le(ptr + offset);
|
|
if (i == _num){return std::string(ptr + offset + 4, len);}
|
|
offset += 4 + len;
|
|
++i;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
uint32_t VorbisComment::getCommentCount(){
|
|
return Bit::btohl_le(ptr + 8 + getVendorSize());
|
|
}
|
|
|
|
void VorbisComment::toPrettyString(std::ostream &out){
|
|
if (len < 4){return;}
|
|
out << getType() << " metadata block (" << getSize() << "b, "
|
|
<< (getLast() ? "last" : "non-last") << "):" << std::endl;
|
|
out << " Vendor: " << getVendor() << std::endl;
|
|
out << " Comment count: " << getCommentCount() << std::endl;
|
|
size_t offset = 12 + getVendorSize();
|
|
while (offset < getSize() - 4){
|
|
size_t len = Bit::btohl_le(ptr + offset);
|
|
out << " " << std::string(ptr + offset + 4, len) << std::endl;
|
|
offset += 4 + len;
|
|
}
|
|
}
|
|
|
|
}; // Namespace FLAC
|