Add Shorten (SHN) support

This commit is contained in:
Stephen F. Booth
2024-12-07 19:34:59 -06:00
parent 90f62a3c94
commit 35edf12850
12 changed files with 1265 additions and 2 deletions

View File

@@ -25,6 +25,7 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/opus
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsf
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsdiff
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/shn
)
set(tag_c_HDRS tag_c.h)

View File

@@ -55,6 +55,7 @@
#include "opusfile.h"
#include "dsffile.h"
#include "dsdifffile.h"
#include "shnfile.h"
#include "tag.h"
#include "id3v2framefactory.h"
@@ -190,6 +191,9 @@ TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
case TagLib_File_DSDIFF:
file = new DSDIFF::File(filename);
break;
case TagLib_File_SHN:
file = new SHN::File(filename);
break;
default:
break;
}

View File

@@ -130,7 +130,8 @@ typedef enum {
TagLib_File_XM,
TagLib_File_Opus,
TagLib_File_DSF,
TagLib_File_DSDIFF
TagLib_File_DSDIFF,
TagLib_File_SHN
} TagLib_File_Type;
/*!

View File

@@ -26,6 +26,7 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/xm
${CMAKE_CURRENT_SOURCE_DIR}/dsf
${CMAKE_CURRENT_SOURCE_DIR}/dsdiff
${CMAKE_CURRENT_SOURCE_DIR}/shn
)
set(tag_HDRS
@@ -141,6 +142,9 @@ set(tag_HDRS
dsdiff/dsdifffile.h
dsdiff/dsdiffproperties.h
dsdiff/dsdiffdiintag.h
shn/shnfile.h
shn/shnproperties.h
shn/shntag.h
)
set(mpeg_SRCS
@@ -308,6 +312,12 @@ set(dsdiff_SRCS
dsdiff/dsdiffdiintag.cpp
)
set(shn_SRCS
shn/shnfile.cpp
shn/shnproperties.cpp
shn/shntag.cpp
)
set(toolkit_SRCS
toolkit/tstring.cpp
toolkit/tstringlist.cpp
@@ -331,7 +341,7 @@ set(tag_LIB_SRCS
${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS}
${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS}
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
${dsf_SRCS} ${dsdiff_SRCS}
${dsf_SRCS} ${dsdiff_SRCS} ${shn_SRCS}
tag.cpp
tagunion.cpp
fileref.cpp

View File

@@ -57,6 +57,7 @@
#include "xmfile.h"
#include "dsffile.h"
#include "dsdifffile.h"
#include "shnfile.h"
using namespace TagLib;
@@ -180,6 +181,8 @@ namespace
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "DFF" || ext == "DSDIFF")
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "SHN")
file = new SHN::File(stream, readAudioProperties, audioPropertiesStyle);
// if file is not valid, leave it to content-based detection.
@@ -231,6 +234,8 @@ namespace
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(DSDIFF::File::isSupported(stream))
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(SHN::File::isSupported(stream))
file = new SHN::File(stream, readAudioProperties, audioPropertiesStyle);
// isSupported() only does a quick check, so double check the file here.
@@ -436,6 +441,7 @@ StringList FileRef::defaultFileExtensions()
l.append("dsf");
l.append("dff");
l.append("dsdiff"); // alias for "dff"
l.append("shn");
return l;
}

623
taglib/shn/shnfile.cpp Normal file
View File

@@ -0,0 +1,623 @@
/***************************************************************************
copyright : (C) 2020-2024 Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <type_traits>
#include "shnfile.h"
#include "shnutils.h"
#include "tdebug.h"
#include "tutils.h"
#include "tagutils.h"
#include "tpropertymap.h"
using namespace TagLib;
namespace {
// MARK: Constants
const auto MIN_SUPPORTED_VERSION { 1 };
const auto MAX_SUPPORTED_VERSION { 3 };
const auto DEFAULT_BLOCK_SIZE { 256 };
const auto CHANSIZE { 0 };
const auto FNSIZE { 2 };
const auto FN_VERBATIM { 9 };
const auto VERBATIM_CKSIZE_SIZE { 5 };
const auto VERBATIM_BYTE_SIZE { 8 };
const auto VERBATIM_CHUNK_MAX { 256 };
const auto ULONGSIZE { 2 };
const auto NSKIPSIZE { 1 };
const auto LPCQSIZE { 2 };
const auto XBYTESIZE { 7 };
const auto TYPESIZE { 4 };
const auto MAX_CHANNELS { 8 };
const auto MAX_BLOCKSIZE { 65535 };
const auto CANONICAL_HEADER_SIZE { 44 };
const auto WAVE_FORMAT_PCM { 0x0001 };
// MARK: Variable-Length Input
/// Variable-length input using Golomb-Rice coding
class vario_input {
public:
static constexpr uint32_t sMaskTable [] = {
0x0,
0x1, 0x3, 0x7, 0xf,
0x1f, 0x3f, 0x7f, 0xff,
0x1ff, 0x3ff, 0x7ff, 0xfff,
0x1fff, 0x3fff, 0x7fff, 0xffff,
0x1ffff, 0x3ffff, 0x7ffff, 0xfffff,
0x1fffff, 0x3fffff, 0x7fffff, 0xffffff,
0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff,
0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff
};
/// Creates a new `vario_input` object with an internal buffer of the specified size
/// - warning: Sizes other than `512` will break seeking
vario_input(File *file, size_t size = 512) noexcept
: file_{file}, size_{size}
{
byte_buffer_ = new (std::nothrow) uint8_t [size_];
byte_buffer_position_ = byte_buffer_;
}
~vario_input()
{
delete [] byte_buffer_;
}
vario_input() = delete;
vario_input(const vario_input&) = delete;
vario_input(vario_input&&) = delete;
vario_input& operator=(const vario_input&) = delete;
vario_input& operator=(vario_input&&) = delete;
explicit operator bool() const noexcept
{
return byte_buffer_ != nullptr;
}
/// Reads a single unsigned value from the specified bin
bool uvar_get(int32_t& i32, size_t bin)
{
if(bits_available_ == 0) {
if(!word_get(bit_buffer_))
return false;
bits_available_ = 32;
}
int32_t result;
for(result = 0; !(bit_buffer_ & (1L << --bits_available_)); ++result) {
if(bits_available_ == 0) {
if(!word_get(bit_buffer_))
return false;
bits_available_ = 32;
}
}
while(bin != 0) {
if(bits_available_ >= bin) {
result = (result << bin) | static_cast<int32_t>((bit_buffer_ >> (bits_available_ - bin)) & sMaskTable[bin]);
bits_available_ -= bin;
bin = 0;
}
else {
result = (result << bits_available_) | static_cast<int32_t>(bit_buffer_ & sMaskTable[bits_available_]);
bin -= bits_available_;
if(!word_get(bit_buffer_))
return false;
bits_available_ = 32;
}
}
i32 = result;
return true;
}
/// Reads the unsigned Golomb-Rice code
bool ulong_get(uint32_t& ui32)
{
int32_t bitcount;
if(!uvar_get(bitcount, ULONGSIZE))
return false;
int32_t i32;
if(!uvar_get(i32, static_cast<uint32_t>(bitcount)))
return false;
ui32 = static_cast<uint32_t>(i32);
return true;
}
bool uint_get(uint32_t& ui32, int version, size_t bin)
{
if(version == 0) {
int32_t i32;
if(!uvar_get(i32, bin))
return false;
ui32 = static_cast<uint32_t>(i32);
return true;
}
else
return ulong_get(ui32);
}
private:
/// Input stream
File *file_ { nullptr };
/// Size of `byte_buffer_` in bytes
size_t size_ { 0 };
/// Byte buffer
uint8_t *byte_buffer_ { nullptr };
/// Current position in `byte_buffer_`
uint8_t *byte_buffer_position_ { nullptr };
/// Bytes available in `byte_buffer_`
size_t bytes_available_ { 0 };
/// Bit buffer
uint32_t bit_buffer_ { 0 };
/// Bits available in `bit_buffer_`
size_t bits_available_ { 0 };
/// Reads a single `uint32_t` from the byte buffer, refilling if necessary
bool word_get(uint32_t& ui32)
{
if(bytes_available_ < 4) {
auto block = file_->readBlock(size_);
if(block.size() < 4)
return false;
memcpy(byte_buffer_, block.data(), block.size());
bytes_available_ += block.size();
byte_buffer_position_ = byte_buffer_;
}
ui32 = static_cast<uint32_t>((static_cast<int32_t>(byte_buffer_position_[0]) << 24) | (static_cast<int32_t>(byte_buffer_position_[1]) << 16) | (static_cast<int32_t>(byte_buffer_position_[2]) << 8) | static_cast<int32_t>(byte_buffer_position_[3]));
byte_buffer_position_ += 4;
bytes_available_ -= 4;
return true;
}
};
// MARK: Unsigned Integer Reading
template <typename T, typename = std::enable_if<std::is_same_v<T, uint16_t> || std::is_same_v<T, uint32_t> || std::is_same_v<T, uint64_t>>>
T read_uint(void *p, uintptr_t n, bool big) noexcept
{
T value = *reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(p) + n);
auto system_big = Utils::systemByteOrder() == Utils::BigEndian;
if(big != system_big)
value = Utils::byteSwap(value);
return value;
}
uint64_t read_uint_big64(void *p, uintptr_t n) noexcept
{
return read_uint<uint64_t>(p, n, true);
}
uint32_t read_uint_big32(void *p, uintptr_t n) noexcept
{
return read_uint<uint32_t>(p, n, true);
}
uint32_t read_uint_little32(void *p, uintptr_t n) noexcept
{
return read_uint<uint32_t>(p, n, false);
}
uint16_t read_uint_big16(void *p, uintptr_t n) noexcept
{
return read_uint<uint16_t>(p, n, true);
}
uint16_t read_uint_little16(void *p, uintptr_t n) noexcept
{
return read_uint<uint16_t>(p, n, false);
}
} // namespace
class SHN::File::FilePrivate
{
public:
FilePrivate() = default;
~FilePrivate() = default;
FilePrivate(const FilePrivate &) = delete;
FilePrivate &operator=(const FilePrivate &) = delete;
std::unique_ptr<Properties> properties;
std::unique_ptr<Tag> tag;
};
bool SHN::File::isSupported(IOStream *stream)
{
// A Shorten file has to start with "ajkg"
const ByteVector id = Utils::readHeader(stream, 4, false);
return id.startsWith("ajkg");
}
SHN::File::File(FileName file, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) :
TagLib::File(file),
d(std::make_unique<FilePrivate>())
{
if(isOpen())
read(propertiesStyle);
}
SHN::File::File(IOStream *stream, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) :
TagLib::File(stream),
d(std::make_unique<FilePrivate>())
{
if(isOpen())
read(propertiesStyle);
}
SHN::File::~File() = default;
SHN::Tag *SHN::File::tag() const
{
return d->tag.get();
}
PropertyMap SHN::File::properties() const
{
return d->tag->properties();
}
PropertyMap SHN::File::setProperties(const PropertyMap &properties)
{
return d->tag->setProperties(properties);
}
SHN::Properties *SHN::File::audioProperties() const
{
return d->properties.get();
}
bool SHN::File::save()
{
if(readOnly()) {
debug("SHN::File::save() - Cannot save to a read only file.");
return false;
}
debug("SHN::File::save() - Saving not supported.");
return false;
}
void SHN::File::read(AudioProperties::ReadStyle propertiesStyle)
{
if(!isOpen())
return;
// Read magic number
auto magic = readBlock(4);
if(magic != "ajkg") {
debug("SHN::File::read() -- Not a Shorten file.");
setValid(false);
return;
}
PropertyValues props{};
// Read file version
auto version = readBlock(1).toUInt();
if(version < MIN_SUPPORTED_VERSION || version > MAX_SUPPORTED_VERSION) {
debug("SHN::File::read() -- Unsupported version.");
setValid(false);
return;
}
props.version = version;
// Set up variable length input
vario_input input{this, 512};
if(!input) {
debug("SHN::File::read() -- Unable to allocate variable-length input.");
setValid(false);
return;
}
// Read internal file type
uint32_t ftype;
if(!input.uint_get(ftype, version, TYPESIZE)) {
debug("SHN::File::read() -- Unable to read internal file type.");
setValid(false);
return;
}
props.internal_file_type = static_cast<int>(ftype);
// Read number of channels
uint32_t nchan = 0;
if(!input.uint_get(nchan, version, CHANSIZE) || nchan == 0 || nchan > MAX_CHANNELS) {
debug("SHN::File::read() -- Invalid or unsupported channel count.");
setValid(false);
return;
}
props.channel_count = static_cast<int>(nchan);
// Read blocksize if version > 0
if(version > 0) {
uint32_t blocksize = 0;
if(!input.uint_get(blocksize, version, static_cast<size_t>(log2(DEFAULT_BLOCK_SIZE))) || blocksize == 0 || blocksize > MAX_BLOCKSIZE) {
debug("SHN::File::read() -- Invalid or unsupported block size.");
setValid(false);
return;
}
uint32_t maxnlpc = 0;
if(!input.uint_get(maxnlpc, version, LPCQSIZE) /*|| maxnlpc > 1024*/) {
debug("SHN::File::read() -- Invalid maxnlpc.");
setValid(false);
return;
}
uint32_t nmean = 0;
if(!input.uint_get(nmean, version, 0) /*|| nmean > 32768*/) {
debug("SHN::File::read() -- Invalid nmean.");
setValid(false);
return;
}
uint32_t nskip;
if(!input.uint_get(nskip, version, NSKIPSIZE)) {
setValid(false);
return;
}
for(uint32_t i = 0; i < nskip; ++i) {
uint32_t dummy;
if(!input.uint_get(dummy, version, XBYTESIZE)) {
setValid(false);
return;
}
}
}
// Parse the WAVE or AIFF header in the verbatim section
int32_t fn;
if(!input.uvar_get(fn, FNSIZE) || fn != FN_VERBATIM) {
debug("SHN::File::read() -- Missing initial verbatim section.");
setValid(false);
return;
}
int32_t header_size;
if(!input.uvar_get(header_size, VERBATIM_CKSIZE_SIZE) || header_size < CANONICAL_HEADER_SIZE || header_size > VERBATIM_CHUNK_MAX) {
debug("SHN::File::read() -- Incorrect header size.");
setValid(false);
return;
}
uint8_t header_bytes [header_size];
for(int32_t i = 0; i < header_size; ++i) {
int32_t byte;
if(!input.uvar_get(byte, VERBATIM_BYTE_SIZE)) {
debug("SHN::File::read() -- Unable to read header.");
setValid(false);
return;
}
header_bytes[i] = static_cast<uint8_t>(byte);
}
// header_bytes is at least CANONICAL_HEADER_SIZE (44) bytes in size
auto chunkID = read_uint_big32(header_bytes, 0);
// auto chunkSize = read_uint_big32(header_bytes, 4);
const auto chunkData = header_bytes + 8;
const auto size = header_size - 8;
// WAVE
if(chunkID == 'RIFF') {
uintptr_t offset = 0;
chunkID = read_uint_big32(chunkData, offset);
offset += 4;
if(chunkID != 'WAVE') {
debug("SHN::File::read() -- Missing 'WAVE' in 'RIFF' chunk.");
setValid(false);
return;
}
auto sawFormatChunk = false;
uint32_t dataChunkSize = 0;
uint16_t blockAlign = 0;
while(offset < size) {
chunkID = read_uint_big32(chunkData, offset);
offset += 4;
auto chunkSize = read_uint_little32(chunkData, offset);
offset += 4;
switch(chunkID) {
case 'fmt ':
{
if(chunkSize < 16) {
debug("SHN::File::read() -- 'fmt ' chunk is too small.");
setValid(false);
return;
}
auto format_tag = read_uint_little16(chunkData, offset);
offset += 2;
if(format_tag != WAVE_FORMAT_PCM) {
debug("SHN::File::read() -- Unsupported WAVE format tag.");
setValid(false);
return;
}
auto channels = read_uint_little16(chunkData, offset);
offset += 2;
if(props.channel_count != channels)
debug("SHN::File::read() -- Channel count mismatch between Shorten and 'fmt ' chunk.");
props.sample_rate = static_cast<int>(read_uint_little32(chunkData, offset));
offset += 4;
// Skip average bytes per second
offset += 4;
blockAlign = read_uint_little16(chunkData, offset);
offset += 2;
props.bits_per_sample = static_cast<int>(read_uint_little16(chunkData, offset));
offset += 2;
if(chunkSize > 16)
debug("SHN::File::read() -- Extra bytes in 'fmt ' chunk not parsed.");
sawFormatChunk = true;
break;
}
case 'data':
dataChunkSize = chunkSize;
break;
}
}
if(!sawFormatChunk) {
debug("SHN::File::read() -- Missing 'fmt ' chunk.");
setValid(false);
return;
}
if(dataChunkSize && blockAlign)
props.sample_frames = static_cast<unsigned long>(dataChunkSize / blockAlign);
}
// AIFF
else if(chunkID == 'FORM') {
uintptr_t offset = 0;
auto chunkID = read_uint_big32(chunkData, offset);
offset += 4;
if(chunkID != 'AIFF' && chunkID != 'AIFC') {
debug("SHN::File::read() -- Missing 'AIFF' or 'AIFC' in 'FORM' chunk.");
setValid(false);
return;
}
// if(chunkID == 'AIFC')
// props.big_endian = true;
auto sawCommonChunk = false;
while(offset < size) {
chunkID = read_uint_big32(chunkData, offset);
offset += 4;
auto chunkSize = read_uint_big32(chunkData, offset);
offset += 4;
// All chunks must have an even length but the pad byte is not included in ckSize
chunkSize += (chunkSize & 1);
switch(chunkID) {
case 'COMM':
{
if(chunkSize < 18) {
debug("SHN::File::read() -- 'COMM' chunk is too small.");
setValid(false);
return;
}
auto channels = read_uint_big16(chunkData, offset);
offset += 2;
if(props.channel_count != channels)
debug("SHN::File::read() -- Channel count mismatch between Shorten and 'COMM' chunk.");
props.sample_frames = static_cast<unsigned long>(read_uint_big32(chunkData, offset));
offset += 4;
props.bits_per_sample = static_cast<int>(read_uint_big16(chunkData, offset));
offset += 2;
// sample rate is IEEE 754 80-bit extended float (16-bit exponent, 1-bit integer part, 63-bit fraction)
auto exp = static_cast<int16_t>(read_uint_big16(chunkData, offset)) - 16383 - 63;
offset += 2;
if(exp < -63 || exp > 63) {
debug("SHN::File::read() -- exp out of range.");
setValid(false);
return;
}
auto frac = read_uint_big64(chunkData, offset);
offset += 8;
if(exp >= 0)
props.sample_rate = static_cast<int>(frac << exp);
else
props.sample_rate = static_cast<int>((frac + (static_cast<uint64_t>(1) << (-exp - 1))) >> -exp);
if(chunkSize > 18)
debug("SHN::File::read() -- Extra bytes in 'COMM' chunk not parsed.");
sawCommonChunk = true;
break;
}
// Skip all other chunks
default:
offset += chunkSize;
break;
}
}
if(!sawCommonChunk) {
debug("SHN::File::read() -- Missing 'COMM' chunk");
setValid(false);
return;
}
}
else {
debug("SHN::File::read() -- Unsupported data format.");
setValid(false);
return;
}
d->tag = std::make_unique<Tag>();
d->properties = std::make_unique<Properties>(&props, propertiesStyle);
}

140
taglib/shn/shnfile.h Normal file
View File

@@ -0,0 +1,140 @@
/***************************************************************************
copyright : (C) 2020-2024 Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_SHNFILE_H
#define TAGLIB_SHNFILE_H
#include <memory>
#include "taglib_export.h"
#include "tfile.h"
#include "shnproperties.h"
#include "shntag.h"
namespace TagLib {
//! An implementation of Shorten metadata
/*!
* This is an implementation of Shorten metadata.
*/
namespace SHN {
//! An implementation of \c TagLib::File with Shorten specific methods
/*!
* This implements and provides an interface for Shorten files to the
* \c TagLib::Tag and \c TagLib::AudioProperties interfaces by way of implementing
* the abstract \c TagLib::File API as well as providing some additional
* information specific to Shorten files.
*/
class TAGLIB_EXPORT File : public TagLib::File {
public:
/*!
* Constructs a Shorten file from \a file.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
/*!
* Constructs a Shorten file from \a stream.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*/
File(IOStream *stream, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
/*!
* Destroys this instance of the File.
*/
~File() override;
File(const File &) = delete;
File &operator=(const File &) = delete;
/*!
* Returns the \c Shorten::Tag for this file.
*
* \note While the returned \c Tag instance is non-null Shorten tags are not supported.
*/
Tag *tag() const override;
/*!
* Implements the unified property interface -- export function.
*/
PropertyMap properties() const override;
/*!
* Implements the unified property interface -- import function.
*/
PropertyMap setProperties(const PropertyMap &) override;
/*!
* Returns the \c Shorten::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
Properties *audioProperties() const override;
/*!
* Save the file.
*
* \note Saving Shorten tags is not supported.
*/
bool save() override;
/*!
* Returns whether or not the given \a stream can be opened as a Shorten
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private:
void read(AudioProperties::ReadStyle propertiesStyle);
class FilePrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FilePrivate> d;
};
} // namespace SHN
} // namespace TagLib
#endif

View File

@@ -0,0 +1,112 @@
/***************************************************************************
copyright : (C) 2020-2024 Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "shnproperties.h"
#include "shnutils.h"
using namespace TagLib;
class SHN::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() = default;
~PropertiesPrivate() = default;
PropertiesPrivate(const PropertiesPrivate &) = delete;
PropertiesPrivate &operator=(const PropertiesPrivate &) = delete;
int version { 0 };
int internalFileType { 0 };
int channelCount { 0 };
unsigned long sampleFrames { 0 };
int sampleRate { 0 };
int bitsPerSample { 0 };
// Computed
int bitrate { 0 };
int length { 0 };
};
SHN::Properties::Properties(const PropertyValues *values, ReadStyle style) :
AudioProperties(style),
d(std::make_unique<PropertiesPrivate>())
{
if(values) {
d->version = values->version;
d->internalFileType = values->internal_file_type;
d->channelCount = values->channel_count;
d->sampleRate = values->sample_rate;
d->bitsPerSample = values->bits_per_sample;
d->sampleFrames = values->sample_frames;
d->bitrate = static_cast<int>(d->sampleRate * d->bitsPerSample * d->channelCount / 1000.0 + 0.5);
if(d->sampleRate > 0)
d->length = static_cast<int>(d->sampleFrames * 1000.0 / d->sampleRate + 0.5);
}
}
SHN::Properties::~Properties() = default;
int SHN::Properties::lengthInMilliseconds() const
{
return d->length;
}
int SHN::Properties::bitrate() const
{
return d->bitrate;
}
int SHN::Properties::sampleRate() const
{
return d->sampleRate;
}
int SHN::Properties::channels() const
{
return d->channelCount;
}
int SHN::Properties::shortenVersion() const
{
return d->version;
}
int SHN::Properties::internalFileType() const
{
return d->internalFileType;
}
int SHN::Properties::bitsPerSample() const
{
return d->bitsPerSample;
}
unsigned long SHN::Properties::sampleFrames() const
{
return d->sampleFrames;
}

View File

@@ -0,0 +1,68 @@
/***************************************************************************
copyright : (C) 2020-2024 Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_SHNPROPERTIES_H
#define TAGLIB_SHNPROPERTIES_H
#include <memory>
#include "taglib_export.h"
#include "audioproperties.h"
namespace TagLib {
namespace SHN {
struct PropertyValues;
//! An implementation of audio properties for Shorten
class TAGLIB_EXPORT Properties : public AudioProperties {
public:
Properties(const PropertyValues *values, ReadStyle style = Average);
~Properties() override;
Properties(const Properties &) = delete;
Properties &operator=(const Properties &) = delete;
int lengthInMilliseconds() const override;
int bitrate() const override;
int sampleRate() const override;
int channels() const override;
//! Returns the Shorten file version (1-3).
int shortenVersion() const;
//! Returns the Shorten internal file type.
int internalFileType() const;
int bitsPerSample() const;
unsigned long sampleFrames() const;
private:
class PropertiesPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PropertiesPrivate> d;
};
} // namespace SHN
} // namespace TagLib
#endif

114
taglib/shn/shntag.cpp Normal file
View File

@@ -0,0 +1,114 @@
/***************************************************************************
copyright : (C) 2020-2024 Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "shntag.h"
#include "tpropertymap.h"
using namespace TagLib;
class SHN::Tag::TagPrivate
{
};
SHN::Tag::Tag() :
d(std::make_unique<TagPrivate>())
{
}
SHN::Tag::~Tag() = default;
String SHN::Tag::title() const
{
return String();
}
String SHN::Tag::artist() const
{
return String();
}
String SHN::Tag::album() const
{
return String();
}
String SHN::Tag::comment() const
{
return String();
}
String SHN::Tag::genre() const
{
return String();
}
unsigned int SHN::Tag::year() const
{
return 0;
}
unsigned int SHN::Tag::track() const
{
return 0;
}
void SHN::Tag::setTitle(const String &)
{
}
void SHN::Tag::setArtist(const String &)
{
}
void SHN::Tag::setAlbum(const String &)
{
}
void SHN::Tag::setComment(const String &)
{
}
void SHN::Tag::setGenre(const String &)
{
}
void SHN::Tag::setYear(unsigned int)
{
}
void SHN::Tag::setTrack(unsigned int)
{
}
PropertyMap SHN::Tag::properties() const
{
return PropertyMap{};
}
PropertyMap SHN::Tag::setProperties(const PropertyMap &origProps)
{
return PropertyMap{origProps};
}

139
taglib/shn/shntag.h Normal file
View File

@@ -0,0 +1,139 @@
/***************************************************************************
copyright : (C) 2020-2024 Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_SHNTAG_H
#define TAGLIB_SHNTAG_H
#include "tag.h"
namespace TagLib {
namespace SHN {
//! A Shorten file tag implementation
/*!
* Tags for Shorten files.
*
* This is a stub class; Shorten files do not support tags.
*/
class TAGLIB_EXPORT Tag : public TagLib::Tag
{
public:
Tag();
~Tag() override;
Tag(const Tag &) = delete;
Tag &operator=(const Tag &) = delete;
/*!
* Not supported by Shorten files. Therefore always returns an empty string.
*/
String title() const override;
/*!
* Not supported by Shorten files. Therefore always returns an empty string.
*/
String artist() const override;
/*!
* Not supported by Shorten files. Therefore always returns an empty string.
*/
String album() const override;
/*!
* Not supported by Shorten files. Therefore always returns an empty string.
*/
String comment() const override;
/*!
* Not supported by Shorten files. Therefore always returns an empty string.
*/
String genre() const override;
/*!
* Not supported by Shorten files. Therefore always returns 0.
*/
unsigned int year() const override;
/*!
* Not supported by Shorten files. Therefore always returns 0.
*/
unsigned int track() const override;
/*!
* Not supported by Shorten files and therefore ignored.
*/
void setTitle(const String &title) override;
/*!
* Not supported by Shorten files and therefore ignored.
*/
void setArtist(const String &artist) override;
/*!
* Not supported by Shorten files and therefore ignored.
*/
void setAlbum(const String &album) override;
/*!
* Not supported by Shorten files and therefore ignored.
*/
void setComment(const String &comment) override;
/*!
* Not supported by Shorten files and therefore ignored.
*/
void setGenre(const String &genre) override;
/*!
* Not supported by Shorten files and therefore ignored.
*/
void setYear(unsigned int year) override;
/*!
* Not supported by Shorten files and therefore ignored.
*/
void setTrack(unsigned int track) override;
/*!
* Implements the unified property interface -- export function.
*/
PropertyMap properties() const override;
/*!
* Implements the unified property interface -- import function.
*/
PropertyMap setProperties(const PropertyMap &) override;
private:
class TagPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<TagPrivate> d;
};
} // namespace SHN
} // namespace TagLib
#endif

45
taglib/shn/shnutils.h Normal file
View File

@@ -0,0 +1,45 @@
/***************************************************************************
copyright : (C) 2020-2024 Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_SHNUTILS_H
#define TAGLIB_SHNUTILS_H
namespace TagLib {
namespace SHN {
/// Values shared with \c SHN::Properties by \c SHN::File
struct PropertyValues
{
int version { 0 };
int internal_file_type { 0 };
int channel_count { 0 };
int sample_rate { 0 };
int bits_per_sample { 0 };
unsigned long sample_frames { 0 };
};
} // namespace SHN
} // namespace TagLib
#endif