/[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 384 by schoenebeck, Thu Feb 17 02:22:26 2005 UTC revision 2609 by schoenebeck, Sun Jun 8 19:00:30 2014 UTC
# Line 1  Line 1 
1  /***************************************************************************  /***************************************************************************
2   *                                                                         *   *                                                                         *
3   *   libgig - C++ cross-platform Gigasampler format file loader library    *   *   libgig - C++ cross-platform Gigasampler format file access library    *
4   *                                                                         *   *                                                                         *
5   *   Copyright (C) 2003-2005 by Christian Schoenebeck                      *   *   Copyright (C) 2003-2013 by Christian Schoenebeck                      *
6   *                              <cuse@users.sourceforge.net>               *   *                              <cuse@users.sourceforge.net>               *
7   *                                                                         *   *                                                                         *
8   *   This library is free software; you can redistribute it and/or modify  *   *   This library is free software; you can redistribute it and/or modify  *
# Line 23  Line 23 
23    
24  #include "DLS.h"  #include "DLS.h"
25    
26    #include <algorithm>
27    #include <time.h>
28    
29    #ifdef __APPLE__
30    #include <CoreFoundation/CFUUID.h>
31    #elif defined(HAVE_UUID_UUID_H)
32    #include <uuid/uuid.h>
33    #endif
34    
35    #include "helper.h"
36    
37    // macros to decode connection transforms
38    #define CONN_TRANSFORM_SRC(x)                   ((x >> 10) & 0x000F)
39    #define CONN_TRANSFORM_CTL(x)                   ((x >> 4) & 0x000F)
40    #define CONN_TRANSFORM_DST(x)                   (x & 0x000F)
41    #define CONN_TRANSFORM_BIPOLAR_SRC(x)   (x & 0x4000)
42    #define CONN_TRANSFORM_BIPOLAR_CTL(x)   (x & 0x0100)
43    #define CONN_TRANSFORM_INVERT_SRC(x)    (x & 0x8000)
44    #define CONN_TRANSFORM_INVERT_CTL(x)    (x & 0x0200)
45    
46    // macros to encode connection transforms
47    #define CONN_TRANSFORM_SRC_ENCODE(x)                    ((x & 0x000F) << 10)
48    #define CONN_TRANSFORM_CTL_ENCODE(x)                    ((x & 0x000F) << 4)
49    #define CONN_TRANSFORM_DST_ENCODE(x)                    (x & 0x000F)
50    #define CONN_TRANSFORM_BIPOLAR_SRC_ENCODE(x)    ((x) ? 0x4000 : 0)
51    #define CONN_TRANSFORM_BIPOLAR_CTL_ENCODE(x)    ((x) ? 0x0100 : 0)
52    #define CONN_TRANSFORM_INVERT_SRC_ENCODE(x)             ((x) ? 0x8000 : 0)
53    #define CONN_TRANSFORM_INVERT_CTL_ENCODE(x)             ((x) ? 0x0200 : 0)
54    
55    #define DRUM_TYPE_MASK                  0x80000000
56    
57    #define F_RGN_OPTION_SELFNONEXCLUSIVE   0x0001
58    
59    #define F_WAVELINK_PHASE_MASTER         0x0001
60    #define F_WAVELINK_MULTICHANNEL         0x0002
61    
62    #define F_WSMP_NO_TRUNCATION            0x0001
63    #define F_WSMP_NO_COMPRESSION           0x0002
64    
65    #define MIDI_BANK_COARSE(x)             ((x & 0x00007F00) >> 8)                 // CC0
66    #define MIDI_BANK_FINE(x)               (x & 0x0000007F)                        // CC32
67    #define MIDI_BANK_MERGE(coarse, fine)   ((((uint16_t) coarse) << 7) | fine)     // CC0 + CC32
68    #define MIDI_BANK_ENCODE(coarse, fine)  (((coarse & 0x0000007F) << 8) | (fine & 0x0000007F))
69    
70  namespace DLS {  namespace DLS {
71    
72  // *************** Connection  ***************  // *************** Connection  ***************
# Line 42  namespace DLS { Line 86  namespace DLS {
86          ControlBipolar       = CONN_TRANSFORM_BIPOLAR_CTL(Header->transform);          ControlBipolar       = CONN_TRANSFORM_BIPOLAR_CTL(Header->transform);
87      }      }
88    
89        Connection::conn_block_t Connection::ToConnBlock() {
90            conn_block_t c;
91            c.source = Source;
92            c.control = Control;
93            c.destination = Destination;
94            c.scale = Scale;
95            c.transform = CONN_TRANSFORM_SRC_ENCODE(SourceTransform) |
96                          CONN_TRANSFORM_CTL_ENCODE(ControlTransform) |
97                          CONN_TRANSFORM_DST_ENCODE(DestinationTransform) |
98                          CONN_TRANSFORM_INVERT_SRC_ENCODE(SourceInvert) |
99                          CONN_TRANSFORM_BIPOLAR_SRC_ENCODE(SourceBipolar) |
100                          CONN_TRANSFORM_INVERT_CTL_ENCODE(ControlInvert) |
101                          CONN_TRANSFORM_BIPOLAR_CTL_ENCODE(ControlBipolar);
102            return c;
103        }
104    
105    
106    
107  // *************** Articulation  ***************  // *************** Articulation  ***************
108  // *  // *
109    
110      Articulation::Articulation(RIFF::List* artList) {      /** @brief Constructor.
111          if (artList->GetListType() != LIST_TYPE_ART2 &&       *
112              artList->GetListType() != LIST_TYPE_ART1) {       * Expects an 'artl' or 'art2' chunk to be given where the articulation
113                throw DLS::Exception("<art1-list> or <art2-list> chunk expected");       * connections will be read from.
114          }       *
115          uint32_t headerSize = artList->ReadUint32();       * @param artl - pointer to an 'artl' or 'art2' chunk
116          Connections         = artList->ReadUint32();       * @throws Exception if no 'artl' or 'art2' chunk was given
117          artList->SetPos(headerSize);       */
118        Articulation::Articulation(RIFF::Chunk* artl) {
119            pArticulationCk = artl;
120            if (artl->GetChunkID() != CHUNK_ID_ART2 &&
121                artl->GetChunkID() != CHUNK_ID_ARTL) {
122                  throw DLS::Exception("<artl-ck> or <art2-ck> chunk expected");
123            }
124            HeaderSize  = artl->ReadUint32();
125            Connections = artl->ReadUint32();
126            artl->SetPos(HeaderSize);
127    
128          pConnections = new Connection[Connections];          pConnections = new Connection[Connections];
129          Connection::conn_block_t connblock;          Connection::conn_block_t connblock;
130          for (uint32_t i = 0; i <= Connections; i++) {          for (uint32_t i = 0; i < Connections; i++) {
131              artList->Read(&connblock.source, 1, 2);              artl->Read(&connblock.source, 1, 2);
132              artList->Read(&connblock.control, 1, 2);              artl->Read(&connblock.control, 1, 2);
133              artList->Read(&connblock.destination, 1, 2);              artl->Read(&connblock.destination, 1, 2);
134              artList->Read(&connblock.transform, 1, 2);              artl->Read(&connblock.transform, 1, 2);
135              artList->Read(&connblock.scale, 1, 4);              artl->Read(&connblock.scale, 1, 4);
136              pConnections[i].Init(&connblock);              pConnections[i].Init(&connblock);
137          }          }
138      }      }
# Line 72  namespace DLS { Line 141  namespace DLS {
141         if (pConnections) delete[] pConnections;         if (pConnections) delete[] pConnections;
142      }      }
143    
144        /**
145         * Apply articulation connections to the respective RIFF chunks. You
146         * have to call File::Save() to make changes persistent.
147         */
148        void Articulation::UpdateChunks() {
149            const int iEntrySize = 12; // 12 bytes per connection block
150            pArticulationCk->Resize(HeaderSize + Connections * iEntrySize);
151            uint8_t* pData = (uint8_t*) pArticulationCk->LoadChunkData();
152            store16(&pData[0], HeaderSize);
153            store16(&pData[2], Connections);
154            for (uint32_t i = 0; i < Connections; i++) {
155                Connection::conn_block_t c = pConnections[i].ToConnBlock();
156                store16(&pData[HeaderSize + i * iEntrySize],     c.source);
157                store16(&pData[HeaderSize + i * iEntrySize + 2], c.control);
158                store16(&pData[HeaderSize + i * iEntrySize + 4], c.destination);
159                store16(&pData[HeaderSize + i * iEntrySize + 6], c.transform);
160                store32(&pData[HeaderSize + i * iEntrySize + 8], c.scale);
161            }
162        }
163    
164    
165    
166  // *************** Articulator  ***************  // *************** Articulator  ***************
# Line 100  namespace DLS { Line 189  namespace DLS {
189          RIFF::List* lart = pParentList->GetSubList(LIST_TYPE_LAR2);          RIFF::List* lart = pParentList->GetSubList(LIST_TYPE_LAR2);
190          if (!lart)  lart = pParentList->GetSubList(LIST_TYPE_LART);          if (!lart)  lart = pParentList->GetSubList(LIST_TYPE_LART);
191          if (lart) {          if (lart) {
192              uint32_t artCkType = (lart->GetListType() == LIST_TYPE_LAR2) ? LIST_TYPE_ART2              uint32_t artCkType = (lart->GetListType() == LIST_TYPE_LAR2) ? CHUNK_ID_ART2
193                                                                           : LIST_TYPE_ART1;                                                                           : CHUNK_ID_ARTL;
194              RIFF::List* art = lart->GetFirstSubList();              RIFF::Chunk* art = lart->GetFirstSubChunk();
195              while (art) {              while (art) {
196                  if (art->GetListType() == artCkType) {                  if (art->GetChunkID() == artCkType) {
197                      if (!pArticulations) pArticulations = new ArticulationList;                      if (!pArticulations) pArticulations = new ArticulationList;
198                      pArticulations->push_back(new Articulation(art));                      pArticulations->push_back(new Articulation(art));
199                  }                  }
200                  art = lart->GetNextSubList();                  art = lart->GetNextSubChunk();
201              }              }
202          }          }
203      }      }
# Line 125  namespace DLS { Line 214  namespace DLS {
214          }          }
215      }      }
216    
217        /**
218         * Apply all articulations to the respective RIFF chunks. You have to
219         * call File::Save() to make changes persistent.
220         */
221        void Articulator::UpdateChunks() {
222            if (pArticulations) {
223                ArticulationList::iterator iter = pArticulations->begin();
224                ArticulationList::iterator end  = pArticulations->end();
225                for (; iter != end; ++iter) {
226                    (*iter)->UpdateChunks();
227                }
228            }
229        }
230        
231        /**
232         * Not yet implemented in this version, since the .gig format does
233         * not need to copy DLS articulators and so far nobody used pure
234         * DLS instrument AFAIK.
235         */
236        void Articulator::CopyAssign(const Articulator* orig) {
237            //TODO: implement deep copy assignment for this class
238        }
239    
240    
241    
242  // *************** Info  ***************  // *************** Info  ***************
243  // *  // *
244    
245        /** @brief Constructor.
246         *
247         * Initializes the info strings with values provided by an INFO list chunk.
248         *
249         * @param list - pointer to a list chunk which contains an INFO list chunk
250         */
251      Info::Info(RIFF::List* list) {      Info::Info(RIFF::List* list) {
252            pFixedStringLengths = NULL;
253            pResourceListChunk = list;
254          if (list) {          if (list) {
255              RIFF::List* lstINFO = list->GetSubList(LIST_TYPE_INFO);              RIFF::List* lstINFO = list->GetSubList(LIST_TYPE_INFO);
256              if (lstINFO) {              if (lstINFO) {
# Line 150  namespace DLS { Line 270  namespace DLS {
270                  LoadString(CHUNK_ID_ISRC, lstINFO, Source);                  LoadString(CHUNK_ID_ISRC, lstINFO, Source);
271                  LoadString(CHUNK_ID_ISRF, lstINFO, SourceForm);                  LoadString(CHUNK_ID_ISRF, lstINFO, SourceForm);
272                  LoadString(CHUNK_ID_ICMS, lstINFO, Commissioned);                  LoadString(CHUNK_ID_ICMS, lstINFO, Commissioned);
273                    LoadString(CHUNK_ID_ISBJ, lstINFO, Subject);
274                }
275            }
276        }
277    
278        Info::~Info() {
279        }
280    
281        /**
282         * Forces specific Info fields to be of a fixed length when being saved
283         * to a file. By default the respective RIFF chunk of an Info field
284         * will have a size analogue to its actual string length. With this
285         * method however this behavior can be overridden, allowing to force an
286         * arbitrary fixed size individually for each Info field.
287         *
288         * This method is used as a workaround for the gig format, not for DLS.
289         *
290         * @param lengths - NULL terminated array of string_length_t elements
291         */
292        void Info::SetFixedStringLengths(const string_length_t* lengths) {
293            pFixedStringLengths = lengths;
294        }
295    
296        /** @brief Load given INFO field.
297         *
298         * Load INFO field from INFO chunk with chunk ID \a ChunkID from INFO
299         * list chunk \a lstINFO and save value to \a s.
300         */
301        void Info::LoadString(uint32_t ChunkID, RIFF::List* lstINFO, String& s) {
302            RIFF::Chunk* ck = lstINFO->GetSubChunk(ChunkID);
303            ::LoadString(ck, s); // function from helper.h
304        }
305    
306        /** @brief Apply given INFO field to the respective chunk.
307         *
308         * Apply given info value to info chunk with ID \a ChunkID, which is a
309         * subchunk of INFO list chunk \a lstINFO. If the given chunk already
310         * exists, value \a s will be applied. Otherwise if it doesn't exist yet
311         * and either \a s or \a sDefault is not an empty string, such a chunk
312         * will be created and either \a s or \a sDefault will be applied
313         * (depending on which one is not an empty string, if both are not an
314         * empty string \a s will be preferred).
315         *
316         * @param ChunkID  - 32 bit RIFF chunk ID of INFO subchunk
317         * @param lstINFO  - parent (INFO) RIFF list chunk
318         * @param s        - current value of info field
319         * @param sDefault - default value
320         */
321        void Info::SaveString(uint32_t ChunkID, RIFF::List* lstINFO, const String& s, const String& sDefault) {
322            int size = 0;
323            if (pFixedStringLengths) {
324                for (int i = 0 ; pFixedStringLengths[i].length ; i++) {
325                    if (pFixedStringLengths[i].chunkId == ChunkID) {
326                        size = pFixedStringLengths[i].length;
327                        break;
328                    }
329                }
330            }
331            RIFF::Chunk* ck = lstINFO->GetSubChunk(ChunkID);
332            ::SaveString(ChunkID, ck, lstINFO, s, sDefault, size != 0, size); // function from helper.h
333        }
334    
335        /** @brief Update chunks with current info values.
336         *
337         * Apply current INFO field values to the respective INFO chunks. You
338         * have to call File::Save() to make changes persistent.
339         */
340        void Info::UpdateChunks() {
341            if (!pResourceListChunk) return;
342    
343            // make sure INFO list chunk exists
344            RIFF::List* lstINFO   = pResourceListChunk->GetSubList(LIST_TYPE_INFO);
345    
346            String defaultName = "";
347            String defaultCreationDate = "";
348            String defaultSoftware = "";
349            String defaultComments = "";
350    
351            uint32_t resourceType = pResourceListChunk->GetListType();
352    
353            if (!lstINFO) {
354                lstINFO = pResourceListChunk->AddSubList(LIST_TYPE_INFO);
355    
356                // assemble default values
357                defaultName = "NONAME";
358    
359                if (resourceType == RIFF_TYPE_DLS) {
360                    // get current date
361                    time_t now = time(NULL);
362                    tm* pNowBroken = localtime(&now);
363                    char buf[11];
364                    strftime(buf, 11, "%F", pNowBroken);
365                    defaultCreationDate = buf;
366    
367                    defaultComments = "Created with " + libraryName() + " " + libraryVersion();
368                }
369                if (resourceType == RIFF_TYPE_DLS || resourceType == LIST_TYPE_INS)
370                {
371                    defaultSoftware = libraryName() + " " + libraryVersion();
372              }              }
373          }          }
374    
375            // save values
376    
377            SaveString(CHUNK_ID_IARL, lstINFO, ArchivalLocation, String(""));
378            SaveString(CHUNK_ID_IART, lstINFO, Artists, String(""));
379            SaveString(CHUNK_ID_ICMS, lstINFO, Commissioned, String(""));
380            SaveString(CHUNK_ID_ICMT, lstINFO, Comments, defaultComments);
381            SaveString(CHUNK_ID_ICOP, lstINFO, Copyright, String(""));
382            SaveString(CHUNK_ID_ICRD, lstINFO, CreationDate, defaultCreationDate);
383            SaveString(CHUNK_ID_IENG, lstINFO, Engineer, String(""));
384            SaveString(CHUNK_ID_IGNR, lstINFO, Genre, String(""));
385            SaveString(CHUNK_ID_IKEY, lstINFO, Keywords, String(""));
386            SaveString(CHUNK_ID_IMED, lstINFO, Medium, String(""));
387            SaveString(CHUNK_ID_INAM, lstINFO, Name, defaultName);
388            SaveString(CHUNK_ID_IPRD, lstINFO, Product, String(""));
389            SaveString(CHUNK_ID_ISBJ, lstINFO, Subject, String(""));
390            SaveString(CHUNK_ID_ISFT, lstINFO, Software, defaultSoftware);
391            SaveString(CHUNK_ID_ISRC, lstINFO, Source, String(""));
392            SaveString(CHUNK_ID_ISRF, lstINFO, SourceForm, String(""));
393            SaveString(CHUNK_ID_ITCH, lstINFO, Technician, String(""));
394        }
395        
396        /**
397         * Make a deep copy of the Info object given by @a orig and assign it to
398         * this object.
399         *
400         * @param orig - original Info object to be copied from
401         */
402        void Info::CopyAssign(const Info* orig) {
403            Name = orig->Name;
404            ArchivalLocation = orig->ArchivalLocation;
405            CreationDate = orig->CreationDate;
406            Comments = orig->Comments;
407            Product = orig->Product;
408            Copyright = orig->Copyright;
409            Artists = orig->Artists;
410            Genre = orig->Genre;
411            Keywords = orig->Keywords;
412            Engineer = orig->Engineer;
413            Technician = orig->Technician;
414            Software = orig->Software;
415            Medium = orig->Medium;
416            Source = orig->Source;
417            SourceForm = orig->SourceForm;
418            Commissioned = orig->Commissioned;
419            Subject = orig->Subject;
420            //FIXME: hmm, is copying this pointer a good idea?
421            pFixedStringLengths = orig->pFixedStringLengths;
422      }      }
423    
424    
# Line 159  namespace DLS { Line 426  namespace DLS {
426  // *************** Resource ***************  // *************** Resource ***************
427  // *  // *
428    
429        /** @brief Constructor.
430         *
431         * Initializes the 'Resource' object with values provided by a given
432         * INFO list chunk and a DLID chunk (the latter optional).
433         *
434         * @param Parent      - pointer to parent 'Resource', NULL if this is
435         *                      the toplevel 'Resource' object
436         * @param lstResource - pointer to an INFO list chunk
437         */
438      Resource::Resource(Resource* Parent, RIFF::List* lstResource) {      Resource::Resource(Resource* Parent, RIFF::List* lstResource) {
439          pParent = Parent;          pParent = Parent;
440            pResourceList = lstResource;
441    
442          pInfo = new Info(lstResource);          pInfo = new Info(lstResource);
443    
# Line 180  namespace DLS { Line 457  namespace DLS {
457          if (pInfo)  delete pInfo;          if (pInfo)  delete pInfo;
458      }      }
459    
460        /** @brief Update chunks with current Resource data.
461         *
462         * Apply Resource data persistently below the previously given resource
463         * list chunk. This will currently only include the INFO data. The DLSID
464         * will not be applied at the moment (yet).
465         *
466         * You have to call File::Save() to make changes persistent.
467         */
468        void Resource::UpdateChunks() {
469            pInfo->UpdateChunks();
470    
471            if (pDLSID) {
472                // make sure 'dlid' chunk exists
473                RIFF::Chunk* ckDLSID = pResourceList->GetSubChunk(CHUNK_ID_DLID);
474                if (!ckDLSID) ckDLSID = pResourceList->AddSubChunk(CHUNK_ID_DLID, 16);
475                uint8_t* pData = (uint8_t*)ckDLSID->LoadChunkData();
476                // update 'dlid' chunk
477                store32(&pData[0], pDLSID->ulData1);
478                store16(&pData[4], pDLSID->usData2);
479                store16(&pData[6], pDLSID->usData3);
480                memcpy(&pData[8], pDLSID->abData, 8);
481            }
482        }
483    
484        /**
485         * Generates a new DLSID for the resource.
486         */
487        void Resource::GenerateDLSID() {
488    #if defined(WIN32) || defined(__APPLE__) || defined(HAVE_UUID_GENERATE)
489    
490            if (!pDLSID) pDLSID = new dlsid_t;
491    
492    #ifdef WIN32
493    
494            UUID uuid;
495            UuidCreate(&uuid);
496            pDLSID->ulData1 = uuid.Data1;
497            pDLSID->usData2 = uuid.Data2;
498            pDLSID->usData3 = uuid.Data3;
499            memcpy(pDLSID->abData, uuid.Data4, 8);
500    
501    #elif defined(__APPLE__)
502    
503            CFUUIDRef uuidRef = CFUUIDCreate(NULL);
504            CFUUIDBytes uuid = CFUUIDGetUUIDBytes(uuidRef);
505            CFRelease(uuidRef);
506            pDLSID->ulData1 = uuid.byte0 | uuid.byte1 << 8 | uuid.byte2 << 16 | uuid.byte3 << 24;
507            pDLSID->usData2 = uuid.byte4 | uuid.byte5 << 8;
508            pDLSID->usData3 = uuid.byte6 | uuid.byte7 << 8;
509            pDLSID->abData[0] = uuid.byte8;
510            pDLSID->abData[1] = uuid.byte9;
511            pDLSID->abData[2] = uuid.byte10;
512            pDLSID->abData[3] = uuid.byte11;
513            pDLSID->abData[4] = uuid.byte12;
514            pDLSID->abData[5] = uuid.byte13;
515            pDLSID->abData[6] = uuid.byte14;
516            pDLSID->abData[7] = uuid.byte15;
517    #else
518            uuid_t uuid;
519            uuid_generate(uuid);
520            pDLSID->ulData1 = uuid[0] | uuid[1] << 8 | uuid[2] << 16 | uuid[3] << 24;
521            pDLSID->usData2 = uuid[4] | uuid[5] << 8;
522            pDLSID->usData3 = uuid[6] | uuid[7] << 8;
523            memcpy(pDLSID->abData, &uuid[8], 8);
524    #endif
525    #endif
526        }
527        
528        /**
529         * Make a deep copy of the Resource object given by @a orig and assign it
530         * to this object.
531         *
532         * @param orig - original Resource object to be copied from
533         */
534        void Resource::CopyAssign(const Resource* orig) {
535            pInfo->CopyAssign(orig->pInfo);
536        }
537    
538    
539  // *************** Sampler ***************  // *************** Sampler ***************
540  // *  // *
541    
542      Sampler::Sampler(RIFF::List* ParentList) {      Sampler::Sampler(RIFF::List* ParentList) {
543            pParentList       = ParentList;
544          RIFF::Chunk* wsmp = ParentList->GetSubChunk(CHUNK_ID_WSMP);          RIFF::Chunk* wsmp = ParentList->GetSubChunk(CHUNK_ID_WSMP);
545          if (!wsmp) throw DLS::Exception("Mandatory <wsmp> chunk not found.");          if (wsmp) {
546          uint32_t headersize = wsmp->ReadUint32();              uiHeaderSize   = wsmp->ReadUint32();
547          UnityNote        = wsmp->ReadUint16();              UnityNote      = wsmp->ReadUint16();
548          FineTune         = wsmp->ReadInt16();              FineTune       = wsmp->ReadInt16();
549          Gain             = wsmp->ReadInt32();              Gain           = wsmp->ReadInt32();
550          SamplerOptions   = wsmp->ReadUint32();              SamplerOptions = wsmp->ReadUint32();
551                SampleLoops    = wsmp->ReadUint32();
552            } else { // 'wsmp' chunk missing
553                uiHeaderSize   = 20;
554                UnityNote      = 60;
555                FineTune       = 0; // +- 0 cents
556                Gain           = 0; // 0 dB
557                SamplerOptions = F_WSMP_NO_COMPRESSION;
558                SampleLoops    = 0;
559            }
560          NoSampleDepthTruncation = SamplerOptions & F_WSMP_NO_TRUNCATION;          NoSampleDepthTruncation = SamplerOptions & F_WSMP_NO_TRUNCATION;
561          NoSampleCompression     = SamplerOptions & F_WSMP_NO_COMPRESSION;          NoSampleCompression     = SamplerOptions & F_WSMP_NO_COMPRESSION;
         SampleLoops             = wsmp->ReadUint32();  
562          pSampleLoops            = (SampleLoops) ? new sample_loop_t[SampleLoops] : NULL;          pSampleLoops            = (SampleLoops) ? new sample_loop_t[SampleLoops] : NULL;
563          wsmp->SetPos(headersize);          if (SampleLoops) {
564          for (uint32_t i = 0; i < SampleLoops; i++) {              wsmp->SetPos(uiHeaderSize);
565              wsmp->Read(pSampleLoops + i, 4, 4);              for (uint32_t i = 0; i < SampleLoops; i++) {
566              if (pSampleLoops[i].Size > sizeof(sample_loop_t)) { // if loop struct was extended                  wsmp->Read(pSampleLoops + i, 4, 4);
567                  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
568                        wsmp->SetPos(pSampleLoops[i].Size - sizeof(sample_loop_t), RIFF::stream_curpos);
569                    }
570              }              }
571          }          }
572      }      }
# Line 210  namespace DLS { Line 575  namespace DLS {
575          if (pSampleLoops) delete[] pSampleLoops;          if (pSampleLoops) delete[] pSampleLoops;
576      }      }
577    
578        void Sampler::SetGain(int32_t gain) {
579            Gain = gain;
580        }
581    
582        /**
583         * Apply all sample player options to the respective RIFF chunk. You
584         * have to call File::Save() to make changes persistent.
585         */
586        void Sampler::UpdateChunks() {
587            // make sure 'wsmp' chunk exists
588            RIFF::Chunk* wsmp = pParentList->GetSubChunk(CHUNK_ID_WSMP);
589            int wsmpSize = uiHeaderSize + SampleLoops * 16;
590            if (!wsmp) {
591                wsmp = pParentList->AddSubChunk(CHUNK_ID_WSMP, wsmpSize);
592            } else if (wsmp->GetSize() != wsmpSize) {
593                wsmp->Resize(wsmpSize);
594            }
595            uint8_t* pData = (uint8_t*) wsmp->LoadChunkData();
596            // update headers size
597            store32(&pData[0], uiHeaderSize);
598            // update respective sampler options bits
599            SamplerOptions = (NoSampleDepthTruncation) ? SamplerOptions | F_WSMP_NO_TRUNCATION
600                                                       : SamplerOptions & (~F_WSMP_NO_TRUNCATION);
601            SamplerOptions = (NoSampleCompression) ? SamplerOptions | F_WSMP_NO_COMPRESSION
602                                                   : SamplerOptions & (~F_WSMP_NO_COMPRESSION);
603            store16(&pData[4], UnityNote);
604            store16(&pData[6], FineTune);
605            store32(&pData[8], Gain);
606            store32(&pData[12], SamplerOptions);
607            store32(&pData[16], SampleLoops);
608            // update loop definitions
609            for (uint32_t i = 0; i < SampleLoops; i++) {
610                //FIXME: this does not handle extended loop structs correctly
611                store32(&pData[uiHeaderSize + i * 16], pSampleLoops[i].Size);
612                store32(&pData[uiHeaderSize + i * 16 + 4], pSampleLoops[i].LoopType);
613                store32(&pData[uiHeaderSize + i * 16 + 8], pSampleLoops[i].LoopStart);
614                store32(&pData[uiHeaderSize + i * 16 + 12], pSampleLoops[i].LoopLength);
615            }
616        }
617    
618        /**
619         * Adds a new sample loop with the provided loop definition.
620         *
621         * @param pLoopDef - points to a loop definition that is to be copied
622         */
623        void Sampler::AddSampleLoop(sample_loop_t* pLoopDef) {
624            sample_loop_t* pNewLoops = new sample_loop_t[SampleLoops + 1];
625            // copy old loops array
626            for (int i = 0; i < SampleLoops; i++) {
627                pNewLoops[i] = pSampleLoops[i];
628            }
629            // add the new loop
630            pNewLoops[SampleLoops] = *pLoopDef;
631            // auto correct size field
632            pNewLoops[SampleLoops].Size = sizeof(DLS::sample_loop_t);
633            // free the old array and update the member variables
634            if (SampleLoops) delete[] pSampleLoops;
635            pSampleLoops = pNewLoops;
636            SampleLoops++;
637        }
638    
639        /**
640         * Deletes an existing sample loop.
641         *
642         * @param pLoopDef - pointer to existing loop definition
643         * @throws Exception - if given loop definition does not exist
644         */
645        void Sampler::DeleteSampleLoop(sample_loop_t* pLoopDef) {
646            sample_loop_t* pNewLoops = new sample_loop_t[SampleLoops - 1];
647            // copy old loops array (skipping given loop)
648            for (int i = 0, o = 0; i < SampleLoops; i++) {
649                if (&pSampleLoops[i] == pLoopDef) continue;
650                if (o == SampleLoops - 1) {
651                    delete[] pNewLoops;
652                    throw Exception("Could not delete Sample Loop, because it does not exist");
653                }
654                pNewLoops[o] = pSampleLoops[i];
655                o++;
656            }
657            // free the old array and update the member variables
658            if (SampleLoops) delete[] pSampleLoops;
659            pSampleLoops = pNewLoops;
660            SampleLoops--;
661        }
662        
663        /**
664         * Make a deep copy of the Sampler object given by @a orig and assign it
665         * to this object.
666         *
667         * @param orig - original Sampler object to be copied from
668         */
669        void Sampler::CopyAssign(const Sampler* orig) {
670            // copy trivial scalars
671            UnityNote = orig->UnityNote;
672            FineTune = orig->FineTune;
673            Gain = orig->Gain;
674            NoSampleDepthTruncation = orig->NoSampleDepthTruncation;
675            NoSampleCompression = orig->NoSampleCompression;
676            SamplerOptions = orig->SamplerOptions;
677            
678            // copy sample loops
679            if (SampleLoops) delete[] pSampleLoops;
680            pSampleLoops = new sample_loop_t[orig->SampleLoops];
681            memcpy(pSampleLoops, orig->pSampleLoops, orig->SampleLoops * sizeof(sample_loop_t));
682            SampleLoops = orig->SampleLoops;
683        }
684    
685    
686  // *************** Sample ***************  // *************** Sample ***************
687  // *  // *
688    
689        /** @brief Constructor.
690         *
691         * Load an existing sample or create a new one. A 'wave' list chunk must
692         * be given to this constructor. In case the given 'wave' list chunk
693         * contains a 'fmt' and 'data' chunk, the format and sample data will be
694         * loaded from there, otherwise default values will be used and those
695         * chunks will be created when File::Save() will be called later on.
696         *
697         * @param pFile          - pointer to DLS::File where this sample is
698         *                         located (or will be located)
699         * @param waveList       - pointer to 'wave' list chunk which is (or
700         *                         will be) associated with this sample
701         * @param WavePoolOffset - offset of this sample data from wave pool
702         *                         ('wvpl') list chunk
703         */
704      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) {
705            pWaveList = waveList;
706          ulWavePoolOffset = WavePoolOffset - LIST_HEADER_SIZE;          ulWavePoolOffset = WavePoolOffset - LIST_HEADER_SIZE;
707          pCkFormat = waveList->GetSubChunk(CHUNK_ID_FMT);          pCkFormat = waveList->GetSubChunk(CHUNK_ID_FMT);
708          pCkData   = waveList->GetSubChunk(CHUNK_ID_DATA);          pCkData   = waveList->GetSubChunk(CHUNK_ID_DATA);
709          if (!pCkFormat || !pCkData) throw DLS::Exception("Mandatory chunks in wave list not found.");          if (pCkFormat) {
710                // common fields
711                FormatTag              = pCkFormat->ReadUint16();
712                Channels               = pCkFormat->ReadUint16();
713                SamplesPerSecond       = pCkFormat->ReadUint32();
714                AverageBytesPerSecond  = pCkFormat->ReadUint32();
715                BlockAlign             = pCkFormat->ReadUint16();
716                // PCM format specific
717                if (FormatTag == DLS_WAVE_FORMAT_PCM) {
718                    BitDepth     = pCkFormat->ReadUint16();
719                    FrameSize    = (BitDepth / 8) * Channels;
720                } else { // unsupported sample data format
721                    BitDepth     = 0;
722                    FrameSize    = 0;
723                }
724            } else { // 'fmt' chunk missing
725                FormatTag              = DLS_WAVE_FORMAT_PCM;
726                BitDepth               = 16;
727                Channels               = 1;
728                SamplesPerSecond       = 44100;
729                AverageBytesPerSecond  = (BitDepth / 8) * SamplesPerSecond * Channels;
730                FrameSize              = (BitDepth / 8) * Channels;
731                BlockAlign             = FrameSize;
732            }
733            SamplesTotal = (pCkData) ? (FormatTag == DLS_WAVE_FORMAT_PCM) ? pCkData->GetSize() / FrameSize
734                                                                          : 0
735                                     : 0;
736        }
737    
738          // common fields      /** @brief Destructor.
739          FormatTag              = pCkFormat->ReadUint16();       *
740          Channels               = pCkFormat->ReadUint16();       * Removes RIFF chunks associated with this Sample and frees all
741          SamplesPerSecond       = pCkFormat->ReadUint32();       * memory occupied by this sample.
742          AverageBytesPerSecond  = pCkFormat->ReadUint32();       */
743          BlockAlign             = pCkFormat->ReadUint16();      Sample::~Sample() {
744            RIFF::List* pParent = pWaveList->GetParent();
745          // PCM format specific          pParent->DeleteSubChunk(pWaveList);
746          if (FormatTag == WAVE_FORMAT_PCM) {      }
747              BitDepth     = pCkFormat->ReadUint16();      
748              FrameSize    = (FormatTag == WAVE_FORMAT_PCM) ? (BitDepth / 8) * Channels      /**
749                                                            : 0;       * Make a deep copy of the Sample object given by @a orig (without the
750              SamplesTotal = (FormatTag == WAVE_FORMAT_PCM) ? pCkData->GetSize() / FrameSize       * actual sample waveform data however) and assign it to this object.
751                                                            : 0;       *
752          }       * This is a special internal variant of CopyAssign() which only copies the
753          else {       * most mandatory member variables. It will be called by gig::Sample
754              BitDepth     = 0;       * descendent instead of CopyAssign() since gig::Sample has its own
755              FrameSize    = 0;       * implementation to access and copy the actual sample waveform data.
756              SamplesTotal = 0;       *
757         * @param orig - original Sample object to be copied from
758         */
759        void Sample::CopyAssignCore(const Sample* orig) {
760            // handle base classes
761            Resource::CopyAssign(orig);
762            // handle actual own attributes of this class
763            FormatTag = orig->FormatTag;
764            Channels = orig->Channels;
765            SamplesPerSecond = orig->SamplesPerSecond;
766            AverageBytesPerSecond = orig->AverageBytesPerSecond;
767            BlockAlign = orig->BlockAlign;
768            BitDepth = orig->BitDepth;
769            SamplesTotal = orig->SamplesTotal;
770            FrameSize = orig->FrameSize;
771        }
772        
773        /**
774         * Make a deep copy of the Sample object given by @a orig and assign it to
775         * this object.
776         *
777         * @param orig - original Sample object to be copied from
778         */
779        void Sample::CopyAssign(const Sample* orig) {
780            CopyAssignCore(orig);
781            
782            // copy sample waveform data (reading directly from disc)
783            Resize(orig->GetSize());
784            char* buf = (char*) LoadSampleData();
785            Sample* pOrig = (Sample*) orig; //HACK: circumventing the constness here for now
786            const unsigned long restorePos = pOrig->pCkData->GetPos();
787            pOrig->SetPos(0);
788            for (unsigned long todo = pOrig->GetSize(), i = 0; todo; ) {
789                const int iReadAtOnce = 64*1024;
790                unsigned long n = (iReadAtOnce < todo) ? iReadAtOnce : todo;
791                n = pOrig->Read(&buf[i], n);
792                if (!n) break;
793                todo -= n;
794                i += (n * pOrig->FrameSize);
795          }          }
796            pOrig->pCkData->SetPos(restorePos);
797      }      }
798    
799        /** @brief Load sample data into RAM.
800         *
801         * In case the respective 'data' chunk exists, the sample data will be
802         * loaded into RAM (if not done already) and a pointer to the data in
803         * RAM will be returned. If this is a new sample, you have to call
804         * Resize() with the desired sample size to create the mandatory RIFF
805         * chunk for the sample wave data.
806         *
807         * You can call LoadChunkData() again if you previously scheduled to
808         * enlarge the sample data RIFF chunk with a Resize() call. In that case
809         * the buffer will be enlarged to the new, scheduled size and you can
810         * already place the sample wave data to the buffer and finally call
811         * File::Save() to enlarge the sample data's chunk physically and write
812         * the new sample wave data in one rush. This approach is definitely
813         * recommended if you have to enlarge and write new sample data to a lot
814         * of samples.
815         *
816         * <b>Caution:</b> the buffer pointer will be invalidated once
817         * File::Save() was called. You have to call LoadChunkData() again to
818         * get a new, valid pointer whenever File::Save() was called.
819         *
820         * @returns pointer to sample data in RAM, NULL in case respective
821         *          'data' chunk does not exist (yet)
822         * @throws Exception if data buffer could not be enlarged
823         * @see Resize(), File::Save()
824         */
825      void* Sample::LoadSampleData() {      void* Sample::LoadSampleData() {
826          return pCkData->LoadChunkData();          return (pCkData) ? pCkData->LoadChunkData() : NULL;
827      }      }
828    
829        /** @brief Free sample data from RAM.
830         *
831         * In case sample data was previously successfully loaded into RAM with
832         * LoadSampleData(), this method will free the sample data from RAM.
833         */
834      void Sample::ReleaseSampleData() {      void Sample::ReleaseSampleData() {
835          pCkData->ReleaseChunkData();          if (pCkData) pCkData->ReleaseChunkData();
836        }
837    
838        /** @brief Returns sample size.
839         *
840         * Returns the sample wave form's data size (in sample points). This is
841         * actually the current, physical size (converted to sample points) of
842         * the RIFF chunk which encapsulates the sample's wave data. The
843         * returned value is dependant to the current FrameSize value.
844         *
845         * @returns number of sample points or 0 if FormatTag != DLS_WAVE_FORMAT_PCM
846         * @see FrameSize, FormatTag
847         */
848        unsigned long Sample::GetSize() const {
849            if (FormatTag != DLS_WAVE_FORMAT_PCM) return 0;
850            return (pCkData) ? pCkData->GetSize() / FrameSize : 0;
851        }
852    
853        /** @brief Resize sample.
854         *
855         * Resizes the sample's wave form data, that is the actual size of
856         * sample wave data possible to be written for this sample. This call
857         * will return immediately and just schedule the resize operation. You
858         * should call File::Save() to actually perform the resize operation(s)
859         * "physically" to the file. As this can take a while on large files, it
860         * is recommended to call Resize() first on all samples which have to be
861         * resized and finally to call File::Save() to perform all those resize
862         * operations in one rush.
863         *
864         * The actual size (in bytes) is dependant to the current FrameSize
865         * value. You may want to set FrameSize before calling Resize().
866         *
867         * <b>Caution:</b> You cannot directly write to enlarged samples before
868         * calling File::Save() as this might exceed the current sample's
869         * boundary!
870         *
871         * Also note: only DLS_WAVE_FORMAT_PCM is currently supported, that is
872         * FormatTag must be DLS_WAVE_FORMAT_PCM. Trying to resize samples with
873         * other formats will fail!
874         *
875         * @param iNewSize - new sample wave data size in sample points (must be
876         *                   greater than zero)
877         * @throws Excecption if FormatTag != DLS_WAVE_FORMAT_PCM
878         * @throws Exception if \a iNewSize is less than 1
879         * @see File::Save(), FrameSize, FormatTag
880         */
881        void Sample::Resize(int iNewSize) {
882            if (FormatTag != DLS_WAVE_FORMAT_PCM) throw Exception("Sample's format is not DLS_WAVE_FORMAT_PCM");
883            if (iNewSize < 1) throw Exception("Sample size must be at least one sample point");
884            const int iSizeInBytes = iNewSize * FrameSize;
885            pCkData = pWaveList->GetSubChunk(CHUNK_ID_DATA);
886            if (pCkData) pCkData->Resize(iSizeInBytes);
887            else pCkData = pWaveList->AddSubChunk(CHUNK_ID_DATA, iSizeInBytes);
888      }      }
889    
890      /**      /**
# Line 256  namespace DLS { Line 892  namespace DLS {
892       * 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
893       * the sample into RAM, thus for disk streaming.       * the sample into RAM, thus for disk streaming.
894       *       *
895         * Also note: only DLS_WAVE_FORMAT_PCM is currently supported, that is
896         * FormatTag must be DLS_WAVE_FORMAT_PCM. Trying to reposition the sample
897         * with other formats will fail!
898         *
899       * @param SampleCount  number of sample points       * @param SampleCount  number of sample points
900       * @param Whence       to which relation \a SampleCount refers to       * @param Whence       to which relation \a SampleCount refers to
901         * @returns new position within the sample, 0 if
902         *          FormatTag != DLS_WAVE_FORMAT_PCM
903         * @throws Exception if no data RIFF chunk was created for the sample yet
904         * @see FrameSize, FormatTag
905       */       */
906      unsigned long Sample::SetPos(unsigned long SampleCount, RIFF::stream_whence_t Whence) {      unsigned long Sample::SetPos(unsigned long SampleCount, RIFF::stream_whence_t Whence) {
907          if (FormatTag != WAVE_FORMAT_PCM) return 0; // failed: wave data not PCM format          if (FormatTag != DLS_WAVE_FORMAT_PCM) return 0; // failed: wave data not PCM format
908            if (!pCkData) throw Exception("No data chunk created for sample yet, call Sample::Resize() to create one");
909          unsigned long orderedBytes = SampleCount * FrameSize;          unsigned long orderedBytes = SampleCount * FrameSize;
910          unsigned long result = pCkData->SetPos(orderedBytes, Whence);          unsigned long result = pCkData->SetPos(orderedBytes, Whence);
911          return (result == orderedBytes) ? SampleCount          return (result == orderedBytes) ? SampleCount
# Line 277  namespace DLS { Line 922  namespace DLS {
922       * @param SampleCount  number of sample points to read       * @param SampleCount  number of sample points to read
923       */       */
924      unsigned long Sample::Read(void* pBuffer, unsigned long SampleCount) {      unsigned long Sample::Read(void* pBuffer, unsigned long SampleCount) {
925          if (FormatTag != WAVE_FORMAT_PCM) return 0; // failed: wave data not PCM format          if (FormatTag != DLS_WAVE_FORMAT_PCM) return 0; // failed: wave data not PCM format
926          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?
927      }      }
928    
929        /** @brief Write sample wave data.
930         *
931         * Writes \a SampleCount number of sample points from the buffer pointed
932         * by \a pBuffer and increments the position within the sample. Use this
933         * method to directly write the sample data to disk, i.e. if you don't
934         * want or cannot load the whole sample data into RAM.
935         *
936         * You have to Resize() the sample to the desired size and call
937         * File::Save() <b>before</b> using Write().
938         *
939         * @param pBuffer     - source buffer
940         * @param SampleCount - number of sample points to write
941         * @throws Exception if current sample size is too small
942         * @see LoadSampleData()
943         */
944        unsigned long Sample::Write(void* pBuffer, unsigned long SampleCount) {
945            if (FormatTag != DLS_WAVE_FORMAT_PCM) return 0; // failed: wave data not PCM format
946            if (GetSize() < SampleCount) throw Exception("Could not write sample data, current sample size to small");
947            return pCkData->Write(pBuffer, SampleCount, FrameSize); // FIXME: channel inversion due to endian correction?
948        }
949    
950        /**
951         * Apply sample and its settings to the respective RIFF chunks. You have
952         * to call File::Save() to make changes persistent.
953         *
954         * @throws Exception if FormatTag != DLS_WAVE_FORMAT_PCM or no sample data
955         *                   was provided yet
956         */
957        void Sample::UpdateChunks() {
958            if (FormatTag != DLS_WAVE_FORMAT_PCM)
959                throw Exception("Could not save sample, only PCM format is supported");
960            // we refuse to do anything if not sample wave form was provided yet
961            if (!pCkData)
962                throw Exception("Could not save sample, there is no sample data to save");
963            // update chunks of base class as well
964            Resource::UpdateChunks();
965            // make sure 'fmt' chunk exists
966            RIFF::Chunk* pCkFormat = pWaveList->GetSubChunk(CHUNK_ID_FMT);
967            if (!pCkFormat) pCkFormat = pWaveList->AddSubChunk(CHUNK_ID_FMT, 16); // assumes PCM format
968            uint8_t* pData = (uint8_t*) pCkFormat->LoadChunkData();
969            // update 'fmt' chunk
970            store16(&pData[0], FormatTag);
971            store16(&pData[2], Channels);
972            store32(&pData[4], SamplesPerSecond);
973            store32(&pData[8], AverageBytesPerSecond);
974            store16(&pData[12], BlockAlign);
975            store16(&pData[14], BitDepth); // assuming PCM format
976        }
977    
978    
979    
980  // *************** Region ***************  // *************** Region ***************
# Line 289  namespace DLS { Line 983  namespace DLS {
983      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) {
984          pCkRegion = rgnList;          pCkRegion = rgnList;
985    
986            // articulation informations
987          RIFF::Chunk* rgnh = rgnList->GetSubChunk(CHUNK_ID_RGNH);          RIFF::Chunk* rgnh = rgnList->GetSubChunk(CHUNK_ID_RGNH);
988          rgnh->Read(&KeyRange, 2, 2);          if (rgnh) {
989          rgnh->Read(&VelocityRange, 2, 2);              rgnh->Read(&KeyRange, 2, 2);
990          uint16_t optionflags = rgnh->ReadUint16();              rgnh->Read(&VelocityRange, 2, 2);
991          SelfNonExclusive = optionflags & F_RGN_OPTION_SELFNONEXCLUSIVE;              FormatOptionFlags = rgnh->ReadUint16();
992          KeyGroup = rgnh->ReadUint16();              KeyGroup = rgnh->ReadUint16();
993          // Layer is optional              // Layer is optional
994          if (rgnh->RemainingBytes() >= sizeof(uint16_t)) {              if (rgnh->RemainingBytes() >= sizeof(uint16_t)) {
995              rgnh->Read(&Layer, 1, sizeof(uint16_t));                  rgnh->Read(&Layer, 1, sizeof(uint16_t));
996                } else Layer = 0;
997            } else { // 'rgnh' chunk is missing
998                KeyRange.low  = 0;
999                KeyRange.high = 127;
1000                VelocityRange.low  = 0;
1001                VelocityRange.high = 127;
1002                FormatOptionFlags = F_RGN_OPTION_SELFNONEXCLUSIVE;
1003                KeyGroup = 0;
1004                Layer = 0;
1005          }          }
1006          else Layer = 0;          SelfNonExclusive = FormatOptionFlags & F_RGN_OPTION_SELFNONEXCLUSIVE;
1007    
1008            // sample informations
1009          RIFF::Chunk* wlnk = rgnList->GetSubChunk(CHUNK_ID_WLNK);          RIFF::Chunk* wlnk = rgnList->GetSubChunk(CHUNK_ID_WLNK);
1010          optionflags  = wlnk->ReadUint16();          if (wlnk) {
1011          PhaseMaster  = optionflags & F_WAVELINK_PHASE_MASTER;              WaveLinkOptionFlags = wlnk->ReadUint16();
1012          MultiChannel = optionflags & F_WAVELINK_MULTICHANNEL;              PhaseGroup          = wlnk->ReadUint16();
1013          PhaseGroup         = wlnk->ReadUint16();              Channel             = wlnk->ReadUint32();
1014          Channel            = wlnk->ReadUint32();              WavePoolTableIndex  = wlnk->ReadUint32();
1015          WavePoolTableIndex = wlnk->ReadUint32();          } else { // 'wlnk' chunk is missing
1016                WaveLinkOptionFlags = 0;
1017                PhaseGroup          = 0;
1018                Channel             = 0; // mono
1019                WavePoolTableIndex  = 0; // first entry in wave pool table
1020            }
1021            PhaseMaster  = WaveLinkOptionFlags & F_WAVELINK_PHASE_MASTER;
1022            MultiChannel = WaveLinkOptionFlags & F_WAVELINK_MULTICHANNEL;
1023    
1024          pSample = NULL;          pSample = NULL;
1025      }      }
1026    
1027        /** @brief Destructor.
1028         *
1029         * Removes RIFF chunks associated with this Region.
1030         */
1031      Region::~Region() {      Region::~Region() {
1032            RIFF::List* pParent = pCkRegion->GetParent();
1033            pParent->DeleteSubChunk(pCkRegion);
1034      }      }
1035    
1036      Sample* Region::GetSample() {      Sample* Region::GetSample() {
# Line 327  namespace DLS { Line 1045  namespace DLS {
1045          return NULL;          return NULL;
1046      }      }
1047    
1048        /**
1049         * Assign another sample to this Region.
1050         *
1051         * @param pSample - sample to be assigned
1052         */
1053        void Region::SetSample(Sample* pSample) {
1054            this->pSample = pSample;
1055            WavePoolTableIndex = 0; // we update this offset when we Save()
1056        }
1057    
1058        /**
1059         * Modifies the key range of this Region and makes sure the respective
1060         * chunks are in correct order.
1061         *
1062         * @param Low  - lower end of key range
1063         * @param High - upper end of key range
1064         */
1065        void Region::SetKeyRange(uint16_t Low, uint16_t High) {
1066            KeyRange.low  = Low;
1067            KeyRange.high = High;
1068    
1069            // make sure regions are already loaded
1070            Instrument* pInstrument = (Instrument*) GetParent();
1071            if (!pInstrument->pRegions) pInstrument->LoadRegions();
1072            if (!pInstrument->pRegions) return;
1073    
1074            // find the r which is the first one to the right of this region
1075            // at its new position
1076            Region* r = NULL;
1077            Region* prev_region = NULL;
1078            for (
1079                Instrument::RegionList::iterator iter = pInstrument->pRegions->begin();
1080                iter != pInstrument->pRegions->end(); iter++
1081            ) {
1082                if ((*iter)->KeyRange.low > this->KeyRange.low) {
1083                    r = *iter;
1084                    break;
1085                }
1086                prev_region = *iter;
1087            }
1088    
1089            // place this region before r if it's not already there
1090            if (prev_region != this) pInstrument->MoveRegion(this, r);
1091        }
1092    
1093        /**
1094         * Apply Region settings to the respective RIFF chunks. You have to
1095         * call File::Save() to make changes persistent.
1096         *
1097         * @throws Exception - if the Region's sample could not be found
1098         */
1099        void Region::UpdateChunks() {
1100            // make sure 'rgnh' chunk exists
1101            RIFF::Chunk* rgnh = pCkRegion->GetSubChunk(CHUNK_ID_RGNH);
1102            if (!rgnh) rgnh = pCkRegion->AddSubChunk(CHUNK_ID_RGNH, Layer ? 14 : 12);
1103            uint8_t* pData = (uint8_t*) rgnh->LoadChunkData();
1104            FormatOptionFlags = (SelfNonExclusive)
1105                                    ? FormatOptionFlags | F_RGN_OPTION_SELFNONEXCLUSIVE
1106                                    : FormatOptionFlags & (~F_RGN_OPTION_SELFNONEXCLUSIVE);
1107            // update 'rgnh' chunk
1108            store16(&pData[0], KeyRange.low);
1109            store16(&pData[2], KeyRange.high);
1110            store16(&pData[4], VelocityRange.low);
1111            store16(&pData[6], VelocityRange.high);
1112            store16(&pData[8], FormatOptionFlags);
1113            store16(&pData[10], KeyGroup);
1114            if (rgnh->GetSize() >= 14) store16(&pData[12], Layer);
1115    
1116            // update chunks of base classes as well (but skip Resource,
1117            // as a rgn doesn't seem to have dlid and INFO chunks)
1118            Articulator::UpdateChunks();
1119            Sampler::UpdateChunks();
1120    
1121            // make sure 'wlnk' chunk exists
1122            RIFF::Chunk* wlnk = pCkRegion->GetSubChunk(CHUNK_ID_WLNK);
1123            if (!wlnk) wlnk = pCkRegion->AddSubChunk(CHUNK_ID_WLNK, 12);
1124            pData = (uint8_t*) wlnk->LoadChunkData();
1125            WaveLinkOptionFlags = (PhaseMaster)
1126                                      ? WaveLinkOptionFlags | F_WAVELINK_PHASE_MASTER
1127                                      : WaveLinkOptionFlags & (~F_WAVELINK_PHASE_MASTER);
1128            WaveLinkOptionFlags = (MultiChannel)
1129                                      ? WaveLinkOptionFlags | F_WAVELINK_MULTICHANNEL
1130                                      : WaveLinkOptionFlags & (~F_WAVELINK_MULTICHANNEL);
1131            // get sample's wave pool table index
1132            int index = -1;
1133            File* pFile = (File*) GetParent()->GetParent();
1134            if (pFile->pSamples) {
1135                File::SampleList::iterator iter = pFile->pSamples->begin();
1136                File::SampleList::iterator end  = pFile->pSamples->end();
1137                for (int i = 0; iter != end; ++iter, i++) {
1138                    if (*iter == pSample) {
1139                        index = i;
1140                        break;
1141                    }
1142                }
1143            }
1144            WavePoolTableIndex = index;
1145            // update 'wlnk' chunk
1146            store16(&pData[0], WaveLinkOptionFlags);
1147            store16(&pData[2], PhaseGroup);
1148            store32(&pData[4], Channel);
1149            store32(&pData[8], WavePoolTableIndex);
1150        }
1151        
1152        /**
1153         * Make a (semi) deep copy of the Region object given by @a orig and assign
1154         * it to this object.
1155         *
1156         * Note that the sample pointer referenced by @a orig is simply copied as
1157         * memory address. Thus the respective sample is shared, not duplicated!
1158         *
1159         * @param orig - original Region object to be copied from
1160         */
1161        void Region::CopyAssign(const Region* orig) {
1162            // handle base classes
1163            Resource::CopyAssign(orig);
1164            Articulator::CopyAssign(orig);
1165            Sampler::CopyAssign(orig);
1166            // handle actual own attributes of this class
1167            // (the trivial ones)
1168            VelocityRange = orig->VelocityRange;
1169            KeyGroup = orig->KeyGroup;
1170            Layer = orig->Layer;
1171            SelfNonExclusive = orig->SelfNonExclusive;
1172            PhaseMaster = orig->PhaseMaster;
1173            PhaseGroup = orig->PhaseGroup;
1174            MultiChannel = orig->MultiChannel;
1175            Channel = orig->Channel;
1176            // only take the raw sample reference if the two Region objects are
1177            // part of the same file
1178            if (GetParent()->GetParent() == orig->GetParent()->GetParent()) {
1179                WavePoolTableIndex = orig->WavePoolTableIndex;
1180                pSample = orig->pSample;
1181            } else {
1182                WavePoolTableIndex = -1;
1183                pSample = NULL;
1184            }
1185            FormatOptionFlags = orig->FormatOptionFlags;
1186            WaveLinkOptionFlags = orig->WaveLinkOptionFlags;
1187            // handle the last, a bit sensible attribute
1188            SetKeyRange(orig->KeyRange.low, orig->KeyRange.high);
1189        }
1190    
1191    
1192  // *************** Instrument ***************  // *************** Instrument ***************
1193  // *  // *
1194    
1195        /** @brief Constructor.
1196         *
1197         * Load an existing instrument definition or create a new one. An 'ins'
1198         * list chunk must be given to this constructor. In case this 'ins' list
1199         * chunk contains a 'insh' chunk, the instrument data fields will be
1200         * loaded from there, otherwise default values will be used and the
1201         * 'insh' chunk will be created once File::Save() was called.
1202         *
1203         * @param pFile   - pointer to DLS::File where this instrument is
1204         *                  located (or will be located)
1205         * @param insList - pointer to 'ins' list chunk which is (or will be)
1206         *                  associated with this instrument
1207         */
1208      Instrument::Instrument(File* pFile, RIFF::List* insList) : Resource(pFile, insList), Articulator(insList) {      Instrument::Instrument(File* pFile, RIFF::List* insList) : Resource(pFile, insList), Articulator(insList) {
1209          pCkInstrument = insList;          pCkInstrument = insList;
1210    
         RIFF::Chunk* insh = pCkInstrument->GetSubChunk(CHUNK_ID_INSH);  
         if (!insh) throw DLS::Exception("Mandatory chunks in <lins> list chunk not found.");  
         Regions = insh->ReadUint32();  
1211          midi_locale_t locale;          midi_locale_t locale;
1212          insh->Read(&locale, 2, 4);          RIFF::Chunk* insh = pCkInstrument->GetSubChunk(CHUNK_ID_INSH);
1213            if (insh) {
1214                Regions = insh->ReadUint32();
1215                insh->Read(&locale, 2, 4);
1216            } else { // 'insh' chunk missing
1217                Regions = 0;
1218                locale.bank       = 0;
1219                locale.instrument = 0;
1220            }
1221    
1222          MIDIProgram    = locale.instrument;          MIDIProgram    = locale.instrument;
1223          IsDrum         = locale.bank & DRUM_TYPE_MASK;          IsDrum         = locale.bank & DRUM_TYPE_MASK;
1224          MIDIBankCoarse = (uint8_t) MIDI_BANK_COARSE(locale.bank);          MIDIBankCoarse = (uint8_t) MIDI_BANK_COARSE(locale.bank);
1225          MIDIBankFine   = (uint8_t) MIDI_BANK_FINE(locale.bank);          MIDIBankFine   = (uint8_t) MIDI_BANK_FINE(locale.bank);
1226          MIDIBank       = MIDI_BANK_MERGE(MIDIBankCoarse, MIDIBankFine);          MIDIBank       = MIDI_BANK_MERGE(MIDIBankCoarse, MIDIBankFine);
1227    
1228          pRegions   = NULL;          pRegions = NULL;
1229      }      }
1230    
1231      Region* Instrument::GetFirstRegion() {      Region* Instrument::GetFirstRegion() {
# Line 363  namespace DLS { Line 1242  namespace DLS {
1242      }      }
1243    
1244      void Instrument::LoadRegions() {      void Instrument::LoadRegions() {
1245            if (!pRegions) pRegions = new RegionList;
1246          RIFF::List* lrgn = pCkInstrument->GetSubList(LIST_TYPE_LRGN);          RIFF::List* lrgn = pCkInstrument->GetSubList(LIST_TYPE_LRGN);
1247          if (!lrgn) throw DLS::Exception("Mandatory chunks in <ins > chunk not found.");          if (lrgn) {
1248          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
1249          RIFF::List* rgn = lrgn->GetFirstSubList();              RIFF::List* rgn = lrgn->GetFirstSubList();
1250          while (rgn) {              while (rgn) {
1251              if (rgn->GetListType() == regionCkType) {                  if (rgn->GetListType() == regionCkType) {
1252                  if (!pRegions) pRegions = new RegionList;                      pRegions->push_back(new Region(this, rgn));
1253                  pRegions->push_back(new Region(this, rgn));                  }
1254                    rgn = lrgn->GetNextSubList();
1255              }              }
             rgn = lrgn->GetNextSubList();  
1256          }          }
1257      }      }
1258    
1259        Region* Instrument::AddRegion() {
1260            if (!pRegions) LoadRegions();
1261            RIFF::List* lrgn = pCkInstrument->GetSubList(LIST_TYPE_LRGN);
1262            if (!lrgn)  lrgn = pCkInstrument->AddSubList(LIST_TYPE_LRGN);
1263            RIFF::List* rgn = lrgn->AddSubList(LIST_TYPE_RGN);
1264            Region* pNewRegion = new Region(this, rgn);
1265            pRegions->push_back(pNewRegion);
1266            Regions = pRegions->size();
1267            return pNewRegion;
1268        }
1269    
1270        void Instrument::MoveRegion(Region* pSrc, Region* pDst) {
1271            RIFF::List* lrgn = pCkInstrument->GetSubList(LIST_TYPE_LRGN);
1272            lrgn->MoveSubChunk(pSrc->pCkRegion, pDst ? pDst->pCkRegion : 0);
1273    
1274            pRegions->remove(pSrc);
1275            RegionList::iterator iter = find(pRegions->begin(), pRegions->end(), pDst);
1276            pRegions->insert(iter, pSrc);
1277        }
1278    
1279        void Instrument::DeleteRegion(Region* pRegion) {
1280            if (!pRegions) return;
1281            RegionList::iterator iter = find(pRegions->begin(), pRegions->end(), pRegion);
1282            if (iter == pRegions->end()) return;
1283            pRegions->erase(iter);
1284            Regions = pRegions->size();
1285            delete pRegion;
1286        }
1287    
1288        /**
1289         * Apply Instrument with all its Regions to the respective RIFF chunks.
1290         * You have to call File::Save() to make changes persistent.
1291         *
1292         * @throws Exception - on errors
1293         */
1294        void Instrument::UpdateChunks() {
1295            // first update base classes' chunks
1296            Resource::UpdateChunks();
1297            Articulator::UpdateChunks();
1298            // make sure 'insh' chunk exists
1299            RIFF::Chunk* insh = pCkInstrument->GetSubChunk(CHUNK_ID_INSH);
1300            if (!insh) insh = pCkInstrument->AddSubChunk(CHUNK_ID_INSH, 12);
1301            uint8_t* pData = (uint8_t*) insh->LoadChunkData();
1302            // update 'insh' chunk
1303            Regions = (pRegions) ? pRegions->size() : 0;
1304            midi_locale_t locale;
1305            locale.instrument = MIDIProgram;
1306            locale.bank       = MIDI_BANK_ENCODE(MIDIBankCoarse, MIDIBankFine);
1307            locale.bank       = (IsDrum) ? locale.bank | DRUM_TYPE_MASK : locale.bank & (~DRUM_TYPE_MASK);
1308            MIDIBank          = MIDI_BANK_MERGE(MIDIBankCoarse, MIDIBankFine); // just a sync, when we're at it
1309            store32(&pData[0], Regions);
1310            store32(&pData[4], locale.bank);
1311            store32(&pData[8], locale.instrument);
1312            // update Region's chunks
1313            if (!pRegions) return;
1314            RegionList::iterator iter = pRegions->begin();
1315            RegionList::iterator end  = pRegions->end();
1316            for (; iter != end; ++iter) {
1317                (*iter)->UpdateChunks();
1318            }
1319        }
1320    
1321        /** @brief Destructor.
1322         *
1323         * Removes RIFF chunks associated with this Instrument and frees all
1324         * memory occupied by this instrument.
1325         */
1326      Instrument::~Instrument() {      Instrument::~Instrument() {
1327          if (pRegions) {          if (pRegions) {
1328              RegionList::iterator iter = pRegions->begin();              RegionList::iterator iter = pRegions->begin();
# Line 386  namespace DLS { Line 1333  namespace DLS {
1333              }              }
1334              delete pRegions;              delete pRegions;
1335          }          }
1336            // remove instrument's chunks
1337            RIFF::List* pParent = pCkInstrument->GetParent();
1338            pParent->DeleteSubChunk(pCkInstrument);
1339        }
1340        
1341        void Instrument::CopyAssignCore(const Instrument* orig) {
1342            // handle base classes
1343            Resource::CopyAssign(orig);
1344            Articulator::CopyAssign(orig);
1345            // handle actual own attributes of this class
1346            // (the trivial ones)
1347            IsDrum = orig->IsDrum;
1348            MIDIBank = orig->MIDIBank;
1349            MIDIBankCoarse = orig->MIDIBankCoarse;
1350            MIDIBankFine = orig->MIDIBankFine;
1351            MIDIProgram = orig->MIDIProgram;
1352        }
1353        
1354        /**
1355         * Make a (semi) deep copy of the Instrument object given by @a orig and assign
1356         * it to this object.
1357         *
1358         * Note that all sample pointers referenced by @a orig are simply copied as
1359         * memory address. Thus the respective samples are shared, not duplicated!
1360         *
1361         * @param orig - original Instrument object to be copied from
1362         */
1363        void Instrument::CopyAssign(const Instrument* orig) {
1364            CopyAssignCore(orig);
1365            // delete all regions first
1366            while (Regions) DeleteRegion(GetFirstRegion());
1367            // now recreate and copy regions
1368            {
1369                RegionList::const_iterator it = orig->pRegions->begin();
1370                for (int i = 0; i < orig->Regions; ++i, ++it) {
1371                    Region* dstRgn = AddRegion();
1372                    //NOTE: Region does semi-deep copy !
1373                    dstRgn->CopyAssign(*it);
1374                }
1375            }
1376      }      }
   
1377    
1378    
1379  // *************** File ***************  // *************** File ***************
1380  // *  // *
1381    
1382        /** @brief Constructor.
1383         *
1384         * Default constructor, use this to create an empty DLS file. You have
1385         * to add samples, instruments and finally call Save() to actually write
1386         * a DLS file.
1387         */
1388        File::File() : Resource(NULL, pRIFF = new RIFF::File(RIFF_TYPE_DLS)) {
1389            pRIFF->SetByteOrder(RIFF::endian_little);
1390            pVersion = new version_t;
1391            pVersion->major   = 0;
1392            pVersion->minor   = 0;
1393            pVersion->release = 0;
1394            pVersion->build   = 0;
1395    
1396            Instruments      = 0;
1397            WavePoolCount    = 0;
1398            pWavePoolTable   = NULL;
1399            pWavePoolTableHi = NULL;
1400            WavePoolHeaderSize = 8;
1401    
1402            pSamples     = NULL;
1403            pInstruments = NULL;
1404    
1405            b64BitWavePoolOffsets = false;
1406        }
1407    
1408        /** @brief Constructor.
1409         *
1410         * Load an existing DLS file.
1411         *
1412         * @param pRIFF - pointer to a RIFF file which is actually the DLS file
1413         *                to load
1414         * @throws Exception if given file is not a DLS file, expected chunks
1415         *                   are missing
1416         */
1417      File::File(RIFF::File* pRIFF) : Resource(NULL, pRIFF) {      File::File(RIFF::File* pRIFF) : Resource(NULL, pRIFF) {
1418          if (!pRIFF) throw DLS::Exception("NULL pointer reference to RIFF::File object.");          if (!pRIFF) throw DLS::Exception("NULL pointer reference to RIFF::File object.");
1419          this->pRIFF = pRIFF;          this->pRIFF = pRIFF;
# Line 409  namespace DLS { Line 1430  namespace DLS {
1430          Instruments = colh->ReadUint32();          Instruments = colh->ReadUint32();
1431    
1432          RIFF::Chunk* ptbl = pRIFF->GetSubChunk(CHUNK_ID_PTBL);          RIFF::Chunk* ptbl = pRIFF->GetSubChunk(CHUNK_ID_PTBL);
1433          if (!ptbl) throw DLS::Exception("Mandatory <ptbl> chunk not found.");          if (!ptbl) { // pool table is missing - this is probably an ".art" file
1434          uint32_t headersize = ptbl->ReadUint32();              WavePoolCount    = 0;
1435          WavePoolCount  = ptbl->ReadUint32();              pWavePoolTable   = NULL;
1436          pWavePoolTable = new uint32_t[WavePoolCount];              pWavePoolTableHi = NULL;
1437          ptbl->SetPos(headersize);              WavePoolHeaderSize = 8;
1438                b64BitWavePoolOffsets = false;
1439          // Check for 64 bit offsets (used in gig v3 files)          } else {
1440          if (ptbl->GetSize() - headersize == WavePoolCount * 8) {              WavePoolHeaderSize = ptbl->ReadUint32();
1441              for (int i = 0 ; i < WavePoolCount ; i++) {              WavePoolCount  = ptbl->ReadUint32();
1442                  // Just ignore the upper bits for now              pWavePoolTable = new uint32_t[WavePoolCount];
1443                  uint32_t upper = ptbl->ReadUint32();              pWavePoolTableHi = new uint32_t[WavePoolCount];
1444                  pWavePoolTable[i] = ptbl->ReadUint32();              ptbl->SetPos(WavePoolHeaderSize);
1445                  if (upper || (pWavePoolTable[i] & 0x80000000))  
1446                      throw DLS::Exception("Files larger than 2 GB not yet supported");              // Check for 64 bit offsets (used in gig v3 files)
1447                b64BitWavePoolOffsets = (ptbl->GetSize() - WavePoolHeaderSize == WavePoolCount * 8);
1448                if (b64BitWavePoolOffsets) {
1449                    for (int i = 0 ; i < WavePoolCount ; i++) {
1450                        pWavePoolTableHi[i] = ptbl->ReadUint32();
1451                        pWavePoolTable[i] = ptbl->ReadUint32();
1452                        if (pWavePoolTable[i] & 0x80000000)
1453                            throw DLS::Exception("Files larger than 2 GB not yet supported");
1454                    }
1455                } else { // conventional 32 bit offsets
1456                    ptbl->Read(pWavePoolTable, WavePoolCount, sizeof(uint32_t));
1457                    for (int i = 0 ; i < WavePoolCount ; i++) pWavePoolTableHi[i] = 0;
1458              }              }
1459          }          }
         else ptbl->Read(pWavePoolTable, WavePoolCount, sizeof(uint32_t));  
1460    
1461          pSamples     = NULL;          pSamples     = NULL;
1462          pInstruments = NULL;          pInstruments = NULL;
         Instruments  = 0;  
1463      }      }
1464    
1465      File::~File() {      File::~File() {
# Line 454  namespace DLS { Line 1484  namespace DLS {
1484          }          }
1485    
1486          if (pWavePoolTable) delete[] pWavePoolTable;          if (pWavePoolTable) delete[] pWavePoolTable;
1487            if (pWavePoolTableHi) delete[] pWavePoolTableHi;
1488          if (pVersion) delete pVersion;          if (pVersion) delete pVersion;
1489            for (std::list<RIFF::File*>::iterator i = ExtensionFiles.begin() ; i != ExtensionFiles.end() ; i++)
1490                delete *i;
1491      }      }
1492    
1493      Sample* File::GetFirstSample() {      Sample* File::GetFirstSample() {
# Line 471  namespace DLS { Line 1504  namespace DLS {
1504      }      }
1505    
1506      void File::LoadSamples() {      void File::LoadSamples() {
1507            if (!pSamples) pSamples = new SampleList;
1508          RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL);          RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL);
1509          if (wvpl) {          if (wvpl) {
1510              unsigned long wvplFileOffset = wvpl->GetFilePos();              unsigned long wvplFileOffset = wvpl->GetFilePos();
1511              RIFF::List* wave = wvpl->GetFirstSubList();              RIFF::List* wave = wvpl->GetFirstSubList();
1512              while (wave) {              while (wave) {
1513                  if (wave->GetListType() == LIST_TYPE_WAVE) {                  if (wave->GetListType() == LIST_TYPE_WAVE) {
                     if (!pSamples) pSamples = new SampleList;  
1514                      unsigned long waveFileOffset = wave->GetFilePos();                      unsigned long waveFileOffset = wave->GetFilePos();
1515                      pSamples->push_back(new Sample(this, wave, waveFileOffset - wvplFileOffset));                      pSamples->push_back(new Sample(this, wave, waveFileOffset - wvplFileOffset));
1516                  }                  }
# Line 491  namespace DLS { Line 1524  namespace DLS {
1524                  RIFF::List* wave = dwpl->GetFirstSubList();                  RIFF::List* wave = dwpl->GetFirstSubList();
1525                  while (wave) {                  while (wave) {
1526                      if (wave->GetListType() == LIST_TYPE_WAVE) {                      if (wave->GetListType() == LIST_TYPE_WAVE) {
                         if (!pSamples) pSamples = new SampleList;  
1527                          unsigned long waveFileOffset = wave->GetFilePos();                          unsigned long waveFileOffset = wave->GetFilePos();
1528                          pSamples->push_back(new Sample(this, wave, waveFileOffset - dwplFileOffset));                          pSamples->push_back(new Sample(this, wave, waveFileOffset - dwplFileOffset));
1529                      }                      }
# Line 501  namespace DLS { Line 1533  namespace DLS {
1533          }          }
1534      }      }
1535    
1536        /** @brief Add a new sample.
1537         *
1538         * This will create a new Sample object for the DLS file. You have to
1539         * call Save() to make this persistent to the file.
1540         *
1541         * @returns pointer to new Sample object
1542         */
1543        Sample* File::AddSample() {
1544           if (!pSamples) LoadSamples();
1545           __ensureMandatoryChunksExist();
1546           RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL);
1547           // create new Sample object and its respective 'wave' list chunk
1548           RIFF::List* wave = wvpl->AddSubList(LIST_TYPE_WAVE);
1549           Sample* pSample = new Sample(this, wave, 0 /*arbitrary value, we update offsets when we save*/);
1550           pSamples->push_back(pSample);
1551           return pSample;
1552        }
1553    
1554        /** @brief Delete a sample.
1555         *
1556         * This will delete the given Sample object from the DLS file. You have
1557         * to call Save() to make this persistent to the file.
1558         *
1559         * @param pSample - sample to delete
1560         */
1561        void File::DeleteSample(Sample* pSample) {
1562            if (!pSamples) return;
1563            SampleList::iterator iter = find(pSamples->begin(), pSamples->end(), pSample);
1564            if (iter == pSamples->end()) return;
1565            pSamples->erase(iter);
1566            delete pSample;
1567        }
1568    
1569      Instrument* File::GetFirstInstrument() {      Instrument* File::GetFirstInstrument() {
1570          if (!pInstruments) LoadInstruments();          if (!pInstruments) LoadInstruments();
1571          if (!pInstruments) return NULL;          if (!pInstruments) return NULL;
# Line 515  namespace DLS { Line 1580  namespace DLS {
1580      }      }
1581    
1582      void File::LoadInstruments() {      void File::LoadInstruments() {
1583            if (!pInstruments) pInstruments = new InstrumentList;
1584          RIFF::List* lstInstruments = pRIFF->GetSubList(LIST_TYPE_LINS);          RIFF::List* lstInstruments = pRIFF->GetSubList(LIST_TYPE_LINS);
1585          if (lstInstruments) {          if (lstInstruments) {
1586              RIFF::List* lstInstr = lstInstruments->GetFirstSubList();              RIFF::List* lstInstr = lstInstruments->GetFirstSubList();
1587              while (lstInstr) {              while (lstInstr) {
1588                  if (lstInstr->GetListType() == LIST_TYPE_INS) {                  if (lstInstr->GetListType() == LIST_TYPE_INS) {
                     if (!pInstruments) pInstruments = new InstrumentList;  
1589                      pInstruments->push_back(new Instrument(this, lstInstr));                      pInstruments->push_back(new Instrument(this, lstInstr));
1590                  }                  }
1591                  lstInstr = lstInstruments->GetNextSubList();                  lstInstr = lstInstruments->GetNextSubList();
# Line 528  namespace DLS { Line 1593  namespace DLS {
1593          }          }
1594      }      }
1595    
1596        /** @brief Add a new instrument definition.
1597         *
1598         * This will create a new Instrument object for the DLS file. You have
1599         * to call Save() to make this persistent to the file.
1600         *
1601         * @returns pointer to new Instrument object
1602         */
1603        Instrument* File::AddInstrument() {
1604           if (!pInstruments) LoadInstruments();
1605           __ensureMandatoryChunksExist();
1606           RIFF::List* lstInstruments = pRIFF->GetSubList(LIST_TYPE_LINS);
1607           RIFF::List* lstInstr = lstInstruments->AddSubList(LIST_TYPE_INS);
1608           Instrument* pInstrument = new Instrument(this, lstInstr);
1609           pInstruments->push_back(pInstrument);
1610           return pInstrument;
1611        }
1612    
1613        /** @brief Delete an instrument.
1614         *
1615         * This will delete the given Instrument object from the DLS file. You
1616         * have to call Save() to make this persistent to the file.
1617         *
1618         * @param pInstrument - instrument to delete
1619         */
1620        void File::DeleteInstrument(Instrument* pInstrument) {
1621            if (!pInstruments) return;
1622            InstrumentList::iterator iter = find(pInstruments->begin(), pInstruments->end(), pInstrument);
1623            if (iter == pInstruments->end()) return;
1624            pInstruments->erase(iter);
1625            delete pInstrument;
1626        }
1627    
1628        /**
1629         * Returns extension file of given index. Extension files are used
1630         * sometimes to circumvent the 2 GB file size limit of the RIFF format and
1631         * of certain operating systems in general. In this case, instead of just
1632         * using one file, the content is spread among several files with similar
1633         * file name scheme. This is especially used by some GigaStudio sound
1634         * libraries.
1635         *
1636         * @param index - index of extension file
1637         * @returns sought extension file, NULL if index out of bounds
1638         * @see GetFileName()
1639         */
1640        RIFF::File* File::GetExtensionFile(int index) {
1641            if (index < 0 || index >= ExtensionFiles.size()) return NULL;
1642            std::list<RIFF::File*>::iterator iter = ExtensionFiles.begin();
1643            for (int i = 0; iter != ExtensionFiles.end(); ++iter, ++i)
1644                if (i == index) return *iter;
1645            return NULL;
1646        }
1647    
1648        /** @brief File name of this DLS file.
1649         *
1650         * This method returns the file name as it was provided when loading
1651         * the respective DLS file. However in case the File object associates
1652         * an empty, that is new DLS file, which was not yet saved to disk,
1653         * this method will return an empty string.
1654         *
1655         * @see GetExtensionFile()
1656         */
1657        String File::GetFileName() {
1658            return pRIFF->GetFileName();
1659        }
1660        
1661        /**
1662         * You may call this method store a future file name, so you don't have to
1663         * to pass it to the Save() call later on.
1664         */
1665        void File::SetFileName(const String& name) {
1666            pRIFF->SetFileName(name);
1667        }
1668    
1669        /**
1670         * Apply all the DLS file's current instruments, samples and settings to
1671         * the respective RIFF chunks. You have to call Save() to make changes
1672         * persistent.
1673         *
1674         * @throws Exception - on errors
1675         */
1676        void File::UpdateChunks() {
1677            // first update base class's chunks
1678            Resource::UpdateChunks();
1679    
1680            // if version struct exists, update 'vers' chunk
1681            if (pVersion) {
1682                RIFF::Chunk* ckVersion    = pRIFF->GetSubChunk(CHUNK_ID_VERS);
1683                if (!ckVersion) ckVersion = pRIFF->AddSubChunk(CHUNK_ID_VERS, 8);
1684                uint8_t* pData = (uint8_t*) ckVersion->LoadChunkData();
1685                store16(&pData[0], pVersion->minor);
1686                store16(&pData[2], pVersion->major);
1687                store16(&pData[4], pVersion->build);
1688                store16(&pData[6], pVersion->release);
1689            }
1690    
1691            // update 'colh' chunk
1692            Instruments = (pInstruments) ? pInstruments->size() : 0;
1693            RIFF::Chunk* colh = pRIFF->GetSubChunk(CHUNK_ID_COLH);
1694            if (!colh)   colh = pRIFF->AddSubChunk(CHUNK_ID_COLH, 4);
1695            uint8_t* pData = (uint8_t*) colh->LoadChunkData();
1696            store32(pData, Instruments);
1697    
1698            // update instrument's chunks
1699            if (pInstruments) {
1700                InstrumentList::iterator iter = pInstruments->begin();
1701                InstrumentList::iterator end  = pInstruments->end();
1702                for (; iter != end; ++iter) {
1703                    (*iter)->UpdateChunks();
1704                }
1705            }
1706    
1707            // update 'ptbl' chunk
1708            const int iSamples = (pSamples) ? pSamples->size() : 0;
1709            const int iPtblOffsetSize = (b64BitWavePoolOffsets) ? 8 : 4;
1710            RIFF::Chunk* ptbl = pRIFF->GetSubChunk(CHUNK_ID_PTBL);
1711            if (!ptbl)   ptbl = pRIFF->AddSubChunk(CHUNK_ID_PTBL, 1 /*anything, we'll resize*/);
1712            const int iPtblSize = WavePoolHeaderSize + iPtblOffsetSize * iSamples;
1713            ptbl->Resize(iPtblSize);
1714            pData = (uint8_t*) ptbl->LoadChunkData();
1715            WavePoolCount = iSamples;
1716            store32(&pData[4], WavePoolCount);
1717            // we actually update the sample offsets in the pool table when we Save()
1718            memset(&pData[WavePoolHeaderSize], 0, iPtblSize - WavePoolHeaderSize);
1719    
1720            // update sample's chunks
1721            if (pSamples) {
1722                SampleList::iterator iter = pSamples->begin();
1723                SampleList::iterator end  = pSamples->end();
1724                for (; iter != end; ++iter) {
1725                    (*iter)->UpdateChunks();
1726                }
1727            }
1728        }
1729    
1730        /** @brief Save changes to another file.
1731         *
1732         * Make all changes persistent by writing them to another file.
1733         * <b>Caution:</b> this method is optimized for writing to
1734         * <b>another</b> file, do not use it to save the changes to the same
1735         * file! Use Save() (without path argument) in that case instead!
1736         * Ignoring this might result in a corrupted file!
1737         *
1738         * After calling this method, this File object will be associated with
1739         * the new file (given by \a Path) afterwards.
1740         *
1741         * @param Path - path and file name where everything should be written to
1742         */
1743        void File::Save(const String& Path) {
1744            UpdateChunks();
1745            pRIFF->Save(Path);
1746            UpdateFileOffsets();
1747        }
1748    
1749        /** @brief Save changes to same file.
1750         *
1751         * Make all changes persistent by writing them to the actual (same)
1752         * file. The file might temporarily grow to a higher size than it will
1753         * have at the end of the saving process.
1754         *
1755         * @throws RIFF::Exception if any kind of IO error occured
1756         * @throws DLS::Exception  if any kind of DLS specific error occured
1757         */
1758        void File::Save() {
1759            UpdateChunks();
1760            pRIFF->Save();
1761            UpdateFileOffsets();
1762        }
1763    
1764        /** @brief Updates all file offsets stored all over the file.
1765         *
1766         * This virtual method is called whenever the overall file layout has been
1767         * changed (i.e. file or individual RIFF chunks have been resized). It is
1768         * then the responsibility of this method to update all file offsets stored
1769         * in the file format. For example samples are referenced by instruments by
1770         * file offsets. The gig format also stores references to instrument
1771         * scripts as file offsets, and thus it overrides this method to update
1772         * those file offsets as well.
1773         */
1774        void File::UpdateFileOffsets() {
1775            __UpdateWavePoolTableChunk();
1776        }
1777    
1778        /**
1779         * Checks if all (for DLS) mandatory chunks exist, if not they will be
1780         * created. Note that those chunks will not be made persistent until
1781         * Save() was called.
1782         */
1783        void File::__ensureMandatoryChunksExist() {
1784           // enusre 'lins' list chunk exists (mandatory for instrument definitions)
1785           RIFF::List* lstInstruments = pRIFF->GetSubList(LIST_TYPE_LINS);
1786           if (!lstInstruments) pRIFF->AddSubList(LIST_TYPE_LINS);
1787           // ensure 'ptbl' chunk exists (mandatory for samples)
1788           RIFF::Chunk* ptbl = pRIFF->GetSubChunk(CHUNK_ID_PTBL);
1789           if (!ptbl) {
1790               const int iOffsetSize = (b64BitWavePoolOffsets) ? 8 : 4;
1791               ptbl = pRIFF->AddSubChunk(CHUNK_ID_PTBL, WavePoolHeaderSize + iOffsetSize);
1792           }
1793           // enusre 'wvpl' list chunk exists (mandatory for samples)
1794           RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL);
1795           if (!wvpl) pRIFF->AddSubList(LIST_TYPE_WVPL);
1796        }
1797    
1798        /**
1799         * Updates (persistently) the wave pool table with offsets to all
1800         * currently available samples. <b>Caution:</b> this method assumes the
1801         * 'ptbl' chunk to be already of the correct size and the file to be
1802         * writable, so usually this method is only called after a Save() call.
1803         *
1804         * @throws Exception - if 'ptbl' chunk is too small (should only occur
1805         *                     if there's a bug)
1806         */
1807        void File::__UpdateWavePoolTableChunk() {
1808            __UpdateWavePoolTable();
1809            RIFF::Chunk* ptbl = pRIFF->GetSubChunk(CHUNK_ID_PTBL);
1810            const int iOffsetSize = (b64BitWavePoolOffsets) ? 8 : 4;
1811            // check if 'ptbl' chunk is large enough
1812            WavePoolCount = (pSamples) ? pSamples->size() : 0;
1813            const unsigned long ulRequiredSize = WavePoolHeaderSize + iOffsetSize * WavePoolCount;
1814            if (ptbl->GetSize() < ulRequiredSize) throw Exception("Fatal error, 'ptbl' chunk too small");
1815            // save the 'ptbl' chunk's current read/write position
1816            unsigned long ulOriginalPos = ptbl->GetPos();
1817            // update headers
1818            ptbl->SetPos(0);
1819            uint32_t tmp = WavePoolHeaderSize;
1820            ptbl->WriteUint32(&tmp);
1821            tmp = WavePoolCount;
1822            ptbl->WriteUint32(&tmp);
1823            // update offsets
1824            ptbl->SetPos(WavePoolHeaderSize);
1825            if (b64BitWavePoolOffsets) {
1826                for (int i = 0 ; i < WavePoolCount ; i++) {
1827                    tmp = pWavePoolTableHi[i];
1828                    ptbl->WriteUint32(&tmp);
1829                    tmp = pWavePoolTable[i];
1830                    ptbl->WriteUint32(&tmp);
1831                }
1832            } else { // conventional 32 bit offsets
1833                for (int i = 0 ; i < WavePoolCount ; i++) {
1834                    tmp = pWavePoolTable[i];
1835                    ptbl->WriteUint32(&tmp);
1836                }
1837            }
1838            // restore 'ptbl' chunk's original read/write position
1839            ptbl->SetPos(ulOriginalPos);
1840        }
1841    
1842        /**
1843         * Updates the wave pool table with offsets to all currently available
1844         * samples. <b>Caution:</b> this method assumes the 'wvpl' list chunk
1845         * exists already.
1846         */
1847        void File::__UpdateWavePoolTable() {
1848            WavePoolCount = (pSamples) ? pSamples->size() : 0;
1849            // resize wave pool table arrays
1850            if (pWavePoolTable)   delete[] pWavePoolTable;
1851            if (pWavePoolTableHi) delete[] pWavePoolTableHi;
1852            pWavePoolTable   = new uint32_t[WavePoolCount];
1853            pWavePoolTableHi = new uint32_t[WavePoolCount];
1854            if (!pSamples) return;
1855            // update offsets int wave pool table
1856            RIFF::List* wvpl = pRIFF->GetSubList(LIST_TYPE_WVPL);
1857            uint64_t wvplFileOffset = wvpl->GetFilePos();
1858            if (b64BitWavePoolOffsets) {
1859                SampleList::iterator iter = pSamples->begin();
1860                SampleList::iterator end  = pSamples->end();
1861                for (int i = 0 ; iter != end ; ++iter, i++) {
1862                    uint64_t _64BitOffset = (*iter)->pWaveList->GetFilePos() - wvplFileOffset - LIST_HEADER_SIZE;
1863                    (*iter)->ulWavePoolOffset = _64BitOffset;
1864                    pWavePoolTableHi[i] = (uint32_t) (_64BitOffset >> 32);
1865                    pWavePoolTable[i]   = (uint32_t) _64BitOffset;
1866                }
1867            } else { // conventional 32 bit offsets
1868                SampleList::iterator iter = pSamples->begin();
1869                SampleList::iterator end  = pSamples->end();
1870                for (int i = 0 ; iter != end ; ++iter, i++) {
1871                    uint64_t _64BitOffset = (*iter)->pWaveList->GetFilePos() - wvplFileOffset - LIST_HEADER_SIZE;
1872                    (*iter)->ulWavePoolOffset = _64BitOffset;
1873                    pWavePoolTable[i] = (uint32_t) _64BitOffset;
1874                }
1875            }
1876        }
1877    
1878    
1879    
1880  // *************** Exception ***************  // *************** Exception ***************
# Line 540  namespace DLS { Line 1887  namespace DLS {
1887          std::cout << "DLS::Exception: " << Message << std::endl;          std::cout << "DLS::Exception: " << Message << std::endl;
1888      }      }
1889    
1890    
1891    // *************** functions ***************
1892    // *
1893    
1894        /**
1895         * Returns the name of this C++ library. This is usually "libgig" of
1896         * course. This call is equivalent to RIFF::libraryName() and
1897         * gig::libraryName().
1898         */
1899        String libraryName() {
1900            return PACKAGE;
1901        }
1902    
1903        /**
1904         * Returns version of this C++ library. This call is equivalent to
1905         * RIFF::libraryVersion() and gig::libraryVersion().
1906         */
1907        String libraryVersion() {
1908            return VERSION;
1909        }
1910    
1911  } // namespace DLS  } // namespace DLS

Legend:
Removed from v.384  
changed lines
  Added in v.2609

  ViewVC Help
Powered by ViewVC