> 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

Reply via email to