Browse Source

Revert "Delete code that ended up not being needed"

This reverts commit 3870412e57.
tags/v1.9.11
falkTX 6 years ago
parent
commit
d28fafb840
15 changed files with 3658 additions and 6 deletions
  1. +976
    -0
      source/modules/water/audiocodecs/WavAudioFormat.cpp
  2. +200
    -0
      source/modules/water/audiocodecs/WavAudioFormat.h
  3. +67
    -0
      source/modules/water/audioformat/AudioFormat.cpp
  4. +134
    -0
      source/modules/water/audioformat/AudioFormat.h
  5. +184
    -0
      source/modules/water/audioformat/AudioFormatManager.cpp
  6. +147
    -0
      source/modules/water/audioformat/AudioFormatManager.h
  7. +191
    -0
      source/modules/water/audioformat/AudioFormatReader.cpp
  8. +234
    -0
      source/modules/water/audioformat/AudioFormatReader.h
  9. +478
    -0
      source/modules/water/buffers/AudioDataConverters.cpp
  10. +719
    -0
      source/modules/water/buffers/AudioDataConverters.h
  11. +2
    -6
      source/modules/water/maths/BigInteger.cpp
  12. +150
    -0
      source/modules/water/text/StringPairArray.cpp
  13. +165
    -0
      source/modules/water/text/StringPairArray.h
  14. +9
    -0
      source/modules/water/water.cpp
  15. +2
    -0
      source/modules/water/water.h

+ 976
- 0
source/modules/water/audiocodecs/WavAudioFormat.cpp View File

@@ -0,0 +1,976 @@
/*
==============================================================================
This file is part of the Water library.
Copyright (c) 2015 ROLI Ltd.
Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
Water 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 General Public License for more details.
==============================================================================
*/
#include "WavAudioFormat.h"
#include "../audioformat/AudioFormatReader.h"
#include "../memory/ByteOrder.h"
#include "../memory/MemoryBlock.h"
#include "../streams/InputStream.h"
#include "../streams/MemoryOutputStream.h"
#include "../xml/XmlDocument.h"
#include "../xml/XmlElement.h"
namespace water {
static const char* const wavFormatName = "WAV file";
//==============================================================================
const char* const WavAudioFormat::bwavDescription = "bwav description";
const char* const WavAudioFormat::bwavOriginator = "bwav originator";
const char* const WavAudioFormat::bwavOriginatorRef = "bwav originator ref";
const char* const WavAudioFormat::bwavOriginationDate = "bwav origination date";
const char* const WavAudioFormat::bwavOriginationTime = "bwav origination time";
const char* const WavAudioFormat::bwavTimeReference = "bwav time reference";
const char* const WavAudioFormat::bwavCodingHistory = "bwav coding history";
#if 0
StringPairArray WavAudioFormat::createBWAVMetadata (const String& description,
const String& originator,
const String& originatorRef,
Time date,
const int64 timeReferenceSamples,
const String& codingHistory)
{
StringPairArray m;
m.set (bwavDescription, description);
m.set (bwavOriginator, originator);
m.set (bwavOriginatorRef, originatorRef);
m.set (bwavOriginationDate, date.formatted ("%Y-%m-%d"));
m.set (bwavOriginationTime, date.formatted ("%H:%M:%S"));
m.set (bwavTimeReference, String (timeReferenceSamples));
m.set (bwavCodingHistory, codingHistory);
return m;
}
#endif
const char* const WavAudioFormat::acidOneShot = "acid one shot";
const char* const WavAudioFormat::acidRootSet = "acid root set";
const char* const WavAudioFormat::acidStretch = "acid stretch";
const char* const WavAudioFormat::acidDiskBased = "acid disk based";
const char* const WavAudioFormat::acidizerFlag = "acidizer flag";
const char* const WavAudioFormat::acidRootNote = "acid root note";
const char* const WavAudioFormat::acidBeats = "acid beats";
const char* const WavAudioFormat::acidDenominator = "acid denominator";
const char* const WavAudioFormat::acidNumerator = "acid numerator";
const char* const WavAudioFormat::acidTempo = "acid tempo";
const char* const WavAudioFormat::riffInfoArchivalLocation = "IARL";
const char* const WavAudioFormat::riffInfoArtist = "IART";
const char* const WavAudioFormat::riffInfoBaseURL = "IBSU";
const char* const WavAudioFormat::riffInfoCinematographer = "ICNM";
const char* const WavAudioFormat::riffInfoComment = "CMNT";
const char* const WavAudioFormat::riffInfoComments = "COMM";
const char* const WavAudioFormat::riffInfoCommissioned = "ICMS";
const char* const WavAudioFormat::riffInfoCopyright = "ICOP";
const char* const WavAudioFormat::riffInfoCostumeDesigner = "ICDS";
const char* const WavAudioFormat::riffInfoCountry = "ICNT";
const char* const WavAudioFormat::riffInfoCropped = "ICRP";
const char* const WavAudioFormat::riffInfoDateCreated = "ICRD";
const char* const WavAudioFormat::riffInfoDateTimeOriginal = "IDIT";
const char* const WavAudioFormat::riffInfoDefaultAudioStream = "ICAS";
const char* const WavAudioFormat::riffInfoDimension = "IDIM";
const char* const WavAudioFormat::riffInfoDirectory = "DIRC";
const char* const WavAudioFormat::riffInfoDistributedBy = "IDST";
const char* const WavAudioFormat::riffInfoDotsPerInch = "IDPI";
const char* const WavAudioFormat::riffInfoEditedBy = "IEDT";
const char* const WavAudioFormat::riffInfoEighthLanguage = "IAS8";
const char* const WavAudioFormat::riffInfoEncodedBy = "CODE";
const char* const WavAudioFormat::riffInfoEndTimecode = "TCDO";
const char* const WavAudioFormat::riffInfoEngineer = "IENG";
const char* const WavAudioFormat::riffInfoFifthLanguage = "IAS5";
const char* const WavAudioFormat::riffInfoFirstLanguage = "IAS1";
const char* const WavAudioFormat::riffInfoFourthLanguage = "IAS4";
const char* const WavAudioFormat::riffInfoGenre = "GENR";
const char* const WavAudioFormat::riffInfoKeywords = "IKEY";
const char* const WavAudioFormat::riffInfoLanguage = "LANG";
const char* const WavAudioFormat::riffInfoLength = "TLEN";
const char* const WavAudioFormat::riffInfoLightness = "ILGT";
const char* const WavAudioFormat::riffInfoLocation = "LOCA";
const char* const WavAudioFormat::riffInfoLogoIconURL = "ILIU";
const char* const WavAudioFormat::riffInfoLogoURL = "ILGU";
const char* const WavAudioFormat::riffInfoMedium = "IMED";
const char* const WavAudioFormat::riffInfoMoreInfoBannerImage = "IMBI";
const char* const WavAudioFormat::riffInfoMoreInfoBannerURL = "IMBU";
const char* const WavAudioFormat::riffInfoMoreInfoText = "IMIT";
const char* const WavAudioFormat::riffInfoMoreInfoURL = "IMIU";
const char* const WavAudioFormat::riffInfoMusicBy = "IMUS";
const char* const WavAudioFormat::riffInfoNinthLanguage = "IAS9";
const char* const WavAudioFormat::riffInfoNumberOfParts = "PRT2";
const char* const WavAudioFormat::riffInfoOrganisation = "TORG";
const char* const WavAudioFormat::riffInfoPart = "PRT1";
const char* const WavAudioFormat::riffInfoProducedBy = "IPRO";
const char* const WavAudioFormat::riffInfoProductionDesigner = "IPDS";
const char* const WavAudioFormat::riffInfoProductionStudio = "ISDT";
const char* const WavAudioFormat::riffInfoRate = "RATE";
const char* const WavAudioFormat::riffInfoRated = "AGES";
const char* const WavAudioFormat::riffInfoRating = "IRTD";
const char* const WavAudioFormat::riffInfoRippedBy = "IRIP";
const char* const WavAudioFormat::riffInfoSecondaryGenre = "ISGN";
const char* const WavAudioFormat::riffInfoSecondLanguage = "IAS2";
const char* const WavAudioFormat::riffInfoSeventhLanguage = "IAS7";
const char* const WavAudioFormat::riffInfoSharpness = "ISHP";
const char* const WavAudioFormat::riffInfoSixthLanguage = "IAS6";
const char* const WavAudioFormat::riffInfoSoftware = "ISFT";
const char* const WavAudioFormat::riffInfoSoundSchemeTitle = "DISP";
const char* const WavAudioFormat::riffInfoSource = "ISRC";
const char* const WavAudioFormat::riffInfoSourceFrom = "ISRF";
const char* const WavAudioFormat::riffInfoStarring_ISTR = "ISTR";
const char* const WavAudioFormat::riffInfoStarring_STAR = "STAR";
const char* const WavAudioFormat::riffInfoStartTimecode = "TCOD";
const char* const WavAudioFormat::riffInfoStatistics = "STAT";
const char* const WavAudioFormat::riffInfoSubject = "ISBJ";
const char* const WavAudioFormat::riffInfoTapeName = "TAPE";
const char* const WavAudioFormat::riffInfoTechnician = "ITCH";
const char* const WavAudioFormat::riffInfoThirdLanguage = "IAS3";
const char* const WavAudioFormat::riffInfoTimeCode = "ISMP";
const char* const WavAudioFormat::riffInfoTitle = "INAM";
const char* const WavAudioFormat::riffInfoTrackNumber = "TRCK";
const char* const WavAudioFormat::riffInfoURL = "TURL";
const char* const WavAudioFormat::riffInfoVegasVersionMajor = "VMAJ";
const char* const WavAudioFormat::riffInfoVegasVersionMinor = "VMIN";
const char* const WavAudioFormat::riffInfoVersion = "TVER";
const char* const WavAudioFormat::riffInfoWatermarkURL = "IWMU";
const char* const WavAudioFormat::riffInfoWrittenBy = "IWRI";
const char* const WavAudioFormat::riffInfoYear = "YEAR";
const char* const WavAudioFormat::ISRC = "ISRC";
const char* const WavAudioFormat::tracktionLoopInfo = "tracktion loop info";
//==============================================================================
namespace WavFileHelpers
{
inline int chunkName (const char* const name) noexcept { return (int) ByteOrder::littleEndianInt (name); }
inline size_t roundUpSize (size_t sz) noexcept { return (sz + 3) & ~3u; }
#if _MSVC_VER
#pragma pack (push, 1)
#endif
struct BWAVChunk
{
char description [256];
char originator [32];
char originatorRef [32];
char originationDate [10];
char originationTime [8];
uint32 timeRefLow;
uint32 timeRefHigh;
uint16 version;
uint8 umid[64];
uint8 reserved[190];
char codingHistory[1];
void copyTo (StringPairArray& values, const int totalSize) const
{
values.set (WavAudioFormat::bwavDescription, String::fromUTF8 (description, sizeof (description)));
values.set (WavAudioFormat::bwavOriginator, String::fromUTF8 (originator, sizeof (originator)));
values.set (WavAudioFormat::bwavOriginatorRef, String::fromUTF8 (originatorRef, sizeof (originatorRef)));
values.set (WavAudioFormat::bwavOriginationDate, String::fromUTF8 (originationDate, sizeof (originationDate)));
values.set (WavAudioFormat::bwavOriginationTime, String::fromUTF8 (originationTime, sizeof (originationTime)));
const uint32 timeLow = ByteOrder::swapIfBigEndian (timeRefLow);
const uint32 timeHigh = ByteOrder::swapIfBigEndian (timeRefHigh);
const int64 time = (((int64) timeHigh) << 32) + timeLow;
values.set (WavAudioFormat::bwavTimeReference, String (time));
values.set (WavAudioFormat::bwavCodingHistory,
String::fromUTF8 (codingHistory, totalSize - (int) offsetof (BWAVChunk, codingHistory)));
}
} WATER_PACKED;
//==============================================================================
struct SMPLChunk
{
struct SampleLoop
{
uint32 identifier;
uint32 type; // these are different in AIFF and WAV
uint32 start;
uint32 end;
uint32 fraction;
uint32 playCount;
} WATER_PACKED;
uint32 manufacturer;
uint32 product;
uint32 samplePeriod;
uint32 midiUnityNote;
uint32 midiPitchFraction;
uint32 smpteFormat;
uint32 smpteOffset;
uint32 numSampleLoops;
uint32 samplerData;
SampleLoop loops[1];
template <typename NameType>
static void setValue (StringPairArray& values, NameType name, uint32 val)
{
values.set (name, String (ByteOrder::swapIfBigEndian (val)));
}
static void setValue (StringPairArray& values, int prefix, const char* name, uint32 val)
{
setValue (values, "Loop" + String (prefix) + name, val);
}
void copyTo (StringPairArray& values, const int totalSize) const
{
setValue (values, "Manufacturer", manufacturer);
setValue (values, "Product", product);
setValue (values, "SamplePeriod", samplePeriod);
setValue (values, "MidiUnityNote", midiUnityNote);
setValue (values, "MidiPitchFraction", midiPitchFraction);
setValue (values, "SmpteFormat", smpteFormat);
setValue (values, "SmpteOffset", smpteOffset);
setValue (values, "NumSampleLoops", numSampleLoops);
setValue (values, "SamplerData", samplerData);
for (int i = 0; i < (int) numSampleLoops; ++i)
{
if ((uint8*) (loops + (i + 1)) > ((uint8*) this) + totalSize)
break;
setValue (values, i, "Identifier", loops[i].identifier);
setValue (values, i, "Type", loops[i].type);
setValue (values, i, "Start", loops[i].start);
setValue (values, i, "End", loops[i].end);
setValue (values, i, "Fraction", loops[i].fraction);
setValue (values, i, "PlayCount", loops[i].playCount);
}
}
template <typename NameType>
static uint32 getValue (const StringPairArray& values, NameType name, const char* def)
{
return ByteOrder::swapIfBigEndian ((uint32) values.getValue (name, def).getIntValue());
}
static uint32 getValue (const StringPairArray& values, int prefix, const char* name, const char* def)
{
return getValue (values, "Loop" + String (prefix) + name, def);
}
} WATER_PACKED;
//==============================================================================
struct InstChunk
{
int8 baseNote;
int8 detune;
int8 gain;
int8 lowNote;
int8 highNote;
int8 lowVelocity;
int8 highVelocity;
static void setValue (StringPairArray& values, const char* name, int val)
{
values.set (name, String (val));
}
void copyTo (StringPairArray& values) const
{
setValue (values, "MidiUnityNote", baseNote);
setValue (values, "Detune", detune);
setValue (values, "Gain", gain);
setValue (values, "LowNote", lowNote);
setValue (values, "HighNote", highNote);
setValue (values, "LowVelocity", lowVelocity);
setValue (values, "HighVelocity", highVelocity);
}
static int8 getValue (const StringPairArray& values, const char* name, const char* def)
{
return (int8) values.getValue (name, def).getIntValue();
}
} WATER_PACKED;
//==============================================================================
struct CueChunk
{
struct Cue
{
uint32 identifier;
uint32 order;
uint32 chunkID;
uint32 chunkStart;
uint32 blockStart;
uint32 offset;
} WATER_PACKED;
uint32 numCues;
Cue cues[1];
static void setValue (StringPairArray& values, int prefix, const char* name, uint32 val)
{
values.set ("Cue" + String (prefix) + name, String (ByteOrder::swapIfBigEndian (val)));
}
void copyTo (StringPairArray& values, const int totalSize) const
{
values.set ("NumCuePoints", String (ByteOrder::swapIfBigEndian (numCues)));
for (int i = 0; i < (int) numCues; ++i)
{
if ((uint8*) (cues + (i + 1)) > ((uint8*) this) + totalSize)
break;
setValue (values, i, "Identifier", cues[i].identifier);
setValue (values, i, "Order", cues[i].order);
setValue (values, i, "ChunkID", cues[i].chunkID);
setValue (values, i, "ChunkStart", cues[i].chunkStart);
setValue (values, i, "BlockStart", cues[i].blockStart);
setValue (values, i, "Offset", cues[i].offset);
}
}
} WATER_PACKED;
//==============================================================================
/** Reads a RIFF List Info chunk from a stream positioned just after the size byte. */
namespace ListInfoChunk
{
static const char* const types[] =
{
WavAudioFormat::riffInfoArchivalLocation,
WavAudioFormat::riffInfoArtist,
WavAudioFormat::riffInfoBaseURL,
WavAudioFormat::riffInfoCinematographer,
WavAudioFormat::riffInfoComment,
WavAudioFormat::riffInfoComments,
WavAudioFormat::riffInfoCommissioned,
WavAudioFormat::riffInfoCopyright,
WavAudioFormat::riffInfoCostumeDesigner,
WavAudioFormat::riffInfoCountry,
WavAudioFormat::riffInfoCropped,
WavAudioFormat::riffInfoDateCreated,
WavAudioFormat::riffInfoDateTimeOriginal,
WavAudioFormat::riffInfoDefaultAudioStream,
WavAudioFormat::riffInfoDimension,
WavAudioFormat::riffInfoDirectory,
WavAudioFormat::riffInfoDistributedBy,
WavAudioFormat::riffInfoDotsPerInch,
WavAudioFormat::riffInfoEditedBy,
WavAudioFormat::riffInfoEighthLanguage,
WavAudioFormat::riffInfoEncodedBy,
WavAudioFormat::riffInfoEndTimecode,
WavAudioFormat::riffInfoEngineer,
WavAudioFormat::riffInfoFifthLanguage,
WavAudioFormat::riffInfoFirstLanguage,
WavAudioFormat::riffInfoFourthLanguage,
WavAudioFormat::riffInfoGenre,
WavAudioFormat::riffInfoKeywords,
WavAudioFormat::riffInfoLanguage,
WavAudioFormat::riffInfoLength,
WavAudioFormat::riffInfoLightness,
WavAudioFormat::riffInfoLocation,
WavAudioFormat::riffInfoLogoIconURL,
WavAudioFormat::riffInfoLogoURL,
WavAudioFormat::riffInfoMedium,
WavAudioFormat::riffInfoMoreInfoBannerImage,
WavAudioFormat::riffInfoMoreInfoBannerURL,
WavAudioFormat::riffInfoMoreInfoText,
WavAudioFormat::riffInfoMoreInfoURL,
WavAudioFormat::riffInfoMusicBy,
WavAudioFormat::riffInfoNinthLanguage,
WavAudioFormat::riffInfoNumberOfParts,
WavAudioFormat::riffInfoOrganisation,
WavAudioFormat::riffInfoPart,
WavAudioFormat::riffInfoProducedBy,
WavAudioFormat::riffInfoProductionDesigner,
WavAudioFormat::riffInfoProductionStudio,
WavAudioFormat::riffInfoRate,
WavAudioFormat::riffInfoRated,
WavAudioFormat::riffInfoRating,
WavAudioFormat::riffInfoRippedBy,
WavAudioFormat::riffInfoSecondaryGenre,
WavAudioFormat::riffInfoSecondLanguage,
WavAudioFormat::riffInfoSeventhLanguage,
WavAudioFormat::riffInfoSharpness,
WavAudioFormat::riffInfoSixthLanguage,
WavAudioFormat::riffInfoSoftware,
WavAudioFormat::riffInfoSoundSchemeTitle,
WavAudioFormat::riffInfoSource,
WavAudioFormat::riffInfoSourceFrom,
WavAudioFormat::riffInfoStarring_ISTR,
WavAudioFormat::riffInfoStarring_STAR,
WavAudioFormat::riffInfoStartTimecode,
WavAudioFormat::riffInfoStatistics,
WavAudioFormat::riffInfoSubject,
WavAudioFormat::riffInfoTapeName,
WavAudioFormat::riffInfoTechnician,
WavAudioFormat::riffInfoThirdLanguage,
WavAudioFormat::riffInfoTimeCode,
WavAudioFormat::riffInfoTitle,
WavAudioFormat::riffInfoTrackNumber,
WavAudioFormat::riffInfoURL,
WavAudioFormat::riffInfoVegasVersionMajor,
WavAudioFormat::riffInfoVegasVersionMinor,
WavAudioFormat::riffInfoVersion,
WavAudioFormat::riffInfoWatermarkURL,
WavAudioFormat::riffInfoWrittenBy,
WavAudioFormat::riffInfoYear
};
static bool isMatchingTypeIgnoringCase (const int value, const char* const name) noexcept
{
for (int i = 0; i < 4; ++i)
if ((uint32) name[i] != CharacterFunctions::toUpperCase ((uint32) ((value >> (i * 8)) & 0xff)))
return false;
return true;
}
static void addToMetadata (StringPairArray& values, InputStream& input, int64 chunkEnd)
{
while (input.getPosition() < chunkEnd)
{
const int infoType = input.readInt();
int64 infoLength = chunkEnd - input.getPosition();
if (infoLength > 0)
{
infoLength = jmin (infoLength, (int64) input.readInt());
if (infoLength <= 0)
return;
for (int i = 0; i < numElementsInArray (types); ++i)
{
if (isMatchingTypeIgnoringCase (infoType, types[i]))
{
MemoryBlock mb;
input.readIntoMemoryBlock (mb, (ssize_t) infoLength);
values.set (types[i], String::createStringFromData ((const char*) mb.getData(),
(int) mb.getSize()));
break;
}
}
}
}
}
}
//==============================================================================
struct AcidChunk
{
/** Reads an acid RIFF chunk from a stream positioned just after the size byte. */
AcidChunk (InputStream& input, size_t length)
{
zerostruct (*this);
input.read (this, (int) jmin (sizeof (*this), length));
}
AcidChunk (const StringPairArray& values)
{
zerostruct (*this);
flags = getFlagIfPresent (values, WavAudioFormat::acidOneShot, 0x01)
| getFlagIfPresent (values, WavAudioFormat::acidRootSet, 0x02)
| getFlagIfPresent (values, WavAudioFormat::acidStretch, 0x04)
| getFlagIfPresent (values, WavAudioFormat::acidDiskBased, 0x08)
| getFlagIfPresent (values, WavAudioFormat::acidizerFlag, 0x10);
if (values[WavAudioFormat::acidRootSet].getIntValue() != 0)
rootNote = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidRootNote].getIntValue());
numBeats = ByteOrder::swapIfBigEndian ((uint32) values[WavAudioFormat::acidBeats].getIntValue());
meterDenominator = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidDenominator].getIntValue());
meterNumerator = ByteOrder::swapIfBigEndian ((uint16) values[WavAudioFormat::acidNumerator].getIntValue());
if (values.containsKey (WavAudioFormat::acidTempo))
tempo = swapFloatByteOrder (values[WavAudioFormat::acidTempo].getFloatValue());
}
MemoryBlock toMemoryBlock() const
{
return (flags != 0 || rootNote != 0 || numBeats != 0 || meterDenominator != 0 || meterNumerator != 0)
? MemoryBlock (this, sizeof (*this)) : MemoryBlock();
}
void addToMetadata (StringPairArray& values) const
{
setBoolFlag (values, WavAudioFormat::acidOneShot, 0x01);
setBoolFlag (values, WavAudioFormat::acidRootSet, 0x02);
setBoolFlag (values, WavAudioFormat::acidStretch, 0x04);
setBoolFlag (values, WavAudioFormat::acidDiskBased, 0x08);
setBoolFlag (values, WavAudioFormat::acidizerFlag, 0x10);
if (flags & 0x02) // root note set
values.set (WavAudioFormat::acidRootNote, String (ByteOrder::swapIfBigEndian (rootNote)));
values.set (WavAudioFormat::acidBeats, String (ByteOrder::swapIfBigEndian (numBeats)));
values.set (WavAudioFormat::acidDenominator, String (ByteOrder::swapIfBigEndian (meterDenominator)));
values.set (WavAudioFormat::acidNumerator, String (ByteOrder::swapIfBigEndian (meterNumerator)));
values.set (WavAudioFormat::acidTempo, String (swapFloatByteOrder (tempo)));
}
void setBoolFlag (StringPairArray& values, const char* name, uint32 mask) const
{
values.set (name, (flags & ByteOrder::swapIfBigEndian (mask)) ? "1" : "0");
}
static uint32 getFlagIfPresent (const StringPairArray& values, const char* name, uint32 flag)
{
return values[name].getIntValue() != 0 ? ByteOrder::swapIfBigEndian (flag) : 0;
}
static float swapFloatByteOrder (const float x) noexcept
{
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
union { uint32 asInt; float asFloat; } n;
n.asFloat = x;
n.asInt = ByteOrder::swap (n.asInt);
return n.asFloat;
#else
return x;
#endif
}
uint32 flags;
uint16 rootNote;
uint16 reserved1;
float reserved2;
uint32 numBeats;
uint16 meterDenominator;
uint16 meterNumerator;
float tempo;
} WATER_PACKED;
//==============================================================================
namespace AXMLChunk
{
static void addToMetadata (StringPairArray& destValues, const String& source)
{
ScopedPointer<XmlElement> xml (XmlDocument::parse (source));
if (xml != nullptr && xml->hasTagName ("ebucore:ebuCoreMain"))
{
if (XmlElement* xml2 = xml->getChildByName ("ebucore:coreMetadata"))
{
if (XmlElement* xml3 = xml2->getChildByName ("ebucore:identifier"))
{
if (XmlElement* xml4 = xml3->getChildByName ("dc:identifier"))
{
const String ISRCCode (xml4->getAllSubText().fromFirstOccurrenceOf ("ISRC:", false, true));
if (ISRCCode.isNotEmpty())
destValues.set (WavAudioFormat::ISRC, ISRCCode);
}
}
}
}
}
};
//==============================================================================
struct ExtensibleWavSubFormat
{
uint32 data1;
uint16 data2;
uint16 data3;
uint8 data4[8];
bool operator== (const ExtensibleWavSubFormat& other) const noexcept { return memcmp (this, &other, sizeof (*this)) == 0; }
bool operator!= (const ExtensibleWavSubFormat& other) const noexcept { return ! operator== (other); }
} WATER_PACKED;
static const ExtensibleWavSubFormat pcmFormat = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
static const ExtensibleWavSubFormat IEEEFloatFormat = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
static const ExtensibleWavSubFormat ambisonicFormat = { 0x00000001, 0x0721, 0x11d3, { 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 } };
struct DataSize64Chunk // chunk ID = 'ds64' if data size > 0xffffffff, 'JUNK' otherwise
{
uint32 riffSizeLow; // low 4 byte size of RF64 block
uint32 riffSizeHigh; // high 4 byte size of RF64 block
uint32 dataSizeLow; // low 4 byte size of data chunk
uint32 dataSizeHigh; // high 4 byte size of data chunk
uint32 sampleCountLow; // low 4 byte sample count of fact chunk
uint32 sampleCountHigh; // high 4 byte sample count of fact chunk
uint32 tableLength; // number of valid entries in array 'table'
} WATER_PACKED;
#if _MSVC_VER
#pragma pack (pop)
#endif
}
//==============================================================================
class WavAudioFormatReader : public AudioFormatReader
{
public:
WavAudioFormatReader (InputStream* const in)
: AudioFormatReader (in, wavFormatName),
bwavChunkStart (0),
bwavSize (0),
dataLength (0),
isRF64 (false)
{
using namespace WavFileHelpers;
uint64 len = 0;
uint64 end = 0;
int cueNoteIndex = 0;
int cueLabelIndex = 0;
int cueRegionIndex = 0;
const int firstChunkType = input->readInt();
if (firstChunkType == chunkName ("RF64"))
{
input->skipNextBytes (4); // size is -1 for RF64
isRF64 = true;
}
else if (firstChunkType == chunkName ("RIFF"))
{
len = (uint64) (uint32) input->readInt();
end = len + (uint64) input->getPosition();
}
else
{
return;
}
const int64 startOfRIFFChunk = input->getPosition();
if (input->readInt() == chunkName ("WAVE"))
{
if (isRF64 && input->readInt() == chunkName ("ds64"))
{
const uint32 length = (uint32) input->readInt();
if (length < 28)
return;
const int64 chunkEnd = input->getPosition() + length + (length & 1);
len = (uint64) input->readInt64();
end = len + (uint64) startOfRIFFChunk;
dataLength = input->readInt64();
input->setPosition (chunkEnd);
}
while ((uint64) input->getPosition() < end && ! input->isExhausted())
{
const int chunkType = input->readInt();
uint32 length = (uint32) input->readInt();
const int64 chunkEnd = input->getPosition() + length + (length & 1);
if (chunkType == chunkName ("fmt "))
{
// read the format chunk
const unsigned short format = (unsigned short) input->readShort();
numChannels = (unsigned int) input->readShort();
sampleRate = input->readInt();
const int bytesPerSec = input->readInt();
input->skipNextBytes (2);
bitsPerSample = (unsigned int) (int) input->readShort();
if (bitsPerSample > 64)
{
bytesPerFrame = bytesPerSec / (int) sampleRate;
bitsPerSample = 8 * (unsigned int) bytesPerFrame / numChannels;
}
else
{
bytesPerFrame = numChannels * bitsPerSample / 8;
}
if (format == 3)
{
usesFloatingPointData = true;
}
else if (format == 0xfffe /*WAVE_FORMAT_EXTENSIBLE*/)
{
if (length < 40) // too short
{
bytesPerFrame = 0;
}
else
{
input->skipNextBytes (4); // skip over size and bitsPerSample
metadataValues.set ("ChannelMask", String (input->readInt()));
ExtensibleWavSubFormat subFormat;
subFormat.data1 = (uint32) input->readInt();
subFormat.data2 = (uint16) input->readShort();
subFormat.data3 = (uint16) input->readShort();
input->read (subFormat.data4, sizeof (subFormat.data4));
if (subFormat == IEEEFloatFormat)
usesFloatingPointData = true;
else if (subFormat != pcmFormat && subFormat != ambisonicFormat)
bytesPerFrame = 0;
}
}
else if (format != 1)
{
bytesPerFrame = 0;
}
}
else if (chunkType == chunkName ("data"))
{
if (! isRF64) // data size is expected to be -1, actual data size is in ds64 chunk
dataLength = length;
dataChunkStart = input->getPosition();
lengthInSamples = (bytesPerFrame > 0) ? (dataLength / bytesPerFrame) : 0;
}
else if (chunkType == chunkName ("bext"))
{
bwavChunkStart = input->getPosition();
bwavSize = length;
HeapBlock<BWAVChunk> bwav;
bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1);
input->read (bwav, (int) length);
bwav->copyTo (metadataValues, (int) length);
}
else if (chunkType == chunkName ("smpl"))
{
HeapBlock<SMPLChunk> smpl;
smpl.calloc (jmax ((size_t) length + 1, sizeof (SMPLChunk)), 1);
input->read (smpl, (int) length);
smpl->copyTo (metadataValues, (int) length);
}
else if (chunkType == chunkName ("inst") || chunkType == chunkName ("INST")) // need to check which...
{
HeapBlock<InstChunk> inst;
inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1);
input->read (inst, (int) length);
inst->copyTo (metadataValues);
}
else if (chunkType == chunkName ("cue "))
{
HeapBlock<CueChunk> cue;
cue.calloc (jmax ((size_t) length + 1, sizeof (CueChunk)), 1);
input->read (cue, (int) length);
cue->copyTo (metadataValues, (int) length);
}
else if (chunkType == chunkName ("axml"))
{
MemoryBlock axml;
input->readIntoMemoryBlock (axml, (ssize_t) length);
AXMLChunk::addToMetadata (metadataValues, axml.toString());
}
else if (chunkType == chunkName ("LIST"))
{
const int subChunkType = input->readInt();
if (subChunkType == chunkName ("info") || subChunkType == chunkName ("INFO"))
{
ListInfoChunk::addToMetadata (metadataValues, *input, chunkEnd);
}
else if (subChunkType == chunkName ("adtl"))
{
while (input->getPosition() < chunkEnd)
{
const int adtlChunkType = input->readInt();
const uint32 adtlLength = (uint32) input->readInt();
const int64 adtlChunkEnd = input->getPosition() + (adtlLength + (adtlLength & 1));
if (adtlChunkType == chunkName ("labl") || adtlChunkType == chunkName ("note"))
{
String prefix;
if (adtlChunkType == chunkName ("labl"))
prefix << "CueLabel" << cueLabelIndex++;
else if (adtlChunkType == chunkName ("note"))
prefix << "CueNote" << cueNoteIndex++;
const uint32 identifier = (uint32) input->readInt();
const int stringLength = (int) adtlLength - 4;
MemoryBlock textBlock;
input->readIntoMemoryBlock (textBlock, stringLength);
metadataValues.set (prefix + "Identifier", String (identifier));
metadataValues.set (prefix + "Text", textBlock.toString());
}
else if (adtlChunkType == chunkName ("ltxt"))
{
const String prefix ("CueRegion" + String (cueRegionIndex++));
const uint32 identifier = (uint32) input->readInt();
const uint32 sampleLength = (uint32) input->readInt();
const uint32 purpose = (uint32) input->readInt();
const uint16 country = (uint16) input->readInt();
const uint16 language = (uint16) input->readInt();
const uint16 dialect = (uint16) input->readInt();
const uint16 codePage = (uint16) input->readInt();
const uint32 stringLength = adtlLength - 20;
MemoryBlock textBlock;
input->readIntoMemoryBlock (textBlock, (int) stringLength);
metadataValues.set (prefix + "Identifier", String (identifier));
metadataValues.set (prefix + "SampleLength", String (sampleLength));
metadataValues.set (prefix + "Purpose", String (purpose));
metadataValues.set (prefix + "Country", String (country));
metadataValues.set (prefix + "Language", String (language));
metadataValues.set (prefix + "Dialect", String (dialect));
metadataValues.set (prefix + "CodePage", String (codePage));
metadataValues.set (prefix + "Text", textBlock.toString());
}
input->setPosition (adtlChunkEnd);
}
}
}
else if (chunkType == chunkName ("acid"))
{
AcidChunk (*input, length).addToMetadata (metadataValues);
}
else if (chunkType == chunkName ("Trkn"))
{
MemoryBlock tracktion;
input->readIntoMemoryBlock (tracktion, (ssize_t) length);
metadataValues.set (WavAudioFormat::tracktionLoopInfo, tracktion.toString());
}
else if (chunkEnd <= input->getPosition())
{
break;
}
input->setPosition (chunkEnd);
}
}
if (cueLabelIndex > 0) metadataValues.set ("NumCueLabels", String (cueLabelIndex));
if (cueNoteIndex > 0) metadataValues.set ("NumCueNotes", String (cueNoteIndex));
if (cueRegionIndex > 0) metadataValues.set ("NumCueRegions", String (cueRegionIndex));
if (metadataValues.size() > 0) metadataValues.set ("MetaDataSource", "WAV");
}
//==============================================================================
bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
int64 startSampleInFile, int numSamples) override
{
clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
startSampleInFile, numSamples, lengthInSamples);
if (numSamples <= 0)
return true;
input->setPosition (dataChunkStart + startSampleInFile * bytesPerFrame);
while (numSamples > 0)
{
const int tempBufSize = 480 * 3 * 4; // (keep this a multiple of 3)
char tempBuffer [tempBufSize];
const int numThisTime = jmin (tempBufSize / bytesPerFrame, numSamples);
const int bytesRead = input->read (tempBuffer, numThisTime * bytesPerFrame);
if (bytesRead < numThisTime * bytesPerFrame)
{
jassert (bytesRead >= 0);
zeromem (tempBuffer + bytesRead, (size_t) (numThisTime * bytesPerFrame - bytesRead));
}
copySampleData (bitsPerSample, usesFloatingPointData,
destSamples, startOffsetInDestBuffer, numDestChannels,
tempBuffer, (int) numChannels, numThisTime);
startOffsetInDestBuffer += numThisTime;
numSamples -= numThisTime;
}
return true;
}
static void copySampleData (unsigned int bitsPerSample, const bool usesFloatingPointData,
int* const* destSamples, int startOffsetInDestBuffer, int numDestChannels,
const void* sourceData, int numChannels, int numSamples) noexcept
{
switch (bitsPerSample)
{
case 8:
ReadHelper<AudioData::Int32, AudioData::UInt8, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples);
break;
case 16:
ReadHelper<AudioData::Int32, AudioData::Int16, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples);
break;
case 24:
ReadHelper<AudioData::Int32, AudioData::Int24, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples);
break;
case 32:
if (usesFloatingPointData)
ReadHelper<AudioData::Float32, AudioData::Float32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples);
else
ReadHelper<AudioData::Int32, AudioData::Int32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples);
break;
default:
jassertfalse;
break;
}
}
int64 bwavChunkStart, bwavSize;
int64 dataChunkStart, dataLength;
int bytesPerFrame;
bool isRF64;
private:
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatReader)
};
//==============================================================================
WavAudioFormat::WavAudioFormat() : AudioFormat (wavFormatName, ".wav .bwf") {}
WavAudioFormat::~WavAudioFormat() {}
Array<int> WavAudioFormat::getPossibleSampleRates()
{
const int rates[] = { 8000, 11025, 12000, 16000, 22050, 32000, 44100,
48000, 88200, 96000, 176400, 192000, 352800, 384000 };
return Array<int> (rates, numElementsInArray (rates));
}
Array<int> WavAudioFormat::getPossibleBitDepths()
{
const int depths[] = { 8, 16, 24, 32 };
return Array<int> (depths, numElementsInArray (depths));
}
bool WavAudioFormat::canDoStereo() { return true; }
bool WavAudioFormat::canDoMono() { return true; }
AudioFormatReader* WavAudioFormat::createReaderFor (InputStream* sourceStream,
const bool deleteStreamIfOpeningFails)
{
ScopedPointer<WavAudioFormatReader> r (new WavAudioFormatReader (sourceStream));
if (r->sampleRate > 0 && r->numChannels > 0 && r->bytesPerFrame > 0)
return r.release();
if (! deleteStreamIfOpeningFails)
r->input = nullptr;
return nullptr;
}
}

+ 200
- 0
source/modules/water/audiocodecs/WavAudioFormat.h View File

@@ -0,0 +1,200 @@
/*
==============================================================================
This file is part of the Water library.
Copyright (c) 2015 ROLI Ltd.
Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
Water 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 General Public License for more details.
==============================================================================
*/
#ifndef WATER_WAV_AUDIOFORMAT_H_INCLUDED
#define WATER_WAV_AUDIOFORMAT_H_INCLUDED
#include "../audioformat/AudioFormat.h"
#include "../containers/Array.h"
#include "CarlaJuceUtils.hpp"
namespace water {
//==============================================================================
/**
Reads and Writes WAV format audio files.
@see AudioFormat
*/
class WavAudioFormat : public AudioFormat
{
public:
//==============================================================================
/** Creates a format object. */
WavAudioFormat();
/** Destructor. */
~WavAudioFormat();
//==============================================================================
// BWAV chunk properties:
static const char* const bwavDescription; /**< Metadata property name used in BWAV chunks. */
static const char* const bwavOriginator; /**< Metadata property name used in BWAV chunks. */
static const char* const bwavOriginatorRef; /**< Metadata property name used in BWAV chunks. */
static const char* const bwavOriginationDate; /**< Metadata property name used in BWAV chunks. The format should be: yyyy-mm-dd */
static const char* const bwavOriginationTime; /**< Metadata property name used in BWAV chunks. The format should be: format is: hh-mm-ss */
static const char* const bwavCodingHistory; /**< Metadata property name used in BWAV chunks. */
/** Metadata property name used in BWAV chunks.
This is the number of samples from the start of an edit that the
file is supposed to begin at. Seems like an obvious mistake to
only allow a file to occur in an edit once, but that's the way
it is..
@see AudioFormatReader::metadataValues, createWriterFor
*/
static const char* const bwavTimeReference;
#if 0
/** Utility function to fill out the appropriate metadata for a BWAV file.
This just makes it easier than using the property names directly, and it
fills out the time and date in the right format.
*/
static StringPairArray createBWAVMetadata (const String& description,
const String& originator,
const String& originatorRef,
Time dateAndTime,
int64 timeReferenceSamples,
const String& codingHistory);
#endif
//==============================================================================
// 'acid' chunk properties:
static const char* const acidOneShot; /**< Metadata property name used in acid chunks. */
static const char* const acidRootSet; /**< Metadata property name used in acid chunks. */
static const char* const acidStretch; /**< Metadata property name used in acid chunks. */
static const char* const acidDiskBased; /**< Metadata property name used in acid chunks. */
static const char* const acidizerFlag; /**< Metadata property name used in acid chunks. */
static const char* const acidRootNote; /**< Metadata property name used in acid chunks. */
static const char* const acidBeats; /**< Metadata property name used in acid chunks. */
static const char* const acidDenominator; /**< Metadata property name used in acid chunks. */
static const char* const acidNumerator; /**< Metadata property name used in acid chunks. */
static const char* const acidTempo; /**< Metadata property name used in acid chunks. */
//==============================================================================
// INFO chunk properties:
static const char* const riffInfoArchivalLocation; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoArtist; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoBaseURL; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoCinematographer; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoComment; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoComments; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoCommissioned; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoCopyright; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoCostumeDesigner; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoCountry; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoCropped; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoDateCreated; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoDateTimeOriginal; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoDefaultAudioStream; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoDimension; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoDirectory; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoDistributedBy; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoDotsPerInch; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoEditedBy; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoEighthLanguage; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoEncodedBy; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoEndTimecode; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoEngineer; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoFifthLanguage; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoFirstLanguage; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoFourthLanguage; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoGenre; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoKeywords; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoLanguage; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoLength; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoLightness; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoLocation; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoLogoIconURL; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoLogoURL; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoMedium; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoMoreInfoBannerImage; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoMoreInfoBannerURL; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoMoreInfoText; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoMoreInfoURL; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoMusicBy; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoNinthLanguage; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoNumberOfParts; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoOrganisation; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoPart; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoProducedBy; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoProductionDesigner; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoProductionStudio; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoRate; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoRated; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoRating; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoRippedBy; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoSecondaryGenre; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoSecondLanguage; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoSeventhLanguage; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoSharpness; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoSixthLanguage; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoSoftware; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoSoundSchemeTitle; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoSource; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoSourceFrom; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoStarring_ISTR; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoStarring_STAR; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoStartTimecode; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoStatistics; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoSubject; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoTapeName; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoTechnician; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoThirdLanguage; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoTimeCode; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoTitle; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoTrackNumber; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoURL; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoVegasVersionMajor; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoVegasVersionMinor; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoVersion; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoWatermarkURL; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoWrittenBy; /**< Metadata property name used in INFO chunks. */
static const char* const riffInfoYear; /**< Metadata property name used in INFO chunks. */
//==============================================================================
/** Metadata property name used when reading an ISRC code from an AXML chunk. */
static const char* const ISRC;
/** Metadata property name used when reading a WAV file with a Tracktion chunk. */
static const char* const tracktionLoopInfo;
//==============================================================================
Array<int> getPossibleSampleRates() override;
Array<int> getPossibleBitDepths() override;
bool canDoStereo() override;
bool canDoMono() override;
//==============================================================================
AudioFormatReader* createReaderFor (InputStream* sourceStream,
bool deleteStreamIfOpeningFails) override;
private:
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormat)
};
}
#endif // WATER_WAV_AUDIOFORMAT_H_INCLUDED

+ 67
- 0
source/modules/water/audioformat/AudioFormat.cpp View File

@@ -0,0 +1,67 @@
/*
==============================================================================
This file is part of the Water library.
Copyright (c) 2015 ROLI Ltd.
Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
Water 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 General Public License for more details.
==============================================================================
*/
#include "AudioFormat.h"
#include "../files/File.h"
namespace water {
AudioFormat::AudioFormat (String name, StringArray extensions)
: formatName (name), fileExtensions (extensions)
{
}
AudioFormat::AudioFormat (StringRef name, StringRef extensions)
: formatName (name.text), fileExtensions (StringArray::fromTokens (extensions, false))
{
}
AudioFormat::~AudioFormat()
{
}
bool AudioFormat::canHandleFile (const File& f)
{
for (int i = 0; i < fileExtensions.size(); ++i)
if (f.hasFileExtension (fileExtensions[i]))
return true;
return false;
}
const String& AudioFormat::getFormatName() const { return formatName; }
const StringArray& AudioFormat::getFileExtensions() const { return fileExtensions; }
bool AudioFormat::isCompressed() { return false; }
StringArray AudioFormat::getQualityOptions() { return StringArray(); }
#if 0
MemoryMappedAudioFormatReader* AudioFormat::createMemoryMappedReader (const File&)
{
return nullptr;
}
MemoryMappedAudioFormatReader* AudioFormat::createMemoryMappedReader (FileInputStream* fin)
{
delete fin;
return nullptr;
}
#endif
}

+ 134
- 0
source/modules/water/audioformat/AudioFormat.h View File

@@ -0,0 +1,134 @@
/*
==============================================================================
This file is part of the Water library.
Copyright (c) 2015 ROLI Ltd.
Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
Water 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 General Public License for more details.
==============================================================================
*/
#ifndef WATER_AUDIOFORMAT_H_INCLUDED
#define WATER_AUDIOFORMAT_H_INCLUDED
#include "../text/StringArray.h"
namespace water {
//==============================================================================
/**
Subclasses of AudioFormat are used to read and write different audio
file formats.
@see AudioFormatReader, AudioFormatWriter, WavAudioFormat, AiffAudioFormat
*/
class AudioFormat
{
public:
//==============================================================================
/** Destructor. */
virtual ~AudioFormat();
//==============================================================================
/** Returns the name of this format.
e.g. "WAV file" or "AIFF file"
*/
const String& getFormatName() const;
/** Returns all the file extensions that might apply to a file of this format.
The first item will be the one that's preferred when creating a new file.
So for a wav file this might just return ".wav"; for an AIFF file it might
return two items, ".aif" and ".aiff"
*/
const StringArray& getFileExtensions() const;
//==============================================================================
/** Returns true if this the given file can be read by this format.
Subclasses shouldn't do too much work here, just check the extension or
file type. The base class implementation just checks the file's extension
against one of the ones that was registered in the constructor.
*/
virtual bool canHandleFile (const File& fileToTest);
/** Returns a set of sample rates that the format can read and write. */
virtual Array<int> getPossibleSampleRates() = 0;
/** Returns a set of bit depths that the format can read and write. */
virtual Array<int> getPossibleBitDepths() = 0;
/** Returns true if the format can do 2-channel audio. */
virtual bool canDoStereo() = 0;
/** Returns true if the format can do 1-channel audio. */
virtual bool canDoMono() = 0;
/** Returns true if the format uses compressed data. */
virtual bool isCompressed();
/** Returns a list of different qualities that can be used when writing.
Non-compressed formats will just return an empty array, but for something
like Ogg-Vorbis or MP3, it might return a list of bit-rates, etc.
When calling createWriterFor(), an index from this array is passed in to
tell the format which option is required.
*/
virtual StringArray getQualityOptions();
//==============================================================================
/** Tries to create an object that can read from a stream containing audio
data in this format.
The reader object that is returned can be used to read from the stream, and
should then be deleted by the caller.
@param sourceStream the stream to read from - the AudioFormatReader object
that is returned will delete this stream when it no longer
needs it.
@param deleteStreamIfOpeningFails if no reader can be created, this determines whether this method
should delete the stream object that was passed-in. (If a valid
reader is returned, it will always be in charge of deleting the
stream, so this parameter is ignored)
@see AudioFormatReader
*/
virtual AudioFormatReader* createReaderFor (InputStream* sourceStream,
bool deleteStreamIfOpeningFails) = 0;
protected:
/** Creates an AudioFormat object.
@param formatName this sets the value that will be returned by getFormatName()
@param fileExtensions an array of file extensions - these will be returned by getFileExtensions()
*/
AudioFormat (String formatName, StringArray fileExtensions);
/** Creates an AudioFormat object.
@param formatName this sets the value that will be returned by getFormatName()
@param fileExtensions a whitespace-separated list of file extensions - these will
be returned by getFileExtensions()
*/
AudioFormat (StringRef formatName, StringRef fileExtensions);
private:
//==============================================================================
String formatName;
StringArray fileExtensions;
};
}
#endif // WATER_AUDIOFORMAT_H_INCLUDED

+ 184
- 0
source/modules/water/audioformat/AudioFormatManager.cpp View File

@@ -0,0 +1,184 @@
/*
==============================================================================
This file is part of the Water library.
Copyright (c) 2015 ROLI Ltd.
Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
Water 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 General Public License for more details.
==============================================================================
*/
#include "AudioFormatManager.h"
#include "../audiocodecs/WavAudioFormat.h"
#include "../files/FileInputStream.h"
namespace water {
AudioFormatManager::AudioFormatManager() : defaultFormatIndex (0) {}
AudioFormatManager::~AudioFormatManager() {}
//==============================================================================
void AudioFormatManager::registerFormat (AudioFormat* newFormat, const bool makeThisTheDefaultFormat)
{
jassert (newFormat != nullptr);
if (newFormat != nullptr)
{
#if DEBUG
for (int i = getNumKnownFormats(); --i >= 0;)
{
if (getKnownFormat (i)->getFormatName() == newFormat->getFormatName())
{
jassertfalse; // trying to add the same format twice!
}
}
#endif
if (makeThisTheDefaultFormat)
defaultFormatIndex = getNumKnownFormats();
knownFormats.add (newFormat);
}
}
void AudioFormatManager::registerBasicFormats()
{
registerFormat (new WavAudioFormat(), true);
#if 0
registerFormat (new AiffAudioFormat(), false);
#endif
#if WATER_USE_FLAC
registerFormat (new FlacAudioFormat(), false);
#endif
#if WATER_USE_OGGVORBIS
registerFormat (new OggVorbisAudioFormat(), false);
#endif
#if WATER_MAC || WATER_IOS
registerFormat (new CoreAudioFormat(), false);
#endif
#if WATER_USE_MP3AUDIOFORMAT
registerFormat (new MP3AudioFormat(), false);
#endif
#if WATER_USE_WINDOWS_MEDIA_FORMAT
registerFormat (new WindowsMediaAudioFormat(), false);
#endif
}
void AudioFormatManager::clearFormats()
{
knownFormats.clear();
defaultFormatIndex = 0;
}
int AudioFormatManager::getNumKnownFormats() const
{
return knownFormats.size();
}
AudioFormat* AudioFormatManager::getKnownFormat (const int index) const
{
return knownFormats [index];
}
AudioFormat* AudioFormatManager::getDefaultFormat() const
{
return getKnownFormat (defaultFormatIndex);
}
AudioFormat* AudioFormatManager::findFormatForFileExtension (const String& fileExtension) const
{
if (! fileExtension.startsWithChar ('.'))
return findFormatForFileExtension ("." + fileExtension);
for (int i = 0; i < getNumKnownFormats(); ++i)
if (getKnownFormat(i)->getFileExtensions().contains (fileExtension, true))
return getKnownFormat(i);
return nullptr;
}
String AudioFormatManager::getWildcardForAllFormats() const
{
StringArray extensions;
for (int i = 0; i < getNumKnownFormats(); ++i)
extensions.addArray (getKnownFormat(i)->getFileExtensions());
extensions.trim();
extensions.removeEmptyStrings();
for (int i = 0; i < extensions.size(); ++i)
extensions.set (i, (extensions[i].startsWithChar ('.') ? "*" : "*.") + extensions[i]);
extensions.removeDuplicates (true);
return extensions.joinIntoString (";");
}
//==============================================================================
AudioFormatReader* AudioFormatManager::createReaderFor (const File& file)
{
// you need to actually register some formats before the manager can
// use them to open a file!
jassert (getNumKnownFormats() > 0);
for (int i = 0; i < getNumKnownFormats(); ++i)
{
AudioFormat* const af = getKnownFormat(i);
if (af->canHandleFile (file))
if (InputStream* const in = file.createInputStream())
if (AudioFormatReader* const r = af->createReaderFor (in, true))
return r;
}
return nullptr;
}
AudioFormatReader* AudioFormatManager::createReaderFor (InputStream* audioFileStream)
{
// you need to actually register some formats before the manager can
// use them to open a file!
jassert (getNumKnownFormats() > 0);
ScopedPointer<InputStream> in (audioFileStream);
if (in != nullptr)
{
const int64 originalStreamPos = in->getPosition();
for (int i = 0; i < getNumKnownFormats(); ++i)
{
if (AudioFormatReader* const r = getKnownFormat(i)->createReaderFor (in, false))
{
in.release();
return r;
}
in->setPosition (originalStreamPos);
// the stream that is passed-in must be capable of being repositioned so
// that all the formats can have a go at opening it.
jassert (in->getPosition() == originalStreamPos);
}
}
return nullptr;
}
}

+ 147
- 0
source/modules/water/audioformat/AudioFormatManager.h View File

@@ -0,0 +1,147 @@
/*
==============================================================================
This file is part of the Water library.
Copyright (c) 2015 ROLI Ltd.
Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
Water 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 General Public License for more details.
==============================================================================
*/
#ifndef WATER_AUDIOFORMATMANAGER_H_INCLUDED
#define WATER_AUDIOFORMATMANAGER_H_INCLUDED
#include "AudioFormat.h"
#include "../containers/OwnedArray.h"
#include "../text/String.h"
#include "CarlaJuceUtils.hpp"
namespace water {
//==============================================================================
/**
A class for keeping a list of available audio formats, and for deciding which
one to use to open a given file.
After creating an AudioFormatManager object, you should call registerFormat()
or registerBasicFormats() to give it a list of format types that it can use.
@see AudioFormat
*/
class AudioFormatManager
{
public:
//==============================================================================
/** Creates an empty format manager.
Before it'll be any use, you'll need to call registerFormat() with all the
formats you want it to be able to recognise.
*/
AudioFormatManager();
/** Destructor. */
~AudioFormatManager();
//==============================================================================
/** Adds a format to the manager's list of available file types.
The object passed-in will be deleted by this object, so don't keep a pointer
to it!
If makeThisTheDefaultFormat is true, then the getDefaultFormat() method will
return this one when called.
*/
void registerFormat (AudioFormat* newFormat,
bool makeThisTheDefaultFormat);
/** Handy method to make it easy to register the formats that come with Water.
Currently, this will add WAV and AIFF to the list.
*/
void registerBasicFormats();
/** Clears the list of known formats. */
void clearFormats();
/** Returns the number of currently registered file formats. */
int getNumKnownFormats() const;
/** Returns one of the registered file formats. */
AudioFormat* getKnownFormat (int index) const;
/** Iterator access to the list of known formats. */
AudioFormat** begin() const noexcept { return knownFormats.begin(); }
/** Iterator access to the list of known formats. */
AudioFormat** end() const noexcept { return knownFormats.end(); }
/** Looks for which of the known formats is listed as being for a given file
extension.
The extension may have a dot before it, so e.g. ".wav" or "wav" are both ok.
*/
AudioFormat* findFormatForFileExtension (const String& fileExtension) const;
/** Returns the format which has been set as the default one.
You can set a format as being the default when it is registered. It's useful
when you want to write to a file, because the best format may change between
platforms, e.g. AIFF is preferred on the Mac, WAV on Windows.
If none has been set as the default, this method will just return the first
one in the list.
*/
AudioFormat* getDefaultFormat() const;
/** Returns a set of wildcards for file-matching that contains the extensions for
all known formats.
E.g. if might return "*.wav;*.aiff" if it just knows about wavs and aiffs.
*/
String getWildcardForAllFormats() const;
//==============================================================================
/** Searches through the known formats to try to create a suitable reader for
this file.
If none of the registered formats can open the file, it'll return 0. If it
returns a reader, it's the caller's responsibility to delete the reader.
*/
AudioFormatReader* createReaderFor (const File& audioFile);
/** Searches through the known formats to try to create a suitable reader for
this stream.
The stream object that is passed-in will be deleted by this method or by the
reader that is returned, so the caller should not keep any references to it.
The stream that is passed-in must be capable of being repositioned so
that all the formats can have a go at opening it.
If none of the registered formats can open the stream, it'll return nullptr.
If it returns a reader, it's the caller's responsibility to delete the reader.
*/
AudioFormatReader* createReaderFor (InputStream* audioFileStream);
private:
//==============================================================================
OwnedArray<AudioFormat> knownFormats;
int defaultFormatIndex;
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatManager)
};
}
#endif // WATER_AUDIOFORMATMANAGER_H_INCLUDED

+ 191
- 0
source/modules/water/audioformat/AudioFormatReader.cpp View File

@@ -0,0 +1,191 @@
/*
==============================================================================
This file is part of the Water library.
Copyright (c) 2015 ROLI Ltd.
Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
Water 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 General Public License for more details.
==============================================================================
*/
#include "AudioFormatReader.h"
#include "../buffers/AudioSampleBuffer.h"
#include "../streams/InputStream.h"
namespace water {
AudioFormatReader::AudioFormatReader (InputStream* const in, const String& name)
: sampleRate (0),
bitsPerSample (0),
lengthInSamples (0),
numChannels (0),
usesFloatingPointData (false),
input (in),
formatName (name)
{
}
AudioFormatReader::~AudioFormatReader()
{
delete input;
}
bool AudioFormatReader::read (int* const* destSamples,
int numDestChannels,
int64 startSampleInSource,
int numSamplesToRead,
const bool fillLeftoverChannelsWithCopies)
{
jassert (numDestChannels > 0); // you have to actually give this some channels to work with!
const size_t originalNumSamplesToRead = (size_t) numSamplesToRead;
int startOffsetInDestBuffer = 0;
if (startSampleInSource < 0)
{
const int silence = (int) jmin (-startSampleInSource, (int64) numSamplesToRead);
for (int i = numDestChannels; --i >= 0;)
if (destSamples[i] != nullptr)
zeromem (destSamples[i], sizeof (int) * (size_t) silence);
startOffsetInDestBuffer += silence;
numSamplesToRead -= silence;
startSampleInSource = 0;
}
if (numSamplesToRead <= 0)
return true;
if (! readSamples (const_cast<int**> (destSamples),
jmin ((int) numChannels, numDestChannels), startOffsetInDestBuffer,
startSampleInSource, numSamplesToRead))
return false;
if (numDestChannels > (int) numChannels)
{
if (fillLeftoverChannelsWithCopies)
{
int* lastFullChannel = destSamples[0];
for (int i = (int) numChannels; --i > 0;)
{
if (destSamples[i] != nullptr)
{
lastFullChannel = destSamples[i];
break;
}
}
if (lastFullChannel != nullptr)
for (int i = (int) numChannels; i < numDestChannels; ++i)
if (destSamples[i] != nullptr)
memcpy (destSamples[i], lastFullChannel, sizeof (int) * originalNumSamplesToRead);
}
else
{
for (int i = (int) numChannels; i < numDestChannels; ++i)
if (destSamples[i] != nullptr)
zeromem (destSamples[i], sizeof (int) * originalNumSamplesToRead);
}
}
return true;
}
static void readChannels (AudioFormatReader& reader,
int** const chans, AudioSampleBuffer* const buffer,
const int startSample, const int numSamples,
const int64 readerStartSample, const int numTargetChannels)
{
for (int j = 0; j < numTargetChannels; ++j)
chans[j] = reinterpret_cast<int*> (buffer->getWritePointer (j, startSample));
chans[numTargetChannels] = nullptr;
reader.read (chans, numTargetChannels, readerStartSample, numSamples, true);
}
#define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \
for (int i = 0; i < num; ++i) normalOp;
static void convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept
{
for (int i = 0; i < num; ++i)
dest[i] = src[i] * multiplier;
}
void AudioFormatReader::read (AudioSampleBuffer* buffer,
int startSample,
int numSamples,
int64 readerStartSample,
bool useReaderLeftChan,
bool useReaderRightChan)
{
jassert (buffer != nullptr);
jassert (startSample >= 0 && startSample + numSamples <= buffer->getNumSamples());
if (numSamples > 0)
{
const int numTargetChannels = buffer->getNumChannels();
if (numTargetChannels <= 2)
{
int* const dest0 = reinterpret_cast<int*> (buffer->getWritePointer (0, startSample));
int* const dest1 = reinterpret_cast<int*> (numTargetChannels > 1 ? buffer->getWritePointer (1, startSample) : nullptr);
int* chans[3];
if (useReaderLeftChan == useReaderRightChan)
{
chans[0] = dest0;
chans[1] = numChannels > 1 ? dest1 : nullptr;
}
else if (useReaderLeftChan || (numChannels == 1))
{
chans[0] = dest0;
chans[1] = nullptr;
}
else if (useReaderRightChan)
{
chans[0] = nullptr;
chans[1] = dest0;
}
chans[2] = nullptr;
read (chans, 2, readerStartSample, numSamples, true);
// if the target's stereo and the source is mono, dupe the first channel..
if (numTargetChannels > 1 && (chans[0] == nullptr || chans[1] == nullptr))
memcpy (dest1, dest0, sizeof (float) * (size_t) numSamples);
}
else if (numTargetChannels <= 64)
{
int* chans[65];
readChannels (*this, chans, buffer, startSample, numSamples, readerStartSample, numTargetChannels);
}
else
{
HeapBlock<int*> chans;
chans.malloc ((size_t) numTargetChannels + 1);
// FIXME, check malloc return
readChannels (*this, chans, buffer, startSample, numSamples, readerStartSample, numTargetChannels);
}
if (! usesFloatingPointData)
for (int j = 0; j < numTargetChannels; ++j)
if (float* const d = buffer->getWritePointer (j, startSample))
convertFixedToFloat (d, reinterpret_cast<const int*> (d), 1.0f / 0x7fffffff, numSamples);
}
}
}

+ 234
- 0
source/modules/water/audioformat/AudioFormatReader.h View File

@@ -0,0 +1,234 @@
/*
==============================================================================
This file is part of the Water library.
Copyright (c) 2015 ROLI Ltd.
Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
Water 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 General Public License for more details.
==============================================================================
*/
#ifndef WATER_AUDIOFORMATREADER_H_INCLUDED
#define WATER_AUDIOFORMATREADER_H_INCLUDED
#include "../buffers/AudioDataConverters.h"
#include "../text/StringPairArray.h"
#include "CarlaJuceUtils.hpp"
namespace water {
//==============================================================================
/**
Reads samples from an audio file stream.
A subclass that reads a specific type of audio format will be created by
an AudioFormat object.
@see AudioFormat, AudioFormatWriter
*/
class AudioFormatReader
{
protected:
//==============================================================================
/** Creates an AudioFormatReader object.
@param sourceStream the stream to read from - this will be deleted
by this object when it is no longer needed. (Some
specialised readers might not use this parameter and
can leave it as nullptr).
@param formatName the description that will be returned by the getFormatName()
method
*/
AudioFormatReader (InputStream* sourceStream,
const String& formatName);
public:
/** Destructor. */
virtual ~AudioFormatReader();
//==============================================================================
/** Returns a description of what type of format this is.
E.g. "AIFF"
*/
const String& getFormatName() const noexcept { return formatName; }
//==============================================================================
/** Reads samples from the stream.
@param destSamples an array of buffers into which the sample data for each
channel will be written.
If the format is fixed-point, each channel will be written
as an array of 32-bit signed integers using the full
range -0x80000000 to 0x7fffffff, regardless of the source's
bit-depth. If it is a floating-point format, you should cast
the resulting array to a (float**) to get the values (in the
range -1.0 to 1.0 or beyond)
If the format is stereo, then destSamples[0] is the left channel
data, and destSamples[1] is the right channel.
The numDestChannels parameter indicates how many pointers this array
contains, but some of these pointers can be null if you don't want to
read data for some of the channels
@param numDestChannels the number of array elements in the destChannels array
@param startSampleInSource the position in the audio file or stream at which the samples
should be read, as a number of samples from the start of the
stream. It's ok for this to be beyond the start or end of the
available data - any samples that are out-of-range will be returned
as zeros.
@param numSamplesToRead the number of samples to read. If this is greater than the number
of samples that the file or stream contains. the result will be padded
with zeros
@param fillLeftoverChannelsWithCopies if true, this indicates that if there's no source data available
for some of the channels that you pass in, then they should be filled with
copies of valid source channels.
E.g. if you're reading a mono file and you pass 2 channels to this method, then
if fillLeftoverChannelsWithCopies is true, both destination channels will be filled
with the same data from the file's single channel. If fillLeftoverChannelsWithCopies
was false, then only the first channel would be filled with the file's contents, and
the second would be cleared. If there are many channels, e.g. you try to read 4 channels
from a stereo file, then the last 3 would all end up with copies of the same data.
@returns true if the operation succeeded, false if there was an error. Note
that reading sections of data beyond the extent of the stream isn't an
error - the reader should just return zeros for these regions
@see readMaxLevels
*/
bool read (int* const* destSamples,
int numDestChannels,
int64 startSampleInSource,
int numSamplesToRead,
bool fillLeftoverChannelsWithCopies);
/** Fills a section of an AudioSampleBuffer from this reader.
This will convert the reader's fixed- or floating-point data to
the buffer's floating-point format, and will try to intelligently
cope with mismatches between the number of channels in the reader
and the buffer.
*/
void read (AudioSampleBuffer* buffer,
int startSampleInDestBuffer,
int numSamples,
int64 readerStartSample,
bool useReaderLeftChan,
bool useReaderRightChan);
//==============================================================================
/** The sample-rate of the stream. */
double sampleRate;
/** The number of bits per sample, e.g. 16, 24, 32. */
unsigned int bitsPerSample;
/** The total number of samples in the audio stream. */
int64 lengthInSamples;
/** The total number of channels in the audio stream. */
unsigned int numChannels;
/** Indicates whether the data is floating-point or fixed. */
bool usesFloatingPointData;
/** A set of metadata values that the reader has pulled out of the stream.
Exactly what these values are depends on the format, so you can
check out the format implementation code to see what kind of stuff
they understand.
*/
StringPairArray metadataValues;
/** The input stream, for use by subclasses. */
InputStream* input;
//==============================================================================
/** Subclasses must implement this method to perform the low-level read operation.
Callers should use read() instead of calling this directly.
@param destSamples the array of destination buffers to fill. Some of these
pointers may be null
@param numDestChannels the number of items in the destSamples array. This
value is guaranteed not to be greater than the number of
channels that this reader object contains
@param startOffsetInDestBuffer the number of samples from the start of the
dest data at which to begin writing
@param startSampleInFile the number of samples into the source data at which
to begin reading. This value is guaranteed to be >= 0.
@param numSamples the number of samples to read
*/
virtual bool readSamples (int** destSamples,
int numDestChannels,
int startOffsetInDestBuffer,
int64 startSampleInFile,
int numSamples) = 0;
protected:
//==============================================================================
/** Used by AudioFormatReader subclasses to copy data to different formats. */
template <class DestSampleType, class SourceSampleType, class SourceEndianness>
struct ReadHelper
{
typedef AudioData::Pointer<DestSampleType, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> DestType;
typedef AudioData::Pointer<SourceSampleType, SourceEndianness, AudioData::Interleaved, AudioData::Const> SourceType;
template <typename TargetType>
static void read (TargetType* const* destData, int destOffset, int numDestChannels,
const void* sourceData, int numSourceChannels, int numSamples) noexcept
{
for (int i = 0; i < numDestChannels; ++i)
{
if (void* targetChan = destData[i])
{
DestType dest (targetChan);
dest += destOffset;
if (i < numSourceChannels)
dest.convertSamples (SourceType (addBytesToPointer (sourceData, i * SourceType::getBytesPerSample()), numSourceChannels), numSamples);
else
dest.clearSamples (numSamples);
}
}
}
};
/** Used by AudioFormatReader subclasses to clear any parts of the data blocks that lie
beyond the end of their available length.
*/
static void clearSamplesBeyondAvailableLength (int** destSamples, int numDestChannels,
int startOffsetInDestBuffer, int64 startSampleInFile,
int& numSamples, int64 fileLengthInSamples)
{
jassert (destSamples != nullptr);
const int64 samplesAvailable = fileLengthInSamples - startSampleInFile;
if (samplesAvailable < numSamples)
{
for (int i = numDestChannels; --i >= 0;)
if (destSamples[i] != nullptr)
zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples);
numSamples = (int) samplesAvailable;
}
}
private:
String formatName;
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatReader)
};
}
#endif // WATER_AUDIOFORMATREADER_H_INCLUDED

+ 478
- 0
source/modules/water/buffers/AudioDataConverters.cpp View File

@@ -0,0 +1,478 @@
/*
==============================================================================
This file is part of the Water library.
Copyright (c) 2016 - ROLI Ltd.
Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
==============================================================================
*/
#include "AudioDataConverters.h"
namespace water {
void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{
const double maxVal = (double) 0x7fff;
char* intData = static_cast<char*> (dest);
if (dest != (void*) source || destBytesPerSample <= 4)
{
for (int i = 0; i < numSamples; ++i)
{
*(uint16*) intData = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
intData += destBytesPerSample;
}
}
else
{
intData += destBytesPerSample * numSamples;
for (int i = numSamples; --i >= 0;)
{
intData -= destBytesPerSample;
*(uint16*) intData = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
}
}
}
void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{
const double maxVal = (double) 0x7fff;
char* intData = static_cast<char*> (dest);
if (dest != (void*) source || destBytesPerSample <= 4)
{
for (int i = 0; i < numSamples; ++i)
{
*(uint16*) intData = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
intData += destBytesPerSample;
}
}
else
{
intData += destBytesPerSample * numSamples;
for (int i = numSamples; --i >= 0;)
{
intData -= destBytesPerSample;
*(uint16*) intData = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
}
}
}
void AudioDataConverters::convertFloatToInt24LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{
const double maxVal = (double) 0x7fffff;
char* intData = static_cast<char*> (dest);
if (dest != (void*) source || destBytesPerSample <= 4)
{
for (int i = 0; i < numSamples; ++i)
{
ByteOrder::littleEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData);
intData += destBytesPerSample;
}
}
else
{
intData += destBytesPerSample * numSamples;
for (int i = numSamples; --i >= 0;)
{
intData -= destBytesPerSample;
ByteOrder::littleEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData);
}
}
}
void AudioDataConverters::convertFloatToInt24BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{
const double maxVal = (double) 0x7fffff;
char* intData = static_cast<char*> (dest);
if (dest != (void*) source || destBytesPerSample <= 4)
{
for (int i = 0; i < numSamples; ++i)
{
ByteOrder::bigEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData);
intData += destBytesPerSample;
}
}
else
{
intData += destBytesPerSample * numSamples;
for (int i = numSamples; --i >= 0;)
{
intData -= destBytesPerSample;
ByteOrder::bigEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData);
}
}
}
void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{
const double maxVal = (double) 0x7fffffff;
char* intData = static_cast<char*> (dest);
if (dest != (void*) source || destBytesPerSample <= 4)
{
for (int i = 0; i < numSamples; ++i)
{
*(uint32*)intData = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
intData += destBytesPerSample;
}
}
else
{
intData += destBytesPerSample * numSamples;
for (int i = numSamples; --i >= 0;)
{
intData -= destBytesPerSample;
*(uint32*)intData = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
}
}
}
void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{
const double maxVal = (double) 0x7fffffff;
char* intData = static_cast<char*> (dest);
if (dest != (void*) source || destBytesPerSample <= 4)
{
for (int i = 0; i < numSamples; ++i)
{
*(uint32*)intData = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
intData += destBytesPerSample;
}
}
else
{
intData += destBytesPerSample * numSamples;
for (int i = numSamples; --i >= 0;)
{
intData -= destBytesPerSample;
*(uint32*)intData = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
}
}
}
void AudioDataConverters::convertFloatToFloat32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{
jassert (dest != (void*) source || destBytesPerSample <= 4); // This op can't be performed on in-place data!
char* d = static_cast<char*> (dest);
for (int i = 0; i < numSamples; ++i)
{
*(float*) d = source[i];
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
*(uint32*) d = ByteOrder::swap (*(uint32*) d);
#endif
d += destBytesPerSample;
}
}
void AudioDataConverters::convertFloatToFloat32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample)
{
jassert (dest != (void*) source || destBytesPerSample <= 4); // This op can't be performed on in-place data!
char* d = static_cast<char*> (dest);
for (int i = 0; i < numSamples; ++i)
{
*(float*) d = source[i];
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
*(uint32*) d = ByteOrder::swap (*(uint32*) d);
#endif
d += destBytesPerSample;
}
}
//==============================================================================
void AudioDataConverters::convertInt16LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{
const float scale = 1.0f / 0x7fff;
const char* intData = static_cast<const char*> (source);
if (source != (void*) dest || srcBytesPerSample >= 4)
{
for (int i = 0; i < numSamples; ++i)
{
dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*(uint16*)intData);
intData += srcBytesPerSample;
}
}
else
{
intData += srcBytesPerSample * numSamples;
for (int i = numSamples; --i >= 0;)
{
intData -= srcBytesPerSample;
dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*(uint16*)intData);
}
}
}
void AudioDataConverters::convertInt16BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{
const float scale = 1.0f / 0x7fff;
const char* intData = static_cast<const char*> (source);
if (source != (void*) dest || srcBytesPerSample >= 4)
{
for (int i = 0; i < numSamples; ++i)
{
dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*(uint16*)intData);
intData += srcBytesPerSample;
}
}
else
{
intData += srcBytesPerSample * numSamples;
for (int i = numSamples; --i >= 0;)
{
intData -= srcBytesPerSample;
dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*(uint16*)intData);
}
}
}
void AudioDataConverters::convertInt24LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{
const float scale = 1.0f / 0x7fffff;
const char* intData = static_cast<const char*> (source);
if (source != (void*) dest || srcBytesPerSample >= 4)
{
for (int i = 0; i < numSamples; ++i)
{
dest[i] = scale * (short) ByteOrder::littleEndian24Bit (intData);
intData += srcBytesPerSample;
}
}
else
{
intData += srcBytesPerSample * numSamples;
for (int i = numSamples; --i >= 0;)
{
intData -= srcBytesPerSample;
dest[i] = scale * (short) ByteOrder::littleEndian24Bit (intData);
}
}
}
void AudioDataConverters::convertInt24BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{
const float scale = 1.0f / 0x7fffff;
const char* intData = static_cast<const char*> (source);
if (source != (void*) dest || srcBytesPerSample >= 4)
{
for (int i = 0; i < numSamples; ++i)
{
dest[i] = scale * (short) ByteOrder::bigEndian24Bit (intData);
intData += srcBytesPerSample;
}
}
else
{
intData += srcBytesPerSample * numSamples;
for (int i = numSamples; --i >= 0;)
{
intData -= srcBytesPerSample;
dest[i] = scale * (short) ByteOrder::bigEndian24Bit (intData);
}
}
}
void AudioDataConverters::convertInt32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{
const float scale = 1.0f / 0x7fffffff;
const char* intData = static_cast<const char*> (source);
if (source != (void*) dest || srcBytesPerSample >= 4)
{
for (int i = 0; i < numSamples; ++i)
{
dest[i] = scale * (int) ByteOrder::swapIfBigEndian (*(uint32*) intData);
intData += srcBytesPerSample;
}
}
else
{
intData += srcBytesPerSample * numSamples;
for (int i = numSamples; --i >= 0;)
{
intData -= srcBytesPerSample;
dest[i] = scale * (int) ByteOrder::swapIfBigEndian (*(uint32*) intData);
}
}
}
void AudioDataConverters::convertInt32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{
const float scale = 1.0f / 0x7fffffff;
const char* intData = static_cast<const char*> (source);
if (source != (void*) dest || srcBytesPerSample >= 4)
{
for (int i = 0; i < numSamples; ++i)
{
dest[i] = scale * (int) ByteOrder::swapIfLittleEndian (*(uint32*) intData);
intData += srcBytesPerSample;
}
}
else
{
intData += srcBytesPerSample * numSamples;
for (int i = numSamples; --i >= 0;)
{
intData -= srcBytesPerSample;
dest[i] = scale * (int) ByteOrder::swapIfLittleEndian (*(uint32*) intData);
}
}
}
void AudioDataConverters::convertFloat32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{
const char* s = static_cast<const char*> (source);
for (int i = 0; i < numSamples; ++i)
{
dest[i] = *(float*)s;
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
uint32* const d = (uint32*) (dest + i);
*d = ByteOrder::swap (*d);
#endif
s += srcBytesPerSample;
}
}
void AudioDataConverters::convertFloat32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample)
{
const char* s = static_cast<const char*> (source);
for (int i = 0; i < numSamples; ++i)
{
dest[i] = *(float*)s;
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
uint32* const d = (uint32*) (dest + i);
*d = ByteOrder::swap (*d);
#endif
s += srcBytesPerSample;
}
}
//==============================================================================
void AudioDataConverters::convertFloatToFormat (const DataFormat destFormat,
const float* const source,
void* const dest,
const int numSamples)
{
switch (destFormat)
{
case int16LE: convertFloatToInt16LE (source, dest, numSamples); break;
case int16BE: convertFloatToInt16BE (source, dest, numSamples); break;
case int24LE: convertFloatToInt24LE (source, dest, numSamples); break;
case int24BE: convertFloatToInt24BE (source, dest, numSamples); break;
case int32LE: convertFloatToInt32LE (source, dest, numSamples); break;
case int32BE: convertFloatToInt32BE (source, dest, numSamples); break;
case float32LE: convertFloatToFloat32LE (source, dest, numSamples); break;
case float32BE: convertFloatToFloat32BE (source, dest, numSamples); break;
default: jassertfalse; break;
}
}
void AudioDataConverters::convertFormatToFloat (const DataFormat sourceFormat,
const void* const source,
float* const dest,
const int numSamples)
{
switch (sourceFormat)
{
case int16LE: convertInt16LEToFloat (source, dest, numSamples); break;
case int16BE: convertInt16BEToFloat (source, dest, numSamples); break;
case int24LE: convertInt24LEToFloat (source, dest, numSamples); break;
case int24BE: convertInt24BEToFloat (source, dest, numSamples); break;
case int32LE: convertInt32LEToFloat (source, dest, numSamples); break;
case int32BE: convertInt32BEToFloat (source, dest, numSamples); break;
case float32LE: convertFloat32LEToFloat (source, dest, numSamples); break;
case float32BE: convertFloat32BEToFloat (source, dest, numSamples); break;
default: jassertfalse; break;
}
}
//==============================================================================
void AudioDataConverters::interleaveSamples (const float** const source,
float* const dest,
const int numSamples,
const int numChannels)
{
for (int chan = 0; chan < numChannels; ++chan)
{
int i = chan;
const float* src = source [chan];
for (int j = 0; j < numSamples; ++j)
{
dest [i] = src [j];
i += numChannels;
}
}
}
void AudioDataConverters::deinterleaveSamples (const float* const source,
float** const dest,
const int numSamples,
const int numChannels)
{
for (int chan = 0; chan < numChannels; ++chan)
{
int i = chan;
float* dst = dest [chan];
for (int j = 0; j < numSamples; ++j)
{
dst [j] = source [i];
i += numChannels;
}
}
}
}

+ 719
- 0
source/modules/water/buffers/AudioDataConverters.h View File

@@ -0,0 +1,719 @@
/*
==============================================================================
This file is part of the Water library.
Copyright (c) 2016 ROLI Ltd.
Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
==============================================================================
*/
#ifndef WATER_AUDIODATACONVERTERS_H_INCLUDED
#define WATER_AUDIODATACONVERTERS_H_INCLUDED
#include "../memory/ByteOrder.h"
namespace water {
//==============================================================================
/**
This class a container which holds all the classes pertaining to the AudioData::Pointer
audio sample format class.
@see AudioData::Pointer.
*/
class AudioData
{
public:
//==============================================================================
// These types can be used as the SampleFormat template parameter for the AudioData::Pointer class.
class Int8; /**< Used as a template parameter for AudioData::Pointer. Indicates an 8-bit integer packed data format. */
class UInt8; /**< Used as a template parameter for AudioData::Pointer. Indicates an 8-bit unsigned integer packed data format. */
class Int16; /**< Used as a template parameter for AudioData::Pointer. Indicates an 16-bit integer packed data format. */
class Int24; /**< Used as a template parameter for AudioData::Pointer. Indicates an 24-bit integer packed data format. */
class Int32; /**< Used as a template parameter for AudioData::Pointer. Indicates an 32-bit integer packed data format. */
class Float32; /**< Used as a template parameter for AudioData::Pointer. Indicates an 32-bit float data format. */
//==============================================================================
// These types can be used as the Endianness template parameter for the AudioData::Pointer class.
class BigEndian; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored in big-endian order. */
class LittleEndian; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored in little-endian order. */
class NativeEndian; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored in the CPU's native endianness. */
//==============================================================================
// These types can be used as the InterleavingType template parameter for the AudioData::Pointer class.
class NonInterleaved; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored contiguously. */
class Interleaved; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are interleaved with a number of other channels. */
//==============================================================================
// These types can be used as the Constness template parameter for the AudioData::Pointer class.
class NonConst; /**< Used as a template parameter for AudioData::Pointer. Indicates that the pointer can be used for non-const data. */
class Const; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples can only be used for const data.. */
#ifndef DOXYGEN
//==============================================================================
class BigEndian
{
public:
template <class SampleFormatType> static inline float getAsFloat (SampleFormatType& s) noexcept { return s.getAsFloatBE(); }
template <class SampleFormatType> static inline void setAsFloat (SampleFormatType& s, float newValue) noexcept { s.setAsFloatBE (newValue); }
template <class SampleFormatType> static inline int32 getAsInt32 (SampleFormatType& s) noexcept { return s.getAsInt32BE(); }
template <class SampleFormatType> static inline void setAsInt32 (SampleFormatType& s, int32 newValue) noexcept { s.setAsInt32BE (newValue); }
template <class SourceType, class DestType> static inline void copyFrom (DestType& dest, SourceType& source) noexcept { dest.copyFromBE (source); }
enum { isBigEndian = 1 };
};
class LittleEndian
{
public:
template <class SampleFormatType> static inline float getAsFloat (SampleFormatType& s) noexcept { return s.getAsFloatLE(); }
template <class SampleFormatType> static inline void setAsFloat (SampleFormatType& s, float newValue) noexcept { s.setAsFloatLE (newValue); }
template <class SampleFormatType> static inline int32 getAsInt32 (SampleFormatType& s) noexcept { return s.getAsInt32LE(); }
template <class SampleFormatType> static inline void setAsInt32 (SampleFormatType& s, int32 newValue) noexcept { s.setAsInt32LE (newValue); }
template <class SourceType, class DestType> static inline void copyFrom (DestType& dest, SourceType& source) noexcept { dest.copyFromLE (source); }
enum { isBigEndian = 0 };
};
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
class NativeEndian : public BigEndian {};
#else
class NativeEndian : public LittleEndian {};
#endif
//==============================================================================
class Int8
{
public:
inline Int8 (void* d) noexcept : data (static_cast<int8*> (d)) {}
inline void advance() noexcept { ++data; }
inline void skip (int numSamples) noexcept { data += numSamples; }
inline float getAsFloatLE() const noexcept { return (float) (*data * (1.0 / (1.0 + maxValue))); }
inline float getAsFloatBE() const noexcept { return getAsFloatLE(); }
inline void setAsFloatLE (float newValue) noexcept { *data = (int8) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))); }
inline void setAsFloatBE (float newValue) noexcept { setAsFloatLE (newValue); }
inline int32 getAsInt32LE() const noexcept { return (int) (*data << 24); }
inline int32 getAsInt32BE() const noexcept { return getAsInt32LE(); }
inline void setAsInt32LE (int newValue) noexcept { *data = (int8) (newValue >> 24); }
inline void setAsInt32BE (int newValue) noexcept { setAsInt32LE (newValue); }
inline void clear() noexcept { *data = 0; }
inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;}
template <class SourceType> inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); }
template <class SourceType> inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); }
inline void copyFromSameType (Int8& source) noexcept { *data = *source.data; }
int8* data;
enum { bytesPerSample = 1, maxValue = 0x7f, resolution = (1 << 24), isFloat = 0 };
};
class UInt8
{
public:
inline UInt8 (void* d) noexcept : data (static_cast<uint8*> (d)) {}
inline void advance() noexcept { ++data; }
inline void skip (int numSamples) noexcept { data += numSamples; }
inline float getAsFloatLE() const noexcept { return (float) ((*data - 128) * (1.0 / (1.0 + maxValue))); }
inline float getAsFloatBE() const noexcept { return getAsFloatLE(); }
inline void setAsFloatLE (float newValue) noexcept { *data = (uint8) jlimit (0, 255, 128 + roundToInt (newValue * (1.0 + maxValue))); }
inline void setAsFloatBE (float newValue) noexcept { setAsFloatLE (newValue); }
inline int32 getAsInt32LE() const noexcept { return (int) ((*data - 128) << 24); }
inline int32 getAsInt32BE() const noexcept { return getAsInt32LE(); }
inline void setAsInt32LE (int newValue) noexcept { *data = (uint8) (128 + (newValue >> 24)); }
inline void setAsInt32BE (int newValue) noexcept { setAsInt32LE (newValue); }
inline void clear() noexcept { *data = 128; }
inline void clearMultiple (int num) noexcept { memset (data, 128, (size_t) num) ;}
template <class SourceType> inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); }
template <class SourceType> inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); }
inline void copyFromSameType (UInt8& source) noexcept { *data = *source.data; }
uint8* data;
enum { bytesPerSample = 1, maxValue = 0x7f, resolution = (1 << 24), isFloat = 0 };
};
class Int16
{
public:
inline Int16 (void* d) noexcept : data (static_cast<uint16*> (d)) {}
inline void advance() noexcept { ++data; }
inline void skip (int numSamples) noexcept { data += numSamples; }
inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int16) ByteOrder::swapIfBigEndian (*data)); }
inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int16) ByteOrder::swapIfLittleEndian (*data)); }
inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint16) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue)))); }
inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint16) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue)))); }
inline int32 getAsInt32LE() const noexcept { return (int32) (ByteOrder::swapIfBigEndian ((uint16) *data) << 16); }
inline int32 getAsInt32BE() const noexcept { return (int32) (ByteOrder::swapIfLittleEndian ((uint16) *data) << 16); }
inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint16) (newValue >> 16)); }
inline void setAsInt32BE (int32 newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint16) (newValue >> 16)); }
inline void clear() noexcept { *data = 0; }
inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;}
template <class SourceType> inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); }
template <class SourceType> inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); }
inline void copyFromSameType (Int16& source) noexcept { *data = *source.data; }
uint16* data;
enum { bytesPerSample = 2, maxValue = 0x7fff, resolution = (1 << 16), isFloat = 0 };
};
class Int24
{
public:
inline Int24 (void* d) noexcept : data (static_cast<char*> (d)) {}
inline void advance() noexcept { data += 3; }
inline void skip (int numSamples) noexcept { data += 3 * numSamples; }
inline float getAsFloatLE() const noexcept { return (float) (ByteOrder::littleEndian24Bit (data) * (1.0 / (1.0 + maxValue))); }
inline float getAsFloatBE() const noexcept { return (float) (ByteOrder::bigEndian24Bit (data) * (1.0 / (1.0 + maxValue))); }
inline void setAsFloatLE (float newValue) noexcept { ByteOrder::littleEndian24BitToChars (jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))), data); }
inline void setAsFloatBE (float newValue) noexcept { ByteOrder::bigEndian24BitToChars (jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))), data); }
inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::littleEndian24Bit (data) << 8; }
inline int32 getAsInt32BE() const noexcept { return (int32) ByteOrder::bigEndian24Bit (data) << 8; }
inline void setAsInt32LE (int32 newValue) noexcept { ByteOrder::littleEndian24BitToChars (newValue >> 8, data); }
inline void setAsInt32BE (int32 newValue) noexcept { ByteOrder::bigEndian24BitToChars (newValue >> 8, data); }
inline void clear() noexcept { data[0] = 0; data[1] = 0; data[2] = 0; }
inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;}
template <class SourceType> inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); }
template <class SourceType> inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); }
inline void copyFromSameType (Int24& source) noexcept { data[0] = source.data[0]; data[1] = source.data[1]; data[2] = source.data[2]; }
char* data;
enum { bytesPerSample = 3, maxValue = 0x7fffff, resolution = (1 << 8), isFloat = 0 };
};
class Int32
{
public:
inline Int32 (void* d) noexcept : data (static_cast<uint32*> (d)) {}
inline void advance() noexcept { ++data; }
inline void skip (int numSamples) noexcept { data += numSamples; }
inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfBigEndian (*data)); }
inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfLittleEndian (*data)); }
inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); }
inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); }
inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::swapIfBigEndian (*data); }
inline int32 getAsInt32BE() const noexcept { return (int32) ByteOrder::swapIfLittleEndian (*data); }
inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) newValue); }
inline void setAsInt32BE (int32 newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) newValue); }
inline void clear() noexcept { *data = 0; }
inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;}
template <class SourceType> inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); }
template <class SourceType> inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); }
inline void copyFromSameType (Int32& source) noexcept { *data = *source.data; }
uint32* data;
enum { bytesPerSample = 4, maxValue = 0x7fffffff, resolution = 1, isFloat = 0 };
};
/** A 32-bit integer type, of which only the bottom 24 bits are used. */
class Int24in32 : public Int32
{
public:
inline Int24in32 (void* d) noexcept : Int32 (d) {}
inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfBigEndian (*data)); }
inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfLittleEndian (*data)); }
inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); }
inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); }
inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::swapIfBigEndian (*data) << 8; }
inline int32 getAsInt32BE() const noexcept { return (int32) ByteOrder::swapIfLittleEndian (*data) << 8; }
inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) newValue >> 8); }
inline void setAsInt32BE (int32 newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) newValue >> 8); }
template <class SourceType> inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); }
template <class SourceType> inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); }
inline void copyFromSameType (Int24in32& source) noexcept { *data = *source.data; }
enum { bytesPerSample = 4, maxValue = 0x7fffff, resolution = (1 << 8), isFloat = 0 };
};
class Float32
{
public:
inline Float32 (void* d) noexcept : data (static_cast<float*> (d)) {}
inline void advance() noexcept { ++data; }
inline void skip (int numSamples) noexcept { data += numSamples; }
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
inline float getAsFloatBE() const noexcept { return *data; }
inline void setAsFloatBE (float newValue) noexcept { *data = newValue; }
inline float getAsFloatLE() const noexcept { union { uint32 asInt; float asFloat; } n; n.asInt = ByteOrder::swap (*(uint32*) data); return n.asFloat; }
inline void setAsFloatLE (float newValue) noexcept { union { uint32 asInt; float asFloat; } n; n.asFloat = newValue; *(uint32*) data = ByteOrder::swap (n.asInt); }
#else
inline float getAsFloatLE() const noexcept { return *data; }
inline void setAsFloatLE (float newValue) noexcept { *data = newValue; }
inline float getAsFloatBE() const noexcept { union { uint32 asInt; float asFloat; } n; n.asInt = ByteOrder::swap (*(uint32*) data); return n.asFloat; }
inline void setAsFloatBE (float newValue) noexcept { union { uint32 asInt; float asFloat; } n; n.asFloat = newValue; *(uint32*) data = ByteOrder::swap (n.asInt); }
#endif
inline int32 getAsInt32LE() const noexcept { return (int32) roundToInt (jlimit (-1.0, 1.0, (double) getAsFloatLE()) * (double) maxValue); }
inline int32 getAsInt32BE() const noexcept { return (int32) roundToInt (jlimit (-1.0, 1.0, (double) getAsFloatBE()) * (double) maxValue); }
inline void setAsInt32LE (int32 newValue) noexcept { setAsFloatLE ((float) (newValue * (1.0 / (1.0 + maxValue)))); }
inline void setAsInt32BE (int32 newValue) noexcept { setAsFloatBE ((float) (newValue * (1.0 / (1.0 + maxValue)))); }
inline void clear() noexcept { *data = 0; }
inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;}
template <class SourceType> inline void copyFromLE (SourceType& source) noexcept { setAsFloatLE (source.getAsFloat()); }
template <class SourceType> inline void copyFromBE (SourceType& source) noexcept { setAsFloatBE (source.getAsFloat()); }
inline void copyFromSameType (Float32& source) noexcept { *data = *source.data; }
float* data;
enum { bytesPerSample = 4, maxValue = 0x7fffffff, resolution = (1 << 8), isFloat = 1 };
};
//==============================================================================
class NonInterleaved
{
public:
inline NonInterleaved() noexcept {}
inline NonInterleaved (const NonInterleaved&) noexcept {}
inline NonInterleaved (const int) noexcept {}
inline void copyFrom (const NonInterleaved&) noexcept {}
template <class SampleFormatType> inline void advanceData (SampleFormatType& s) noexcept { s.advance(); }
template <class SampleFormatType> inline void advanceDataBy (SampleFormatType& s, int numSamples) noexcept { s.skip (numSamples); }
template <class SampleFormatType> inline void clear (SampleFormatType& s, int numSamples) noexcept { s.clearMultiple (numSamples); }
template <class SampleFormatType> inline static int getNumBytesBetweenSamples (const SampleFormatType&) noexcept { return SampleFormatType::bytesPerSample; }
enum { isInterleavedType = 0, numInterleavedChannels = 1 };
};
class Interleaved
{
public:
inline Interleaved() noexcept : numInterleavedChannels (1) {}
inline Interleaved (const Interleaved& other) noexcept : numInterleavedChannels (other.numInterleavedChannels) {}
inline Interleaved (const int numInterleavedChans) noexcept : numInterleavedChannels (numInterleavedChans) {}
inline void copyFrom (const Interleaved& other) noexcept { numInterleavedChannels = other.numInterleavedChannels; }
template <class SampleFormatType> inline void advanceData (SampleFormatType& s) noexcept { s.skip (numInterleavedChannels); }
template <class SampleFormatType> inline void advanceDataBy (SampleFormatType& s, int numSamples) noexcept { s.skip (numInterleavedChannels * numSamples); }
template <class SampleFormatType> inline void clear (SampleFormatType& s, int numSamples) noexcept { while (--numSamples >= 0) { s.clear(); s.skip (numInterleavedChannels); } }
template <class SampleFormatType> inline int getNumBytesBetweenSamples (const SampleFormatType&) const noexcept { return numInterleavedChannels * SampleFormatType::bytesPerSample; }
int numInterleavedChannels;
enum { isInterleavedType = 1 };
};
//==============================================================================
class NonConst
{
public:
typedef void VoidType;
static inline void* toVoidPtr (VoidType* v) noexcept { return v; }
enum { isConst = 0 };
};
class Const
{
public:
typedef const void VoidType;
static inline void* toVoidPtr (VoidType* v) noexcept { return const_cast<void*> (v); }
enum { isConst = 1 };
};
#endif
//==============================================================================
/**
A pointer to a block of audio data with a particular encoding.
This object can be used to read and write from blocks of encoded audio samples. To create one, you specify
the audio format as a series of template parameters, e.g.
@code
// this creates a pointer for reading from a const array of 16-bit little-endian packed samples.
AudioData::Pointer <AudioData::Int16,
AudioData::LittleEndian,
AudioData::NonInterleaved,
AudioData::Const> pointer (someRawAudioData);
// These methods read the sample that is being pointed to
float firstSampleAsFloat = pointer.getAsFloat();
int32 firstSampleAsInt = pointer.getAsInt32();
++pointer; // moves the pointer to the next sample.
pointer += 3; // skips the next 3 samples.
@endcode
The convertSamples() method lets you copy a range of samples from one format to another, automatically
converting its format.
@see AudioData::Converter
*/
template <typename SampleFormat,
typename Endianness,
typename InterleavingType,
typename Constness>
class Pointer : private InterleavingType // (inherited for EBCO)
{
public:
//==============================================================================
/** Creates a non-interleaved pointer from some raw data in the appropriate format.
This constructor is only used if you've specified the AudioData::NonInterleaved option -
for interleaved formats, use the constructor that also takes a number of channels.
*/
Pointer (typename Constness::VoidType* sourceData) noexcept
: data (Constness::toVoidPtr (sourceData))
{
// If you're using interleaved data, call the other constructor! If you're using non-interleaved data,
// you should pass NonInterleaved as the template parameter for the interleaving type!
static_jassert (InterleavingType::isInterleavedType == 0);
}
/** Creates a pointer from some raw data in the appropriate format with the specified number of interleaved channels.
For non-interleaved data, use the other constructor.
*/
Pointer (typename Constness::VoidType* sourceData, int numInterleaved) noexcept
: InterleavingType (numInterleaved), data (Constness::toVoidPtr (sourceData))
{
}
/** Creates a copy of another pointer. */
Pointer (const Pointer& other) noexcept
: InterleavingType (other), data (other.data)
{
}
Pointer& operator= (const Pointer& other) noexcept
{
InterleavingType::operator= (other);
data = other.data;
return *this;
}
//==============================================================================
/** Returns the value of the first sample as a floating point value.
The value will be in the range -1.0 to 1.0 for integer formats. For floating point
formats, the value could be outside that range, although -1 to 1 is the standard range.
*/
inline float getAsFloat() const noexcept { return Endianness::getAsFloat (data); }
/** Sets the value of the first sample as a floating point value.
(This method can only be used if the AudioData::NonConst option was used).
The value should be in the range -1.0 to 1.0 - for integer formats, values outside that
range will be clipped. For floating point formats, any value passed in here will be
written directly, although -1 to 1 is the standard range.
*/
inline void setAsFloat (float newValue) noexcept
{
static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead!
Endianness::setAsFloat (data, newValue);
}
/** Returns the value of the first sample as a 32-bit integer.
The value returned will be in the range 0x80000000 to 0x7fffffff, and shorter values will be
shifted to fill this range (e.g. if you're reading from 24-bit data, the values will be shifted up
by 8 bits when returned here). If the source data is floating point, values beyond -1.0 to 1.0 will
be clipped so that -1.0 maps onto -0x7fffffff and 1.0 maps to 0x7fffffff.
*/
inline int32 getAsInt32() const noexcept { return Endianness::getAsInt32 (data); }
/** Sets the value of the first sample as a 32-bit integer.
This will be mapped to the range of the format that is being written - see getAsInt32().
*/
inline void setAsInt32 (int32 newValue) noexcept
{
static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead!
Endianness::setAsInt32 (data, newValue);
}
/** Moves the pointer along to the next sample. */
inline Pointer& operator++() noexcept { advance(); return *this; }
/** Moves the pointer back to the previous sample. */
inline Pointer& operator--() noexcept { this->advanceDataBy (data, -1); return *this; }
/** Adds a number of samples to the pointer's position. */
Pointer& operator+= (int samplesToJump) noexcept { this->advanceDataBy (data, samplesToJump); return *this; }
/** Writes a stream of samples into this pointer from another pointer.
This will copy the specified number of samples, converting between formats appropriately.
*/
void convertSamples (Pointer source, int numSamples) const noexcept
{
static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead!
for (Pointer dest (*this); --numSamples >= 0;)
{
dest.data.copyFromSameType (source.data);
dest.advance();
source.advance();
}
}
/** Writes a stream of samples into this pointer from another pointer.
This will copy the specified number of samples, converting between formats appropriately.
*/
template <class OtherPointerType>
void convertSamples (OtherPointerType source, int numSamples) const noexcept
{
static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead!
Pointer dest (*this);
if (source.getRawData() != getRawData() || source.getNumBytesBetweenSamples() >= getNumBytesBetweenSamples())
{
while (--numSamples >= 0)
{
Endianness::copyFrom (dest.data, source);
dest.advance();
++source;
}
}
else // copy backwards if we're increasing the sample width..
{
dest += numSamples;
source += numSamples;
while (--numSamples >= 0)
Endianness::copyFrom ((--dest).data, --source);
}
}
/** Sets a number of samples to zero. */
void clearSamples (int numSamples) const noexcept
{
Pointer dest (*this);
dest.clear (dest.data, numSamples);
}
#if 0
/** Scans a block of data, returning the lowest and highest levels as floats */
Range<float> findMinAndMax (size_t numSamples) const noexcept
{
if (numSamples == 0)
return Range<float>();
Pointer dest (*this);
if (isFloatingPoint())
{
float mn = dest.getAsFloat();
dest.advance();
float mx = mn;
while (--numSamples > 0)
{
const float v = dest.getAsFloat();
dest.advance();
if (mx < v) mx = v;
if (v < mn) mn = v;
}
return Range<float> (mn, mx);
}
int32 mn = dest.getAsInt32();
dest.advance();
int32 mx = mn;
while (--numSamples > 0)
{
const int v = dest.getAsInt32();
dest.advance();
if (mx < v) mx = v;
if (v < mn) mn = v;
}
return Range<float> (mn * (float) (1.0 / (1.0 + Int32::maxValue)),
mx * (float) (1.0 / (1.0 + Int32::maxValue)));
}
/** Scans a block of data, returning the lowest and highest levels as floats */
void findMinAndMax (size_t numSamples, float& minValue, float& maxValue) const noexcept
{
Range<float> r (findMinAndMax (numSamples));
minValue = r.getStart();
maxValue = r.getEnd();
}
#endif
/** Returns true if the pointer is using a floating-point format. */
static bool isFloatingPoint() noexcept { return (bool) SampleFormat::isFloat; }
/** Returns true if the format is big-endian. */
static bool isBigEndian() noexcept { return (bool) Endianness::isBigEndian; }
/** Returns the number of bytes in each sample (ignoring the number of interleaved channels). */
static int getBytesPerSample() noexcept { return (int) SampleFormat::bytesPerSample; }
/** Returns the number of interleaved channels in the format. */
int getNumInterleavedChannels() const noexcept { return (int) this->numInterleavedChannels; }
/** Returns the number of bytes between the start address of each sample. */
int getNumBytesBetweenSamples() const noexcept { return InterleavingType::getNumBytesBetweenSamples (data); }
/** Returns the accuracy of this format when represented as a 32-bit integer.
This is the smallest number above 0 that can be represented in the sample format, converted to
a 32-bit range. E,g. if the format is 8-bit, its resolution is 0x01000000; if the format is 24-bit,
its resolution is 0x100.
*/
static int get32BitResolution() noexcept { return (int) SampleFormat::resolution; }
/** Returns a pointer to the underlying data. */
const void* getRawData() const noexcept { return data.data; }
private:
//==============================================================================
SampleFormat data;
inline void advance() noexcept { this->advanceData (data); }
Pointer operator++ (int); // private to force you to use the more efficient pre-increment!
Pointer operator-- (int);
};
//==============================================================================
/** A base class for objects that are used to convert between two different sample formats.
The AudioData::ConverterInstance implements this base class and can be templated, so
you can create an instance that converts between two particular formats, and then
store this in the abstract base class.
@see AudioData::ConverterInstance
*/
class Converter
{
public:
virtual ~Converter() {}
/** Converts a sequence of samples from the converter's source format into the dest format. */
virtual void convertSamples (void* destSamples, const void* sourceSamples, int numSamples) const = 0;
/** Converts a sequence of samples from the converter's source format into the dest format.
This method takes sub-channel indexes, which can be used with interleaved formats in order to choose a
particular sub-channel of the data to be used.
*/
virtual void convertSamples (void* destSamples, int destSubChannel,
const void* sourceSamples, int sourceSubChannel, int numSamples) const = 0;
};
//==============================================================================
/**
A class that converts between two templated AudioData::Pointer types, and which
implements the AudioData::Converter interface.
This can be used as a concrete instance of the AudioData::Converter abstract class.
@see AudioData::Converter
*/
template <class SourceSampleType, class DestSampleType>
class ConverterInstance : public Converter
{
public:
ConverterInstance (int numSourceChannels = 1, int numDestChannels = 1)
: sourceChannels (numSourceChannels), destChannels (numDestChannels)
{}
void convertSamples (void* dest, const void* source, int numSamples) const override
{
SourceSampleType s (source, sourceChannels);
DestSampleType d (dest, destChannels);
d.convertSamples (s, numSamples);
}
void convertSamples (void* dest, int destSubChannel,
const void* source, int sourceSubChannel, int numSamples) const override
{
jassert (destSubChannel < destChannels && sourceSubChannel < sourceChannels);
SourceSampleType s (addBytesToPointer (source, sourceSubChannel * SourceSampleType::getBytesPerSample()), sourceChannels);
DestSampleType d (addBytesToPointer (dest, destSubChannel * DestSampleType::getBytesPerSample()), destChannels);
d.convertSamples (s, numSamples);
}
private:
const int sourceChannels, destChannels;
CARLA_DECLARE_NON_COPY_CLASS (ConverterInstance)
};
};
//==============================================================================
/**
A set of routines to convert buffers of 32-bit floating point data to and from
various integer formats.
Note that these functions are deprecated - the AudioData class provides a much more
flexible set of conversion classes now.
*/
class AudioDataConverters
{
public:
//==============================================================================
static void convertFloatToInt16LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 2);
static void convertFloatToInt16BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 2);
static void convertFloatToInt24LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 3);
static void convertFloatToInt24BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 3);
static void convertFloatToInt32LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4);
static void convertFloatToInt32BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4);
static void convertFloatToFloat32LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4);
static void convertFloatToFloat32BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4);
//==============================================================================
static void convertInt16LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 2);
static void convertInt16BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 2);
static void convertInt24LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 3);
static void convertInt24BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 3);
static void convertInt32LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4);
static void convertInt32BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4);
static void convertFloat32LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4);
static void convertFloat32BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4);
//==============================================================================
enum DataFormat
{
int16LE,
int16BE,
int24LE,
int24BE,
int32LE,
int32BE,
float32LE,
float32BE,
};
static void convertFloatToFormat (DataFormat destFormat,
const float* source, void* dest, int numSamples);
static void convertFormatToFloat (DataFormat sourceFormat,
const void* source, float* dest, int numSamples);
//==============================================================================
static void interleaveSamples (const float** source, float* dest,
int numSamples, int numChannels);
static void deinterleaveSamples (const float* source, float** dest,
int numSamples, int numChannels);
private:
AudioDataConverters();
CARLA_DECLARE_NON_COPY_CLASS (AudioDataConverters)
};
}
#endif // WATER_AUDIODATACONVERTERS_H_INCLUDED

+ 2
- 6
source/modules/water/maths/BigInteger.cpp View File

@@ -160,7 +160,7 @@ uint32* BigInteger::ensureSize (const size_t numVals) noexcept
//==============================================================================
bool BigInteger::operator[] (const int bit) const noexcept
{
if (bit < 0 || bit > highestBit)
if (bit > highestBit || bit < 0)
return false;
if (const uint32* const values = getValues())
@@ -182,9 +182,6 @@ void BigInteger::clear() noexcept
bool BigInteger::setBit (const int bit) noexcept
{
if (bit < 0)
return false;
CARLA_SAFE_ASSERT_RETURN(bit >= 0, false);
if (bit > highestBit)
@@ -216,8 +213,7 @@ bool BigInteger::setBit (const int bit, const bool shouldBeSet) noexcept
bool BigInteger::clearBit (const int bit) noexcept
{
if (bit < 0 || bit > highestBit)
return false;
CARLA_SAFE_ASSERT_RETURN(bit <= highestBit && bit >= 0, false);
uint32* const values = getValues();
CARLA_SAFE_ASSERT_RETURN(values != nullptr, false);


+ 150
- 0
source/modules/water/text/StringPairArray.cpp View File

@@ -0,0 +1,150 @@
/*
==============================================================================
This file is part of the Water library.
Copyright (c) 2016 ROLI Ltd.
Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
==============================================================================
*/
#include "StringPairArray.h"
namespace water {
StringPairArray::StringPairArray (const bool ignoreCase_)
: ignoreCase (ignoreCase_)
{
}
StringPairArray::StringPairArray (const StringPairArray& other)
: keys (other.keys),
values (other.values),
ignoreCase (other.ignoreCase)
{
}
StringPairArray::~StringPairArray()
{
}
StringPairArray& StringPairArray::operator= (const StringPairArray& other)
{
keys = other.keys;
values = other.values;
return *this;
}
bool StringPairArray::operator== (const StringPairArray& other) const
{
for (int i = keys.size(); --i >= 0;)
if (other [keys[i]] != values[i])
return false;
return true;
}
bool StringPairArray::operator!= (const StringPairArray& other) const
{
return ! operator== (other);
}
const String& StringPairArray::operator[] (StringRef key) const
{
return values [keys.indexOf (key, ignoreCase)];
}
String StringPairArray::getValue (StringRef key, const String& defaultReturnValue) const
{
const int i = keys.indexOf (key, ignoreCase);
if (i >= 0)
return values[i];
return defaultReturnValue;
}
bool StringPairArray::containsKey (StringRef key) const noexcept
{
return keys.contains (key);
}
void StringPairArray::set (const String& key, const String& value)
{
const int i = keys.indexOf (key, ignoreCase);
if (i >= 0)
{
values.set (i, value);
}
else
{
keys.add (key);
values.add (value);
}
}
void StringPairArray::addArray (const StringPairArray& other)
{
for (int i = 0; i < other.size(); ++i)
set (other.keys[i], other.values[i]);
}
void StringPairArray::clear()
{
keys.clear();
values.clear();
}
void StringPairArray::remove (StringRef key)
{
remove (keys.indexOf (key, ignoreCase));
}
void StringPairArray::remove (const int index)
{
keys.remove (index);
values.remove (index);
}
void StringPairArray::setIgnoresCase (const bool shouldIgnoreCase)
{
ignoreCase = shouldIgnoreCase;
}
String StringPairArray::getDescription() const
{
String s;
for (int i = 0; i < keys.size(); ++i)
{
s << keys[i] << " = " << values[i];
if (i < keys.size())
s << ", ";
}
return s;
}
void StringPairArray::minimiseStorageOverheads()
{
keys.minimiseStorageOverheads();
values.minimiseStorageOverheads();
}
}

+ 165
- 0
source/modules/water/text/StringPairArray.h View File

@@ -0,0 +1,165 @@
/*
==============================================================================
This file is part of the Water library.
Copyright (c) 2016 ROLI Ltd.
Copyright (C) 2018 Filipe Coelho <falktx@falktx.com>
Permission is granted to use this software under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license/
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
-----------------------------------------------------------------------------
To release a closed-source product which uses other parts of JUCE not
licensed under the ISC terms, commercial licenses are available: visit
www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_STRINGPAIRARRAY_H_INCLUDED
#define JUCE_STRINGPAIRARRAY_H_INCLUDED
#include "StringArray.h"
#include "StringRef.h"
namespace water {
//==============================================================================
/**
A container for holding a set of strings which are keyed by another string.
@see StringArray
*/
class StringPairArray
{
public:
//==============================================================================
/** Creates an empty array */
StringPairArray (bool ignoreCaseWhenComparingKeys = true);
/** Creates a copy of another array */
StringPairArray (const StringPairArray& other);
/** Destructor. */
~StringPairArray();
/** Copies the contents of another string array into this one */
StringPairArray& operator= (const StringPairArray& other);
//==============================================================================
/** Compares two arrays.
Comparisons are case-sensitive.
@returns true only if the other array contains exactly the same strings with the same keys
*/
bool operator== (const StringPairArray& other) const;
/** Compares two arrays.
Comparisons are case-sensitive.
@returns false if the other array contains exactly the same strings with the same keys
*/
bool operator!= (const StringPairArray& other) const;
//==============================================================================
/** Finds the value corresponding to a key string.
If no such key is found, this will just return an empty string. To check whether
a given key actually exists (because it might actually be paired with an empty string), use
the getAllKeys() method to obtain a list.
Obviously the reference returned shouldn't be stored for later use, as the
string it refers to may disappear when the array changes.
@see getValue
*/
const String& operator[] (StringRef key) const;
/** Finds the value corresponding to a key string.
If no such key is found, this will just return the value provided as a default.
@see operator[]
*/
String getValue (StringRef, const String& defaultReturnValue) const;
/** Returns true if the given key exists. */
bool containsKey (StringRef key) const noexcept;
/** Returns a list of all keys in the array. */
const StringArray& getAllKeys() const noexcept { return keys; }
/** Returns a list of all values in the array. */
const StringArray& getAllValues() const noexcept { return values; }
/** Returns the number of strings in the array */
inline int size() const noexcept { return keys.size(); };
//==============================================================================
/** Adds or amends a key/value pair.
If a value already exists with this key, its value will be overwritten,
otherwise the key/value pair will be added to the array.
*/
void set (const String& key, const String& value);
/** Adds the items from another array to this one.
This is equivalent to using set() to add each of the pairs from the other array.
*/
void addArray (const StringPairArray& other);
//==============================================================================
/** Removes all elements from the array. */
void clear();
/** Removes a string from the array based on its key.
If the key isn't found, nothing will happen.
*/
void remove (StringRef key);
/** Removes a string from the array based on its index.
If the index is out-of-range, no action will be taken.
*/
void remove (int index);
//==============================================================================
/** Indicates whether to use a case-insensitive search when looking up a key string.
*/
void setIgnoresCase (bool shouldIgnoreCase);
//==============================================================================
/** Returns a descriptive string containing the items.
This is handy for dumping the contents of an array.
*/
String getDescription() const;
//==============================================================================
/** Reduces the amount of storage being used by the array.
Arrays typically allocate slightly more storage than they need, and after
removing elements, they may have quite a lot of unused space allocated.
This method will reduce the amount of allocated storage to a minimum.
*/
void minimiseStorageOverheads();
private:
//==============================================================================
StringArray keys, values;
bool ignoreCase;
CARLA_LEAK_DETECTOR (StringPairArray)
};
}
#endif // JUCE_STRINGPAIRARRAY_H_INCLUDED

+ 9
- 0
source/modules/water/water.cpp View File

@@ -36,6 +36,14 @@ HINSTANCE water_getCurrentModuleInstanceHandle() noexcept
}
#include "audiocodecs/WavAudioFormat.cpp"
#include "audioformat/AudioFormat.cpp"
#include "audioformat/AudioFormatManager.cpp"
#include "audioformat/AudioFormatReader.cpp"
#include "buffers/AudioDataConverters.cpp"
#include "containers/NamedValueSet.cpp"
#include "containers/Variant.cpp"
@@ -72,6 +80,7 @@ HINSTANCE water_getCurrentModuleInstanceHandle() noexcept
#include "text/CharacterFunctions.cpp"
#include "text/Identifier.cpp"
#include "text/StringArray.cpp"
#include "text/StringPairArray.cpp"
#include "text/StringPool.cpp"
#include "text/String.cpp"


+ 2
- 0
source/modules/water/water.h View File

@@ -84,6 +84,8 @@
namespace water
{
class AudioFormatManager;
class AudioFormatReader;
class AudioProcessor;
class AudioSampleBuffer;
class File;


Loading…
Cancel
Save