/[svn]/libgig/trunk/src/tools/gig2stereo.cpp
ViewVC logotype

Annotation of /libgig/trunk/src/tools/gig2stereo.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3048 - (hide annotations) (download)
Fri Nov 25 18:34:45 2016 UTC (7 years, 4 months ago) by schoenebeck
File size: 32945 byte(s)
- Fixed various spelling mistakes (patch by Debian maintainer).

1 schoenebeck 2685 /***************************************************************************
2     * *
3     * libgig - C++ cross-platform Gigasampler format file access library *
4     * *
5     * Copyright (C) 2003-2015 by Christian Schoenebeck *
6     * <cuse@users.sourceforge.net> *
7     * *
8     * This program is part of libgig. *
9     * *
10     * This program is free software; you can redistribute it and/or modify *
11     * it under the terms of the GNU General Public License as published by *
12     * the Free Software Foundation; either version 2 of the License, or *
13     * (at your option) any later version. *
14     * *
15     * This program is distributed in the hope that it will be useful, *
16     * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18     * GNU General Public License for more details. *
19     * *
20     * You should have received a copy of the GNU General Public License *
21     * along with this program; if not, write to the Free Software *
22     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
23     * MA 02111-1307 USA *
24     ***************************************************************************/
25    
26     #ifdef HAVE_CONFIG_H
27     # include <config.h>
28     #endif
29     #include <errno.h>
30     #include <sys/stat.h>
31     #include <dirent.h>
32     #include <string.h>
33     #include <iostream>
34     #include <cstdlib>
35     #include <string>
36     #include <set>
37     #include <vector>
38     #include <map>
39     #include <utility>
40    
41     #ifdef WIN32
42     # define DIR_SEPARATOR '\\'
43     #else
44     # define DIR_SEPARATOR '/'
45     #endif
46    
47     #include "../gig.h"
48    
49     using namespace std;
50    
51     static set<string> g_files;
52    
53     static void printUsage() {
54     cout << "gig2stereo - converts Gigasampler files from mono sample pairs to" << endl;
55     cout << " true stereo interleaved samples." << endl;
56     cout << endl;
57     cout << "Usage: gig2stereo [-r] [--keep] [--verbose [LEVEL]] [--force-replace] FILE_OR_DIR1 [ FILE_OR_DIR2 ... ]" << endl;
58     cout << " gig2stereo -v" << endl;
59     cout << endl;
60     cout << " --force-replace Replace all old mono references by the new stereo ones." << endl;
61     cout << endl;
62 schoenebeck 2693 cout << " --incompatible Also match sample pairs that seem to be incompatible." << endl;
63     cout << endl;
64 schoenebeck 2685 cout << " --keep Keep orphaned mono samples after conversion." << endl;
65     cout << endl;
66     cout << " -r Recurse through subdirectories." << endl;
67     cout << endl;
68     cout << " -v Print version and exit." << endl;
69     cout << endl;
70 schoenebeck 3048 cout << " --verbose Print additional information while converting." << endl;
71 schoenebeck 2685 cout << endl;
72     cout << "Read `man gig2stereo' for details." << endl;
73     cout << endl;
74     }
75    
76     static string programRevision() {
77 schoenebeck 2706 string s = "$Revision$";
78 schoenebeck 2685 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
79     }
80    
81     static void printVersion() {
82     cout << "gig2stereo revision " << programRevision() << endl;
83     cout << "using " << gig::libraryName() << " " << gig::libraryVersion() << endl;
84     }
85    
86     static bool isGigFileName(const string path) {
87     const string t = ".gig";
88     return path.substr(path.length() - t.length()) == t;
89     }
90    
91     static bool endsWith(const string& haystack, const string& needle, bool caseSensitive) {
92     const string sub = haystack.substr(haystack.size() - needle.size(), needle.size());
93     return (caseSensitive) ? (sub == needle) : (!strcasecmp(sub.c_str(), needle.c_str()));
94     }
95    
96     /// Removes spaces from the beginning and end of @a s.
97     static string stripWhiteSpace(string s) {
98     // strip white space at the beginning
99     for (int i = 0; i < s.size(); ++i) {
100     if (s[i] != ' ') {
101     s = s.substr(i);
102     break;
103     }
104     }
105     // strip white space at the end
106     for (int i = s.size() - 1; i >= 0; --i) {
107     if (s[i] != ' ') {
108     s = s.substr(0, i+1);
109     break;
110     }
111     }
112     return s;
113     }
114    
115     static void collectFilesOfDir(string path, bool bRecurse, bool* pbError = NULL) {
116     DIR* d = opendir(path.c_str());
117     if (!d) {
118     if (pbError) *pbError = true;
119     cerr << strerror(errno) << " : '" << path << "'" << endl;
120     return;
121     }
122    
123     for (struct dirent* e = readdir(d); e; e = readdir(d)) {
124     if (string(e->d_name) == "." || string(e->d_name) == "..")
125     continue;
126    
127     const string fullName = path + DIR_SEPARATOR + e->d_name;
128    
129     struct stat s;
130     if (stat(fullName.c_str(), &s)) {
131     if (pbError) *pbError = true;
132     cerr << strerror(errno) << " : '" << fullName << "'" << endl;
133     continue;
134     }
135    
136     if (S_ISREG(s.st_mode) && isGigFileName(fullName)) {
137     g_files.insert(fullName);
138     } else if (S_ISDIR(s.st_mode) && bRecurse) {
139     collectFilesOfDir(fullName, bRecurse, pbError);
140     }
141     }
142    
143     closedir(d);
144     }
145    
146     static void collectFiles(string path, bool bRecurse, bool* pbError = NULL) {
147     struct stat s;
148     if (stat(path.c_str(), &s)) {
149     if (pbError) *pbError = true;
150     cerr << strerror(errno) << " : '" << path << "'" << endl;
151     return;
152     }
153     if (S_ISREG(s.st_mode) && isGigFileName(path)) {
154     g_files.insert(path);
155     } else if (S_ISDIR(s.st_mode)) {
156     collectFilesOfDir(path, bRecurse, pbError);
157     } else {
158     if (pbError) *pbError = true;
159     cerr << "Neither a regular (.gig) file nor directory : '" << path << "'" << endl;
160     }
161     }
162    
163     static string stripAudioChannelFromName(const string& s) {
164     if (endsWith(s, "-L", false) || endsWith(s, "-R", false) ||
165     endsWith(s, "_L", false) || endsWith(s, "_R", false) ||
166     endsWith(s, " L", false) || endsWith(s, " R", false) )
167     {
168     return s.substr(0, s.size() - 2);
169     }
170     if (endsWith(s, "-LEFT", false) || endsWith(s, "_LEFT", false) || endsWith(s, " LEFT", false) ) {
171     return s.substr(0, s.size() - 5);
172     }
173     if (endsWith(s, "-RIGHT", false) || endsWith(s, "_RIGHT", false) || endsWith(s, " RIGHT", false) ) {
174     return s.substr(0, s.size() - 6);
175     }
176     return s;
177     }
178    
179 schoenebeck 2693 #define OPTIONAL_SKIP_CHECK() \
180     if (skipIncompatible) { \
181     cerr << " Skipping!\n"; \
182     continue; /* skip to convert this sample pair */ \
183     } else { \
184     cerr << " Merging anyway (upon request)!\n"; \
185     }
186    
187 schoenebeck 2685 /**
188     * Converts .gig file given by @a path towards using true stereo interleaved
189     * samples.
190     *
191     * @param path - path and file name of .gig file to be converted
192     * @param keep - if true: do not delete the old mono samples, even if they are
193     * not referenced at all anymore after conversion
194     * @param forceReplace - By default certain references of the old mono samples
195     * are not replaced by the new true stereo samples,
196     * usually because the respective mono reference is been
197     * used in an instrument context that seems to be entirely
198     * mono, not stereo. By enabling this argument all those
199     * old references will be replaced by the new stereo
200     * sample reference instead as well.
201 schoenebeck 2693 * @param matchIncompatible - if set to true, only mono sample pairs will be
202     * qualified to be merged to true stereo samples if
203     * their main sample characteristics match, if set
204     * to true this sanity check will be skipped
205 schoenebeck 2685 * @param verbosity - verbosity level, defines whether and how much additional
206 schoenebeck 3048 * information to be printed to the console while doing the
207 schoenebeck 2685 * conversion (0 .. 2)
208     */
209 schoenebeck 2693 static bool convertFileToStereo(const string path, bool keep, bool forceReplace, bool skipIncompatible, int verbose) {
210 schoenebeck 2685 try {
211     // open .gig file
212     RIFF::File riff(path);
213     gig::File gig(&riff);
214    
215     typedef pair<gig::DimensionRegion*, gig::DimensionRegion*> DimRgnPair;
216     typedef pair<gig::Sample*, gig::Sample*> SamplePair;
217    
218     // find and collect all dimension regions with a 2 mono samples pair
219     if (verbose) cout << "Searching mono sample pairs ... " << flush;
220     if (verbose >= 2) cout << endl;
221     map<SamplePair, vector<DimRgnPair> > samplePairs;
222     map<gig::Sample*, SamplePair> samplePairsByL;
223     map<gig::Sample*, SamplePair> samplePairsByR;
224     for (gig::Instrument* instr = gig.GetFirstInstrument(); instr; instr = gig.GetNextInstrument()) {
225     for (gig::Region* rgn = instr->GetFirstRegion(); rgn; rgn = instr->GetNextRegion()) {
226     int bits = 0;
227     for (int d = 0; d < 8; ++d) {
228     if (rgn->pDimensionDefinitions[d].dimension == gig::dimension_samplechannel) {
229     for (int dr = 0; dr < rgn->DimensionRegions && rgn->pDimensionRegions[dr]; ++dr) {
230     if (!rgn->pDimensionRegions[dr]->pSample) continue; // no sample assigned, ignore
231     if (rgn->pDimensionRegions[dr]->pSample->Channels != 1) continue; // seems already to be a true stereo sample, ignore
232    
233 schoenebeck 2693 const int channel = (dr & (1 << bits)) ? 1 : 0;
234 schoenebeck 2685 if (channel == 1) continue; // ignore right audio channel dimension region here
235    
236     // so current dimension region is for left channel, so now get its matching right channel dimension region ...
237     const int drR = dr | (1 << bits);
238     if (drR >= rgn->DimensionRegions || !rgn->pDimensionRegions[drR]) {
239     cerr << "WARNING: Could not find matching right channel for left channel dimension region!" << endl;
240     continue;
241     }
242     if (!rgn->pDimensionRegions[drR]->pSample) continue; // only a sample for left channel, but none for right channel, ignore
243     if (rgn->pDimensionRegions[drR]->pSample->Channels != 1) {
244     cerr << "WARNING: Right channel seems already to be a true stereo sample!" << endl;
245     continue;
246     }
247     if (rgn->pDimensionRegions[dr]->pSample == rgn->pDimensionRegions[drR]->pSample) continue; // both channels use same sample, thus actually a mono thing here, ignore
248    
249     // found a valid mono pair, remember it
250     SamplePair samplePair(
251     rgn->pDimensionRegions[dr]->pSample,
252     rgn->pDimensionRegions[drR]->pSample
253     );
254     SamplePair samplePairInversed(
255     rgn->pDimensionRegions[drR]->pSample,
256     rgn->pDimensionRegions[dr]->pSample
257     );
258     DimRgnPair dimRgnpair(
259     rgn->pDimensionRegions[dr],
260     rgn->pDimensionRegions[drR]
261     );
262     if (samplePairs.count(samplePairInversed)) {
263     cerr << "WARNING: Sample pair ['" << rgn->pDimensionRegions[dr]->pSample->pInfo->Name << "', '" << rgn->pDimensionRegions[drR]->pSample->pInfo->Name << "'] already referenced in inverse channel order!\n";
264     } else {
265     if (samplePairsByL.count(rgn->pDimensionRegions[drR]->pSample)) {
266     cerr << "WARNING: Right mono sample '" << rgn->pDimensionRegions[drR]->pSample->pInfo->Name << "' already referenced as left sample!\n";
267     }
268     if (samplePairsByR.count(rgn->pDimensionRegions[dr]->pSample)) {
269     cerr << "WARNING: Left mono sample '" << rgn->pDimensionRegions[dr]->pSample->pInfo->Name << "' already referenced as right sample!\n";
270     }
271     }
272     samplePairs[samplePair].push_back(dimRgnpair);
273     if (verbose >= 2) cout << "Found mono sample pair ['" << rgn->pDimensionRegions[dr]->pSample->pInfo->Name << "', '" << rgn->pDimensionRegions[drR]->pSample->pInfo->Name << "'].\n" << flush;
274     }
275     goto nextRegion;
276     }
277     bits += rgn->pDimensionDefinitions[d].bits;
278     }
279     nextRegion:;
280     }
281     }
282     if (verbose) cout << samplePairs.size() << " mono pairs found.\n" << flush;
283    
284     // create a true stereo (interleaved) sample for each one of the found
285     // mono sample pairs
286     if (verbose) cout << "Creating true stereo samples ... " << flush;
287     if (verbose >= 2) cout << endl;
288     map<SamplePair, vector<DimRgnPair> > samplePairsFiltered;
289     map<SamplePair, gig::Sample*> targetStereoSamples;
290     for (map<SamplePair, vector<DimRgnPair> >::iterator it = samplePairs.begin();
291     it != samplePairs.end(); ++it)
292     {
293     gig::Sample* pSampleL = it->first.first;
294     gig::Sample* pSampleR = it->first.second;
295    
296     // bunch of sanity checks which ensure that both mono samples are
297     // using the same base parameter characteristics
298     if (pSampleL->SamplesPerSecond != pSampleR->SamplesPerSecond) {
299 schoenebeck 2693 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different sample rate! Skipping!\n";
300     continue;
301 schoenebeck 2685 }
302     if (pSampleL->FrameSize != pSampleR->FrameSize) {
303 schoenebeck 2693 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different frame size! Skipping!\n";
304     continue;
305 schoenebeck 2685 }
306     if (pSampleL->MIDIUnityNote != pSampleR->MIDIUnityNote) {
307 schoenebeck 2693 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different unity notes!";
308     OPTIONAL_SKIP_CHECK();
309 schoenebeck 2685 }
310     if (pSampleL->FineTune != pSampleR->FineTune) {
311 schoenebeck 2693 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different fine tune!";
312     OPTIONAL_SKIP_CHECK();
313 schoenebeck 2685 }
314     if (pSampleL->Loops != pSampleR->Loops) {
315 schoenebeck 2693 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different loop amount! Skipping!\n";
316 schoenebeck 2685 continue; // skip to convert this sample pair
317     }
318     if (pSampleL->Loops == 1 && pSampleR->Loops == 1) {
319     if (pSampleL->LoopType != pSampleR->LoopType) {
320 schoenebeck 2693 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different loop type!";
321     OPTIONAL_SKIP_CHECK();
322 schoenebeck 2685 }
323     if (pSampleL->LoopStart != pSampleR->LoopStart) {
324 schoenebeck 2693 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different loop start! Skipping!\n";
325 schoenebeck 2685 continue; // skip to convert this sample pair
326     }
327     if (pSampleL->LoopEnd != pSampleR->LoopEnd) {
328 schoenebeck 2693 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different loop end! Skipping!\n";
329 schoenebeck 2685 continue; // skip to convert this sample pair
330     }
331     if (pSampleL->LoopSize != pSampleR->LoopSize) {
332 schoenebeck 2693 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different loop size! Skipping!\n";
333 schoenebeck 2685 continue; // skip to convert this sample pair
334     }
335     if (pSampleL->LoopPlayCount != pSampleR->LoopPlayCount) {
336 schoenebeck 2693 cerr << "WARNING: Sample pair ['" << pSampleL->pInfo->Name << "', '" << pSampleR->pInfo->Name << "'] with different loop play count!";
337     OPTIONAL_SKIP_CHECK();
338 schoenebeck 2685 }
339     }
340    
341     // create the stereo sample
342     gig::Sample* pStereoSample = gig.AddSample();
343    
344     // add the new stereo sample to an appropriate sample group
345     const string groupName = stripWhiteSpace(stripAudioChannelFromName(pSampleL->GetGroup()->Name)) + " STEREO";
346     gig::Group* pGroup = NULL;
347     for (gig::Group* gr = gig.GetFirstGroup(); gr; gr = gig.GetNextGroup()) {
348     if (gr->Name == groupName) {
349     pGroup = gr;
350     break;
351     }
352     }
353     if (!pGroup) {
354     pGroup = gig.AddGroup();
355     pGroup->Name = groupName;
356     }
357     pGroup->AddSample(pStereoSample);
358    
359     pStereoSample->pInfo->Name = stripWhiteSpace(stripAudioChannelFromName(pSampleL->pInfo->Name));
360     pStereoSample->Channels = 2;
361     pStereoSample->SamplesPerSecond = pSampleL->SamplesPerSecond;
362     pStereoSample->BitDepth = pSampleL->BitDepth;
363     pStereoSample->FrameSize = pSampleL->FrameSize * 2;
364     pStereoSample->MIDIUnityNote = pSampleL->MIDIUnityNote;
365     pStereoSample->Loops = pSampleL->Loops;
366     pStereoSample->LoopType = pSampleL->LoopType;
367     pStereoSample->LoopStart = pSampleL->LoopStart;
368     pStereoSample->LoopEnd = pSampleL->LoopEnd;
369     pStereoSample->LoopSize = pSampleL->LoopSize;
370     pStereoSample->LoopPlayCount = pSampleL->LoopPlayCount;
371    
372     // libgig does not support saving of compressed samples
373     pStereoSample->Compressed = false;
374    
375     // schedule new sample for resize (will be performed when gig.Save() is called)
376     const long newStereoSamplesTotal = max(pSampleL->SamplesTotal, pSampleR->SamplesTotal);
377     if (verbose >= 2) cout << "Resize new stereo sample '" << pStereoSample->pInfo->Name << "' to " << newStereoSamplesTotal << " sample points.\n" << flush;
378     pStereoSample->Resize(newStereoSamplesTotal);
379    
380     samplePairsFiltered[it->first] = it->second;
381     targetStereoSamples[it->first] = pStereoSample;
382     }
383     if (verbose) cout << "Done.\n" << flush;
384    
385     // increase the .gig file size appropriately
386     if (verbose) cout << "Increase file size ... " << flush;
387     gig.Save();
388     if (verbose) cout << "Done.\n" << flush;
389    
390     vector<uint8_t> bufferMono[2];
391     vector<uint8_t> stereoBuffer;
392     gig::buffer_t decompressionBuffer;
393     unsigned long decompressionBufferSize = 0; // in sample points, not bytes
394    
395     // do the actual sample conversion of each mono -> stereo sample by
396     // reading/writing directly from/to disk
397     if (verbose) cout << "Convert mono pair data to true stereo data ... " << flush;
398     if (verbose >= 2) cout << endl;
399     set<gig::Sample*> allAffectedMonoSamples;
400     for (map<SamplePair, vector<DimRgnPair> >::iterator it = samplePairsFiltered.begin();
401     it != samplePairsFiltered.end(); ++it)
402     {
403     gig::Sample* pSampleL = it->first.first;
404     gig::Sample* pSampleR = it->first.second;
405     gig::Sample* pSampleMono[2] = { pSampleL, pSampleR };
406     gig::Sample* pStereoSample = targetStereoSamples[it->first];
407    
408     if ((pSampleMono[0]->SamplesTotal != pStereoSample->GetSize() &&
409     pSampleMono[1]->SamplesTotal != pStereoSample->GetSize()) || pStereoSample->GetSize() <= 0)
410     {
411     cerr << "Stereo length " << pStereoSample->GetSize() << ", mono L size " << pSampleL->SamplesTotal << ", mono R size " << pSampleR->SamplesTotal << "\n" << flush;
412     cerr << "CRITICAL ERROR: Ill sample sizes detected! Aborting.\n";
413     exit(-1);
414     }
415    
416     allAffectedMonoSamples.insert(pSampleL);
417     allAffectedMonoSamples.insert(pSampleR);
418    
419     const long neededStereoSize = pStereoSample->GetSize() * pStereoSample->FrameSize;
420     if (stereoBuffer.size() < neededStereoSize)
421     stereoBuffer.resize(neededStereoSize);
422    
423     if (bufferMono[0].size() < neededStereoSize / 2) {
424     bufferMono[0].resize(neededStereoSize / 2);
425     bufferMono[1].resize(neededStereoSize / 2);
426     }
427     memset(&bufferMono[0][0], 0, bufferMono[0].size());
428     memset(&bufferMono[1][0], 0, bufferMono[1].size());
429    
430     // read mono samples from disk
431     for (int iChan = 0; iChan < 2; ++iChan) {
432     pSampleMono[iChan]->SetPos(0);
433     unsigned long pointsRead = 0;
434     if (pSampleMono[iChan]->Compressed) {
435     if (decompressionBufferSize < pSampleMono[iChan]->SamplesTotal) {
436     decompressionBufferSize = pSampleMono[iChan]->SamplesTotal;
437     gig::Sample::DestroyDecompressionBuffer(decompressionBuffer);
438     decompressionBuffer = gig::Sample::CreateDecompressionBuffer(pSampleMono[iChan]->SamplesTotal);
439     }
440     pointsRead = pSampleMono[iChan]->Read(&bufferMono[iChan][0], pSampleMono[iChan]->SamplesTotal, &decompressionBuffer);
441     } else {
442     pointsRead = pSampleMono[iChan]->Read(&bufferMono[iChan][0], pSampleMono[iChan]->SamplesTotal);
443     }
444     if (pointsRead != pSampleMono[iChan]->SamplesTotal) {
445     cerr << "Read " << pointsRead << " sample points from mono sample " << ((iChan == 0) ? "L" : "R") << ".\n";
446     cerr << "CRITICAL ERROR: Reading entire mono sample data from disk failed! Aborting.\n";
447     exit(-1);
448     }
449     }
450    
451     if (pSampleL->FrameSize != pSampleR->FrameSize || pStereoSample->FrameSize / 2 != pSampleL->FrameSize || pStereoSample->FrameSize < 1) {
452     cerr << "Stereo frame size " << pStereoSample->FrameSize << ", mono L frame size " << pSampleL->FrameSize << ", mono R frame size " << pSampleL->FrameSize << "\n" << flush;
453     cerr << "CRITICAL ERROR: Ill frame sizes detected! Aborting.\n";
454     exit(-1);
455     }
456    
457     // convert from the 2 mono buffers to the stereo buffer
458     for (int s = 0; s < pStereoSample->GetSize(); ++s) {
459     memcpy(&stereoBuffer[s * pStereoSample->FrameSize], &bufferMono[0][s * pSampleL->FrameSize], pSampleL->FrameSize);
460     memcpy(&stereoBuffer[s * pStereoSample->FrameSize + pSampleL->FrameSize], &bufferMono[1][s * pSampleR->FrameSize], pSampleR->FrameSize);
461     }
462    
463     // write converted stereo sample data directly to disk
464     if (verbose >= 2) cout << "Writing stereo sample '" << pStereoSample->pInfo->Name << "' (" << pStereoSample->GetSize() << " sample points).\n" << flush;
465     pStereoSample->SetPos(0);
466     unsigned long pointsWritten = pStereoSample->Write(&stereoBuffer[0], pStereoSample->GetSize());
467     if (verbose >= 2) cout << "Written " << pointsWritten << " sample points.\n" << flush;
468     }
469     if (verbose) cout << "Done.\n" << flush;
470    
471     // decompression buffer not needed anymore
472     gig::Sample::DestroyDecompressionBuffer(decompressionBuffer);
473    
474     // replace the old mono sample references by the new stereo sample
475     // references for all instruments of the .gig file
476     if (verbose) cout << "Replace old mono sample references by new stereo sample references ... " << flush;
477     for (map<SamplePair, vector<DimRgnPair> >::iterator it = samplePairsFiltered.begin();
478     it != samplePairsFiltered.end(); ++it)
479     {
480     if (forceReplace) {
481     // replace EVERY occurence of the old mono sample references by
482     // the new true stereo ones
483     gig::Sample* pMonoSamples[2] = { it->first.first, it->first.second };
484     gig::Sample* pStereoSample = targetStereoSamples[it->first];
485     for (int iChan = 0; iChan < 2; ++iChan) {
486     gig::Sample* pMonoSample = pMonoSamples[iChan];
487     // iterate through all instruments and replace all references of that sample
488     for (gig::Instrument* instr = gig.GetFirstInstrument(); instr; instr = gig.GetNextInstrument()) {
489     for (gig::Region* rgn = instr->GetFirstRegion(); rgn; rgn = instr->GetNextRegion()) {
490     for (int dr = 0; dr < rgn->DimensionRegions; ++dr) {
491     if (!rgn->pDimensionRegions[dr])
492     continue;
493     if (rgn->pDimensionRegions[dr]->pSample == pMonoSample) {
494     rgn->pDimensionRegions[dr]->pSample = pStereoSample;
495     }
496     }
497     }
498     }
499     }
500     } else {
501     // only replace old mono sample references of instruments if
502     // they really seem to be used in a stereo context
503     const vector<DimRgnPair>& dimRgnPairs = it->second;
504     gig::Sample* pStereoSample = targetStereoSamples[it->first];
505     for (int drp = 0; drp < dimRgnPairs.size(); ++drp) {
506     dimRgnPairs[drp].first->pSample = pStereoSample;
507     dimRgnPairs[drp].second->pSample = pStereoSample;
508     }
509     }
510     }
511     if (verbose) cout << "Done.\n" << flush;
512    
513     if (!keep) {
514     if (verbose) cout << "Removing orphaned mono samples ... " << flush;
515     int iDeletedSamples = 0;
516     // remove all old 2 mono sample pairs that are not referenced anymore
517     for (map<SamplePair, vector<DimRgnPair> >::iterator it = samplePairsFiltered.begin();
518     it != samplePairsFiltered.end(); ++it)
519     {
520     gig::Sample* pMonoSamples[2] = { it->first.first, it->first.second };
521     for (int iChan = 0; iChan < 2; ++iChan) {
522     gig::Sample* pMonoSample = pMonoSamples[iChan];
523    
524     // iterate through all instruments and check if that
525     // old mono sample is still referenced somewhere
526     for (gig::Instrument* instr = gig.GetFirstInstrument(); instr; instr = gig.GetNextInstrument()) {
527     for (gig::Region* rgn = instr->GetFirstRegion(); rgn; rgn = instr->GetNextRegion()) {
528     for (int dr = 0; dr < rgn->DimensionRegions; ++dr) {
529     if (!rgn->pDimensionRegions[dr])
530     continue;
531     if (rgn->pDimensionRegions[dr]->pSample == pMonoSample) {
532     std::cout << "NOTE: Mono sample '" + pMonoSample->pInfo->Name + "' is still referenced by instrument '" << instr->pInfo->Name << "'. Thus not deleting sample.\n";
533     goto nextSample; // still referenced, don't delete sample
534     }
535     }
536     }
537     }
538    
539     // if this point is reached, then pMonoSample is not referenced
540     // by any instrument anymore, so delete this old mono sample now
541     {
542     gig::Group* pGroup = pMonoSample->GetGroup();
543     gig.DeleteSample(pMonoSample);
544     iDeletedSamples++;
545    
546     // if deleted sample's group is now empty, delete the group
547     if (!pGroup->GetFirstSample()) gig.DeleteGroup(pGroup);
548     }
549    
550     nextSample:;
551     }
552     }
553     if (verbose) {
554     cout << "Done (deleted " << iDeletedSamples << " of " << allAffectedMonoSamples.size() << " mono samples).\n" << flush;
555     if (iDeletedSamples < allAffectedMonoSamples.size())
556     cout << "(Use --force-replace to replace all old mono references.)\n" << flush;
557     }
558     }
559    
560     // save sample reference changes (and drop deleted mono samples)
561     if (verbose) cout << "Saving final sample references to disk and shrink file ... " << flush;
562     gig.Save();
563     if (verbose) cout << "Done.\n" << flush;
564    
565     } catch (RIFF::Exception e) {
566     cerr << "Failed converting file:" << endl;
567     e.PrintMessage();
568     return false; // error
569     } catch (...) {
570 schoenebeck 3048 cerr << "Unknown exception occurred while trying to convert file." << endl;
571 schoenebeck 2685 return false; // error
572     }
573     return true; // success
574     }
575    
576     int main(int argc, char *argv[]) {
577     int nOptions = 0;
578     bool bOptionRecurse = false;
579     bool bOptionKeep = false;
580     bool bOptionForceReplace = false;
581 schoenebeck 2693 bool bOptionMatchIncompatible = false;
582 schoenebeck 2685 int iVerbose = 0;
583    
584     // validate & parse arguments provided to this program
585     if (argc <= 1) {
586     printUsage();
587     return EXIT_FAILURE;
588     }
589     for (int i = 1; i < argc; ++i) {
590     const string opt = argv[i];
591     if (opt == "--") { // common for all command line tools: separator between initial option arguments and i.e. subsequent file arguments
592     nOptions++;
593     break;
594     }
595     if (opt.substr(0, 1) != "-") break;
596    
597     if (opt == "-v") {
598     printVersion();
599     return EXIT_SUCCESS;
600     } else if (opt == "-r") {
601     nOptions++;
602     bOptionRecurse = true;
603     } else if (opt == "--keep") {
604     nOptions++;
605     bOptionKeep = true;
606     } else if (opt == "--force-replace") {
607     nOptions++;
608     bOptionForceReplace = true;
609 schoenebeck 2693 } else if (opt == "--incompatible") {
610     nOptions++;
611     bOptionMatchIncompatible = true;
612 schoenebeck 2685 } else if (opt == "--verbose") {
613     nOptions++;
614     iVerbose = 1;
615     if (i+1 < argc) {
616     char* s = argv[i+1];
617     char* endptr = NULL;
618     errno = 0;
619     long val = strtol(s, &endptr, 10);
620     if (errno != EINVAL && errno != ERANGE && endptr != s) {
621     if (val < 0) {
622     cerr << "Numeric argument for --verbose must not be negative!\n";
623     return -1;
624     }
625     iVerbose = val;
626     nOptions++;
627     i++;
628     }
629     }
630    
631     }
632     }
633     const int nFileArguments = argc - nOptions - 1;
634     if (nFileArguments < 1) {
635     printUsage();
636     return EXIT_FAILURE;
637     }
638    
639     bool bError = false;
640    
641     // assemble list of .gig file names to be converted
642     for (int i = nOptions + 1; i < argc; ++i) {
643     collectFiles(argv[i], bOptionRecurse, &bError);
644     }
645     if (g_files.size() < 1) {
646     cerr << "No file given to be converted!" << endl;
647     return EXIT_FAILURE;
648     }
649     if (bError) {
650     cerr << "Aborted due to error. No file has been changed." << endl;
651     return EXIT_FAILURE;
652     }
653    
654     // convert each file in the assembled list of file names
655     {
656     int i = 0;
657     for (set<string>::const_iterator it = g_files.begin(); it != g_files.end(); ++it, ++i) {
658     cout << "Converting file " << (i+1) << "/" << int(g_files.size()) << ": " << *it << " ... " << flush;
659     if (iVerbose) cout << endl;
660 schoenebeck 2693 bool bSuccess = convertFileToStereo(
661     *it, bOptionKeep, bOptionForceReplace, !bOptionMatchIncompatible,
662     iVerbose
663     );
664 schoenebeck 2685 if (!bSuccess) return EXIT_FAILURE;
665     cout << "OK" << endl;
666     }
667     }
668    
669     return EXIT_SUCCESS;
670     }

Properties

Name Value
svn:keywords Revision

  ViewVC Help
Powered by ViewVC