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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3175 - (show annotations) (download)
Thu May 11 11:34:19 2017 UTC (6 years, 11 months ago) by schoenebeck
File size: 32895 byte(s)
* Fixed potential crash in command line tools gig2stereo, korg2gig,
  korgdump and sf2extract.

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

Properties

Name Value
svn:keywords Revision

  ViewVC Help
Powered by ViewVC