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

Diff of /libgig/trunk/src/tools/wav2gig.cpp

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

revision 3981 by schoenebeck, Tue Aug 3 15:19:08 2021 UTC revision 3992 by schoenebeck, Thu Sep 2 15:15:43 2021 UTC
# Line 82  static void printVersion() { Line 82  static void printVersion() {
82  static void printUsage() {  static void printUsage() {
83      cout << "wav2gig - Create GigaStudio file from a set of WAV files." << endl;      cout << "wav2gig - Create GigaStudio file from a set of WAV files." << endl;
84      cout << endl;      cout << endl;
85      cout << "Usage: wav2gig [-v] [-f] [-r] GIGFILE WAVFILEORDIR1 [ WAVFILEORDIR2 ... ]" << endl;      cout << "Usage: wav2gig [OPTIONS] GIGFILE WAVFILEORDIR1 [ WAVFILEORDIR2 ... ]" << endl;
86      cout << endl;      cout << endl;
87      cout << "  -v  Print version and exit." << endl;      cout << "  -v  Print version and exit." << endl;
88      cout << endl;      cout << endl;
# Line 90  static void printUsage() { Line 90  static void printUsage() {
90      cout << endl;      cout << endl;
91      cout << "  -r  Recurse through all subdirs of provided input WAV dirs." << endl;      cout << "  -r  Recurse through all subdirs of provided input WAV dirs." << endl;
92      cout << endl;      cout << endl;
93        cout << "  --dry-run" << endl;
94        cout << endl;
95        cout << "      Scan input sample (.wav) files, but exit before creating any .gig file." << endl;
96        cout << endl;
97        cout << "  --verbose" << endl;
98        cout << endl;
99        cout << "      Increase amount of info being shown." << endl;
100        cout << endl;
101        cout << "  --regex-name1 PATTERN" << endl;
102        cout << endl;
103        cout << "      Regular expression for overriding the NAME1 part of the input sample file name scheme." << endl;
104        cout << endl;
105        cout << "  --regex-name2 PATTERN" << endl;
106        cout << endl;
107        cout << "      Regular expression for overriding the NAME2 part of the input sample file name scheme." << endl;
108        cout << endl;
109        cout << "  --regex-velocity-nr PATTERN" << endl;
110        cout << endl;
111        cout << "      Regular expression for overriding the VELOCITY_NR part of the input sample file name scheme." << endl;
112        cout << endl;
113        cout << "  --regex-note-nr PATTERN" << endl;
114        cout << endl;
115        cout << "      Regular expression for overriding the NOTE_NR part of the input sample file name scheme." << endl;
116        cout << endl;
117        cout << "  --regex-note-name PATTERN" << endl;
118        cout << endl;
119        cout << "      Regular expression for overriding the NOTE_NAME part of the input sample file name scheme." << endl;
120        cout << endl;
121        cout << "Read 'man wav2gig' for detailed help." << endl;
122        cout << endl;
123  }  }
124    
125  static bool beginsWith(const string& haystack, const string& needle) {  static bool beginsWith(const string& haystack, const string& needle) {
# Line 100  static bool endsWith(const string& hayst Line 130  static bool endsWith(const string& hayst
130      return haystack.substr(haystack.size() - needle.size(), needle.size()) == needle;      return haystack.substr(haystack.size() - needle.size(), needle.size()) == needle;
131  }  }
132    
133    static string tokenByRegExGroup(const string& haystack, const string& pattern,
134                                    size_t group = 1)
135    {
136        regex rx(pattern);
137        smatch m;
138        regex_search(haystack, m, rx);
139        return (m.size() <= group) ? (string) "" : (string) m[group];
140    }
141    
142  static bool fileExists(const string& filename) {  static bool fileExists(const string& filename) {
143      FILE* hFile = fopen(filename.c_str(), "r");      FILE* hFile = fopen(filename.c_str(), "r");
144      if (!hFile) return false;      if (!hFile) return false;
# Line 159  static bool isValidWavFile(const string& Line 198  static bool isValidWavFile(const string&
198      return false;      return false;
199  }  }
200    
201    struct FilenameRegExPatterns {
202        string name1;
203        string name2;
204        string velocityNr;
205        string noteNr;
206        string noteName;
207    };
208    
209  static void collectWavFilesOfDir(set<string>& result, string path, bool bRecurse, bool* pbError = NULL) {  static void collectWavFilesOfDir(set<string>& result, string path, bool bRecurse, bool* pbError = NULL) {
210      DIR* d = opendir(path.c_str());      DIR* d = opendir(path.c_str());
211      if (!d) {      if (!d) {
# Line 265  public: Line 312  public:
312    
313  typedef map<int,WavRegion> WavInstrument;  typedef map<int,WavRegion> WavInstrument;
314    
315  static WavInfo getWavInfo(string filename) {  static WavInfo getWavInfo(string filename,
316                              const FilenameRegExPatterns& patterns)
317    {
318      WavInfo wav;      WavInfo wav;
319      wav.fileName = filename;      wav.fileName = filename;
320      wav.sfinfo = {};      wav.sfinfo = {};
# Line 288  static WavInfo getWavInfo(string filenam Line 337  static WavInfo getWavInfo(string filenam
337          }          }
338      }      }
339      {      {
340          regex rx(          wav.name1 = tokenByRegExGroup(filename, patterns.name1);
341              "^([^-]+) - " // name 1 (e.g. "BSTEIN18")          if (wav.name1.empty()) {
342              "([^-]+) - "  // name 2 (e.g. "noname")              cerr << "Unexpected file name format: \"" << filename
343              "([^-]+) - "  // velocity value (e.g. "18")                   << "\" for 'name1' RegEx pattern \"" << patterns.name1
344              "([^-]+) - "  // note number (e.g. "021")                   << "\" !" << endl;
345              "([^.]+)"   // note name (e.g. "a-1")              exit(EXIT_FAILURE);
346          );          }
347          smatch m;          wav.name2 = tokenByRegExGroup(filename, patterns.name2);
348          regex_search(filename, m, rx);          if (wav.name2.empty()) {
349          if (m.size() < 5) {              cerr << "Unexpected file name format: \"" << filename
350              cerr << "Invalid file name format: \"" << filename << "\"!" << endl;                   << "\" for 'name2' RegEx pattern \"" << patterns.name2
351                     << "\" !" << endl;
352                exit(EXIT_FAILURE);
353            }
354            string sVelocity = tokenByRegExGroup(filename, patterns.velocityNr);
355            if (sVelocity.empty()) {
356                cerr << "Unexpected file name format: \"" << filename
357                     << "\" for 'velocity-nr' RegEx pattern \"" << patterns.velocityNr
358                     << "\" !" << endl;
359              exit(EXIT_FAILURE);              exit(EXIT_FAILURE);
360          }          }
         wav.name1    = m[1];  
         string sVelocity = m[3];  
361          wav.velocity = atoi(sVelocity.c_str());          wav.velocity = atoi(sVelocity.c_str());
362          string sNote = m[4];          string sNoteNr = tokenByRegExGroup(filename, patterns.noteNr);
363          wav.note     = atoi(sNote.c_str());          if (sNoteNr.empty()) {
364          wav.name2    = m[2];              cerr << "Unexpected file name format: \"" << filename
365          wav.noteName = m[5];                   << "\" for 'note-nr' RegEx pattern \"" << patterns.noteNr
366                     << "\" !" << endl;
367                exit(EXIT_FAILURE);
368            }
369            wav.note = atoi(sNoteNr.c_str());
370            wav.noteName = tokenByRegExGroup(filename, patterns.noteName);
371            if (wav.noteName.empty()) {
372                cerr << "Unexpected file name format: \"" << filename
373                     << "\" for 'note-name' RegEx pattern \"" << patterns.noteName
374                     << "\" !" << endl;
375                exit(EXIT_FAILURE);
376            }
377      }      }
378      return wav;      return wav;
379  }  }
# Line 319  inline int getDimensionIndex(gig::Region Line 385  inline int getDimensionIndex(gig::Region
385      return -1;      return -1;
386  }  }
387    
388  static gig::Sample* createSample(gig::File* gig, WavInfo* wav) {  static gig::Sample* createSample(gig::File* gig, WavInfo* wav, bool bVerbose) {
389      gig::Sample* s = gig->AddSample();      gig::Sample* s = gig->AddSample();
390    
391      s->pInfo->Name      = wav->outputSampleName();      s->pInfo->Name      = wav->outputSampleName();
392      s->Channels         = wav->sfinfo.channels;      s->Channels         = wav->sfinfo.channels;
393      s->SamplesPerSecond = wav->sfinfo.samplerate;      s->SamplesPerSecond = wav->sfinfo.samplerate;
394        
395        if (bVerbose) {
396            cout << "Add Sample [" << gig->CountSamples() << "] '"
397                 << s->pInfo->Name << "' to gig file:" << endl;
398        }
399    
400      switch (wav->sfinfo.format & 0xff) {      switch (wav->sfinfo.format & 0xff) {
401          case SF_FORMAT_PCM_S8:          case SF_FORMAT_PCM_S8:
402          case SF_FORMAT_PCM_16:          case SF_FORMAT_PCM_16:
# Line 343  static gig::Sample* createSample(gig::Fi Line 414  static gig::Sample* createSample(gig::Fi
414      }      }
415    
416      s->FrameSize = s->Channels * s->BitDepth / 8;      s->FrameSize = s->Channels * s->BitDepth / 8;
417    
418        if (bVerbose) {
419            cout << "\t" << s->BitDepth << " Bits "
420                 << s->SamplesPerSecond << " Hz "
421                 << s->Channels << " Channels"
422                 << endl;
423        }
424    
425      if (wav->hasSfInst) {      if (wav->hasSfInst) {
426          s->MIDIUnityNote = wav->sfinst.basenote;          s->MIDIUnityNote = wav->sfinst.basenote;
427          s->FineTune      = wav->sfinst.detune;          s->FineTune      = wav->sfinst.detune;
428            if (bVerbose) {
429                cout << "\tRoot Note " << s->MIDIUnityNote
430                    << " [Source: .WAV Internal Content]" << endl;
431                cout << "\tFine Tune " << s->FineTune << endl;
432            }
433          if (wav->sfinst.loop_count && wav->sfinst.loops[0].mode != SF_LOOP_NONE) {          if (wav->sfinst.loop_count && wav->sfinst.loops[0].mode != SF_LOOP_NONE) {
434              s->Loops = 1;              s->Loops = 1;
435                if (bVerbose) cout << "\t";
436              switch (wav->sfinst.loops[0].mode) {              switch (wav->sfinst.loops[0].mode) {
437                  case SF_LOOP_FORWARD:                  case SF_LOOP_FORWARD:
438                      s->LoopType = gig::loop_type_normal;                      s->LoopType = gig::loop_type_normal;
439                        if (bVerbose) cout << "Normal ";
440                      break;                      break;
441                  case SF_LOOP_BACKWARD:                  case SF_LOOP_BACKWARD:
442                      s->LoopType = gig::loop_type_backward;                      s->LoopType = gig::loop_type_backward;
443                        if (bVerbose) cout << "Backward ";
444                      break;                      break;
445                  case SF_LOOP_ALTERNATING:                  case SF_LOOP_ALTERNATING:
446                      s->LoopType = gig::loop_type_bidirectional;                      s->LoopType = gig::loop_type_bidirectional;
447                        if (bVerbose) cout << "Pingpong ";
448                      break;                      break;
449              }              }
450              s->LoopStart = wav->sfinst.loops[0].start;              s->LoopStart = wav->sfinst.loops[0].start;
451              s->LoopEnd = wav->sfinst.loops[0].end;              s->LoopEnd = wav->sfinst.loops[0].end;
452              s->LoopPlayCount = wav->sfinst.loops[0].count;              s->LoopPlayCount = wav->sfinst.loops[0].count;
453              s->LoopSize = s->LoopEnd - s->LoopStart + 1;              s->LoopSize = s->LoopEnd - s->LoopStart + 1;
454                if (bVerbose) {
455                    cout << "Loop " << s->LoopPlayCount << " times from "
456                         << s->LoopStart << " to " << s->LoopEnd
457                         << " (Size " << s->LoopSize << ")" << endl;
458                }
459          }          }
460        } else {
461            s->MIDIUnityNote = wav->note;
462            cout << "\tRoot Note " << s->MIDIUnityNote << " [Source: .WAV Filename Schema]" << endl;
463      }      }
464    
465      // schedule for resize (will be performed when gig->Save() is called)      // schedule for resize (will be performed when gig->Save() is called)
# Line 375  static gig::Sample* createSample(gig::Fi Line 471  static gig::Sample* createSample(gig::Fi
471  int main(int argc, char *argv[]) {  int main(int argc, char *argv[]) {
472      bool bForce = false;      bool bForce = false;
473      bool bRecursive = false;      bool bRecursive = false;
474        bool bDryRun = false;
475        bool bVerbose = false;
476        FilenameRegExPatterns patterns = {
477            // name 1 (e.g. "BSTEIN18")
478            .name1 = "^([^-]+) - [^-]+ - [^-]+ - [^-]+ - [^.]+",
479            // name 2 (e.g. "noname")
480            .name2 = "^[^-]+ - ([^-]+) - [^-]+ - [^-]+ - [^.]+",
481            // velocity value (e.g. "18")
482            .velocityNr = "^[^-]+ - [^-]+ - ([^-]+) - [^-]+ - [^.]+",
483            // note number (e.g. "021")
484            .noteNr = "^[^-]+ - [^-]+ - [^-]+ - ([^-]+) - [^.]+",
485            // note name (e.g. "a-1")
486            .noteName = "^[^-]+ - [^-]+ - [^-]+ - [^-]+ - ([^.]+)",
487        };
488    
489      // validate & parse arguments provided to this program      // validate & parse arguments provided to this program
490      int iArg;      int iArg;
491      for (iArg = 1; iArg < argc; ++iArg) {      for (iArg = 1; iArg < argc; ++iArg) {
492          const string opt = argv[iArg];          const string opt = argv[iArg];
493            const string nextOpt = (iArg + 1 < argc) ? argv[iArg + 1] : "";
494          if (opt == "--") { // common for all command line tools: separator between initial option arguments and subsequent file arguments          if (opt == "--") { // common for all command line tools: separator between initial option arguments and subsequent file arguments
495              iArg++;              iArg++;
496              break;              break;
# Line 393  int main(int argc, char *argv[]) { Line 504  int main(int argc, char *argv[]) {
504              bForce = true;              bForce = true;
505          } else if (opt == "-r") {          } else if (opt == "-r") {
506              bRecursive = true;              bRecursive = true;
507            } else if (opt == "--dry-run") {
508                bDryRun = true;
509            } else if (opt == "--verbose") {
510                bVerbose = true;
511            } else if (opt == "--regex-name1") {
512                if (nextOpt.empty() || beginsWith(nextOpt, "-")) {
513                    cerr << "Missing argument for option '" << opt << "'" << endl;
514                    return EXIT_FAILURE;
515                }
516                patterns.name1 = nextOpt;
517            } else if (opt == "--regex-name2") {
518                if (nextOpt.empty() || beginsWith(nextOpt, "-")) {
519                    cerr << "Missing argument for option '" << opt << "'" << endl;
520                    return EXIT_FAILURE;
521                }
522                patterns.name2 = nextOpt;
523            } else if (opt == "--regex-velocity-nr") {
524                if (nextOpt.empty() || beginsWith(nextOpt, "-")) {
525                    cerr << "Missing argument for option '" << opt << "'" << endl;
526                    return EXIT_FAILURE;
527                }
528                patterns.velocityNr = nextOpt;
529            } else if (opt == "--regex-note-nr") {
530                if (nextOpt.empty() || beginsWith(nextOpt, "-")) {
531                    cerr << "Missing argument for option '" << opt << "'" << endl;
532                    return EXIT_FAILURE;
533                }
534                patterns.noteNr = nextOpt;
535            } else if (opt == "--regex-note-name") {
536                if (nextOpt.empty() || beginsWith(nextOpt, "-")) {
537                    cerr << "Missing argument for option '" << opt << "'" << endl;
538                    return EXIT_FAILURE;
539                }
540                patterns.noteName = nextOpt;
541          } else {          } else {
542              cerr << "Unknown option '" << opt << "'" << endl;              cerr << "Unknown option '" << opt << "'" << endl;
543              cerr << endl;              cerr << endl;
# Line 446  int main(int argc, char *argv[]) { Line 591  int main(int argc, char *argv[]) {
591      cout << "(" << int(wavFileNames.size()) << " found).\n";      cout << "(" << int(wavFileNames.size()) << " found).\n";
592    
593      // check if output file already exists      // check if output file already exists
594      if (fileExists(outFileName)) {      if (fileExists(outFileName) && !bDryRun) {
595          if (bForce) deleteFile(outFileName);          if (bForce) deleteFile(outFileName);
596          else {          else {
597              cerr << "Output file '" << outFileName << "' already exists. Use -f to overwrite it." << endl;              cerr << "Output file '" << outFileName << "' already exists. Use -f to overwrite it." << endl;
# Line 460  int main(int argc, char *argv[]) { Line 605  int main(int argc, char *argv[]) {
605      for (set<string>::const_iterator it = wavFileNames.begin();      for (set<string>::const_iterator it = wavFileNames.begin();
606           it != wavFileNames.end(); ++it)           it != wavFileNames.end(); ++it)
607      {      {
608          WavInfo wavInfo = getWavInfo(*it);          WavInfo wavInfo = getWavInfo(*it, patterns);
609          wavInfo.assertValid(); // make sure collected informations are OK          wavInfo.assertValid(); // make sure collected informations are OK
610          if (wavInstrument[wavInfo.note].count(wavInfo.velocity)) {          if (wavInstrument[wavInfo.note].count(wavInfo.velocity)) {
611              cerr << "Velocity conflict between file '" << wavInfo.fileName              cerr << "Velocity conflict between file '" << wavInfo.fileName
# Line 524  int main(int argc, char *argv[]) { Line 669  int main(int argc, char *argv[]) {
669              for (auto& itWav : wavRgn) {              for (auto& itWav : wavRgn) {
670                  const int velocity = itWav.first;                  const int velocity = itWav.first;
671                  WavInfo& wav = itWav.second;                  WavInfo& wav = itWav.second;
672                  gig::Sample* gigSample = createSample(&gig, &wav);                  gig::Sample* gigSample = createSample(&gig, &wav, bVerbose);
673                  queuedSamples[gigSample] = wav;                  queuedSamples[gigSample] = wav;
674    
675                  uint8_t iDimBits[8] = {};                  uint8_t iDimBits[8] = {};
# Line 575  int main(int argc, char *argv[]) { Line 720  int main(int argc, char *argv[]) {
720              }              }
721          }          }
722          cout << "OK\n";          cout << "OK\n";
723            
724            if (bDryRun)
725                return EXIT_SUCCESS;
726    
727          cout << "Saving initial gig file layout ... " << flush;          cout << "Saving initial gig file layout ... " << flush;
728          // save result to disk (as .gig file)          // save result to disk (as .gig file)
729          gig.Save(outFileName);          gig.Save(outFileName);

Legend:
Removed from v.3981  
changed lines
  Added in v.3992

  ViewVC Help
Powered by ViewVC