--- libgig/trunk/src/DLS.h 2005/06/19 15:18:59 666 +++ libgig/trunk/src/DLS.h 2021/06/18 14:06:20 3941 @@ -1,8 +1,8 @@ /*************************************************************************** * * - * libgig - C++ cross-platform Gigasampler format file loader library * + * libgig - C++ cross-platform Gigasampler format file access library * * * - * Copyright (C) 2003-2005 by Christian Schoenebeck * + * Copyright (C) 2003-2021 by Christian Schoenebeck * * * * * * This library is free software; you can redistribute it and/or modify * @@ -27,7 +27,7 @@ #include "RIFF.h" #if WORDS_BIGENDIAN -# define LIST_TYPE_INFO 0x494E464F +# define RIFF_TYPE_DLS 0x444C5320 # define LIST_TYPE_WVPL 0x7776706C # define LIST_TYPE_DWPL 0x6477706C ///< Seen on some files instead of a wvpl list chunk. # define LIST_TYPE_WAVE 0x77617665 @@ -38,22 +38,13 @@ # define LIST_TYPE_LAR2 0x6C617232 # define LIST_TYPE_RGN 0x72676E20 # define LIST_TYPE_RGN2 0x72676E32 -# define LIST_TYPE_ART1 0x61727431 -# define LIST_TYPE_ART2 0x61727432 # define CHUNK_ID_IARL 0x4941524C # define CHUNK_ID_IART 0x49415254 # define CHUNK_ID_ICMS 0x49434D53 -# define CHUNK_ID_ICMT 0x49434D54 -# define CHUNK_ID_ICOP 0x49434F50 -# define CHUNK_ID_ICRD 0x49435244 -# define CHUNK_ID_IENG 0x49454E47 # define CHUNK_ID_IGNR 0x49474E52 # define CHUNK_ID_IKEY 0x494B4559 # define CHUNK_ID_IMED 0x494D4544 -# define CHUNK_ID_INAM 0x494E414D -# define CHUNK_ID_IPRD 0x49505244 # define CHUNK_ID_ISBJ 0x4953424A -# define CHUNK_ID_ISFT 0x49534654 # define CHUNK_ID_ISRC 0x49535243 # define CHUNK_ID_ISRF 0x49535246 # define CHUNK_ID_ITCH 0x49544348 @@ -67,8 +58,12 @@ # define CHUNK_ID_PTBL 0x7074626C # define CHUNK_ID_WSMP 0x77736D70 # define CHUNK_ID_COLH 0x636F6C68 +# define CHUNK_ID_ARTL 0x6172746C +# define CHUNK_ID_ART2 0x61727432 +# define CHUNK_ID_XFIL 0x7866696C +# define CHUNK_ID_DOXF 0x646F7866 #else // little endian -# define LIST_TYPE_INFO 0x4F464E49 +# define RIFF_TYPE_DLS 0x20534C44 # define LIST_TYPE_WVPL 0x6C707677 # define LIST_TYPE_DWPL 0x6C707764 ///< Seen on some files instead of a wvpl list chunk. # define LIST_TYPE_WAVE 0x65766177 @@ -79,22 +74,13 @@ # define LIST_TYPE_LAR2 0x3272616C # define LIST_TYPE_RGN 0x206E6772 # define LIST_TYPE_RGN2 0x326E6772 -# define LIST_TYPE_ART1 0x31747261 -# define LIST_TYPE_ART2 0x32747261 # define CHUNK_ID_IARL 0x4C524149 # define CHUNK_ID_IART 0x54524149 # define CHUNK_ID_ICMS 0x534D4349 -# define CHUNK_ID_ICMT 0x544D4349 -# define CHUNK_ID_ICOP 0x504F4349 -# define CHUNK_ID_ICRD 0x44524349 -# define CHUNK_ID_IENG 0x474E4549 # define CHUNK_ID_IGNR 0x524E4749 # define CHUNK_ID_IKEY 0x59454B49 -# define CHUNK_ID_IMED 0x44525049 -# define CHUNK_ID_INAM 0x4D414E49 -# define CHUNK_ID_IPRD 0x44525049 +# define CHUNK_ID_IMED 0x44454D49 # define CHUNK_ID_ISBJ 0x4A425349 -# define CHUNK_ID_ISFT 0x54465349 # define CHUNK_ID_ISRC 0x43525349 # define CHUNK_ID_ISRF 0x46525349 # define CHUNK_ID_ITCH 0x48435449 @@ -108,31 +94,13 @@ # define CHUNK_ID_PTBL 0x6C627470 # define CHUNK_ID_WSMP 0x706D7377 # define CHUNK_ID_COLH 0x686C6F63 +# define CHUNK_ID_ARTL 0x6C747261 +# define CHUNK_ID_ART2 0x32747261 +# define CHUNK_ID_XFIL 0x6C696678 +# define CHUNK_ID_DOXF 0x66786F64 #endif // WORDS_BIGENDIAN -#define WAVE_FORMAT_PCM 0x0001 - -#define DRUM_TYPE_MASK 0x00000001 - -#define F_RGN_OPTION_SELFNONEXCLUSIVE 0x0001 - -#define F_WAVELINK_PHASE_MASTER 0x0001 -#define F_WAVELINK_MULTICHANNEL 0x0002 - -#define F_WSMP_NO_TRUNCATION 0x0001 -#define F_WSMP_NO_COMPRESSION 0x0002 - -#define MIDI_BANK_COARSE(x) ((x & 0x00007F00) >> 8) // CC0 -#define MIDI_BANK_FINE(x) (x & 0x0000007F) // CC32 -#define MIDI_BANK_MERGE(coarse, fine) ((((uint16_t) coarse) << 7) | fine) // CC0 + CC32 -#define CONN_TRANSFORM_SRC(x) ((x >> 10) & 0x000F) -#define CONN_TRANSFORM_CTL(x) ((x >> 4) & 0x000F) -#define CONN_TRANSFORM_DST(x) (x & 0x000F) -#define CONN_TRANSFORM_BIPOLAR_SRC(x) (x & 0x4000) -#define CONN_TRANSFORM_BIPOLAR_CTL(x) (x & 0x0100) -#define CONN_TRANSFORM_INVERT_SRC(x) (x & 0x8000) -#define CONN_TRANSFORM_INVERT_CTL(x) (x & 0x0200) - +#define DLS_WAVE_FORMAT_PCM 0x0001 //TODO: no support for conditional chunks yet @@ -140,6 +108,8 @@ namespace DLS { typedef std::string String; + typedef RIFF::progress_t progress_t; + typedef RIFF::file_offset_t file_offset_t; /** Quadtuple version number ("major.minor.release.build"). */ struct version_t { @@ -158,7 +128,7 @@ }; /** Connection Sources */ - typedef enum { + enum conn_src_t { // Modulator Sources conn_src_none = 0x0000, conn_src_lfo = 0x0001, @@ -181,10 +151,10 @@ conn_src_rpn0 = 0x0100, conn_src_rpn1 = 0x0101, conn_src_rpn2 = 0x0102 - } conn_src_t; + }; /** Connection Destinations */ - typedef enum { + enum conn_dst_t { // Generic Destinations conn_dst_none = 0x0000, conn_dst_gain = 0x0001, @@ -226,28 +196,47 @@ // Filter Destinations conn_dst_filter_cutoff = 0x0500, conn_dst_filter_q = 0x0501 - } conn_dst_t; + }; /** Connection Transforms */ - typedef enum { + enum conn_trn_t { conn_trn_none = 0x0000, conn_trn_concave = 0x0001, conn_trn_convex = 0x0002, conn_trn_switch = 0x0003 - } conn_trn_t; + }; /** Lower and upper limit of a range. */ struct range_t { uint16_t low; ///< Low value of range. uint16_t high; ///< High value of range. + + inline bool operator< (const range_t& other) const { + if (low < other.low) return true; + if (low > other.low) return false; + return high < other.high; + } + + inline bool operator== (const range_t& other) const { + return low == other.low && high == other.high; + } + + inline bool overlaps(uint16_t scalar) const { + return low <= scalar && scalar <= high; + } + + inline bool overlaps(const range_t& other) const { + return overlaps(other.low) || overlaps(other.high) || + other.overlaps(low) || other.overlaps(high); + } }; /** Defines Sample Loop Points. */ struct sample_loop_t { - uint32_t Size; - uint32_t LoopType; - uint32_t LoopStart; - uint32_t LoopLength; + uint32_t Size; ///< For internal usage only: usually reflects exactly @c sizeof(sample_loop_t), otherwise if the value is larger then the DLS format was extended! + uint32_t LoopType; ///< Defines how the waveform samples will be looped (appropriate loop types for the gig format are defined by gig::loop_type_t). + uint32_t LoopStart; ///< The start value specifies the offset (in sample points) in the waveform data of the first sample point to be played in the loop. + uint32_t LoopLength; ///< Length of the looping area (in sample points). }; // just symbol prototyping @@ -278,29 +267,90 @@ uint16_t transform; uint32_t scale; }; - Connection() {}; + Connection() {} void Init(conn_block_t* Header); - virtual ~Connection() {}; + conn_block_t ToConnBlock(); + virtual ~Connection() {} friend class Articulation; }; + /** @brief Abstract base class for all classes using RIFF::Chunks for persistency. + * + * This abstract base class defines the general interface for all classes + * which are using RIFF::Chunks to actually load and store their data + * persistently from/to disk. + */ + class Storage { + public: + /** @brief Apply object's changes to the respective RIF::Chunks. + * + * This abstract interface method is intended to be implemented by the + * deriving classes by updating the respective RIFF chunks associated + * with the object such that those RIFF chunks reflect the object's + * current data (i.e. object's current member variables). So the purpose + * of this method is to prepare for saving the object's current state + * persistently to the actual RIFF file. + * + * After returning from this method the changes are just scheduled to be + * saved to the RIFF file, it is required to call File::Save() + * subsequently to make the changes actually persistent on file level. + * + * Usually there is no need to call this method directly from an + * application. This method is called automatically by libgig if one of + * the respective API methods is called to save the file persistently + * to disk (i.e. DLS::File::Save() or gig::File::Save()). + * + * @param pProgress - callback function for progress notification + */ + virtual void UpdateChunks(progress_t* pProgress) = 0; + + /** @brief Remove all RIFF chunks associated with this object. + * + * This abstract interface method is intended to be implemented by the + * deriving classes by removing every RIFF::Chunk the deriving overall + * object is using to store the object in the final RIFF::File. In other + * words: the intention is to remove the deriving class(es)'s object + * persistently from the currently open file. + * + * Note that the RIFF::Chunks deletions is just scheduled after + * returning from this method. You have to call File::Save() to make + * these changes persistent on file level. + * + * Usually there is no need to call this method directly from an + * application. This method is called automatically by libgig if one of + * the respective API methods is called to remove the respective object + * persistently (i.e. File::DeleteInstrument() or File::DeleteSample()). + */ + virtual void DeleteChunks() = 0; + }; + /** Provides access to the defined connections used for the synthesis model. */ - class Articulation { + class Articulation : public Storage { public: Connection* pConnections; ///< Points to the beginning of a Connection array. uint32_t Connections; ///< Reflects the number of Connections. - Articulation(RIFF::List* artList); + + Articulation(RIFF::Chunk* artl); virtual ~Articulation(); + virtual void UpdateChunks(progress_t* pProgress) OVERRIDE; + virtual void DeleteChunks() OVERRIDE; + protected: + RIFF::Chunk* pArticulationCk; + uint32_t HeaderSize; }; /** Abstract base class for classes that provide articulation information (thus for Instrument and Region class). */ - class Articulator { + class Articulator : public Storage { public: Articulator(RIFF::List* ParentList); - Articulation* GetFirstArticulation(); - Articulation* GetNextArticulation(); + Articulation* GetArticulation(size_t pos); + Articulation* GetFirstArticulation() LIBGIG_DEPRECATED_API("Use GetArticulation() instead."); + Articulation* GetNextArticulation() LIBGIG_DEPRECATED_API("Use GetArticulation() instead."); + virtual void UpdateChunks(progress_t* pProgress) OVERRIDE; + virtual void DeleteChunks() OVERRIDE; + virtual void CopyAssign(const Articulator* orig); protected: - typedef std::list ArticulationList; + typedef std::vector ArticulationList; RIFF::List* pParentList; ArticulationList* pArticulations; ArticulationList::iterator ArticulationsIterator; @@ -310,7 +360,7 @@ }; /** Optional information for DLS files, instruments, samples, etc. */ - class Info { + class Info : public Storage { public: String Name; ///< . Stores the title of the subject of the file, such as, Seattle From Above. String ArchivalLocation; ///< . Indicates where the subject of the file is stored. @@ -328,71 +378,113 @@ String Source; ///< . Identifies the name of the person or organization who supplied the original subject of the file. String SourceForm; ///< . Identifies the original form of the material that was digitized, such as record, sampling CD, TV sound track. This is not neccessarily the same as Medium. String Commissioned; ///< . Lists the name of the person or organization that commissioned the subject of the file, e.g., Pope Julian II. + String Subject; ///< . Describes the contents of the file. + bool UseFixedLengthStrings; ///< @deprecated Not used anymore, use SetFixedStringLengths() instead. + + struct string_length_t { + uint32_t chunkId; + int length; + }; Info(RIFF::List* list); + void SetFixedStringLengths(const string_length_t* lengths); + virtual ~Info(); + virtual void UpdateChunks(progress_t* pProgress) OVERRIDE; + virtual void DeleteChunks() OVERRIDE; + virtual void CopyAssign(const Info* orig); private: - inline void LoadString(uint32_t ChunkID, RIFF::List* lstINFO, String& s) { - RIFF::Chunk* ck = lstINFO->GetSubChunk(ChunkID); - if (ck) { - // TODO: no check for ZSTR terminated strings yet - s = (char*) ck->LoadChunkData(); - ck->ReleaseChunkData(); - } - } + RIFF::List* pResourceListChunk; + const string_length_t* pFixedStringLengths; ///< List of IDs and string lengths for strings that should be stored in a fixed length format. This is used for gig files, not for ordinary DLS files. + + static void LoadString(uint32_t ChunkID, RIFF::List* lstINFO, String& s); + void SaveString(uint32_t ChunkID, RIFF::List* lstINFO, const String& s, const String& sDefault); }; /** Abstract base class which encapsulates data structures which all DLS resources are able to provide. */ - class Resource { + class Resource : public Storage { public: - Info* pInfo; ///< Points (in any case) to an Info object, providing additional, optional infos and comments. - dlsid_t* pDLSID; ///< Points to a dlsid_t structure if the file provided a DLS ID else is NULL. + Info* pInfo; ///< Points (in any case) to an Info object, providing additional, optional infos and comments. + dlsid_t* pDLSID; ///< Points to a dlsid_t structure if the file provided a DLS ID else is NULL. - Resource* GetParent() { return pParent; }; + Resource* GetParent() { return pParent; } + const Resource* GetParent() const { return pParent; } + virtual void UpdateChunks(progress_t* pProgress) OVERRIDE; + virtual void DeleteChunks() OVERRIDE; + void GenerateDLSID(); + static void GenerateDLSID(dlsid_t* pDLSID); + virtual void CopyAssign(const Resource* orig); protected: Resource* pParent; + RIFF::List* pResourceList; Resource(Resource* Parent, RIFF::List* lstResource); virtual ~Resource(); }; /** Abstract base class which provides mandatory informations about sample players in general. */ - class Sampler { + class Sampler : public Storage { public: uint8_t UnityNote; int16_t FineTune; - int32_t Gain; + int32_t Gain; ///< @deprecated Don't alter directly, use SetGain() instead! bool NoSampleDepthTruncation; bool NoSampleCompression; - uint32_t SampleLoops; ///< Reflects the number of sample loops. - sample_loop_t* pSampleLoops; ///< Points to the beginning of a sample loop array, or is NULL if there are no loops defined. + uint32_t SampleLoops; ///< Reflects the number of sample loops. + sample_loop_t* pSampleLoops; ///< Points to the beginning of a sample loop array, or is NULL if there are no loops defined. + + void AddSampleLoop(sample_loop_t* pLoopDef); + void DeleteSampleLoop(sample_loop_t* pLoopDef); + virtual void SetGain(int32_t gain); + virtual void UpdateChunks(progress_t* pProgress) OVERRIDE; + virtual void DeleteChunks() OVERRIDE; + virtual void CopyAssign(const Sampler* orig); protected: + RIFF::List* pParentList; + uint32_t uiHeaderSize; uint32_t SamplerOptions; Sampler(RIFF::List* ParentList); virtual ~Sampler(); }; - /** Encapsulates sample waves used for playback. */ + /** @brief Encapsulates sample waves used for playback. + * + * In case you created a new sample with File::AddSample(), you should + * first update all attributes with the desired meta informations + * (amount of channels, bit depth, sample rate, etc.), then call + * Resize() with the desired sample size. The latter will create + * the mandatory RIFF chunk which will hold the sample wave data. + */ class Sample : public Resource { public: - uint16_t FormatTag; ///< Format ID of the waveform data (should be WAVE_FORMAT_PCM for DLS1 compliant files). - uint16_t Channels; ///< Number of channels represented in the waveform data, e.g. 1 for mono, 2 for stereo (). - uint32_t SamplesPerSecond; ///< Sampling rate at which each channel should be played. + uint16_t FormatTag; ///< Format ID of the waveform data (should be DLS_WAVE_FORMAT_PCM for DLS1 compliant files, this is also the default value if Sample was created with Instrument::AddSample()). + uint16_t Channels; ///< Number of channels represented in the waveform data, e.g. 1 for mono, 2 for stereo (defaults to 1=mono if Sample was created with Instrument::AddSample() previously). + uint32_t SamplesPerSecond; ///< Sampling rate at which each channel should be played (defaults to 44100 if Sample was created with Instrument::AddSample() previously). uint32_t AverageBytesPerSecond; ///< The average number of bytes per second at which the waveform data should be transferred (Playback software can estimate the buffer size using this value). uint16_t BlockAlign; ///< The block alignment (in bytes) of the waveform data. Playback software needs to process a multiple of BlockAlign bytes of data at a time, so the value of BlockAlign can be used for buffer alignment. uint16_t BitDepth; ///< Size of each sample per channel (only if known sample data format is used, 0 otherwise). - unsigned long SamplesTotal; ///< Reflects total number of samples (only if known sample data format is used, 0 otherwise). - uint FrameSize; ///< Reflects the size (in bytes) of one single sample (only if known sample data format is used, 0 otherwise). + file_offset_t SamplesTotal; ///< Reflects total number of sample points (only if known sample data format is used, 0 otherwise), do not bother to change this value, it will not be saved. + uint FrameSize; ///< Reflects the size (in bytes) of one single sample point (only if known sample data format is used, 0 otherwise). Caution: with the current version of libgig you have to upate this field by yourself whenever you change one of the following fields: Channels, BitDepth ! Ignoring this might lead to undesired behavior when i.e. calling Resize(), SetPos(), Write() or Read(). + + void* LoadSampleData(); + void ReleaseSampleData(); + file_offset_t GetSize() const; + void Resize(file_offset_t NewSize); + file_offset_t SetPos(file_offset_t SampleCount, RIFF::stream_whence_t Whence = RIFF::stream_start); + file_offset_t Read(void* pBuffer, file_offset_t SampleCount); + file_offset_t Write(void* pBuffer, file_offset_t SampleCount); + virtual void UpdateChunks(progress_t* pProgress) OVERRIDE; + virtual void DeleteChunks() OVERRIDE; + virtual void CopyAssign(const Sample* orig); - void* LoadSampleData(); ///< Load sample data into RAM. Returns a pointer to the data in RAM on success, NULL otherwise. - void ReleaseSampleData(); ///< Release the samples once you used them if you don't want to be bothered to. - unsigned long SetPos(unsigned long SampleCount, RIFF::stream_whence_t Whence = RIFF::stream_start); - unsigned long Read(void* pBuffer, unsigned long SampleCount); protected: + RIFF::List* pWaveList; RIFF::Chunk* pCkData; RIFF::Chunk* pCkFormat; - unsigned long ulWavePoolOffset; // needed for comparison with the wave pool link table, thus the link to instruments + file_offset_t ullWavePoolOffset; // needed for comparison with the wave pool link table, thus the link to instruments - Sample(File* pFile, RIFF::List* waveList, unsigned long WavePoolOffset); + Sample(File* pFile, RIFF::List* waveList, file_offset_t WavePoolOffset); + virtual ~Sample(); + void CopyAssignCore(const Sample* orig); friend class File; friend class Region; // Region has to compare the wave pool offset to get its sample }; @@ -400,7 +492,7 @@ /** Defines Region information of an Instrument. */ class Region : public Resource, public Articulator, public Sampler { public: - range_t KeyRange; + range_t KeyRange; ///< @deprecated Only read, don't write! Use SetKeyRange() instead. range_t VelocityRange; uint16_t KeyGroup; uint16_t Layer; @@ -411,10 +503,17 @@ uint32_t Channel; Sample* GetSample(); + void SetSample(Sample* pSample); + virtual void SetKeyRange(uint16_t Low, uint16_t High); + virtual void UpdateChunks(progress_t* pProgress) OVERRIDE; + virtual void DeleteChunks() OVERRIDE; + virtual void CopyAssign(const Region* orig); protected: RIFF::List* pCkRegion; uint32_t WavePoolTableIndex; // index in the wave pool table to the sample wave this region is linked to Sample* pSample; // every region refers to exactly one sample + uint16_t FormatOptionFlags; + uint16_t WaveLinkOptionFlags; Region(Instrument* pInstrument, RIFF::List* rgnList); virtual ~Region(); @@ -425,16 +524,22 @@ class Instrument : public Resource, public Articulator { public: bool IsDrum; ///< Indicates if the Instrument is a drum type, as they differ in the synthesis model of DLS from melodic instruments. - uint16_t MIDIBank; ///< Reflects combination of MIDIBankCoarse and MIDIBankFine (bank 1 - bank 16384). + uint16_t MIDIBank; ///< Reflects combination of MIDIBankCoarse and MIDIBankFine (bank 1 - bank 16384). Do not change this value, it will not be saved! Change MIDIBankCoarse and MIDIBankFine instead (we might change that in future). uint8_t MIDIBankCoarse; ///< Reflects the MIDI Bank number for MIDI Control Change 0 (bank 1 - 128). uint8_t MIDIBankFine; ///< Reflects the MIDI Bank number for MIDI Control Change 32 (bank 1 - 128). uint32_t MIDIProgram; ///< Specifies the MIDI Program Change Number this Instrument should be assigned to. uint32_t Regions; ///< Reflects the number of Region defintions this Instrument has. - Region* GetFirstRegion(); - Region* GetNextRegion(); + Region* GetRegionAt(size_t pos); + Region* GetFirstRegion() LIBGIG_DEPRECATED_API("Use GetRegionAt() instead."); + Region* GetNextRegion() LIBGIG_DEPRECATED_API("Use GetRegionAt() instead."); + Region* AddRegion(); + void DeleteRegion(Region* pRegion); + virtual void UpdateChunks(progress_t* pProgress) OVERRIDE; + virtual void DeleteChunks() OVERRIDE; + virtual void CopyAssign(const Instrument* orig); protected: - typedef std::list RegionList; + typedef std::vector RegionList; struct midi_locale_t { uint32_t bank; uint32_t instrument; @@ -445,9 +550,13 @@ RegionList::iterator RegionsIterator; Instrument(File* pFile, RIFF::List* insList); - void LoadRegions(); + void CopyAssignCore(const Instrument* orig); + virtual void LoadRegions(); virtual ~Instrument(); friend class File; + friend class Region; + private: + void MoveRegion(Region* pSrc, Region* pDst); }; /** Parses DLS Level 1 and 2 compliant files and provides abstract access to the data. */ @@ -456,35 +565,67 @@ version_t* pVersion; ///< Points to a version_t structure if the file provided a version number else is set to NULL. uint32_t Instruments; ///< Reflects the number of available Instrument objects. + File(); File(RIFF::File* pRIFF); - Sample* GetFirstSample(); ///< Returns a pointer to the first Sample object of the file, NULL otherwise. - Sample* GetNextSample(); ///< Returns a pointer to the next Sample object of the file, NULL otherwise. - Instrument* GetFirstInstrument(); ///< Returns a pointer to the first Instrument object of the file, NULL otherwise. - Instrument* GetNextInstrument(); ///< Returns a pointer to the next Instrument object of the file, NULL otherwise. + String GetFileName(); + void SetFileName(const String& name); + Sample* GetSample(size_t index); + Sample* GetFirstSample() LIBGIG_DEPRECATED_API("Use GetSample() instead."); + Sample* GetNextSample() LIBGIG_DEPRECATED_API("Use GetSample() instead."); + Sample* AddSample(); + void DeleteSample(Sample* pSample); + Instrument* GetInstrument(size_t index); + Instrument* GetFirstInstrument() LIBGIG_DEPRECATED_API("Use GetInstrument() instead."); + Instrument* GetNextInstrument() LIBGIG_DEPRECATED_API("Use GetInstrument() instead."); + Instrument* AddInstrument(); + void DeleteInstrument(Instrument* pInstrument); + RIFF::File* GetRiffFile(); + RIFF::File* GetExtensionFile(int index); + virtual void UpdateChunks(progress_t* pProgress) OVERRIDE; + virtual void Save(const String& Path, progress_t* pProgress = NULL); + virtual void Save(progress_t* pProgress = NULL); virtual ~File(); protected: - typedef std::list SampleList; - typedef std::list InstrumentList; + typedef std::vector SampleList; + typedef std::vector InstrumentList; RIFF::File* pRIFF; + std::list ExtensionFiles; //FIXME: These should automatically be freed, since implicitly allocated. SampleList* pSamples; SampleList::iterator SamplesIterator; InstrumentList* pInstruments; InstrumentList::iterator InstrumentsIterator; + uint32_t WavePoolHeaderSize; uint32_t WavePoolCount; uint32_t* pWavePoolTable; uint32_t* pWavePoolTableHi; + bool b64BitWavePoolOffsets; + bool bOwningRiff; ///< If @c true then @c pRIFF was implicitly allocated by this class and hence pRIFF will automatically be freed by the @c DLS::File destructor in that case. - void LoadSamples(); - void LoadInstruments(); + virtual void LoadSamples(); + virtual void LoadInstruments(); + virtual void UpdateFileOffsets(); + void __ensureMandatoryChunksExist(); friend class Region; // Region has to look in the wave pool table to get its sample + private: + void __UpdateWavePoolTableChunk(); + void __UpdateWavePoolTable(); }; - /** Will be thrown whenever a DLS specific error occurs while trying to access a DLS File. */ + /** + * Will be thrown whenever a DLS specific error occurs while trying to + * access a DLS File. Note: In your application you should better catch + * for RIFF::Exception rather than this one, except you explicitly want + * to catch and handle DLS::Exception and RIFF::Exception independently, + * which usually shouldn't be necessary though. + */ class Exception : public RIFF::Exception { public: - Exception(String Message); + Exception(String format, ...); + Exception(String format, va_list arg); void PrintMessage(); + protected: + Exception(); }; String libraryName();