> Could you please elaborate, i.e. examples of where you would see
> > issues by
> > just supplying regex patterns from the command line?
Sure!
I don't know in which order the attributes might be.
The regex you implemented uses a fixed order:
'(Name1)(Name2)(Velocity)(Note_Nr)(Note_Name)'
Take the following filename:
'Name - 64 - Bb3 - articulation.wav'
A regex-pattern could look like this:
'(.*) - (\d*) - (.*) - (.*).wav'
But how would the code know, that '64' is meant to be the velocity?
Or that 'Bb3' is a NoteName and 'articulation' is Name2?
One would also somehow have to enter the correct order, like
'(Name1)(Velocity)(Note_Name)(Name2)',
or am I missing something?
By using format specifiers, like in printf, you would simply enter:
'%n - %v - %a - %m.wav'
(where %n = Name1, %v = velocity, %a = Note_Name, %m = Name2)
I implemented some more checks today, each attribute is now checked
against a given regex-string, e.g. NoteName:
(^[c|C|d|D|e|E|f|F|g|G|a|A|b|B].*-?\\d+$)
where a pattern for the signs ('.*' in the string) will have to be
supplied if no Note_Nr is available in the filename (defaults to '#'
and 'b').
Also, it is checked that all required values for further processing are
available (for now: Name1, Velocity, Note_Nr).
It 'should' be quite robust now, I believe.
Both updated files are attached (patch created against wav2gig.cpp from
today).
Of course, all the information and examples at the beginning could go
into the manpage, but I would recommend to at least print the available
specifiers (if you want to use the filenamescanner at all...;).
> The patch also adds
> > > "
> > > } else {
> > > s->MIDIUnityNote = wav->note;
> > > "
> > > as you proposed.
> >
> > I committed that now, because no matter how the naming scheme
> > evolves, this
> > change makes sense anyway.
Cool, thanks!
Cheers,
Kolja
#include<iostream>
#include <string>
#include <regex>
using namespace std;
// This is the container for available specifiers, their names and their
// required (regex-)format.
// Additional specifiers can be added, but don't change/use already existent
// names, since there are some checks against them.
string fs_mapping[][3] = {
{"n", "NAME1", "(.+)"},
{"m", "NAME2", "(.*)"},
{"v", "VELOCITY_NR", "(\\d+)"},
{"r", "NOTE_NR", "(\\d*)"},
{"a", "NOTE_NAME", "(^[c|C|d|D|e|E|f|F|g|G|a|A|b|B].*-?\\d+$)"}
};
const int ATTRIBUTES = sizeof(fs_mapping)/sizeof(fs_mapping[0]);
// Required attributes for processing filenames.
const string fs_req = "[n|v|r]";
// A NOTE_NAME consists of "note[sign]octave"
const string NOTES[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};
// This struct keeps/gets all required elements for scanning the filenames
// including all specifiers as of fs_mapping.
struct fs_struct {
string filename_rx=""; // regex-string to scan filenames
struct attributes_struct {
string spec; // specifier as of fs_mapping
string spec_name; // specifier's name
string spec_rx; // regex pattern that has to be matched
string str; // resulting pattern from filename
int map; // n-th result in filename-regex-results,
// initially -10, -1 when attribute is static.
} attr[ATTRIBUTES];
char notenr; // attribute-number of NOTE_NR
char notename; // attribute-number of NOTE_NAME
string sharpsign = "#"; // needed to eventually calculate -
string flatsign = "b"; // NOTE_NR from given NOTE_NAME or vice versa
fs_struct() {
for (int i = 0; i < ATTRIBUTES; i++) {
attr[i].str = "";
attr[i].map = -10;
attr[i].spec = fs_mapping[i][0];
attr[i].spec_name = fs_mapping[i][1];
attr[i].spec_rx = fs_mapping[i][2];
if (attr[i].spec_name == "NOTE_NR")
notenr = i;
else if (attr[i].spec_name == "NOTE_NAME")
notename = i;
}
}
void clear() { // clear fs to defaults.
filename_rx="";
for (int i = 0; i < ATTRIBUTES; i++) {
attr[i].str = "";
attr[i].map = -10;
}
sharpsign = "#";
flatsign = "b";
}
};
// return attribute_nr for given symbol, regex will already have all unknown specifiers filtered
int fs_getAttributeNumber(char symbol, struct fs_struct &fs) {
for (int i = 0; i < ATTRIBUTES; i++)
if (fs.attr[i].spec.c_str()[0] == symbol)
return i;
return -1;
}
// generate NOTE_NR out of NOTE_NAME
string fs_getNoteNr(struct fs_struct &fs) {
string notename = fs.attr[fs.notename].str;
int note = -1;
int sign = 0;
int oct = 0;
for (int i = 0; i < 12; i++)
if (NOTES[i].size() == 1)
if (toupper(notename[0]) == toupper(NOTES[i][0]))
note = i;
if (note == -1)
return "-1";
notename.erase(0,1);
if (notename.compare(0,fs.sharpsign.size(), fs.sharpsign) == 0) { // is sharp-sign?
sign = 1;
notename.erase(0,fs.sharpsign.size());
}
else if (fs.attr[fs.notename].str.compare(1,fs.flatsign.size(), fs.flatsign) == 0) { // is flat-sign?
sign = -1;
notename.erase(0,fs.sharpsign.size());
}
oct = atoi(notename.c_str());
return to_string(24 + note + sign + oct*12);
}
// generate NOTE_NAME out of Note_NR
string fs_getNoteName(struct fs_struct &fs) {
int notenr = atoi(fs.attr[fs.notenr].str.c_str());
int note, oct;
oct = notenr / 12 - 2;
note = notenr % 12;
return NOTES[note] + to_string(oct);;
}
// get fileinfo from file via fs
void fs_get_fileinfo(string file, struct fs_struct &fs) {
// const regex input_e (fs.filename_rx);
cmatch cm;
regex_match (file.c_str(), cm, regex (fs.filename_rx));
for (unsigned i=1; i<cm.size(); ++i) // loop through regex-results
for (int j = 0; j < ATTRIBUTES; j++)
if (fs.attr[j].map == i - 1)
fs.attr[j].str = cm[i];
// Check if Note_NR is available
if (fs.attr[fs.notenr].map == -10)
fs.attr[fs.notenr].str = fs_getNoteNr(fs); // generate NOTE_NR from NOTE_NAME
// Check if Note_NAME is available
if (fs.attr[fs.notename].map == -10)
fs.attr[fs.notename].str = fs_getNoteName(fs); // generate NOTE_NAME from NOTE_NR
}
// check attributes against spec_rx
int fs_checkAttributes (int i, struct fs_struct &fs) {
const regex input_e (fs.attr[i].spec_rx);
cmatch cm;
return regex_match (fs.attr[i].str.c_str(), cm, input_e);
}
// Enter scheme and thereof create regex-pattern and mapping to scan filenames
int fs_schemeinput(string filename, struct fs_struct &fs) {
cout << "\nFileNameScanner\n"
<< "---------------\n"
<< "You can enter a string in order to map patterns of the filenames\n"
<< "to required attributes. The logic is similar to 'printf'.\n"
<< endl
<< "Use any number for [n] in order to specify the number of characters,\n"
<< "or omit for a variable length.\n"
<< "You can also enter (constant) substrings of the filename, these will\n"
<< "be used as delimiters.\n"
<< endl
<< "Example 1:\n"
<< "filename: Name1 - Name2 - 64 - 80\n"
<< "scheme : %n - %m - %v - %r\n"
<< endl
<< "Example 2:\n"
<< "filename: /home/me/samples/ASTRA 2 L.wav\n"
<< "scheme : %ies/%2n%2m%3a%i\n"
<< "Note: The string for the sign will have to be entered later on,\n"
<< "' ' for 'sharp' in this case ('A 2' = A#2).\n"
<< "The corresponding NOTE_NRs will be calculated based on this."
<< endl;
bool repeat = false;
while (true) {
string s;
cout << endl;
cout << "Available specifiers:\n";
for (int i = 0; i < ATTRIBUTES; i++) {
cout.width(10);
cout << left << "%[n]" + fs.attr[i].spec;
cout.width(15);
cout << left << fs.attr[i].spec_name;
cout << left <<fs.attr[i].spec_rx << endl;
}
cout.width(10);
cout << left << "%[n]i";
cout.width(15);
cout << left << "ignore" << "(.*)" << endl;
cout << endl;
cout << "filename:" << filename << endl;
cout << "scheme :";
string scheme;
getline (cin,scheme);
// Construct regex-string for scanning scheme.
string rx_str = "([^%]+)|(%\\d*[";
for (int i = 0; i < ATTRIBUTES; i++)
rx_str += fs.attr[i].spec + "|";
rx_str += "i])";
const regex e (rx_str);
// scan scheme and create regex-pattern to scan filenames
smatch m;
int it = 0;
fs.clear();
while (regex_search (scheme,m,e)) {
if (m.str(0)[0] == '%') {
char symbol = m.str(0)[m.str(0).size()-1];
if (m.str(0).size() > 2) {
if (symbol == 'i')
fs.filename_rx += ".{" + m.str(0).substr(1, m.str(0).size()-2) + "}";
else {
fs.filename_rx += "(.{" + m.str(0).substr(1, m.str(0).size()-2) + "})";
fs.attr[fs_getAttributeNumber(symbol,fs)].map = it;
it +=1;
}
} else {
if (symbol == 'i')
fs.filename_rx += ".*";
else {
fs.filename_rx += "(.*)";
fs.attr[fs_getAttributeNumber(symbol,fs)].map = it;
it+=1;
}
}
} else {
fs.filename_rx += m.str(0);
}
scheme = m.suffix().str();
}
// check if scheme fits filename
if (!regex_match(filename, regex(fs.filename_rx))) {
cout << "Given scheme doesn't match filename-strcuture.\n";
cout << "Calculated regex: <" << fs.filename_rx << ">" << endl;
cout << "(R)e-enter scheme, (a)bort: ";
getline (cin, s);
if ((s == "") or (s == "r") or (s == "R")) {
repeat == true;
continue;
} else
return 0;
}
// Check if neither NOTE_NR nor NOTE_NAME is available.
// If so, a static note might be entered, although it might be quite rare,
// that someone would want to create a single-noted instrument (maybe with several
// velocity-layers).
if ((fs.attr[fs.notenr].map == -10) and (fs.attr[fs.notename].map == -10)) {
cout << "No pattern for Note_Nr nor Note_Name found,\n"
<< "please enter a static value for Note_Nr, \n"
<< "leave empty to enter a static Note_Name instead.\n"
<< "Note_Nr: ";
getline (cin,s);
if (s != "") {
fs.attr[fs.notenr].str = s;
fs.attr[fs.notenr].map = -1;
} else {
cout << "Note_Name: ";
getline (cin,s);
if (s != "") {
fs.attr[fs.notename].str = s;
fs.attr[fs.notename].map = -1;
}
else {
cout << "No Note_Nr nor Note_Name specified." << endl;
cout << "(R)e-enter scheme, (a)bort: ";
getline (cin, s);
if ((s == "") or (s == "r") or (s == "R")) {
repeat == true;
continue;
} else
return 0;
}
}
}
// No NOTE_NR (pattern) given, need to calculate from noteName
if (fs.attr[fs.notenr].map == -10) {
cout << "Calculating Note_Nr from given Note_Name.\n"
<< "Please enter sign for 'sharp' (default: '#'): ";
getline (cin,s);
if (s != "")
fs.sharpsign = s;
cout << "Please enter sign for 'flat' (default: 'b'): ";
getline (cin,s);
cout << endl;
if (s != "")
fs.flatsign = s;
}
// check against filename
fs_get_fileinfo(filename, fs);
for (int i = 0; i < ATTRIBUTES; i++) {
while (true) {
if ((regex_match(fs.attr[i].spec, regex(fs_req))) and // is in fs_req
(!regex_match(fs.attr[i].str, regex(fs.attr[i].spec_rx)))) { // and insane
cout << fs.attr[i].spec_name << ": <" << fs.attr[i].str
<< "> doesn't have the required regex-structure of <"
<< fs.attr[i].spec_rx << ">" << endl
<< "(R)e-enter scheme, enter (s)tatic value, (a)bort: ";
getline(cin,s);
if ((s == "a") or (s == "A"))
return 0;
else if ((s == "s") or (s == "S")) {
cout << "Enter static value for " << fs.attr[i].spec_name << ": ";
getline (cin,fs.attr[i].str);
fs.attr[i].map = -1;
} else if ((s == "r") or (s == "R") or (s == "")) {
repeat = true;
break;
}
} else
break;
}
if (repeat)
break;
}
if (!repeat) { // Fine so far, ask if that is the expected result
fs_get_fileinfo(filename, fs);
cout << endl;
cout << "Result:" << endl;
for (int i = 0; i < ATTRIBUTES; i++) {
cout.width(15);
cout << fs_mapping[i][1] << ":\t" << fs.attr[i].str << endl;
}
cout << endl;
cout << "Is this what you wanted?\n"
<< "(Y)es, (r)e-enter scheme, (a)bort: ";
getline (cin,s);
if ((s == "y") or (s == "Y") or (s == ""))
return 1;
else if ((s == "a") or (s == "A"))
return 0;
}
}
return 0;
}
--- wav2gig.cpp 2021-08-28 17:59:09.749186944 +0200
+++ wav2gig_kk.cpp 2021-08-28 18:04:28.489184386 +0200
@@ -42,6 +42,8 @@
#include "../gig.h"
#include "../helper.h" // for ToString()
+#include "./filenamescanner.cpp"
+
// only libsndfile is available for Windows, so we use that for writing the sound files
#ifdef WIN32
# define HAVE_SNDFILE 1
@@ -70,7 +72,7 @@
using namespace std;
static string Revision() {
- string s = "$Revision: 3989 $";
+ string s = "$Revision: 3983 $";
return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
}
@@ -90,6 +92,8 @@
cout << endl;
cout << " -r Recurse through all subdirs of provided input WAV dirs." << endl;
cout << endl;
+ cout << " -s Enter scheme to scan filenames for attributes" << endl;
+ cout << endl;
}
static bool beginsWith(const string& haystack, const string& needle) {
@@ -312,6 +316,43 @@
return wav;
}
+static WavInfo fs_getWavInfo(string filename, struct fs_struct &fs) {
+ WavInfo wav;
+ wav.fileName = filename;
+ wav.sfinfo = {};
+ {
+ SNDFILE* hFile = sf_open(filename.c_str(), SFM_READ, &wav.sfinfo);
+ if (!hFile) {
+ cerr << "Could not open input wav file \"" << filename << "\"" << endl;
+ exit(EXIT_FAILURE);
+ }
+ wav.hasSfInst = (sf_command(hFile, SFC_GET_INSTRUMENT,
+ &wav.sfinst, sizeof(wav.sfinst)) != SF_FALSE);
+ sf_close(hFile);
+ switch (wav.sfinfo.channels) {
+ case 1:
+ case 2:
+ break;
+ default:
+ cerr << int(wav.sfinfo.channels) << " audio channels in WAV file \"" << filename << "\"; this is not supported!" << endl;
+ exit(EXIT_FAILURE);
+ }
+ }
+ {
+ fs_get_fileinfo(filename, fs);
+
+ wav.name1 = fs.attr[0].str;
+ string sVelocity = fs.attr[2].str;
+ wav.velocity = atoi(sVelocity.c_str());
+ string sNote = fs.attr[3].str;
+ wav.note = atoi(sNote.c_str());
+ wav.name2 = fs.attr[1].str;
+ wav.noteName = fs.attr[4].str;
+ }
+ return wav;
+}
+
+
inline int getDimensionIndex(gig::Region* region, gig::dimension_t type) {
for (int d = 0; d < region->Dimensions; ++d)
if (region->pDimensionDefinitions[d].dimension == type)
@@ -377,7 +418,8 @@
int main(int argc, char *argv[]) {
bool bForce = false;
bool bRecursive = false;
-
+ bool bfilescan = false;
+
// validate & parse arguments provided to this program
int iArg;
for (iArg = 1; iArg < argc; ++iArg) {
@@ -395,6 +437,8 @@
bForce = true;
} else if (opt == "-r") {
bRecursive = true;
+ } else if (opt == "-s") {
+ bfilescan = true;
} else {
cerr << "Unknown option '" << opt << "'" << endl;
cerr << endl;
@@ -458,11 +502,24 @@
// order all input wav files into regions and velocity splits
WavInstrument wavInstrument;
+
+ fs_struct fs;
+ if (bfilescan)
+ if (fs_schemeinput(*wavFileNames.begin(), fs) == 0) {
+ cout << "aborting..." << endl;
+ exit(0);
+ }
+
+
cout << "Preprocessing input WAV files by their names ... " << flush;
for (set<string>::const_iterator it = wavFileNames.begin();
it != wavFileNames.end(); ++it)
{
- WavInfo wavInfo = getWavInfo(*it);
+ WavInfo wavInfo;
+ if (bfilescan)
+ wavInfo = fs_getWavInfo(*it,fs);
+ else
+ wavInfo = getWavInfo(*it);
wavInfo.assertValid(); // make sure collected informations are OK
if (wavInstrument[wavInfo.note].count(wavInfo.velocity)) {
cerr << "Velocity conflict between file '" << wavInfo.fileName
_______________________________________________
Linuxsampler-devel mailing list
Linuxsampler-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxsampler-devel