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

Annotation of /libgig/trunk/src/tools/sf2extract.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2780 - (hide annotations) (download)
Thu Jul 2 20:04:16 2015 UTC (8 years, 10 months ago) by schoenebeck
File size: 17469 byte(s)
* src/SF.cpp, src/SF.h:
- Added new method Sample::ReadNoClear().
* Added new command line tool "sf2extract" (and a man page for it).
* Bumped version (3.3.0.svn29).

1 schoenebeck 2780 /***************************************************************************
2     * *
3     * sf2extract tool: Copyright (C) 2015 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     return haystack.substr(haystack.size() - needle.size(), needle.size()) == needle;
236     }
237    
238     inline bool hasLeftOrRightMarker(string s) {
239     stripWhiteSpace(s);
240     return endsWith(s, "-L") || endsWith(s, "-R") ||
241     endsWith(s, "_L") || endsWith(s, "_R") ||
242     endsWith(s, " L") || endsWith(s, " R");
243     }
244    
245     inline void stripLeftOrRightMarkerAtEnd(string& s) {
246     if (hasLeftOrRightMarker(s)) {
247     s = s.substr(0, s.size() - 2);
248     stripWhiteSpace(s);
249     }
250     }
251    
252     void ExtractSamples(sf2::File* sf, char* destdir, OrderMap& selection) {
253     if (sf->GetSampleCount() <= 0) {
254     cerr << "No samples found in file.\n";
255     return;
256     }
257    
258     #if !HAVE_SNDFILE // use libaudiofile
259     hAFlib = NULL;
260     openAFlib();
261     #endif // !HAVE_SNDFILE
262     uint8_t* pWave = NULL;
263     int* pIntWave = NULL;
264     long BufferSize = 0;
265     sf2::Sample::buffer_t tmpBuf;
266    
267     for (int iSample = 0; iSample < sf->GetSampleCount(); ++iSample) {
268     sf2::Sample* pSample = sf->GetSample(iSample);
269     if (selection[iSample] == false)
270     continue;
271     sf2::Sample* pSample2 =
272     (pSample->GetChannelCount() > 1) ?
273     sf->GetSample(pSample->SampleLink) : NULL;
274    
275     string name = pSample->Name;
276     if (pSample2)
277     stripLeftOrRightMarkerAtEnd(name);
278    
279     long totalFrameCount = pSample->GetTotalFrameCount();
280     if (pSample2 && pSample2->GetTotalFrameCount() > totalFrameCount)
281     totalFrameCount = pSample2->GetTotalFrameCount();
282    
283     string filename = destdir;
284     if (filename[filename.size() - 1] != '/') filename += "/";
285     filename += ToString(iSample+1);
286     filename += "_";
287     if (name == "") {
288     name = "(NO NAME)";
289     filename += "NONAME";
290     } else {
291     filename += name;
292     name.insert(0, "\"");
293     name += "\"";
294     }
295     filename += ".wav";
296     const int bitdepth =
297     pSample->GetFrameSize() / pSample->GetChannelCount() * 8;
298     cout << "Extracting Sample " << (iSample+1) << ") " << name << " ("
299     << bitdepth << "Bits, " << pSample->SampleRate << "Hz, " << pSample->GetChannelCount() << " Channels, " << totalFrameCount << " Samples";
300     if (pSample->HasLoops()) {
301     cout << ", LoopStart " << pSample->StartLoop
302     << ", LoopEnd " << pSample->EndLoop;
303     }
304     cout << ")..." << flush;
305    
306     // some sanity checks whether the stereo sample pair matches
307     if (pSample2) {
308     if (pSample->GetFrameSize() != pSample2->GetFrameSize() ||
309     pSample->GetChannelCount() != pSample2->GetChannelCount())
310     {
311     cerr << "Error: stereo sample pair does not match [ignoring]\n";
312     continue;
313     }
314     }
315    
316     const long neededsize = (bitdepth == 24) ?
317     totalFrameCount * pSample->GetChannelCount() * sizeof(int) :
318     totalFrameCount * pSample->GetFrameSize();
319     if (BufferSize < neededsize) {
320     if (pWave) delete[] pWave;
321     pWave = new uint8_t[neededsize];
322     BufferSize = neededsize;
323     }
324     pIntWave = (int*)pWave;
325     memset(pWave, 0, neededsize);
326    
327     pSample->SetPos(0);
328     unsigned long nRead = pSample->Read(pWave, pSample->GetTotalFrameCount());
329     if (nRead <= 0)
330     throw RIFF::Exception("Could not load sample data.");
331    
332     if (pSample2) {
333     if (tmpBuf.Size < neededsize) {
334     if (tmpBuf.pStart) delete[] (uint8_t*)tmpBuf.pStart;
335     tmpBuf.pStart = new uint8_t[neededsize];
336     tmpBuf.Size = neededsize;
337     }
338     pSample2->SetPos(0);
339     unsigned long nRead = pSample2->ReadNoClear(pWave, pSample2->GetTotalFrameCount(), tmpBuf);
340     if (nRead <= 0)
341     throw RIFF::Exception("Could not load sample data.");
342     }
343    
344     // Both libsndfile and libaudiofile uses int for 24 bit
345     // samples. libgig however returns 3 bytes per sample, so
346     // we have to convert the wave data before writing.
347     if (bitdepth == 24) {
348     int n = totalFrameCount * pSample->GetChannelCount();
349     for (int i = n - 1 ; i >= 0 ; i--) {
350     #if HAVE_SNDFILE
351     pIntWave[i] = pWave[i * 3] << 8 | pWave[i * 3 + 1] << 16 | pWave[i * 3 + 2] << 24;
352     #else
353     pIntWave[i] = pWave[i * 3] | pWave[i * 3 + 1] << 8 | pWave[i * 3 + 2] << 16;
354     #endif
355     }
356     }
357    
358     int res = writeWav(pSample, filename.c_str(), pWave, totalFrameCount, bitdepth);
359     if (res < 0) cout << "Couldn't write sample data." << endl;
360     else cout << "ok" << endl;
361    
362     // make sure we don't extract this stereo sample pair a 2nd time
363     if (pSample2)
364     selection[pSample->SampleLink] = false;
365     }
366     if (pWave) delete[] pWave;
367     if (tmpBuf.pStart) delete[] (uint8_t*)tmpBuf.pStart;
368     #if !HAVE_SNDFILE // use libaudiofile
369     closeAFlib();
370     #endif // !HAVE_SNDFILE
371     }
372    
373     int writeWav(sf2::Sample* sample, const char* filename, void* samples, long totalFrameCount, int bitdepth) {
374     #if HAVE_SNDFILE
375     SNDFILE* hfile;
376     SF_INFO sfinfo;
377     SF_INSTRUMENT instr;
378     int format = SF_FORMAT_WAV;
379     switch (bitdepth) {
380     case 8:
381     format |= SF_FORMAT_PCM_S8;
382     break;
383     case 16:
384     format |= SF_FORMAT_PCM_16;
385     break;
386     case 24:
387     format |= SF_FORMAT_PCM_24;
388     break;
389     case 32:
390     format |= SF_FORMAT_PCM_32;
391     break;
392     default:
393     cerr << "Error: Bithdepth " << ToString(bitdepth) << " not supported by libsndfile, ignoring sample!\n" << flush;
394     return -1;
395     }
396     memset(&sfinfo, 0, sizeof (sfinfo));
397     memset(&instr, 0, sizeof (instr));
398     sfinfo.samplerate = sample->SampleRate;
399     sfinfo.frames = totalFrameCount;
400     sfinfo.channels = sample->GetChannelCount();
401     sfinfo.format = format;
402     if (!(hfile = sf_open(filename, SFM_WRITE, &sfinfo))) {
403     cerr << "Error: Unable to open output file \'" << filename << "\'.\n" << flush;
404     return -1;
405     }
406     instr.basenote = sample->OriginalPitch;
407     instr.detune = sample->PitchCorrection;
408     if (sample->HasLoops()) {
409     instr.loop_count = 1;
410     instr.loops[0].mode = SF_LOOP_FORWARD;
411     instr.loops[0].start = sample->StartLoop;
412     instr.loops[0].end = sample->EndLoop;
413     instr.loops[0].count = 1;
414     }
415     sf_command(hfile, SFC_SET_INSTRUMENT, &instr, sizeof(instr));
416     sf_count_t res = (bitdepth == 24) ?
417     sf_write_int(hfile, static_cast<int*>(samples), sample->GetChannelCount() * totalFrameCount) :
418     sf_write_short(hfile, static_cast<short*>(samples), sample->GetChannelCount() * totalFrameCount);
419     if (res != sample->GetChannelCount() * totalFrameCount) {
420     cerr << sf_strerror(hfile) << endl << flush;
421     sf_close(hfile);
422     return -1;
423     }
424     sf_close(hfile);
425     #else // use libaudiofile
426     AFfilesetup setup = _afNewFileSetup();
427     _afInitFileFormat(setup, AF_FILE_WAVE);
428     _afInitChannels(setup, AF_DEFAULT_TRACK, sample->GetChannelCount());
429     _afInitSampleFormat(setup, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP, bitdepth);
430     _afInitRate(setup, AF_DEFAULT_TRACK, sample->SampleRate);
431     if (setup == AF_NULL_FILESETUP) return -1;
432     AFfilehandle hFile = _afOpenFile(filename, "w", setup);
433     if (hFile == AF_NULL_FILEHANDLE) return -1;
434     if (_afWriteFrames(hFile, AF_DEFAULT_TRACK, samples, totalFrameCount) < 0) return -1;
435     _afCloseFile(hFile);
436     _afFreeFileSetup(setup);
437     #endif // HAVE_SNDFILE
438    
439     return 0; // success
440     }
441    
442     #if !HAVE_SNDFILE // use libaudiofile
443     void openAFlib() {
444     hAFlib = dlopen("libaudiofile.so", RTLD_NOW);
445     if (!hAFlib) {
446     cout << "Unable to load library libaudiofile.so: " << dlerror() << endl;
447     return;
448     }
449     _afNewFileSetup = (AFfilesetup(*)(void)) dlsym(hAFlib, "afNewFileSetup");
450     _afFreeFileSetup = (void(*)(AFfilesetup)) dlsym(hAFlib, "afFreeFileSetup");
451     _afInitChannels = (void(*)(AFfilesetup,int,int)) dlsym(hAFlib, "afInitChannels");
452     _afInitSampleFormat = (void(*)(AFfilesetup,int,int,int)) dlsym(hAFlib, "afInitSampleFormat");
453     _afInitFileFormat = (void(*)(AFfilesetup,int)) dlsym(hAFlib, "afInitFileFormat");
454     _afInitRate = (void(*)(AFfilesetup,int,double)) dlsym(hAFlib, "afInitRate");
455     _afWriteFrames = (int(*)(AFfilehandle,int,const void*,int)) dlsym(hAFlib, "afWriteFrames");
456     _afOpenFile = (AFfilehandle(*)(const char*,const char*,AFfilesetup)) dlsym(hAFlib, "afOpenFile");
457     _afCloseFile = (int(*)(AFfilehandle file)) dlsym(hAFlib, "afCloseFile");
458     if (dlerror()) cout << "Failed to load function from libaudiofile.so: " << dlerror() << endl;
459     }
460    
461     void closeAFlib() {
462     if (hAFlib) dlclose(hAFlib);
463     }
464     #endif // !HAVE_SNDFILE
465    
466    

Properties

Name Value
svn:executable *
svn:keywords Revision

  ViewVC Help
Powered by ViewVC