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

Contents of /libgig/trunk/src/tools/sf2extract.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: 17524 byte(s)
* Fixed potential crash in command line tools gig2stereo, korg2gig,
  korgdump and sf2extract.

1 /***************************************************************************
2 * *
3 * sf2extract tool: Copyright (C) 2015 - 2017 by Christian Schoenebeck *
4 * <cuse@users.sf.net> *
5 * *
6 * SF2 C++ Library: Copyright (C) 2009-2010 by Grigor Iliev *
7 * <grigor@grigoriliev.com> *
8 * *
9 * This program is part of libgig. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License for more details. *
20 * *
21 * You should have received a copy of the GNU General Public License *
22 * along with this program; if not, write to the Free Software *
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
24 * MA 02111-1307 USA *
25 ***************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 # include <config.h>
29 #endif
30
31 #include <iostream>
32 #include <cstdlib>
33 #include <string.h>
34 #include <string>
35 #include <stdlib.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <errno.h>
39
40 #include "../SF.h"
41
42 #ifdef _MSC_VER
43 #define S_ISDIR(x) (S_IFDIR & (x))
44 #define S_IWUSR S_IWRITE
45 #define S_IXUSR S_IEXEC
46 #endif
47
48 #if POSIX
49 # include <dlfcn.h>
50 #endif
51
52 // only libsndfile is available for Windows, so we use that for writing the sound files
53 #ifdef WIN32
54 # define HAVE_SNDFILE 1
55 #endif // WIN32
56
57 // abort compilation here if neither libsndfile nor libaudiofile are available
58 #if !HAVE_SNDFILE && !HAVE_AUDIOFILE
59 # error "Neither libsndfile nor libaudiofile seem to be available!"
60 # error "(HAVE_SNDFILE and HAVE_AUDIOFILE are both false)"
61 #endif
62
63 // we prefer libsndfile before libaudiofile
64 #if HAVE_SNDFILE
65 # include <sndfile.h>
66 #else
67 # include <audiofile.h>
68 #endif // HAVE_SNDFILE
69
70 using namespace std;
71
72 typedef map<unsigned int, bool> OrderMap;
73
74 void ExtractSamples(sf2::File* gig, char* destdir, OrderMap& selection);
75 int writeWav(sf2::Sample* sample, const char* filename, void* samples, long totalFrameCount, int bitdepth);
76
77 string ToString(int i) {
78 const int SZ = 64;
79 static char strbuf[SZ];
80 snprintf(strbuf, SZ, "%d", i);
81 string s = strbuf;
82 return s;
83 }
84
85 string Revision() {
86 string s = "$Revision$";
87 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
88 }
89
90 void PrintVersion() {
91 cout << "sf2extract revision " << Revision() << endl;
92 cout << "using " << sf2::libraryName() << " " << sf2::libraryVersion();
93 #if HAVE_SNDFILE
94 char versionBuffer[128];
95 sf_command(NULL, SFC_GET_LIB_VERSION, versionBuffer, 128);
96 cout << ", " << versionBuffer;
97 #else // use libaudiofile
98 cout << "\nbuilt against libaudiofile "
99 << LIBAUDIOFILE_MAJOR_VERSION << "." << LIBAUDIOFILE_MINOR_VERSION;
100 # ifdef LIBAUDIOFILE_MICRO_VERSION
101 cout << "." << LIBAUDIOFILE_MICRO_VERSION;
102 # endif // LIBAUDIOFILE_MICRO_VERSION
103 #endif // HAVE_SNDFILE
104 cout << endl;
105 }
106
107 static void PrintUsage() {
108 cout << "sf2extract - extracts samples from a SoundFont version 2 file." << endl;
109 cout << endl;
110 cout << "Usage: sf2extract [-v] SF2FILE DESTDIR [SAMPLENR] [ [SAMPLENR] ...]" << endl;
111 cout << endl;
112 cout << " SF2FILE Input SoundFont (.sf2) file." << endl;
113 cout << endl;
114 cout << " DESTDIR Destination directory where all .wav files shall be written to." << endl;
115 cout << endl;
116 cout << " SAMPLENR Index (/indices) of Sample(s) which should be extracted." << endl;
117 cout << " If no sample indices are given, all samples will be extracted" << endl;
118 cout << " (use sf2dump to look for available samples)." << endl;
119 cout << endl;
120 cout << " -v Print version and exit." << endl;
121 cout << endl;
122 }
123
124 #if !HAVE_SNDFILE // use libaudiofile
125 void* hAFlib; // handle to libaudiofile
126 void openAFlib(void);
127 void closeAFlib(void);
128 // pointers to libaudiofile functions
129 AFfilesetup(*_afNewFileSetup)(void);
130 void(*_afFreeFileSetup)(AFfilesetup);
131 void(*_afInitChannels)(AFfilesetup,int,int);
132 void(*_afInitSampleFormat)(AFfilesetup,int,int,int);
133 void(*_afInitFileFormat)(AFfilesetup,int);
134 void(*_afInitRate)(AFfilesetup,int,double);
135 int(*_afWriteFrames)(AFfilehandle,int,const void*,int);
136 AFfilehandle(*_afOpenFile)(const char*,const char*,AFfilesetup);
137 int(*_afCloseFile)(AFfilehandle file);
138 #endif // !HAVE_SNDFILE
139
140 int main(int argc, char *argv[]) {
141 OrderMap selectedSamples;
142 if (argc >= 2) {
143 if (argv[1][0] == '-') {
144 switch (argv[1][1]) {
145 case 'v':
146 PrintVersion();
147 return EXIT_SUCCESS;
148 }
149 }
150 }
151 if (argc < 3) {
152 PrintUsage();
153 return EXIT_FAILURE;
154 }
155 FILE* hFile = fopen(argv[1], "r");
156 if (!hFile) {
157 cout << "Invalid input file argument!" << endl;
158 return EXIT_FAILURE;
159 }
160 fclose(hFile);
161 struct stat buf;
162 if (stat(argv[2], &buf) == -1) {
163 cout << "Unable to open DESTDIR: ";
164 switch (errno) {
165 case EACCES: cout << "Permission denied." << endl;
166 break;
167 case ENOENT: cout << "Directory does not exist, or name is an empty string." << endl;
168 break;
169 case ENOMEM: cout << "Insufficient memory to complete the operation." << endl;
170 break;
171 case ENOTDIR: cout << "Is not a directory." << endl;
172 break;
173 default: cout << "Unknown error" << endl;
174 }
175 return EXIT_FAILURE;
176 } else if (!S_ISDIR(buf.st_mode)) {
177 cout << "Unable to open DESTDIR: Is not a directory." << endl;
178 return EXIT_FAILURE;
179 } else if (!(S_IWUSR & buf.st_mode) || !(S_IXUSR & buf.st_mode)) {
180 cout << "Unable to open DESTDIR: Permission denied." << endl;
181 return EXIT_FAILURE;
182 }
183 try {
184 RIFF::File* riff = new RIFF::File(argv[1]);
185 sf2::File* sf = new sf2::File(riff);
186
187 // assemble list of selected samples (by sample index)
188 if (argc > 3) { // extracting specific samples ...
189 for (int i = 3; i < argc; i++) {
190 unsigned int index = atoi(argv[i]);
191 selectedSamples[index-1] = true;
192 }
193 } else { // extract all samples ...
194 for (int i = 0; i < sf->GetSampleCount(); ++i)
195 selectedSamples[i] = true;
196 }
197
198 cout << "Extracting samples from \"" << argv[1] << "\" to directory \"" << argv[2] << "\"." << endl << flush;
199 ExtractSamples(sf, argv[2], selectedSamples);
200 cout << "Extraction finished." << endl << flush;
201 delete sf;
202 delete riff;
203 }
204 catch (RIFF::Exception e) {
205 e.PrintMessage();
206 return EXIT_FAILURE;
207 }
208 catch (...) {
209 cout << "Unknown exception while trying to parse file." << endl;
210 return EXIT_FAILURE;
211 }
212
213 return EXIT_SUCCESS;
214 }
215
216 /// Removes spaces from the beginning and end of @a s.
217 inline void stripWhiteSpace(string& s) {
218 // strip white space at the beginning
219 for (int i = 0; i < s.size(); ++i) {
220 if (s[i] != ' ') {
221 s = s.substr(i);
222 break;
223 }
224 }
225 // strip white space at the end
226 for (int i = s.size() - 1; i >= 0; --i) {
227 if (s[i] != ' ') {
228 s = s.substr(0, i+1);
229 break;
230 }
231 }
232 }
233
234 static bool endsWith(const string& haystack, const string& needle) {
235 if (haystack.size() < needle.size()) return false;
236 return haystack.substr(haystack.size() - needle.size(), needle.size()) == needle;
237 }
238
239 inline bool hasLeftOrRightMarker(string s) {
240 stripWhiteSpace(s);
241 return endsWith(s, "-L") || endsWith(s, "-R") ||
242 endsWith(s, "_L") || endsWith(s, "_R") ||
243 endsWith(s, " L") || endsWith(s, " R");
244 }
245
246 inline void stripLeftOrRightMarkerAtEnd(string& s) {
247 if (hasLeftOrRightMarker(s)) {
248 s = s.substr(0, s.size() - 2);
249 stripWhiteSpace(s);
250 }
251 }
252
253 void ExtractSamples(sf2::File* sf, char* destdir, OrderMap& selection) {
254 if (sf->GetSampleCount() <= 0) {
255 cerr << "No samples found in file.\n";
256 return;
257 }
258
259 #if !HAVE_SNDFILE // use libaudiofile
260 hAFlib = NULL;
261 openAFlib();
262 #endif // !HAVE_SNDFILE
263 uint8_t* pWave = NULL;
264 int* pIntWave = NULL;
265 long BufferSize = 0;
266 sf2::Sample::buffer_t tmpBuf;
267
268 for (int iSample = 0; iSample < sf->GetSampleCount(); ++iSample) {
269 sf2::Sample* pSample = sf->GetSample(iSample);
270 if (selection[iSample] == false)
271 continue;
272 sf2::Sample* pSample2 =
273 (pSample->GetChannelCount() > 1) ?
274 sf->GetSample(pSample->SampleLink) : NULL;
275
276 string name = pSample->Name;
277 if (pSample2)
278 stripLeftOrRightMarkerAtEnd(name);
279
280 long totalFrameCount = pSample->GetTotalFrameCount();
281 if (pSample2 && pSample2->GetTotalFrameCount() > totalFrameCount)
282 totalFrameCount = pSample2->GetTotalFrameCount();
283
284 string filename = destdir;
285 if (filename[filename.size() - 1] != '/') filename += "/";
286 filename += ToString(iSample+1);
287 filename += "_";
288 if (name == "") {
289 name = "(NO NAME)";
290 filename += "NONAME";
291 } else {
292 filename += name;
293 name.insert(0, "\"");
294 name += "\"";
295 }
296 filename += ".wav";
297 const int bitdepth =
298 pSample->GetFrameSize() / pSample->GetChannelCount() * 8;
299 cout << "Extracting Sample " << (iSample+1) << ") " << name << " ("
300 << bitdepth << "Bits, " << pSample->SampleRate << "Hz, " << pSample->GetChannelCount() << " Channels, " << totalFrameCount << " Samples";
301 if (pSample->HasLoops()) {
302 cout << ", LoopStart " << pSample->StartLoop
303 << ", LoopEnd " << pSample->EndLoop;
304 }
305 cout << ")..." << flush;
306
307 // some sanity checks whether the stereo sample pair matches
308 if (pSample2) {
309 if (pSample->GetFrameSize() != pSample2->GetFrameSize() ||
310 pSample->GetChannelCount() != pSample2->GetChannelCount())
311 {
312 cerr << "Error: stereo sample pair does not match [ignoring]\n";
313 continue;
314 }
315 }
316
317 const long neededsize = (bitdepth == 24) ?
318 totalFrameCount * pSample->GetChannelCount() * sizeof(int) :
319 totalFrameCount * pSample->GetFrameSize();
320 if (BufferSize < neededsize) {
321 if (pWave) delete[] pWave;
322 pWave = new uint8_t[neededsize];
323 BufferSize = neededsize;
324 }
325 pIntWave = (int*)pWave;
326 memset(pWave, 0, neededsize);
327
328 pSample->SetPos(0);
329 unsigned long nRead = pSample->Read(pWave, pSample->GetTotalFrameCount());
330 if (nRead <= 0)
331 throw RIFF::Exception("Could not load sample data.");
332
333 if (pSample2) {
334 if (tmpBuf.Size < neededsize) {
335 if (tmpBuf.pStart) delete[] (uint8_t*)tmpBuf.pStart;
336 tmpBuf.pStart = new uint8_t[neededsize];
337 tmpBuf.Size = neededsize;
338 }
339 pSample2->SetPos(0);
340 unsigned long nRead = pSample2->ReadNoClear(pWave, pSample2->GetTotalFrameCount(), tmpBuf);
341 if (nRead <= 0)
342 throw RIFF::Exception("Could not load sample data.");
343 }
344
345 // Both libsndfile and libaudiofile uses int for 24 bit
346 // samples. libgig however returns 3 bytes per sample, so
347 // we have to convert the wave data before writing.
348 if (bitdepth == 24) {
349 int n = totalFrameCount * pSample->GetChannelCount();
350 for (int i = n - 1 ; i >= 0 ; i--) {
351 #if HAVE_SNDFILE
352 pIntWave[i] = pWave[i * 3] << 8 | pWave[i * 3 + 1] << 16 | pWave[i * 3 + 2] << 24;
353 #else
354 pIntWave[i] = pWave[i * 3] | pWave[i * 3 + 1] << 8 | pWave[i * 3 + 2] << 16;
355 #endif
356 }
357 }
358
359 int res = writeWav(pSample, filename.c_str(), pWave, totalFrameCount, bitdepth);
360 if (res < 0) cout << "Couldn't write sample data." << endl;
361 else cout << "ok" << endl;
362
363 // make sure we don't extract this stereo sample pair a 2nd time
364 if (pSample2)
365 selection[pSample->SampleLink] = false;
366 }
367 if (pWave) delete[] pWave;
368 if (tmpBuf.pStart) delete[] (uint8_t*)tmpBuf.pStart;
369 #if !HAVE_SNDFILE // use libaudiofile
370 closeAFlib();
371 #endif // !HAVE_SNDFILE
372 }
373
374 int writeWav(sf2::Sample* sample, const char* filename, void* samples, long totalFrameCount, int bitdepth) {
375 #if HAVE_SNDFILE
376 SNDFILE* hfile;
377 SF_INFO sfinfo;
378 SF_INSTRUMENT instr;
379 int format = SF_FORMAT_WAV;
380 switch (bitdepth) {
381 case 8:
382 format |= SF_FORMAT_PCM_S8;
383 break;
384 case 16:
385 format |= SF_FORMAT_PCM_16;
386 break;
387 case 24:
388 format |= SF_FORMAT_PCM_24;
389 break;
390 case 32:
391 format |= SF_FORMAT_PCM_32;
392 break;
393 default:
394 cerr << "Error: Bithdepth " << ToString(bitdepth) << " not supported by libsndfile, ignoring sample!\n" << flush;
395 return -1;
396 }
397 memset(&sfinfo, 0, sizeof (sfinfo));
398 memset(&instr, 0, sizeof (instr));
399 sfinfo.samplerate = sample->SampleRate;
400 sfinfo.frames = totalFrameCount;
401 sfinfo.channels = sample->GetChannelCount();
402 sfinfo.format = format;
403 if (!(hfile = sf_open(filename, SFM_WRITE, &sfinfo))) {
404 cerr << "Error: Unable to open output file \'" << filename << "\'.\n" << flush;
405 return -1;
406 }
407 instr.basenote = sample->OriginalPitch;
408 instr.detune = sample->PitchCorrection;
409 if (sample->HasLoops()) {
410 instr.loop_count = 1;
411 instr.loops[0].mode = SF_LOOP_FORWARD;
412 instr.loops[0].start = sample->StartLoop;
413 instr.loops[0].end = sample->EndLoop;
414 instr.loops[0].count = 1;
415 }
416 sf_command(hfile, SFC_SET_INSTRUMENT, &instr, sizeof(instr));
417 sf_count_t res = (bitdepth == 24) ?
418 sf_write_int(hfile, static_cast<int*>(samples), sample->GetChannelCount() * totalFrameCount) :
419 sf_write_short(hfile, static_cast<short*>(samples), sample->GetChannelCount() * totalFrameCount);
420 if (res != sample->GetChannelCount() * totalFrameCount) {
421 cerr << sf_strerror(hfile) << endl << flush;
422 sf_close(hfile);
423 return -1;
424 }
425 sf_close(hfile);
426 #else // use libaudiofile
427 AFfilesetup setup = _afNewFileSetup();
428 _afInitFileFormat(setup, AF_FILE_WAVE);
429 _afInitChannels(setup, AF_DEFAULT_TRACK, sample->GetChannelCount());
430 _afInitSampleFormat(setup, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP, bitdepth);
431 _afInitRate(setup, AF_DEFAULT_TRACK, sample->SampleRate);
432 if (setup == AF_NULL_FILESETUP) return -1;
433 AFfilehandle hFile = _afOpenFile(filename, "w", setup);
434 if (hFile == AF_NULL_FILEHANDLE) return -1;
435 if (_afWriteFrames(hFile, AF_DEFAULT_TRACK, samples, totalFrameCount) < 0) return -1;
436 _afCloseFile(hFile);
437 _afFreeFileSetup(setup);
438 #endif // HAVE_SNDFILE
439
440 return 0; // success
441 }
442
443 #if !HAVE_SNDFILE // use libaudiofile
444 void openAFlib() {
445 hAFlib = dlopen("libaudiofile.so", RTLD_NOW);
446 if (!hAFlib) {
447 cout << "Unable to load library libaudiofile.so: " << dlerror() << endl;
448 return;
449 }
450 _afNewFileSetup = (AFfilesetup(*)(void)) dlsym(hAFlib, "afNewFileSetup");
451 _afFreeFileSetup = (void(*)(AFfilesetup)) dlsym(hAFlib, "afFreeFileSetup");
452 _afInitChannels = (void(*)(AFfilesetup,int,int)) dlsym(hAFlib, "afInitChannels");
453 _afInitSampleFormat = (void(*)(AFfilesetup,int,int,int)) dlsym(hAFlib, "afInitSampleFormat");
454 _afInitFileFormat = (void(*)(AFfilesetup,int)) dlsym(hAFlib, "afInitFileFormat");
455 _afInitRate = (void(*)(AFfilesetup,int,double)) dlsym(hAFlib, "afInitRate");
456 _afWriteFrames = (int(*)(AFfilehandle,int,const void*,int)) dlsym(hAFlib, "afWriteFrames");
457 _afOpenFile = (AFfilehandle(*)(const char*,const char*,AFfilesetup)) dlsym(hAFlib, "afOpenFile");
458 _afCloseFile = (int(*)(AFfilehandle file)) dlsym(hAFlib, "afCloseFile");
459 if (dlerror()) cout << "Failed to load function from libaudiofile.so: " << dlerror() << endl;
460 }
461
462 void closeAFlib() {
463 if (hAFlib) dlclose(hAFlib);
464 }
465 #endif // !HAVE_SNDFILE
466
467

Properties

Name Value
svn:executable *
svn:keywords Revision

  ViewVC Help
Powered by ViewVC