From a580996b1323d15dfe1d0f6d266fec668afd1896 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Wed, 24 Jan 2018 20:11:55 +0100 Subject: [PATCH] Backported RIFF library from Pro edition --- CMakeLists.txt | 2 + lib/riff.cpp | 175 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/riff.h | 79 ++++++++++++++++++++++ 3 files changed, 256 insertions(+) create mode 100644 lib/riff.cpp create mode 100644 lib/riff.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ae664f3..d1457feb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,6 +133,7 @@ set(libHeaders lib/util.h lib/vorbis.h lib/opus.h + lib/riff.h lib/ebml.h lib/ebml_socketglue.h ) @@ -174,6 +175,7 @@ add_library (mist lib/util.cpp lib/vorbis.cpp lib/opus.cpp + lib/riff.cpp lib/ebml.cpp lib/ebml_socketglue.cpp ) diff --git a/lib/riff.cpp b/lib/riff.cpp new file mode 100644 index 00000000..1315e9bd --- /dev/null +++ b/lib/riff.cpp @@ -0,0 +1,175 @@ +#include "riff.h" + +namespace RIFF{ + + Chunk::Chunk(const void *_p, uint32_t len){ + p = (const char *)_p; + if (len && len < getPayloadSize() + 8){ + FAIL_MSG("Chunk %s (%lub) does not fit in %lu bytes length!", getType().c_str(), + getPayloadSize() + 8, len); + p = 0; + } + } + + Chunk::Chunk(void *_p, const char * t, uint32_t len){ + p = (const char *)_p; + memcpy((void*)p, t, 4); + Bit::htobl_le((char*)p+4, len); + } + + void Chunk::toPrettyString(std::ostream &o, size_t indent) const{ + if (!p){ + o << std::string(indent, ' ') << "INVALID CHUNK" << std::endl; + return; + } + switch (Bit::btohl(p)){ + case 0x52494646lu: // RIFF + case 0x4C495354lu: // LIST + return ListChunk(p).toPrettyString(o, indent); + case 0x666D7420: // "fmt " + return fmt(p).toPrettyString(o, indent); + case 0x66616374: // fact + return fact(p).toPrettyString(o, indent); + case 0x49534654: // ISFT + return ISFT(p).toPrettyString(o, indent); + default: + o << std::string(indent, ' ') << "[" << getType() << "] UNIMPLEMENTED (" + << (getPayloadSize() + 8) << "b)" << std::endl; + } + } + + void ListChunk::toPrettyString(std::ostream &o, size_t indent) const{ + o << std::string(indent, ' ') << "[" << getType() << "] " << getIdentifier() << " (" + << (getPayloadSize() + 8) << "b):" << std::endl; + indent += 2; + uint32_t i = 12; + uint32_t len = getPayloadSize() + 8; + while (i + 8 <= len){ + const Chunk C(p + i); + C.toPrettyString(o, indent); + i += C.getPayloadSize() + 8; + if (!C){return;} + } + } + + uint16_t fmt::getFormat() const{ + if (!p){return 0;} + return Bit::btohs_le(p + 8); + } + std::string fmt::getCodec() const{ + switch (getFormat()){ + case 0x1: return "PCM"; + case 0x2: return "ADPCM"; + case 0x3: return "FLOAT"; + case 0x102: + case 0x172: + case 0x6: return "PCMA"; + case 0x101: + case 0x171: + case 0x7: return "PCMU"; + case 0x55: return "MP3"; + case 0xFFFE: // Extended, not implemented + default: return "?"; + } + } + uint16_t fmt::getChannels() const{ + if (!p){return 0;} + return Bit::btohs_le(p + 10); + } + uint32_t fmt::getHz() const{ + if (!p){return 0;} + return Bit::btohl_le(p + 12); + } + uint32_t fmt::getBPS() const{ + if (!p){return 0;} + return Bit::btohl_le(p + 16); + } + uint16_t fmt::getBlockSize() const{ + if (!p){return 0;} + return Bit::btohs_le(p + 20); + } + uint16_t fmt::getSize() const{ + if (!p){return 0;} + return Bit::btohs_le(p + 22); + } + uint16_t fmt::getExtLen() const{ + if (getPayloadSize() < 18){return 0;} + return Bit::btohs_le(p + 24); + } + uint16_t fmt::getValidBits() const{ + if (getPayloadSize() < 20 || getExtLen() < 2){return 0;} + return Bit::btohs_le(p + 26); + } + uint32_t fmt::getChannelMask() const{ + if (getPayloadSize() < 24 || getExtLen() < 6){return 0;} + return Bit::btohl_le(p + 28); + } + std::string fmt::getGUID() const{ + if (getPayloadSize() < 40 || getExtLen() < 22){return "";} + return std::string(p + 32, 16); + } + void fmt::toPrettyString(std::ostream &o, size_t indent) const{ + o << std::string(indent, ' ') << "[" << getType() << "] (" << (getPayloadSize() + 8) + << "b):" << std::endl; + indent += 1; + o << std::string(indent, ' ') << "Codec: " << getCodec() << " (" << getFormat() << ")" + << std::endl; + o << std::string(indent, ' ') << "Channels: " << getChannels() << std::endl; + o << std::string(indent, ' ') << "Sample rate: " << getHz() << "Hz" << std::endl; + o << std::string(indent, ' ') << "Bytes/s: " << getBPS() << std::endl; + o << std::string(indent, ' ') << "Block size: " << getBlockSize() << " bytes" << std::endl; + o << std::string(indent, ' ') << "Sample size: " << getSize() << " bits" << std::endl; + if (getExtLen()){ + o << std::string(indent, ' ') << "-- extended " << getExtLen() << "bytes --" << std::endl; + if (getExtLen() >= 2){ + o << std::string(indent, ' ') << "Valid bits: " << getValidBits() << std::endl; + } + if (getExtLen() >= 6){ + o << std::string(indent, ' ') << "Channel mask: " << getChannelMask() << std::endl; + } + if (getExtLen() >= 22){ + o << std::string(indent, ' ') << "GUID: " << getGUID() << std::endl; + } + } + } + std::string fmt::generate(uint16_t format, uint16_t channels, uint32_t hz, uint32_t bps, uint16_t blocksize, uint16_t size){ + std::string ret("fmt \022\000\000\000", 8); + ret.append(std::string((size_t)18, '\000')); + Bit::htobs_le((char*)ret.data()+8, format); + Bit::htobs_le((char*)ret.data()+10, channels); + Bit::htobl_le((char*)ret.data()+12, hz); + Bit::htobl_le((char*)ret.data()+16, bps); + Bit::htobs_le((char*)ret.data()+20, blocksize); + Bit::htobs_le((char*)ret.data()+22, size); + Bit::htobs_le((char*)ret.data()+24, 0); + return ret; + } + + uint32_t fact::getSamplesPerChannel() const{ + if (!p){return 0;} + return Bit::btohl_le(p + 8); + } + void fact::toPrettyString(std::ostream &o, size_t indent) const{ + o << std::string(indent, ' ') << "[" << getType() << "] (" << (getPayloadSize() + 8) + << "b):" << std::endl; + indent += 1; + o << std::string(indent, ' ') << "Samples per channel: " << getSamplesPerChannel() << std::endl; + } + std::string fact::generate(uint32_t samples){ + std::string ret("fact\004\000\000\000\000\000\000\000", 12); + Bit::htobl_le((char*)ret.data()+8, samples); + return ret; + } + + std::string ISFT::getSoftware() const{ + if (!p){return 0;} + return std::string(p+8, getPayloadSize()); + } + void ISFT::toPrettyString(std::ostream &o, size_t indent) const{ + o << std::string(indent, ' ') << "[" << getType() << "] (" << (getPayloadSize() + 8) + << "b):" << std::endl; + indent += 1; + o << std::string(indent, ' ') << "Software: " << getSoftware() << std::endl; + } +} + diff --git a/lib/riff.h b/lib/riff.h new file mode 100644 index 00000000..7b038598 --- /dev/null +++ b/lib/riff.h @@ -0,0 +1,79 @@ +#pragma once +#include "bitfields.h" +#include "defines.h" +#include + +namespace RIFF{ + + /// Basic RIFF chunk class - can only read type and size. + /// All RIFF chunks have this format. + class Chunk{ + public: + Chunk(const void *_p = 0, uint32_t len = 0); + Chunk(void *_p, const char * t, uint32_t len); + inline operator bool() const{return p;} + inline std::string getType() const{ + if (!p){return "";} + return std::string(p, 4); + } + inline uint32_t getPayloadSize() const{ + if (!p){return 0;} + return Bit::btohl_le(p + 4); + } + virtual void toPrettyString(std::ostream &o, size_t indent = 0) const; + + protected: + const char *p; + }; + + /// List-type RIFF chunk. Can read list identifier. + /// "RIFF" and "LIST" type chunks follow this format. + class ListChunk : public Chunk{ + public: + ListChunk(const void *_p = 0, uint32_t len = 0) : Chunk(_p, len){} + inline std::string getIdentifier() const{ + if (!p){return "";} + return std::string(p + 8, 4); + } + virtual void toPrettyString(std::ostream &o, size_t indent = 0) const; + }; + + /// WAVE "fmt " class. + class fmt : public Chunk{ + public: + static std::string generate(uint16_t format, uint16_t channels, uint32_t hz, uint32_t bps, uint16_t blocksize, uint16_t size); + fmt(const void *_p = 0, uint32_t len = 0) : Chunk(_p, len){} + uint16_t getFormat() const; + std::string getCodec() const; + uint16_t getChannels() const; + uint32_t getHz() const; + uint32_t getBPS() const; + uint16_t getBlockSize() const; + uint16_t getSize() const; + uint16_t getExtLen() const; + uint16_t getValidBits() const; + uint32_t getChannelMask() const; + std::string getGUID() const; + virtual void toPrettyString(std::ostream &o, size_t indent = 0) const; + }; + + /// WAVE fact class. + class fact : public Chunk { + public: + static std::string generate(uint32_t samples); + fact(const void *_p = 0, uint32_t len = 0) : Chunk(_p, len){} + uint32_t getSamplesPerChannel() const; + virtual void toPrettyString(std::ostream &o, size_t indent = 0) const; + }; + + /// ISFT class. Contains software name. + class ISFT : public Chunk { + public: + ISFT(const void *_p = 0, uint32_t len = 0) : Chunk(_p, len){} + std::string getSoftware() const; + virtual void toPrettyString(std::ostream &o, size_t indent = 0) const; + }; + + +} +