/[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 2685 - (show annotations) (download)
Sat Jan 3 21:44:42 2015 UTC (9 years, 3 months ago) by schoenebeck
File size: 32153 byte(s)
* RIFF: Fixed embarrassing old bug: POSIX read() errors were never detected
  on Chunk::Read() calls due to signment incompatible variable.
* Added new command line tool "gig2stereo" (and a man page for it).
* Bumped version (v3.3.0.svn23).

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

  ViewVC Help
Powered by ViewVC