/[svn]/libgig/trunk/src/DLS.cpp
ViewVC logotype

Diff of /libgig/trunk/src/DLS.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 666 by persson, Sun Jun 19 15:18:59 2005 UTC revision 928 by persson, Tue Oct 24 19:32:47 2006 UTC
# Line 23  Line 23 
23    
24  #include "DLS.h"  #include "DLS.h"
25    
26    #include <time.h>
27    
28    #include "helper.h"
29    
30    // macros to decode connection transforms
31    #define CONN_TRANSFORM_SRC(x)                   ((x >> 10) & 0x000F)
32    #define CONN_TRANSFORM_CTL(x)                   ((x >> 4) & 0x000F)
33    #define CONN_TRANSFORM_DST(x)                   (x & 0x000F)
34    #define CONN_TRANSFORM_BIPOLAR_SRC(x)   (x & 0x4000)
35    #define CONN_TRANSFORM_BIPOLAR_CTL(x)   (x & 0x0100)
36    #define CONN_TRANSFORM_INVERT_SRC(x)    (x & 0x8000)
37    #define CONN_TRANSFORM_INVERT_CTL(x)    (x & 0x0200)
38    
39    // macros to encode connection transforms
40    #define CONN_TRANSFORM_SRC_ENCODE(x)                    ((x & 0x000F) << 10)
41    #define CONN_TRANSFORM_CTL_ENCODE(x)                    ((x & 0x000F) << 4)
42    #define CONN_TRANSFORM_DST_ENCODE(x)                    (x & 0x000F)
43    #define CONN_TRANSFORM_BIPOLAR_SRC_ENCODE(x)    ((x) ? 0x4000 : 0)
44    #define CONN_TRANSFORM_BIPOLAR_CTL_ENCODE(x)    ((x) ? 0x0100 : 0)
45    #define CONN_TRANSFORM_INVERT_SRC_ENCODE(x)             ((x) ? 0x8000 : 0)
46    #define CONN_TRANSFORM_INVERT_CTL_ENCODE(x)             ((x) ? 0x0200 : 0)
47    
48    #define DRUM_TYPE_MASK                  0x80000000
49    
50    #define F_RGN_OPTION_SELFNONEXCLUSIVE   0x0001
51    
52    #define F_WAVELINK_PHASE_MASTER         0x0001
53    #define F_WAVELINK_MULTICHANNEL         0x0002
54    
55    #define F_WSMP_NO_TRUNCATION            0x0001
56    #define F_WSMP_NO_COMPRESSION           0x0002
57    
58    #define MIDI_BANK_COARSE(x)             ((x & 0x00007F00) >> 8)                 // CC0
59    #define MIDI_BANK_FINE(x)               (x & 0x0000007F)                        // CC32
60    #define MIDI_BANK_MERGE(coarse, fine)   ((((uint16_t) coarse) << 7) | fine)     // CC0 + CC32
61    #define MIDI_BANK_ENCODE(coarse, fine)  (((coarse & 0x0000007F) << 8) | (fine & 0x0000007F))
62    
63  namespace DLS {  namespace DLS {
64    
65  // *************** Connection  ***************  // *************** Connection  ***************
# Line 42  namespace DLS { Line 79  namespace DLS {
79          ControlBipolar       = CONN_TRANSFORM_BIPOLAR_CTL(Header->transform);          ControlBipolar       = CONN_TRANSFORM_BIPOLAR_CTL(Header->transform);
80      }      }
81    
82        Connection::conn_block_t Connection::ToConnBlock() {
83            conn_block_t c;
84            c.source = Source;
85            c.control = Control;
86            c.destination = Destination;
87            c.scale = Scale;
88            c.transform = CONN_TRANSFORM_SRC_ENCODE(SourceTransform) |
89                          CONN_TRANSFORM_CTL_ENCODE(ControlTransform) |
90                          CONN_TRANSFORM_DST_ENCODE(DestinationTransform) |
91                          CONN_TRANSFORM_INVERT_SRC_ENCODE(SourceInvert) |
92                          CONN_TRANSFORM_BIPOLAR_SRC_ENCODE(SourceBipolar) |
93                          CONN_TRANSFORM_INVERT_CTL_ENCODE(ControlInvert) |
94                          CONN_TRANSFORM_BIPOLAR_CTL_ENCODE(ControlBipolar);
95            return c;
96        }
97    
98    
99    
100  // *************** Articulation  ***************  // *************** Articulation  ***************
101  // *  // *
102    
103      Articulation::Articulation(RIFF::List* artList) {      /** @brief Constructor.
104          if (artList->GetListType() != LIST_TYPE_ART2 &&       *
105              artList->GetListType() != LIST_TYPE_ART1) {       * Expects an 'artl' or 'art2' chunk to be given where the articulation
106                throw DLS::Exception("<art1-list> or <art2-list> chunk expected");       * connections will be read from.
107          }       *
108          uint32_t headerSize = artList->ReadUint32();       * @param artl - pointer to an 'artl' or 'art2' chunk
109          Connections         = artList->ReadUint32();       * @throws Exception if no 'artl' or 'art2' chunk was given
110          artList->SetPos(headerSize);       */
111        Articulation::Articulation(RIFF::Chunk* artl) {
112            pArticulationCk = artl;
113            if (artl->GetChunkID() != CHUNK_ID_ART2 &&
114                artl->GetChunkID() != CHUNK_ID_ARTL) {
115                  throw DLS::Exception("<artl-ck> or <art2-ck> chunk expected");
116            }
117            HeaderSize  = artl->ReadUint32();
118            Connections = artl->ReadUint32();
119            artl->SetPos(HeaderSize);
120    
121          pConnections = new Connection[Connections];          pConnections = new Connection[Connections];
122          Connection::conn_block_t connblock;          Connection::conn_block_t connblock;
123          for (uint32_t i = 0; i <= Connections; i++) {          for (uint32_t i = 0; i < Connections; i++) {
124              artList->Read(&connblock.source, 1, 2);              artl->Read(&connblock.source, 1, 2);
125              artList->Read(&connblock.control, 1, 2);              artl->Read(&connblock.control, 1, 2);
126              artList->Read(&connblock.destination, 1, 2);              artl->Read(&connblock.destination, 1, 2);
127              artList->Read(&connblock.transform, 1, 2);              artl->Read(&connblock.transform, 1, 2);
128              artList->Read(&connblock.scale, 1, 4);              artl->Read(&connblock.scale, 1, 4);
129              pConnections[i].Init(&connblock);              pConnections[i].Init(&connblock);
130          }          }
131      }      }
# Line 72  namespace DLS { Line 134  namespace DLS {
134         if (pConnections) delete[] pConnections;         if (pConnections) delete[] pConnections;
135      }      }
136    
137        /**
138         * Apply articulation connections to the respective RIFF chunks. You
139         * have to call File::Save() to make changes persistent.
140         */
141        void Articulation::UpdateChunks() {
142            const int iEntrySize = 12; // 12 bytes per connection block
143            pArticulationCk->Resize(HeaderSize + Connections * iEntrySize);
144            uint8_t* pData = (uint8_t*) pArticulationCk->LoadChunkData();
145            memcpy(&pData[0], &HeaderSize, 2);
146            memcpy(&pData[2], &Connections, 2);
147            for (uint32_t i = 0; i < Connections; i++) {
148                Connection::conn_block_t c = pConnections[i].ToConnBlock();
149                memcpy(&pData[HeaderSize + i * iEntrySize],     &c.source, 2);
150                memcpy(&pData[HeaderSize + i * iEntrySize + 2], &c.control, 2);
151                memcpy(&pData[HeaderSize + i * iEntrySize + 4], &c.destination, 2);
152                memcpy(&pData[HeaderSize + i * iEntrySize + 6], &c.transform, 2);
153                memcpy(&pData[HeaderSize + i * iEntrySize + 8], &c.scale, 4);
154            }
155        }
156    
157    
158    
159  // *************** Articulator  ***************  // *************** Articulator  ***************
# Line 100  namespace DLS { Line 182  namespace DLS {
182          RIFF::List* lart = pParentList->GetSubList(LIST_TYPE_LAR2);          RIFF::List* lart = pParentList->GetSubList(LIST_TYPE_LAR2);
183          if (!lart)  lart = pParentList->GetSubList(LIST_TYPE_LART);          if (!lart)  lart = pParentList->GetSubList(LIST_TYPE_LART);
184          if (lart) {          if (lart) {
185              uint32_t artCkType = (lart->GetListType() == LIST_TYPE_LAR2) ? LIST_TYPE_ART2              uint32_t artCkType = (lart->GetListType() == LIST_TYPE_LAR2) ? CHUNK_ID_ART2
186                                                                           : LIST_TYPE_ART1;                                                                           : CHUNK_ID_ARTL;
187              RIFF::List* art = lart->GetFirstSubList();              RIFF::Chunk* art = lart->GetFirstSubChunk();
188              while (art) {              while (art) {
189                  if (art->GetListType() == artCkType) {                  if (art->GetChunkID() == artCkType) {
190                      if (!pArticulations) pArticulations = new ArticulationList;                      if (!pArticulations) pArticulations = new ArticulationList;
191                      pArticulations->push_back(new Articulation(art));                      pArticulations->push_back(new Articulation(art));
192                  }                  }
193                  art = lart->GetNextSubList();                  art = lart->GetNextSubChunk();
194              }              }
195          }          }
196      }      }
# Line 125  namespace DLS { Line 207  namespace DLS {
207          }          }
208      }      }
209    
210        /**
211         * Apply all articulations to the respective RIFF chunks. You have to
212         * call File::Save() to make changes persistent.
213         */
214        void Articulator::UpdateChunks() {
215            if (pArticulations) {
216                ArticulationList::iterator iter = pArticulations->begin();
217                ArticulationList::iterator end  = pArticulations->end();
218                for (; iter != end; ++iter) {
219                    (*iter)->UpdateChunks();
220                }
221            }
222        }
223    
224    
225    
226  // *************** Info  ***************  // *************** Info  ***************
227  // *  // *
228    
229        /** @brief Constructor.
230         *
231         * Initializes the info strings with values provided by a INFO list chunk.
232         *
233         * @param list - pointer to a list chunk which contains a INFO list chunk
234         */
235      Info::Info(RIFF::List* list) {      Info::Info(RIFF::List* list) {
236            UseFixedLengthStrings = false;
237            pResourceListChunk = list;
238          if (list) {          if (list) {
239              RIFF::List* lstINFO = list->GetSubList(LIST_TYPE_INFO);              RIFF::List* lstINFO = list->GetSubList(LIST_TYPE_INFO);
240              if (lstINFO) {              if (lstINFO) {
# Line 150  namespace DLS { Line 254  namespace DLS {
254                  LoadString(CHUNK_ID_ISRC, lstINFO, Source);                  LoadString(CHUNK_ID_ISRC, lstINFO, Source);
255                  LoadString(CHUNK_ID_ISRF, lstINFO, SourceForm);                  LoadString(CHUNK_ID_ISRF, lstINFO, SourceForm);
256                  LoadString(CHUNK_ID_ICMS, lstINFO, Commissioned);                  LoadString(CHUNK_ID_ICMS, lstINFO, Commissioned);
257                    LoadString(CHUNK_ID_ISBJ, lstINFO, Subject);
258              }              }
259          }          }
260      }      }
261    
262        Info::~Info() {
263        }
264    
265        /** @brief Load given INFO field.
266         *
267         * Load INFO field from INFO chunk with chunk ID \a ChunkID from INFO
268         * list chunk \a lstINFO and save value to \a s.
269         */
270        void Info::LoadString(uint32_t ChunkID, RIFF::List* lstINFO, String& s) {
271            RIFF::Chunk* ck = lstINFO->GetSubChunk(ChunkID);
272            if (ck) {
273                const char* str = (char*)ck->LoadChunkData();
274                int size = ck->GetSize();
275                int len;
276                for (len = 0 ; len < size ; len++)
277                    if (str[len] == '\0') break;
278                s.assign(str, len);
279                ck->ReleaseChunkData();
280            }
281        }
282    
283        /** @brief Apply given INFO field to the respective chunk.
284         *
285         * Apply given info value to info chunk with ID \a ChunkID, which is a
286         * subchunk of INFO list chunk \a lstINFO. If the given chunk already
287         * exists, value \a s will be applied. Otherwise if it doesn't exist yet
288         * and either \a s or \a sDefault is not an empty string, such a chunk
289         * will be created and either \a s or \a sDefault will be applied
290         * (depending on which one is not an empty string, if both are not an
291         * empty string \a s will be preferred).
292         *
293         * @param ChunkID  - 32 bit RIFF chunk ID of INFO subchunk
294         * @param lstINFO  - parent (INFO) RIFF list chunk
295         * @param s        - current value of info field
296         * @param sDefault - default value
297         * @param size     - wanted size of the INFO chunk. This is ignored if UseFixedLengthStrings is false.
298         */
299        void Info::SaveString(uint32_t ChunkID, RIFF::List* lstINFO, const String& s, const String& sDefault, int size) {
300            RIFF::Chunk* ck = lstINFO->GetSubChunk(ChunkID);
301            if (ck) { // if chunk exists already, use 's' as value
302                if (!UseFixedLengthStrings) size = s.size() + 1;
303                ck->Resize(size);
304                char* pData = (char*) ck->LoadChunkData();
305                strncpy(pData, s.c_str(), size);
306            } else if (s != "" || sDefault != "") { // create chunk
307                const String& sToSave = (s != "") ? s : sDefault;
308                if (!UseFixedLengthStrings) size = sToSave.size() + 1;
309                ck = lstINFO->AddSubChunk(ChunkID, size);
310                char* pData = (char*) ck->LoadChunkData();
311                strncpy(pData, sToSave.c_str(), size);
312            }
313        }
314    
315        /** @brief Update chunks with current info values.
316         *
317         * Apply current INFO field values to the respective INFO chunks. You
318         * have to call File::Save() to make changes persistent.
319         */
320        void Info::UpdateChunks() {
321            if (!pResourceListChunk) return;
322    
323            // make sure INFO list chunk exists
324            RIFF::List* lstINFO   = pResourceListChunk->GetSubList(LIST_TYPE_INFO);
325    
326            String defaultName = "";
327            String defaultCreationDate = "";
328            String defaultSoftware = "";
329            String defaultComments = "";
330    
331            uint32_t resourceType = pResourceListChunk->GetListType();
332    
333            if (!lstINFO) {
334                lstINFO = pResourceListChunk->AddSubList(LIST_TYPE_INFO);
335    
336                // assemble default values
337                defaultName = "NONAME";
338    
339                if (resourceType == RIFF_TYPE_DLS) {
340                    // get current date
341                    time_t now = time(NULL);
342                    tm* pNowBroken = localtime(&now);
343                    char buf[11];
344                    strftime(buf, 11, "%F", pNowBroken);
345                    defaultCreationDate = buf;
346    
347                    defaultComments = "Created with " + libraryName() + " " + libraryVersion();
348                }
349                if (resourceType == RIFF_TYPE_DLS || resourceType == LIST_TYPE_INS)
350                {
351                    defaultSoftware = libraryName() + " " + libraryVersion();
352                }
353            }
354    
355            // save values
356    
357            // (the string size values are for gig files; they are only
358            // used if UseFixedLengthStrings is set to true)
359            SaveString(CHUNK_ID_INAM, lstINFO, Name, defaultName,
360                       resourceType == RIFF_TYPE_DLS ? 128 : 64);
361            SaveString(CHUNK_ID_IARL, lstINFO, ArchivalLocation, String(""), 256);
362            SaveString(CHUNK_ID_ICRD, lstINFO, CreationDate, defaultCreationDate, 128);
363            SaveString(CHUNK_ID_ICMT, lstINFO, Comments, defaultComments, 1024);
364            SaveString(CHUNK_ID_IPRD, lstINFO, Product, String(""), 128);
365            SaveString(CHUNK_ID_ICOP, lstINFO, Copyright, String(""), 128);
366            SaveString(CHUNK_ID_IART, lstINFO, Artists, String(""), 128);
367            SaveString(CHUNK_ID_IGNR, lstINFO, Genre, String(""), 128);
368            SaveString(CHUNK_ID_IKEY, lstINFO, Keywords, String(""), 128);
369            SaveString(CHUNK_ID_IENG, lstINFO, Engineer, String(""), 128);
370            SaveString(CHUNK_ID_ITCH, lstINFO, Technician, String(""), 128);
371            SaveString(CHUNK_ID_ISFT, lstINFO, Software, defaultSoftware,
372                       resourceType == LIST_TYPE_INS ?
373                       (Software == "" ? defaultSoftware.length() : Software.length()) : 128);
374            SaveString(CHUNK_ID_IMED, lstINFO, Medium, String(""), 128);
375            SaveString(CHUNK_ID_ISRC, lstINFO, Source, String(""), 128);
376            SaveString(CHUNK_ID_ISRF, lstINFO, SourceForm, String(""), 128);
377            SaveString(CHUNK_ID_ICMS, lstINFO, Commissioned, String(""), 128);
378            SaveString(CHUNK_ID_ISBJ, lstINFO, Subject, String(""), 128);
379        }
380    
381    
382    
383  // *************** Resource ***************  // *************** Resource ***************
384  // *  // *
385    
386        /** @brief Constructor.
387         *
388         * Initializes the 'Resource' object with values provided by a given
389         * INFO list chunk and a DLID chunk (the latter optional).
390         *
391         * @param Parent      - pointer to parent 'Resource', NULL if this is
392         *                      the toplevel 'Resource' object
393         * @param lstResource - pointer to an INFO list chunk
394         */
395      Resource::Resource(Resource* Parent, RIFF::List* lstResource) {      Resource::Resource(Resource* Parent, RIFF::List* lstResource) {
396          pParent = Parent;          pParent = Parent;
397            pResourceList = lstResource;
398    
399          pInfo = new Info(lstResource);          pInfo = new Info(lstResource);
400    
# Line 180  namespace DLS { Line 414  namespace DLS {
414          if (pInfo)  delete pInfo;          if (pInfo)  delete pInfo;
415      }      }
416    
417        /** @brief Update chunks with current Resource data.
418         *
419         * Apply Resource data persistently below the previously given resource
420         * list chunk. This will currently only include the INFO data. The DLSID
421         * will not be applied at the moment (yet).
422         *
423         * You have to call File::Save() to make changes persistent.
424         */
425        void Resource::UpdateChunks() {
426            pInfo->UpdateChunks();
427            //TODO: save DLSID
428        }
429    
430    
431    
432  // *************** Sampler ***************  // *************** Sampler ***************
433  // *  // *
434    
435      Sampler::Sampler(RIFF::List* ParentList) {      Sampler::Sampler(RIFF::List* ParentList) {
436            pParentList       = ParentList;
437          RIFF::Chunk* wsmp = ParentList->GetSubChunk(CHUNK_ID_WSMP);          RIFF::Chunk* wsmp = ParentList->GetSubChunk(CHUNK_ID_WSMP);
438          if (!wsmp) throw DLS::Exception("Mandatory <wsmp> chunk not found.");          if (wsmp) {
439          uint32_t headersize = wsmp->ReadUint32();              uiHeaderSize   = wsmp->ReadUint32();
440          UnityNote        = wsmp->ReadUint16();              UnityNote      = wsmp->ReadUint16();
441          FineTune         = wsmp->ReadInt16();              FineTune       = wsmp->ReadInt16();
442          Gain             = wsmp->ReadInt32();              Gain           = wsmp->ReadInt32();
443          SamplerOptions   = wsmp->ReadUint32();              SamplerOptions = wsmp->ReadUint32();
444                SampleLoops    = wsmp->ReadUint32();
445            } else { // 'wsmp' chunk missing
446                uiHeaderSize   = 0;
447                UnityNote      = 64;
448                FineTune       = 0; // +- 0 cents
449                Gain           = 0; // 0 dB
450                SamplerOptions = F_WSMP_NO_COMPRESSION;
451                SampleLoops    = 0;
452            }
453          NoSampleDepthTruncation = SamplerOptions & F_WSMP_NO_TRUNCATION;          NoSampleDepthTruncation = SamplerOptions & F_WSMP_NO_TRUNCATION;
454          NoSampleCompression     = SamplerOptions & F_WSMP_NO_COMPRESSION;          NoSampleCompression     = SamplerOptions & F_WSMP_NO_COMPRESSION;
         SampleLoops             = wsmp->ReadUint32();  
455          pSampleLoops            = (SampleLoops) ? new sample_loop_t[SampleLoops] : NULL;          pSampleLoops            = (SampleLoops) ? new sample_loop_t[SampleLoops] : NULL;
456          wsmp->SetPos(headersize);          if (SampleLoops) {
457          for (uint32_t i = 0; i < SampleLoops; i++) {              wsmp->SetPos(uiHeaderSize);
458              wsmp->Read(pSampleLoops + i, 4, 4);              for (uint32_t i = 0; i < SampleLoops; i++) {
459              if (pSampleLoops[i].Size > sizeof(sample_loop_t)) { // if loop struct was extended                  wsmp->Read(pSampleLoops + i, 4, 4);
460                  wsmp->SetPos(pSampleLoops[i].Size - sizeof(sample_loop_t), RIFF::stream_curpos);                  if (pSampleLoops[i].Size > sizeof(sample_loop_t)) { // if loop struct was extended
461                        wsmp->SetPos(pSampleLoops[i].Size - sizeof(sample_loop_t), RIFF::stream_curpos);
462                    }
463              }              }
464          }          }
465      }      }
# Line 210  namespace DLS { Line 468  namespace DLS {
468          if (pSampleLoops) delete[] pSampleLoops;          if (pSampleLoops) delete[] pSampleLoops;
469      }      }
470    
471        /**
472         * Apply all sample player options to the respective RIFF chunk. You
473         * have to call File::Save() to make changes persistent.
474         */
475        void Sampler::UpdateChunks() {
476            // make sure 'wsmp' chunk exists
477            RIFF::Chunk* wsmp = pParentList->GetSubChunk(CHUNK_ID_WSMP);
478            if (!wsmp) {
479                uiHeaderSize = 20;
480                wsmp = pParentList->AddSubChunk(CHUNK_ID_WSMP, uiHeaderSize + SampleLoops * 16);
481            }
482            uint8_t* pData = (uint8_t*) wsmp->LoadChunkData();
483            // update headers size
484            memcpy(&pData[0], &uiHeaderSize, 4);
485            // update respective sampler options bits
486            SamplerOptions = (NoSampleDepthTruncation) ? SamplerOptions | F_WSMP_NO_TRUNCATION
487                                                       : SamplerOptions & (~F_WSMP_NO_TRUNCATION);
488            SamplerOptions = (NoSampleCompression) ? SamplerOptions | F_WSMP_NO_COMPRESSION
489                                                   : SamplerOptions & (~F_WSMP_NO_COMPRESSION);
490            memcpy(&pData[4], &UnityNote, 2);
491            memcpy(&pData[6], &FineTune, 2);
492            memcpy(&pData[8], &Gain, 4);
493            memcpy(&pData[12], &SamplerOptions, 4);
494            memcpy(&pData[16], &SampleLoops, 4);
495            // update loop definitions
496            for (uint32_t i = 0; i < SampleLoops; i++) {
497                //FIXME: this does not handle extended loop structs correctly
498                memcpy(&pData[uiHeaderSize + i * 16], pSampleLoops + i, 4 * 4);
499            }
500        }
501    
502    
503    
504  // *************** Sample ***************  // *************** Sample ***************
505  // *  // *
506    
507        /** @brief Constructor.
508         *
509         * Load an existing sample or create a new one. A 'wave' list chunk must
510         * be given to this constructor. In case the given 'wave' list chunk
511         * contains a 'fmt' and 'data' chunk, the format and sample data will be
512         * loaded from there, otherwise default values will be used and those
513         * chunks will be created when File::Save() will be called later on.
514         *
515         * @param pFile          - pointer to DLS::File where this sample is
516         *                         located (or will be located)
517         * @param waveList       - pointer to 'wave' list chunk which is (or
518         *                         will be) associated with this sample
519         * @param WavePoolOffset - offset of this sample data from wave pool
520         *                         ('wvpl') list chunk
521         */
522      Sample::Sample(File* pFile, RIFF::List* waveList, unsigned long WavePoolOffset) : Resource(pFile, waveList) {      Sample::Sample(File* pFile, RIFF::List* waveList, unsigned long WavePoolOffset) : Resource(pFile, waveList) {
523            pWaveList = waveList;
524          ulWavePoolOffset = WavePoolOffset - LIST_HEADER_SIZE;          ulWavePoolOffset = WavePoolOffset - LIST_HEADER_SIZE;
525          pCkFormat = waveList->GetSubChunk(CHUNK_ID_FMT);          pCkFormat = waveList->GetSubChunk(CHUNK_ID_FMT);
526          pCkData   = waveList->GetSubChunk(CHUNK_ID_DATA);          pCkData   = waveList->GetSubChunk(CHUNK_ID_DATA);
527          if (!pCkFormat || !pCkData) throw DLS::Exception("Mandatory chunks in wave list not found.");          if (pCkFormat) {
528                // common fields
529                FormatTag              = pCkFormat->ReadUint16();
530                Channels               = pCkFormat->ReadUint16();
531                SamplesPerSecond       = pCkFormat->ReadUint32();
532                AverageBytesPerSecond  = pCkFormat->ReadUint32();
533                BlockAlign             = pCkFormat->ReadUint16();
534                // PCM format specific
535                if (FormatTag == WAVE_FORMAT_PCM) {
536                    BitDepth     = pCkFormat->ReadUint16();
537                    FrameSize    = (BitDepth / 8) * Channels;
538                } else { // unsupported sample data format
539                    BitDepth     = 0;
540                    FrameSize    = 0;
541                }
542            } else { // 'fmt' chunk missing
543                FormatTag              = WAVE_FORMAT_PCM;
544                BitDepth               = 16;
545                Channels               = 1;
546                SamplesPerSecond       = 44100;
547                AverageBytesPerSecond  = (BitDepth / 8) * SamplesPerSecond * Channels;
548                FrameSize              = (BitDepth / 8) * Channels;
549                BlockAlign             = FrameSize;
550            }
551            SamplesTotal = (pCkData) ? (FormatTag == WAVE_FORMAT_PCM) ? pCkData->GetSize() / FrameSize
552                                                                      : 0
553                                     : 0;
554        }
555    
556          // common fields      /** @brief Destructor.
557          FormatTag              = pCkFormat->ReadUint16();       *
558          Channels               = pCkFormat->ReadUint16();       * Removes RIFF chunks associated with this Sample and frees all
559          SamplesPerSecond       = pCkFormat->ReadUint32();       * memory occupied by this sample.
560          AverageBytesPerSecond  = pCkFormat->ReadUint32();       */
561          BlockAlign             = pCkFormat->ReadUint16();      Sample::~Sample() {
562            RIFF::List* pParent = pWaveList->GetParent();
563          // PCM format specific          pParent->DeleteSubChunk(pWaveList);
         if (FormatTag == WAVE_FORMAT_PCM) {  
             BitDepth     = pCkFormat->ReadUint16();  
             FrameSize    = (FormatTag == WAVE_FORMAT_PCM) ? (BitDepth / 8) * Channels  
                                                           : 0;  
             SamplesTotal = (FormatTag == WAVE_FORMAT_PCM) ? pCkData->GetSize() / FrameSize  
                                                           : 0;  
         }  
         else {  
             BitDepth     = 0;  
             FrameSize    = 0;  
             SamplesTotal = 0;  
         }  
564      }      }
565    
566        /** @brief Load sample data into RAM.
567         *
568         * In case the respective 'data' chunk exists, the sample data will be
569         * loaded into RAM (if not done already) and a pointer to the data in
570         * RAM will be returned. If this is a new sample, you have to call
571         * Resize() with the desired sample size to create the mandatory RIFF
572         * chunk for the sample wave data.
573         *
574         * You can call LoadChunkData() again if you previously scheduled to
575         * enlarge the sample data RIFF chunk with a Resize() call. In that case
576         * the buffer will be enlarged to the new, scheduled size and you can
577         * already place the sample wave data to the buffer and finally call
578         * File::Save() to enlarge the sample data's chunk physically and write
579         * the new sample wave data in one rush. This approach is definitely
580         * recommended if you have to enlarge and write new sample data to a lot
581         * of samples.
582         *
583         * <b>Caution:</b> the buffer pointer will be invalidated once
584         * File::Save() was called. You have to call LoadChunkData() again to
585         * get a new, valid pointer whenever File::Save() was called.
586         *
587         * @returns pointer to sample data in RAM, NULL in case respective
588         *          'data' chunk does not exist (yet)
589         * @throws Exception if data buffer could not be enlarged
590         * @see Resize(), File::Save()
591         */
592      void* Sample::LoadSampleData() {      void* Sample::LoadSampleData() {
593          return pCkData->LoadChunkData();          return (pCkData) ? pCkData->LoadChunkData() : NULL;
594      }      }
595    
596        /** @brief Free sample data from RAM.
597         *
598         * In case sample data was previously successfully loaded into RAM with
599         * LoadSampleData(), this method will free the sample data from RAM.
600         */
601      void Sample::ReleaseSampleData() {      void Sample::ReleaseSampleData() {
602          pCkData->ReleaseChunkData();          if (pCkData) pCkData->ReleaseChunkData();
603        }
604    
605        /** @brief Returns sample size.
606         *
607         * Returns the sample wave form's data size (in sample points). This is
608         * actually the current, physical size (converted to sample points) of
609         * the RIFF chunk which encapsulates the sample's wave data. The
610         * returned value is dependant to the current FrameSize value.
611         *
612         * @returns number of sample points or 0 if FormatTag != WAVE_FORMAT_PCM
613         * @see FrameSize, FormatTag
614         */
615        unsigned long Sample::GetSize() {
616            if (FormatTag != WAVE_FORMAT_PCM) return 0;
617            return (pCkData) ? pCkData->GetSize() / FrameSize : 0;
618        }
619    
620        /** @brief Resize sample.
621         *
622         * Resizes the sample's wave form data, that is the actual size of
623         * sample wave data possible to be written for this sample. This call
624         * will return immediately and just schedule the resize operation. You
625         * should call File::Save() to actually perform the resize operation(s)
626         * "physically" to the file. As this can take a while on large files, it
627         * is recommended to call Resize() first on all samples which have to be
628         * resized and finally to call File::Save() to perform all those resize
629         * operations in one rush.
630         *
631         * The actual size (in bytes) is dependant to the current FrameSize
632         * value. You may want to set FrameSize before calling Resize().
633         *
634         * <b>Caution:</b> You cannot directly write to enlarged samples before
635         * calling File::Save() as this might exceed the current sample's
636         * boundary!
637         *
638         * Also note: only WAVE_FORMAT_PCM is currently supported, that is
639         * FormatTag must be WAVE_FORMAT_PCM. Trying to resize samples with
640         * other formats will fail!
641         *
642         * @param iNewSize - new sample wave data size in sample points (must be
643         *                   greater than zero)
644         * @throws Excecption if FormatTag != WAVE_FORMAT_PCM
645         * @throws Exception if \a iNewSize is less than 1
646         * @see File::Save(), FrameSize, FormatTag
647         */
648        void Sample::Resize(int iNewSize) {
649            if (FormatTag != WAVE_FORMAT_PCM) throw Exception("Sample's format is not WAVE_FORMAT_PCM");
650            if (iNewSize < 1) throw Exception("Sample size must be at least one sample point");
651            const int iSizeInBytes = iNewSize * FrameSize;
652            pCkData = pWaveList->GetSubChunk(CHUNK_ID_DATA);
653            if (pCkData) pCkData->Resize(iSizeInBytes);
654            else pCkData = pWaveList->AddSubChunk(CHUNK_ID_DATA, iSizeInBytes);
655      }      }
656    
657      /**      /**
# Line 256  namespace DLS { Line 659  namespace DLS {
659       * bytes). Use this method and <i>Read()</i> if you don't want to load       * bytes). Use this method and <i>Read()</i> if you don't want to load
660       * the sample into RAM, thus for disk streaming.       * the sample into RAM, thus for disk streaming.
661       *       *
662         * Also note: only WAVE_FORMAT_PCM is currently supported, that is
663         * FormatTag must be WAVE_FORMAT_PCM. Trying to reposition the sample
664         * with other formats will fail!
665         *
666       * @param SampleCount  number of sample points       * @param SampleCount  number of sample points
667       * @param Whence       to which relation \a SampleCount refers to       * @param Whence       to which relation \a SampleCount refers to
668         * @returns new position within the sample, 0 if
669         *          FormatTag != WAVE_FORMAT_PCM
670         * @throws Exception if no data RIFF chunk was created for the sample yet
671         * @see FrameSize, FormatTag
672       */       */
673      unsigned long Sample::SetPos(unsigned long SampleCount, RIFF::stream_whence_t Whence) {      unsigned long Sample::SetPos(unsigned long SampleCount, RIFF::stream_whence_t Whence) {
674          if (FormatTag != WAVE_FORMAT_PCM) return 0; // failed: wave data not PCM format          if (FormatTag != WAVE_FORMAT_PCM) return 0; // failed: wave data not PCM format
675            if (!pCkData) throw Exception("No data chunk created for sample yet, call Sample::Resize() to create one");
676          unsigned long orderedBytes = SampleCount * FrameSize;          unsigned long orderedBytes = SampleCount * FrameSize;
677          unsigned long result = pCkData->SetPos(orderedBytes, Whence);          unsigned long result = pCkData->SetPos(orderedBytes, Whence);
678          return (result == orderedBytes) ? SampleCount          return (result == orderedBytes) ? SampleCount
# Line 281  namespace DLS { Line 693  namespace DLS {
693          return pCkData->Read(pBuffer, SampleCount, FrameSize); // FIXME: channel inversion due to endian correction?          return pCkData->Read(pBuffer, SampleCount, FrameSize); // FIXME: channel inversion due to endian correction?
694      }      }
695    
696        /** @brief Write sample wave data.
697         *
698         * Writes \a SampleCount number of sample points from the buffer pointed
699         * by \a pBuffer and increments the position within the sample. Use this
700         * method to directly write the sample data to disk, i.e. if you don't
701         * want or cannot load the whole sample data into RAM.
702         *
703         * You have to Resize() the sample to the desired size and call
704         * File::Save() <b>before</b> using Write().
705         *
706         * @param pBuffer     - source buffer
707         * @param SampleCount - number of sample points to write
708         * @throws Exception if current sample size is too small
709         * @see LoadSampleData()
710         */
711        unsigned long Sample::Write(void* pBuffer, unsigned long SampleCount) {
712            if (FormatTag != WAVE_FORMAT_PCM) return 0; // failed: wave data not PCM format
713            if (GetSize() < SampleCount) throw Exception("Could not write sample data, current sample size to small");
714            return pCkData->Write(pBuffer, SampleCount, FrameSize); // FIXME: channel inversion due to endian correction?
715        }
716    
717        /**
718         * Apply sample and its settings to the respective RIFF chunks. You have
719         * to call File::Save() to make changes persistent.
720         *
721         * @throws Exception if FormatTag != WAVE_FORMAT_PCM or no sample data
722         *                   was provided yet
723         */
724        void Sample::UpdateChunks() {
725            if (FormatTag != WAVE_FORMAT_PCM)
726                throw Exception("Could not save sample, only PCM format is supported");
727            // we refuse to do anything if not sample wave form was provided yet
728            if (!pCkData)
729                throw Exception("Could not save sample, there is no sample data to save");
730            // update chunks of base class as well
731            Resource::UpdateChunks();
732            // make sure 'fmt' chunk exists
733            RIFF::Chunk* pCkFormat = pWaveList->GetSubChunk(CHUNK_ID_FMT);
734            if (!pCkFormat) pCkFormat = pWaveList->AddSubChunk(CHUNK_ID_FMT, 16); // assumes PCM format
735            uint8_t* pData = (uint8_t*) pCkFormat->LoadChunkData();
736            // update 'fmt' chunk
737            memcpy(&pData[0], &FormatTag, 2);
738            memcpy(&pData[2], &Channels,  2);
739            memcpy(&pData[4], &SamplesPerSecond, 4);
740            memcpy(&pData[8], &AverageBytesPerSecond, 4);
741            memcpy(&pData[12], &BlockAlign, 2);
742            memcpy(&pData[14], &BitDepth, 2); // assuming PCM format
743        }
744    
745    
746    
747  // *************** Region ***************  // *************** Region ***************
# Line 289  namespace DLS { Line 750  namespace DLS {
750      Region::Region(Instrument* pInstrument, RIFF::List* rgnList) : Resource(pInstrument, rgnList), Articulator(rgnList), Sampler(rgnList) {      Region::Region(Instrument* pInstrument, RIFF::List* rgnList) : Resource(pInstrument, rgnList), Articulator(rgnList), Sampler(rgnList) {
751          pCkRegion = rgnList;          pCkRegion = rgnList;
752    
753            // articulation informations
754          RIFF::Chunk* rgnh = rgnList->GetSubChunk(CHUNK_ID_RGNH);          RIFF::Chunk* rgnh = rgnList->GetSubChunk(CHUNK_ID_RGNH);
755          rgnh->Read(&KeyRange, 2, 2);          if (rgnh) {
756          rgnh->Read(&VelocityRange, 2, 2);              rgnh->Read(&KeyRange, 2, 2);
757          uint16_t optionflags = rgnh->ReadUint16();              rgnh->Read(&VelocityRange, 2, 2);
758          SelfNonExclusive = optionflags & F_RGN_OPTION_SELFNONEXCLUSIVE;              FormatOptionFlags = rgnh->ReadUint16();
759          KeyGroup = rgnh->ReadUint16();              KeyGroup = rgnh->ReadUint16();
760          // Layer is optional              // Layer is optional
761          if (rgnh->RemainingBytes() >= sizeof(uint16_t)) {              if (rgnh->RemainingBytes() >= sizeof(uint16_t)) {
762              rgnh->Read(&Layer, 1, sizeof(uint16_t));                  rgnh->Read(&Layer, 1, sizeof(uint16_t));
763                } else Layer = 0;
764            } else { // 'rgnh' chunk is missing
765                KeyRange.low  = 0;
766                KeyRange.high = 127;
767                VelocityRange.low  = 0;
768                VelocityRange.high = 127;
769                FormatOptionFlags = F_RGN_OPTION_SELFNONEXCLUSIVE;
770                KeyGroup = 0;
771                Layer = 0;
772          }          }
773          else Layer = 0;          SelfNonExclusive = FormatOptionFlags & F_RGN_OPTION_SELFNONEXCLUSIVE;
774    
775            // sample informations
776          RIFF::Chunk* wlnk = rgnList->GetSubChunk(CHUNK_ID_WLNK);          RIFF::Chunk* wlnk = rgnList->GetSubChunk(CHUNK_ID_WLNK);
777          optionflags  = wlnk->ReadUint16();          if (wlnk) {
778          PhaseMaster  = optionflags & F_WAVELINK_PHASE_MASTER;              WaveLinkOptionFlags = wlnk->ReadUint16();
779          MultiChannel = optionflags & F_WAVELINK_MULTICHANNEL;              PhaseGroup          = wlnk->ReadUint16();
780          PhaseGroup         = wlnk->ReadUint16();              Channel             = wlnk->ReadUint32();
781          Channel            = wlnk->ReadUint32();              WavePoolTableIndex  = wlnk->ReadUint32();
782          WavePoolTableIndex = wlnk->ReadUint32();          } else { // 'wlnk' chunk is missing
783                WaveLinkOptionFlags = 0;
784                PhaseGroup          = 0;
785                Channel             = 0; // mono
786                WavePoolTableIndex  = 0; // first entry in wave pool table
787            }
788            PhaseMaster  = WaveLinkOptionFlags & F_WAVELINK_PHASE_MASTER;
789            MultiChannel = WaveLinkOptionFlags & F_WAVELINK_MULTICHANNEL;
790    
791          pSample = NULL;          pSample = NULL;
792      }      }
793    
794        /** @brief Destructor.
795         *
796         * Removes RIFF chunks associated with this Region.
797         */
798      Region::~Region() {      Region::~Region() {
799            RIFF::List* pParent = pCkRegion->GetParent();
800            pParent->DeleteSubChunk(pCkRegion);
801      }      }
802    
803      Sample* Region::GetSample() {      Sample* Region::GetSample() {
# Line 327  namespace DLS { Line 812  namespace DLS {
812          return NULL;          return NULL;
813      }      }
814    
815        /**
816         * Assign another sample to this Region.
817         *
818         * @param pSample - sample to be assigned
819         */
820        void Region::SetSample(Sample* pSample) {
821            this->pSample = pSample;
822            WavePoolTableIndex = 0; // we update this offset when we Save()
823        }
824    
825        /**
826         * Apply Region settings to the respective RIFF chunks. You have to
827         * call File::Save() to make changes persistent.
828         *
829         * @throws Exception - if the Region's sample could not be found
830         */
831        void Region::UpdateChunks() {
832            // make sure 'rgnh' chunk exists
833            RIFF::Chunk* rgnh = pCkRegion->GetSubChunk(CHUNK_ID_RGNH);
834            if (!rgnh) rgnh = pCkRegion->AddSubChunk(CHUNK_ID_RGNH, Layer ? 14 : 12);
835            uint8_t* pData = (uint8_t*) rgnh->LoadChunkData();
836            FormatOptionFlags = (SelfNonExclusive)
837                                    ? FormatOptionFlags | F_RGN_OPTION_SELFNONEXCLUSIVE
838                                    : FormatOptionFlags & (~F_RGN_OPTION_SELFNONEXCLUSIVE);
839            // update 'rgnh' chunk
840            memcpy(&pData[0], &KeyRange, 2 * 2);
841            memcpy(&pData[4], &VelocityRange, 2 * 2);
842            memcpy(&pData[8], &FormatOptionFlags, 2);
843            memcpy(&pData[10], &KeyGroup, 2);
844            if (rgnh->GetSize() >= 14) memcpy(&pData[12], &Layer, 2);
845    
846            // update chunks of base classes as well (but skip Resource,
847            // as a rgn doesn't seem to have dlid and INFO chunks)
848            Articulator::UpdateChunks();
849            Sampler::UpdateChunks();
850    
851            // make sure 'wlnk' chunk exists
852            RIFF::Chunk* wlnk = pCkRegion->GetSubChunk(CHUNK_ID_WLNK);
853            if (!wlnk) wlnk = pCkRegion->AddSubChunk(CHUNK_ID_WLNK, 12);
854            pData = (uint8_t*) wlnk->LoadChunkData();
855            WaveLinkOptionFlags = (PhaseMaster)
856                                      ? WaveLinkOptionFlags | F_WAVELINK_PHASE_MASTER
857                                      : WaveLinkOptionFlags & (~F_WAVELINK_PHASE_MASTER);
858            WaveLinkOptionFlags = (MultiChannel)
859                                      ? WaveLinkOptionFlags | F_WAVELINK_MULTICHANNEL
860                                      : WaveLinkOptionFlags & (~F_WAVELINK_MULTICHANNEL);
861            // get sample's wave pool table index
862            int index = -1;
863            File* pFile = (File*) GetParent()->GetParent();
864            if (pFile->pSamples) {
865                File::SampleList::iterator iter = pFile->pSamples->begin();
866                File::SampleList::iterator end  = pFile->pSamples->end();
867                for (int i = 0; iter != end; ++iter, i++) {
868                    if (*iter == pSample) {
869                        index = i;
870                        break;
871                    }
872                }
873            }
874            if (index < 0) throw Exception("Could not save Region, could not find Region's sample");
875            WavePoolTableIndex = index;
876            // update 'wlnk' chunk
877            memcpy(&pData[0], &WaveLinkOptionFlags, 2);
878            memcpy(&pData[2], &PhaseGroup, 2);
879            memcpy(&pData[4], &Channel, 4);
880            memcpy(&pData[8], &WavePoolTableIndex, 4);
881        }
882    
883    
884    
885  // *************** Instrument ***************  // *************** Instrument ***************
886  // *  // *
887    
888        /** @brief Constructor.
889         *
890         * Load an existing instrument definition or create a new one. An 'ins'
891         * list chunk must be given to this constructor. In case this 'ins' list
892         * chunk contains a 'insh' chunk, the instrument data fields will be
893         * loaded from there, otherwise default values will be used and the
894         * 'insh' chunk will be created once File::Save() was called.
895         *
896         * @param pFile   - pointer to DLS::File where this instrument is
897         *                  located (or will be located)
898         * @param insList - pointer to 'ins' list chunk which is (or will be)
899         *                  associated with this instrument
900         */
901      Instrument::Instrument(File* pFile, RIFF::List* insList) : Resource(pFile, insList), Articulator(insList) {      Instrument::Instrument(File* pFile, RIFF::List* insList) : Resource(pFile, insList), Articulator(insList) {
902          pCkInstrument = insList;          pCkInstrument = insList;
903    
         RIFF::Chunk* insh = pCkInstrument->GetSubChunk(CHUNK_ID_INSH);  
         if (!insh) throw DLS::Exception("Mandatory chunks in <lins> list chunk not found.");  
         Regions = insh->ReadUint32();  
904          midi_locale_t locale;          midi_locale_t locale;
905          insh->Read(&locale, 2, 4);          RIFF::Chunk* insh = pCkInstrument->GetSubChunk(CHUNK_ID_INSH);
906            if (insh) {
907                Regions = insh->ReadUint32();
908                insh->Read(&locale, 2, 4);
909            } else { // 'insh' chunk missing
910                Regions = 0;
911                locale.bank       = 0;
912                locale.instrument = 0;
913            }
914    
915          MIDIProgram    = locale.instrument;          MIDIProgram    = locale.instrument;
916          IsDrum         = locale.bank & DRUM_TYPE_MASK;          IsDrum         = locale.bank & DRUM_TYPE_MASK;
917          MIDIBankCoarse = (uint8_t) MIDI_BANK_COARSE(locale.bank);          MIDIBankCoarse = (uint8_t) MIDI_BANK_COARSE(locale.bank);
918          MIDIBankFine   = (uint8_t) MIDI_BANK_FINE(locale.bank);          MIDIBankFine   = (uint8_t) MIDI_BANK_FINE(locale.bank);
919          MIDIBank       = MIDI_BANK_MERGE(MIDIBankCoarse, MIDIBankFine);          MIDIBank       = MIDI_BANK_MERGE(MIDIBankCoarse, MIDIBankFine);
920    
921          pRegions   = NULL;          pRegions = NULL;
922      }      }
923    
924      Region* Instrument::GetFirstRegion() {      Region* Instrument::GetFirstRegion() {
# Line 363  namespace DLS { Line 935  namespace DLS {
935      }      }
936    
937      void Instrument::LoadRegions() {      void Instrument::LoadRegions() {
938            if (!pRegions) pRegions = new RegionList;
939          RIFF::List* lrgn = pCkInstrument->GetSubList(LIST_TYPE_LRGN);          RIFF::List* lrgn = pCkInstrument->GetSubList(LIST_TYPE_LRGN);
940          if (!lrgn) throw DLS::Exception("Mandatory chunks in <ins > chunk not found.");          if (lrgn) {
941          uint32_t regionCkType = (lrgn->GetSubList(LIST_TYPE_RGN2)) ? LIST_TYPE_RGN2 : LIST_TYPE_RGN; // prefer regions level 2              uint32_t regionCkType = (lrgn->GetSubList(LIST_TYPE_RGN2)) ? LIST_TYPE_RGN2 : LIST_TYPE_RGN; // prefer regions level 2
942          RIFF::List* rgn = lrgn->GetFirstSubList();              RIFF::List* rgn = lrgn->GetFirstSubList();
943          while (rgn) {              while (rgn) {
944              if (rgn->GetListType() == regionCkType) {                  if (rgn->GetListType() == regionCkType) {
945                  if (!pRegions) pRegions = new RegionList;                      pRegions->push_back(new Region(this, rgn));
946                  pRegions->push_back(new Region(this, rgn));                  }
947                    rgn = lrgn->GetNextSubList();
948              }              }
             rgn = lrgn->GetNextSubList();  
949          }          }
950      }      }
951    
952        Region* Instrument::AddRegion() {
953            if (!pRegions) LoadRegions();
954            RIFF::List* lrgn = pCkInstrument->GetSubList(LIST_TYPE_LRGN);
955            if (!lrgn)  lrgn = pCkInstrument->AddSubList(LIST_TYPE_LRGN);
956            RIFF::List* rgn = lrgn->AddSubList(LIST_TYPE_RGN);
957            Region* pNewRegion = new Region(this, rgn);
958            pRegions->push_back(pNewRegion);
959            Regions = pRegions->size();
960            return pNewRegion;
961        }
962    
963        void Instrument::DeleteRegion(Region* pRegion) {
964            if (!pRegions) return;
965            RegionList::iterator iter = find(pRegions->begin(), pRegions->end(), pRegion);
966            if (iter == pRegions->end()) return;
967            pRegions->erase(iter);
968            Regions = pRegions->size();
969            delete pRegion;
970        }
971    
972        /**
973         * Apply Instrument with all its Regions to the respective RIFF chunks.
974         * You have to call File::Save() to make changes persistent.
975         *
976         * @throws Exception - on errors
977         */
978        void Instrument::UpdateChunks() {
979            // first update base classes' chunks
980            Resource::UpdateChunks();
981            Articulator::UpdateChunks();
982            // make sure 'insh' chunk exists
983            RIFF::Chunk* insh = pCkInstrument->GetSubChunk(CHUNK_ID_INSH);
984            if (!insh) insh = pCkInstrument->AddSubChunk(CHUNK_ID_INSH, 12);
985            uint8_t* pData = (uint8_t*) insh->LoadChunkData();
986            // update 'insh' chunk
987            Regions = (pRegions) ? pRegions->size() : 0;
988            midi_locale_t locale;
989            locale.instrument = MIDIProgram;
990            locale.bank       = MIDI_BANK_ENCODE(MIDIBankCoarse, MIDIBankFine);
991            locale.bank       = (IsDrum) ? locale.bank | DRUM_TYPE_MASK : locale.bank & (~DRUM_TYPE_MASK);
992            MIDIBank          = MIDI_BANK_MERGE(MIDIBankCoarse, MIDIBankFine); // just a sync, when we're at it
993            memcpy(&pData[0], &Regions, 4);
994            memcpy(&pData[4], &locale, 2 * 4);
995            // update Region's chunks
996            if (!pRegions) return;
997            RegionList::iterator iter = pRegions->begin();
998            RegionList::iterator end  = pRegions->end();
999            for (; iter != end; ++iter) {
1000                (*iter)->UpdateChunks();
1001            }
1002        }
1003    
1004        /** @brief Destructor.
1005         *
1006         * Removes RIFF chunks associated with this Instrument and frees all
1007         * memory occupied by this instrument.
1008         */
1009      Instrument::~Instrument() {      Instrument::~Instrument() {
1010          if (pRegions) {          if (pRegions) {
1011              RegionList::iterator iter = pRegions->begin();              RegionList::iterator iter = pRegions->begin();
# Line 386  namespace DLS { Line 1016  namespace DLS {
1016              }              }
1017              delete pRegions;              delete pRegions;
1018          }          }
1019            // remove instrument's chunks
1020            RIFF::List* pParent = pCkInstrument->GetParent();
1021            pParent->DeleteSubChunk(pCkInstrument);
1022      }      }
1023    
1024    
# Line 393  namespace DLS { Line 1026  namespace DLS {
1026  // *************** File ***************  // *************** File ***************
1027  // *  // *
1028    
1029        /** @brief Constructor.
1030         *
1031         * Default constructor, use this to create an empty DLS file. You have
1032         * to add samples, instruments and finally call Save() to actually write
1033         * a DLS file.
1034         */
1035        File::File() : Resource(NULL, pRIFF = new RIFF::File(RIFF_TYPE_DLS)) {
1036            pVersion = new version_t;
1037            pVersion->major   = 0;
1038            pVersion->minor   = 0;
1039            pVersion->release = 0;
1040            pVersion->build   = 0;
1041    
1042            Instruments      = 0;
1043            WavePoolCount    = 0;
1044            pWavePoolTable   = NULL;
1045            pWavePoolTableHi = NULL;
1046            WavePoolHeaderSize = 8;
1047    
1048            pSamples     = NULL;
1049            pInstruments = NULL;
1050    
1051            b64BitWavePoolOffsets = false;
1052        }
1053    
1054        /** @brief Constructor.
1055         *
1056         * Load an existing DLS file.
1057         *
1058         * @param pRIFF - pointer to a RIFF file which is actually the DLS file
1059         *                to load
1060         * @throws Exception if given file is not a DLS file, expected chunks
1061         *                   are missing
1062         */
1063      File::File(RIFF::File* pRIFF) : Resource(NULL, pRIFF) {      File::File(RIFF::File* pRIFF) : Resource(NULL, pRIFF) {
1064          if (!pRIFF) throw DLS::Exception("NULL pointer reference to RIFF::File object.");          if (!pRIFF) throw DLS::Exception("NULL pointer reference to RIFF::File object.");
1065          this->pRIFF = pRIFF;          this->pRIFF = pRIFF;
# Line 409  namespace DLS { Line 1076  namespace DLS {
1076          Instruments = colh->ReadUint32();          Instruments = colh->ReadUint32();
1077    
1078          RIFF::Chunk* ptbl = pRIFF->GetSubChunk(CHUNK_ID_PTBL);          RIFF::Chunk* ptbl = pRIFF->GetSubChunk(CHUNK_ID_PTBL);
1079          if (!ptbl) throw DLS::Exception("Mandatory <ptbl> chunk not found.");          if (!ptbl) { // pool table is missing - this is probably an ".art" file
1080          uint32_t headersize = ptbl->ReadUint32();              WavePoolCount    = 0;
1081          WavePoolCount  = ptbl->ReadUint32();              pWavePoolTable   = NULL;
1082          pWavePoolTable = new uint32_t[WavePoolCount];              pWavePoolTableHi = NULL;
1083          pWavePoolTableHi = new uint32_t[WavePoolCount];              WavePoolHeaderSize = 8;
1084          ptbl->SetPos(headersize);              b64BitWavePoolOffsets = false;
1085            } else {
1086          // Check for 64 bit offsets (used in gig v3 files)              WavePoolHeaderSize = ptbl->ReadUint32();
1087          if (ptbl->GetSize() - headersize == WavePoolCount * 8) {              WavePoolCount  = ptbl->ReadUint32();
1088              for (int i = 0 ; i < WavePoolCount ; i++) {              pWavePoolTable = new uint32_t[WavePoolCount];
1089                  pWavePoolTableHi[i] = ptbl->ReadUint32();              pWavePoolTableHi = new uint32_t[WavePoolCount];
1090                  pWavePoolTable[i] = ptbl->ReadUint32();              ptbl->SetPos(WavePoolHeaderSize);
1091                  if (pWavePoolTable[i] & 0x80000000)  
1092                      throw DLS::Exception("Files larger than 2 GB not yet supported");              // Check for 64 bit offsets (used in gig v3 files)
1093                b64BitWavePoolOffsets = (ptbl->GetSize() - WavePoolHeaderSize == WavePoolCount * 8);
1094                if (b64BitWavePoolOffsets) {
1095                    for (int i = 0 ; i < WavePoolCount ; i++) {
1096                        pWavePoolTableHi[i] = ptbl->ReadUint32();
1097                        pWavePoolTable[i] = ptbl->ReadUint32();
1098                        if (pWavePoolTable[i] & 0x80000000)
1099                            throw DLS::Exception("Files larger than 2 GB not yet supported");
1100                    }
1101                } else { // conventional 32 bit offsets
1102                    ptbl->Read(pWavePoolTable, WavePoolCount, sizeof(uint32_t));
1103                    for (int i = 0 ; i < WavePoolCount ; i++) pWavePoolTableHi[i] = 0;
1104              }              }
1105          }          }
         else {  
             ptbl->Read(pWavePoolTable, WavePoolCount, sizeof(uint32_t));  
             for (int i = 0 ; i < WavePoolCount ; i++) pWavePoolTableHi[i] = 0;  
         }  
1106    
1107          pSamples     = NULL;          pSamples     = NULL;
1108          pInstruments = NULL;          pInstruments = NULL;
# Line 458  namespace DLS { Line 1132  namespace DLS {
1132          if (pWavePoolTable) delete[] pWavePoolTable;          if (pWavePoolTable) delete[] pWavePoolTable;
1133          if (pWavePoolTableHi) delete[] pWavePoolTableHi;          if (pWavePoolTableHi) delete[] pWavePoolTableHi;
1134          if (pVersion) delete pVersion;          if (pVersion) delete pVersion;
1135            for (std::list<RIFF::File*>::iterator i = ExtensionFiles.begin() ; i != ExtensionFiles.end() ; i++)
1136                delete *i;
1137      }      }
1138    
1139      Sample* File::GetFirstSample() {      Sample* File::GetFirstSample() {
# Line 474  namespace DLS { Line 1150  namespace DLS {
1150      }      }
1151    
1152      void File::LoadSamples() {      void File::LoadSamples() {
1153            if (!pSamples) pSamples = new SampleList;
1154          RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL);          RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL);
1155          if (wvpl) {          if (wvpl) {
1156              unsigned long wvplFileOffset = wvpl->GetFilePos();              unsigned long wvplFileOffset = wvpl->GetFilePos();
1157              RIFF::List* wave = wvpl->GetFirstSubList();              RIFF::List* wave = wvpl->GetFirstSubList();
1158              while (wave) {              while (wave) {
1159                  if (wave->GetListType() == LIST_TYPE_WAVE) {                  if (wave->GetListType() == LIST_TYPE_WAVE) {
                     if (!pSamples) pSamples = new SampleList;  
1160                      unsigned long waveFileOffset = wave->GetFilePos();                      unsigned long waveFileOffset = wave->GetFilePos();
1161                      pSamples->push_back(new Sample(this, wave, waveFileOffset - wvplFileOffset));                      pSamples->push_back(new Sample(this, wave, waveFileOffset - wvplFileOffset));
1162                  }                  }
# Line 494  namespace DLS { Line 1170  namespace DLS {
1170                  RIFF::List* wave = dwpl->GetFirstSubList();                  RIFF::List* wave = dwpl->GetFirstSubList();
1171                  while (wave) {                  while (wave) {
1172                      if (wave->GetListType() == LIST_TYPE_WAVE) {                      if (wave->GetListType() == LIST_TYPE_WAVE) {
                         if (!pSamples) pSamples = new SampleList;  
1173                          unsigned long waveFileOffset = wave->GetFilePos();                          unsigned long waveFileOffset = wave->GetFilePos();
1174                          pSamples->push_back(new Sample(this, wave, waveFileOffset - dwplFileOffset));                          pSamples->push_back(new Sample(this, wave, waveFileOffset - dwplFileOffset));
1175                      }                      }
# Line 504  namespace DLS { Line 1179  namespace DLS {
1179          }          }
1180      }      }
1181    
1182        /** @brief Add a new sample.
1183         *
1184         * This will create a new Sample object for the DLS file. You have to
1185         * call Save() to make this persistent to the file.
1186         *
1187         * @returns pointer to new Sample object
1188         */
1189        Sample* File::AddSample() {
1190           if (!pSamples) LoadSamples();
1191           __ensureMandatoryChunksExist();
1192           RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL);
1193           // create new Sample object and its respective 'wave' list chunk
1194           RIFF::List* wave = wvpl->AddSubList(LIST_TYPE_WAVE);
1195           Sample* pSample = new Sample(this, wave, 0 /*arbitrary value, we update offsets when we save*/);
1196           pSamples->push_back(pSample);
1197           return pSample;
1198        }
1199    
1200        /** @brief Delete a sample.
1201         *
1202         * This will delete the given Sample object from the DLS file. You have
1203         * to call Save() to make this persistent to the file.
1204         *
1205         * @param pSample - sample to delete
1206         */
1207        void File::DeleteSample(Sample* pSample) {
1208            if (!pSamples) return;
1209            SampleList::iterator iter = find(pSamples->begin(), pSamples->end(), pSample);
1210            if (iter == pSamples->end()) return;
1211            pSamples->erase(iter);
1212            delete pSample;
1213        }
1214    
1215      Instrument* File::GetFirstInstrument() {      Instrument* File::GetFirstInstrument() {
1216          if (!pInstruments) LoadInstruments();          if (!pInstruments) LoadInstruments();
1217          if (!pInstruments) return NULL;          if (!pInstruments) return NULL;
# Line 518  namespace DLS { Line 1226  namespace DLS {
1226      }      }
1227    
1228      void File::LoadInstruments() {      void File::LoadInstruments() {
1229            if (!pInstruments) pInstruments = new InstrumentList;
1230          RIFF::List* lstInstruments = pRIFF->GetSubList(LIST_TYPE_LINS);          RIFF::List* lstInstruments = pRIFF->GetSubList(LIST_TYPE_LINS);
1231          if (lstInstruments) {          if (lstInstruments) {
1232              RIFF::List* lstInstr = lstInstruments->GetFirstSubList();              RIFF::List* lstInstr = lstInstruments->GetFirstSubList();
1233              while (lstInstr) {              while (lstInstr) {
1234                  if (lstInstr->GetListType() == LIST_TYPE_INS) {                  if (lstInstr->GetListType() == LIST_TYPE_INS) {
                     if (!pInstruments) pInstruments = new InstrumentList;  
1235                      pInstruments->push_back(new Instrument(this, lstInstr));                      pInstruments->push_back(new Instrument(this, lstInstr));
1236                  }                  }
1237                  lstInstr = lstInstruments->GetNextSubList();                  lstInstr = lstInstruments->GetNextSubList();
1238              }              }
1239          }          }
1240      }      }
1241    
1242        /** @brief Add a new instrument definition.
1243         *
1244         * This will create a new Instrument object for the DLS file. You have
1245         * to call Save() to make this persistent to the file.
1246         *
1247         * @returns pointer to new Instrument object
1248         */
1249        Instrument* File::AddInstrument() {
1250           if (!pInstruments) LoadInstruments();
1251           __ensureMandatoryChunksExist();
1252           RIFF::List* lstInstruments = pRIFF->GetSubList(LIST_TYPE_LINS);
1253           RIFF::List* lstInstr = lstInstruments->AddSubList(LIST_TYPE_INS);
1254           Instrument* pInstrument = new Instrument(this, lstInstr);
1255           pInstruments->push_back(pInstrument);
1256           return pInstrument;
1257        }
1258    
1259        /** @brief Delete an instrument.
1260         *
1261         * This will delete the given Instrument object from the DLS file. You
1262         * have to call Save() to make this persistent to the file.
1263         *
1264         * @param pInstrument - instrument to delete
1265         */
1266        void File::DeleteInstrument(Instrument* pInstrument) {
1267            if (!pInstruments) return;
1268            InstrumentList::iterator iter = find(pInstruments->begin(), pInstruments->end(), pInstrument);
1269            if (iter == pInstruments->end()) return;
1270            pInstruments->erase(iter);
1271            delete pInstrument;
1272        }
1273    
1274        /**
1275         * Apply all the DLS file's current instruments, samples and settings to
1276         * the respective RIFF chunks. You have to call Save() to make changes
1277         * persistent.
1278         *
1279         * @throws Exception - on errors
1280         */
1281        void File::UpdateChunks() {
1282            // first update base class's chunks
1283            Resource::UpdateChunks();
1284    
1285            // if version struct exists, update 'vers' chunk
1286            if (pVersion) {
1287                RIFF::Chunk* ckVersion    = pRIFF->GetSubChunk(CHUNK_ID_VERS);
1288                if (!ckVersion) ckVersion = pRIFF->AddSubChunk(CHUNK_ID_VERS, 8);
1289                uint8_t* pData = (uint8_t*) ckVersion->LoadChunkData();
1290                memcpy(pData, pVersion, 2 * 4);
1291            }
1292    
1293            // update 'colh' chunk
1294            Instruments = (pInstruments) ? pInstruments->size() : 0;
1295            RIFF::Chunk* colh = pRIFF->GetSubChunk(CHUNK_ID_COLH);
1296            if (!colh)   colh = pRIFF->AddSubChunk(CHUNK_ID_COLH, 4);
1297            uint8_t* pData = (uint8_t*) colh->LoadChunkData();
1298            memcpy(pData, &Instruments, 4);
1299    
1300            // update instrument's chunks
1301            if (pInstruments) {
1302                InstrumentList::iterator iter = pInstruments->begin();
1303                InstrumentList::iterator end  = pInstruments->end();
1304                for (; iter != end; ++iter) {
1305                    (*iter)->UpdateChunks();
1306                }
1307            }
1308    
1309            // update 'ptbl' chunk
1310            const int iSamples = (pSamples) ? pSamples->size() : 0;
1311            const int iPtblOffsetSize = (b64BitWavePoolOffsets) ? 8 : 4;
1312            RIFF::Chunk* ptbl = pRIFF->GetSubChunk(CHUNK_ID_PTBL);
1313            if (!ptbl)   ptbl = pRIFF->AddSubChunk(CHUNK_ID_PTBL, 1 /*anything, we'll resize*/);
1314            const int iPtblSize = WavePoolHeaderSize + iPtblOffsetSize * iSamples;
1315            ptbl->Resize(iPtblSize);
1316            pData = (uint8_t*) ptbl->LoadChunkData();
1317            WavePoolCount = iSamples;
1318            memcpy(&pData[4], &WavePoolCount, 4);
1319            // we actually update the sample offsets in the pool table when we Save()
1320            memset(&pData[WavePoolHeaderSize], 0, iPtblSize - WavePoolHeaderSize);
1321    
1322            // update sample's chunks
1323            if (pSamples) {
1324                SampleList::iterator iter = pSamples->begin();
1325                SampleList::iterator end  = pSamples->end();
1326                for (; iter != end; ++iter) {
1327                    (*iter)->UpdateChunks();
1328                }
1329            }
1330        }
1331    
1332        /** @brief Save changes to another file.
1333         *
1334         * Make all changes persistent by writing them to another file.
1335         * <b>Caution:</b> this method is optimized for writing to
1336         * <b>another</b> file, do not use it to save the changes to the same
1337         * file! Use Save() (without path argument) in that case instead!
1338         * Ignoring this might result in a corrupted file!
1339         *
1340         * After calling this method, this File object will be associated with
1341         * the new file (given by \a Path) afterwards.
1342         *
1343         * @param Path - path and file name where everything should be written to
1344         */
1345        void File::Save(const String& Path) {
1346            UpdateChunks();
1347            pRIFF->Save(Path);
1348            __UpdateWavePoolTableChunk();
1349        }
1350    
1351        /** @brief Save changes to same file.
1352         *
1353         * Make all changes persistent by writing them to the actual (same)
1354         * file. The file might temporarily grow to a higher size than it will
1355         * have at the end of the saving process.
1356         *
1357         * @throws RIFF::Exception if any kind of IO error occured
1358         * @throws DLS::Exception  if any kind of DLS specific error occured
1359         */
1360        void File::Save() {
1361            UpdateChunks();
1362            pRIFF->Save();
1363            __UpdateWavePoolTableChunk();
1364        }
1365    
1366        /**
1367         * Checks if all (for DLS) mandatory chunks exist, if not they will be
1368         * created. Note that those chunks will not be made persistent until
1369         * Save() was called.
1370         */
1371        void File::__ensureMandatoryChunksExist() {
1372           // enusre 'lins' list chunk exists (mandatory for instrument definitions)
1373           RIFF::List* lstInstruments = pRIFF->GetSubList(LIST_TYPE_LINS);
1374           if (!lstInstruments) pRIFF->AddSubList(LIST_TYPE_LINS);
1375           // ensure 'ptbl' chunk exists (mandatory for samples)
1376           RIFF::Chunk* ptbl = pRIFF->GetSubChunk(CHUNK_ID_PTBL);
1377           if (!ptbl) {
1378               const int iOffsetSize = (b64BitWavePoolOffsets) ? 8 : 4;
1379               ptbl = pRIFF->AddSubChunk(CHUNK_ID_PTBL, WavePoolHeaderSize + iOffsetSize);
1380           }
1381           // enusre 'wvpl' list chunk exists (mandatory for samples)
1382           RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL);
1383           if (!wvpl) pRIFF->AddSubList(LIST_TYPE_WVPL);
1384        }
1385    
1386        /**
1387         * Updates (persistently) the wave pool table with offsets to all
1388         * currently available samples. <b>Caution:</b> this method assumes the
1389         * 'ptbl' chunk to be already of the correct size and the file to be
1390         * writable, so usually this method is only called after a Save() call.
1391         *
1392         * @throws Exception - if 'ptbl' chunk is too small (should only occur
1393         *                     if there's a bug)
1394         */
1395        void File::__UpdateWavePoolTableChunk() {
1396            __UpdateWavePoolTable();
1397            RIFF::Chunk* ptbl = pRIFF->GetSubChunk(CHUNK_ID_PTBL);
1398            const int iOffsetSize = (b64BitWavePoolOffsets) ? 8 : 4;
1399            // check if 'ptbl' chunk is large enough
1400            WavePoolCount = (pSamples) ? pSamples->size() : 0;
1401            const unsigned long ulRequiredSize = WavePoolHeaderSize + iOffsetSize * WavePoolCount;
1402            if (ptbl->GetSize() < ulRequiredSize) throw Exception("Fatal error, 'ptbl' chunk too small");
1403            // save the 'ptbl' chunk's current read/write position
1404            unsigned long ulOriginalPos = ptbl->GetPos();
1405            // update headers
1406            ptbl->SetPos(0);
1407            ptbl->WriteUint32(&WavePoolHeaderSize);
1408            ptbl->WriteUint32(&WavePoolCount);
1409            // update offsets
1410            ptbl->SetPos(WavePoolHeaderSize);
1411            if (b64BitWavePoolOffsets) {
1412                for (int i = 0 ; i < WavePoolCount ; i++) {
1413                    ptbl->WriteUint32(&pWavePoolTableHi[i]);
1414                    ptbl->WriteUint32(&pWavePoolTable[i]);
1415                }
1416            } else { // conventional 32 bit offsets
1417                for (int i = 0 ; i < WavePoolCount ; i++)
1418                    ptbl->WriteUint32(&pWavePoolTable[i]);
1419            }
1420            // restore 'ptbl' chunk's original read/write position
1421            ptbl->SetPos(ulOriginalPos);
1422        }
1423    
1424        /**
1425         * Updates the wave pool table with offsets to all currently available
1426         * samples. <b>Caution:</b> this method assumes the 'wvpl' list chunk
1427         * exists already.
1428         */
1429        void File::__UpdateWavePoolTable() {
1430            WavePoolCount = (pSamples) ? pSamples->size() : 0;
1431            // resize wave pool table arrays
1432            if (pWavePoolTable)   delete[] pWavePoolTable;
1433            if (pWavePoolTableHi) delete[] pWavePoolTableHi;
1434            pWavePoolTable   = new uint32_t[WavePoolCount];
1435            pWavePoolTableHi = new uint32_t[WavePoolCount];
1436            if (!pSamples) return;
1437            // update offsets int wave pool table
1438            RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL);
1439            uint64_t wvplFileOffset = wvpl->GetFilePos();
1440            if (b64BitWavePoolOffsets) {
1441                SampleList::iterator iter = pSamples->begin();
1442                SampleList::iterator end  = pSamples->end();
1443                for (int i = 0 ; iter != end ; ++iter, i++) {
1444                    uint64_t _64BitOffset = (*iter)->pWaveList->GetFilePos() - wvplFileOffset - LIST_HEADER_SIZE;
1445                    (*iter)->ulWavePoolOffset = _64BitOffset;
1446                    pWavePoolTableHi[i] = (uint32_t) (_64BitOffset >> 32);
1447                    pWavePoolTable[i]   = (uint32_t) _64BitOffset;
1448                }
1449            } else { // conventional 32 bit offsets
1450                SampleList::iterator iter = pSamples->begin();
1451                SampleList::iterator end  = pSamples->end();
1452                for (int i = 0 ; iter != end ; ++iter, i++) {
1453                    uint64_t _64BitOffset = (*iter)->pWaveList->GetFilePos() - wvplFileOffset - LIST_HEADER_SIZE;
1454                    (*iter)->ulWavePoolOffset = _64BitOffset;
1455                    pWavePoolTable[i] = (uint32_t) _64BitOffset;
1456                }
1457            }
1458        }
1459    
1460    
1461    

Legend:
Removed from v.666  
changed lines
  Added in v.928

  ViewVC Help
Powered by ViewVC