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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3983 - (hide annotations) (download)
Tue Aug 3 16:05:32 2021 UTC (2 years, 8 months ago) by schoenebeck
File size: 20967 byte(s)
Drop "svn:executable" property from all source files.

1 schoenebeck 2572 /*
2     libakai - C++ cross-platform akai sample disk reader
3     Copyright (C) 2002-2003 Sébastien Métrot
4    
5 persson 2836 Linux port by Christian Schoenebeck <cuse@users.sourceforge.net> 2003-2015
6 schoenebeck 2572
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Lesser General Public
9     License as published by the Free Software Foundation; either
10     version 2.1 of the License, or (at your option) any later version.
11    
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15     Lesser General Public License for more details.
16    
17     You should have received a copy of the GNU Lesser General Public
18     License along with this library; if not, write to the Free Software
19     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20     */
21     // akaiextract.cpp : Defines the entry point for the console application.
22     //
23    
24     #ifdef HAVE_CONFIG_H
25     #include <config.h>
26     #endif
27    
28 schoenebeck 2574 #ifdef WIN32
29 schoenebeck 2572 # define _WIN32_WINNT 0x0500
30     # include <windows.h>
31     # include <conio.h>
32     #else
33     # include <errno.h>
34     # include <dlfcn.h>
35     #endif
36    
37 schoenebeck 2574 #include <unistd.h>
38     #include <dirent.h>
39     #include <sys/stat.h>
40     #include <sys/types.h>
41    
42 schoenebeck 2572 // for testing the disk streaming methods
43     #define USE_DISK_STREAMING 1
44    
45     #include <stdio.h>
46     #include <stdlib.h>
47     #include <iostream>
48     #include <sstream>
49     #include <string.h>
50    
51     #include "../Akai.h"
52    
53     // only libsndfile is available for Windows, so we use that for writing the sound files
54     #ifdef WIN32
55     # define HAVE_SNDFILE 1
56     #endif // WIN32
57    
58     // abort compilation here if neither libsndfile nor libaudiofile are available
59     #if !HAVE_SNDFILE && !HAVE_AUDIOFILE
60     # error "Neither libsndfile nor libaudiofile seem to be available!"
61     # error "(HAVE_SNDFILE and HAVE_AUDIOFILE are both false)"
62     #endif
63    
64     // we prefer libsndfile before libaudiofile
65     #if HAVE_SNDFILE
66     # include <sndfile.h>
67     #else
68     # include <audiofile.h>
69     #endif // HAVE_SNDFILE
70    
71     #if !HAVE_SNDFILE // use libaudiofile
72     void* hAFlib; // handle to libaudiofile
73     void openAFlib(void);
74     void closeAFlib(void);
75     // pointers to libaudiofile functions
76     AFfilesetup(*_afNewFileSetup)(void);
77     void(*_afFreeFileSetup)(AFfilesetup);
78     void(*_afInitChannels)(AFfilesetup,int,int);
79     void(*_afInitSampleFormat)(AFfilesetup,int,int,int);
80     void(*_afInitFileFormat)(AFfilesetup,int);
81     void(*_afInitRate)(AFfilesetup,int,double);
82     int(*_afWriteFrames)(AFfilehandle,int,const void*,int);
83     AFfilehandle(*_afOpenFile)(const char*,const char*,AFfilesetup);
84     int(*_afCloseFile)(AFfilehandle file);
85     #endif // !HAVE_SNDFILE
86    
87     int writeWav(AkaiSample* sample, const char* filename, void* samples, long samplecount, int channels = 1, int bitdepth = 16, long rate = 44100);
88     void ConvertAkaiToAscii(char * buffer, int length); // debugging purpose only
89    
90     using namespace std;
91    
92     template<class T> inline std::string ToString(T o) {
93     std::stringstream ss;
94     ss << o;
95     return ss.str();
96     }
97    
98     void PrintLastError(char* file, int line)
99     {
100 schoenebeck 2574 #ifdef WIN32
101 schoenebeck 2572 LPVOID lpMsgBuf;
102     FormatMessage(
103     FORMAT_MESSAGE_ALLOCATE_BUFFER |
104     FORMAT_MESSAGE_FROM_SYSTEM |
105     FORMAT_MESSAGE_IGNORE_INSERTS,
106     NULL,
107     GetLastError(),
108     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
109     (LPTSTR) &lpMsgBuf,
110     0,
111     NULL
112     );
113     // Display the string.
114     char buf[2048];
115     sprintf(buf,"%s(%d): %s",file, line, (LPCTSTR)lpMsgBuf);
116     printf("%s",buf);
117     OutputDebugString(buf);
118     // Free the buffer.
119     LocalFree( lpMsgBuf );
120     #endif
121     }
122    
123     #define SHOWERROR PrintLastError(__FILE__,__LINE__)
124    
125     static void printUsage() {
126 schoenebeck 2574 #ifdef WIN32
127 persson 2836 const wchar_t* msg =
128     L"akaiextract <source-drive-letter>: <destination-dir>\n"
129     "by S\351bastien M\351trot (meeloo@meeloo.net)\n\n"
130 schoenebeck 2572 "Reads an AKAI media (i.e. CDROM, ZIP disk) and extracts all its audio\n"
131     "samples as .wav files to the given output directory. If the given output\n"
132     "directory does not exist yet, then it will be created.\n\n"
133 persson 2836 "Available types of your drives:\n";
134     DWORD n = wcslen(msg);
135     WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), msg, n, &n, NULL);
136 schoenebeck 2572 char rootpath[4]="a:\\";
137     for (char drive ='a'; drive <= 'z'; drive++) {
138     rootpath[0] = drive;
139     int type = GetDriveType(rootpath);
140     if (type != DRIVE_NO_ROOT_DIR) {
141     printf("Drive %s is a ", rootpath);
142     switch(type) {
143     case DRIVE_UNKNOWN: printf("Unknown\n"); break;
144     case DRIVE_NO_ROOT_DIR: printf("Unavailable\n");
145     case DRIVE_REMOVABLE: printf("removable disk\n"); break;
146     case DRIVE_FIXED: printf("fixed disk\n"); break;
147     case DRIVE_REMOTE: printf("remote (network) drive\n"); break;
148     case DRIVE_CDROM: printf("CD-ROM drive\n"); break;
149     case DRIVE_RAMDISK: printf("RAM disk\n"); break;
150     }
151     }
152     }
153     printf("\n");
154     #elif defined _CARBON_
155 persson 2836 setlocale(LC_CTYPE, "");
156     printf("%ls",
157     L"akaiextract [<source-path>] <destination-dir>\n"
158     "by S\351bastien M\351trot (meeloo@meeloo.net)\n\n"
159 schoenebeck 2572 "Reads an AKAI media (i.e. CDROM, ZIP disk) and extracts all its audio\n"
160     "samples as .wav files to the given output directory. If the given output\n"
161     "directory does not exist yet, then it will be created.\n\n"
162     "<source-path> - path of the source AKAI image file, if omitted CDROM\n"
163     " drive ('/dev/rdisk1s0') will be accessed\n\n"
164     "<destination-dir> - target directory where samples will be written to\n\n"
165     );
166     #else
167 persson 2836 setlocale(LC_CTYPE, "");
168     printf("%ls",
169     L"akaiextract <source-path> <destination-dir>\n"
170     "by S\351bastien M\351trot (meeloo@meeloo.net)\n"
171 schoenebeck 2572 "Linux port by Christian Schoenebeck\n\n"
172     "Reads an AKAI media (i.e. CDROM, ZIP disk) and extracts all its audio\n"
173     "samples as .wav files to the given output directory. If the given output\n"
174     "directory does not exist yet, then it will be created.\n\n"
175     "<source-path> - path of the source drive or image file (i.e. /dev/cdrom)\n\n"
176     "<destination-dir> - target directory where samples will be written to\n\n"
177     );
178     #endif
179     }
180    
181     int main(int argc, char** argv) {
182     #if defined _CARBON_
183     if (argc < 2) {
184     printUsage();
185     return -1;
186     }
187     #else
188     if (argc < 3) {
189     printUsage();
190     return -1;
191     }
192     #endif
193    
194     // open input source
195     DiskImage* pImage = NULL;
196 schoenebeck 2574 #ifdef WIN32
197 schoenebeck 2572 char drive = toupper(*(argv[1]))-'A';
198     printf("opening drive %c:\n",drive+'a');
199     pImage = new DiskImage(drive);
200     #elif defined _CARBON_
201     if (argc == 2) {
202     printf("Opening AKAI media at '/dev/rdisk1s0'\n");
203     pImage = new DiskImage((int)0); // arbitrary int, argument will be ignored
204     } else {
205     printf("Opening source AKAI image file at '%s'\n", argv[1]);
206     pImage = new DiskImage(argv[1]);
207     }
208     #else
209     printf("Opening AKAI media or image file at '%s'\n", argv[1]);
210     pImage = new DiskImage(argv[1]);
211     #endif
212    
213     // determine output directory path
214     #if defined _CARBON_
215     const char* outPath = (argc == 2) ? argv[1] : argv[2];
216     #else
217     const char* outPath = argv[2];
218     #endif
219    
220     AkaiDisk* pAkai = new AkaiDisk(pImage);
221    
222     printf("Reading Akai file entries (this may take a while)...\n");
223     printf("Partitions: %d\n", pAkai->GetPartitionCount());
224     printf("Partitions list:\n");
225     long totalSamplesSize = 0, totalSamples = 0;
226     uint i;
227     for (i = 0; i < pAkai->GetPartitionCount(); i++)
228     {
229     printf("%2.2d: Partition %c\n",i,'A'+i);
230     AkaiPartition* pPartition = pAkai->GetPartition(i);
231     if (pPartition)
232     {
233     std::list<AkaiDirEntry> Volumes;
234     pPartition->ListVolumes(Volumes);
235     std::list<AkaiDirEntry>::iterator it;
236     std::list<AkaiDirEntry>::iterator end = Volumes.end();
237     int vol = 0;
238     for (it = Volumes.begin(); it != end; it++)
239     {
240     AkaiDirEntry DirEntry = *it;
241     printf(" %d (%d) '%.12s' [start=%d - type=%d]\n", vol,DirEntry.mIndex, DirEntry.mName.c_str(), DirEntry.mStart, DirEntry.mType);
242     AkaiVolume* pVolume = pPartition->GetVolume(vol);
243     if (pVolume)
244     {
245     std::list<AkaiDirEntry> Programs;
246     pVolume->ListPrograms(Programs);
247     std::list<AkaiDirEntry>::iterator it;
248     std::list<AkaiDirEntry>::iterator end = Programs.end();
249    
250     uint prog = 0;
251     for (it = Programs.begin(); it != end; it++)
252     {
253     AkaiDirEntry DirEntry = *it;
254     printf(" Program %d (%d) '%.12s' [start=%d size=%d - type=%c]\n", prog,DirEntry.mIndex, DirEntry.mName.c_str(), DirEntry.mStart, DirEntry.mSize, DirEntry.mType);
255     AkaiProgram* pProgram = pVolume->GetProgram(prog);
256     printf(" Number of key groups: %d\n",pProgram->mNumberOfKeygroups);
257     pProgram->Release();
258     prog++;
259     }
260    
261     std::list<AkaiDirEntry> Samples;
262     pVolume->ListSamples(Samples);
263     end = Samples.end();
264    
265     uint samp = 0;
266     for (it = Samples.begin(); it != end; it++)
267     {
268     AkaiDirEntry DirEntry = *it;
269     printf(" Sample %d (%d) '%.12s' [start=%d size=%d - type=%c]\n", samp,DirEntry.mIndex, DirEntry.mName.c_str(), DirEntry.mStart, DirEntry.mSize, DirEntry.mType);
270     AkaiSample* pSample= pVolume->GetSample(samp);
271     printf(" Number of samples: %d (%d Hz)\n",pSample->mNumberOfSamples,pSample->mSamplingFrequency);
272     totalSamplesSize += pSample->mNumberOfSamples;
273     totalSamples++;
274     pSample->Release();
275     samp++;
276     }
277    
278     pVolume->Release();
279     }
280    
281     vol++;
282     }
283    
284     pPartition->Release();
285     }
286     }
287    
288     #if 0
289     printf("Writing Akai track to hard disk...");
290     fflush(stdout);
291     bool success = pImage->WriteImage("/tmp/some.akai");
292     if (success) printf("ok\n");
293     else printf("error\n");
294     #endif
295    
296     #if !HAVE_SNDFILE // use libaudiofile
297     hAFlib = NULL;
298     openAFlib();
299     #endif // !HAVE_SNDFILE
300    
301     totalSamplesSize *= 2; // due to 16 bit samples
302 persson 2836 printf("There are %ld samples on this disk with a total size of %ld Bytes. ",
303 schoenebeck 2572 totalSamples, totalSamplesSize);
304     printf("Do you want to extract them (.wav files will be written to '%s')? [y/n]\n", outPath);
305     char c = getchar();
306     if (c == 'y' || c == 'Y') {
307     bool errorOccured = false;
308     DIR* dir = opendir(outPath);
309     if (!dir) {
310     if (errno == ENOENT) {
311     struct stat filestat;
312 schoenebeck 2574 #ifdef WIN32
313     if (stat(outPath, &filestat) < 0) {
314     #else
315 schoenebeck 2572 if (lstat(outPath, &filestat) < 0) {
316 schoenebeck 2574 #endif
317 schoenebeck 2572 printf("Creating output directory '%s'...", outPath);
318     fflush(stdout);
319 schoenebeck 2574 #ifdef WIN32
320     if (mkdir(outPath) < 0) {
321     #else
322 schoenebeck 2572 if (mkdir(outPath, 0770) < 0) {
323 schoenebeck 2574 #endif
324 schoenebeck 2572 perror("failed");
325     errorOccured = true;
326     }
327     else printf("ok\n");
328     }
329     else {
330 schoenebeck 2574 #if !defined(WIN32)
331 schoenebeck 2572 if (!S_ISLNK(filestat.st_mode)) {
332 schoenebeck 2574 #endif
333 schoenebeck 2572 printf("Cannot create output directory '%s': ", outPath);
334     printf("a file of that name already exists\n");
335     errorOccured = true;
336 schoenebeck 2574 #if !defined(WIN32)
337 schoenebeck 2572 }
338 schoenebeck 2574 #endif
339 schoenebeck 2572 }
340     }
341     else {
342     perror("error while opening output directory");
343     errorOccured = true;
344     }
345     }
346     if (dir) closedir(dir);
347     if (!errorOccured) {
348     printf("Starting extraction...\n");
349     long currentsample = 1;
350     uint i;
351     for (i = 0; i < pAkai->GetPartitionCount() && !errorOccured; i++) {
352     AkaiPartition* pPartition = pAkai->GetPartition(i);
353     if (pPartition) {
354     std::list<AkaiDirEntry> Volumes;
355     pPartition->ListVolumes(Volumes);
356     std::list<AkaiDirEntry>::iterator it;
357     std::list<AkaiDirEntry>::iterator end = Volumes.end();
358     int vol = 0;
359     for (it = Volumes.begin(); it != end && !errorOccured; it++) {
360     AkaiDirEntry DirEntry = *it;
361     AkaiVolume* pVolume = pPartition->GetVolume(vol);
362     if (pVolume) {
363     std::list<AkaiDirEntry> Samples;
364     pVolume->ListSamples(Samples);
365     std::list<AkaiDirEntry>::iterator it;
366     std::list<AkaiDirEntry>::iterator end = Samples.end();
367     uint samp = 0;
368     for (it = Samples.begin(); it != end && !errorOccured; it++) {
369     AkaiDirEntry DirEntry = *it;
370     AkaiSample* pSample = pVolume->GetSample(samp);
371 persson 2836 printf("Extracting Sample (%ld/%ld) %s...",
372 schoenebeck 2572 currentsample++,
373     totalSamples,
374     DirEntry.mName.c_str());
375     fflush(stdout);
376     #if USE_DISK_STREAMING
377     if (pSample->LoadHeader()) {
378     uint16_t* pSampleBuf = new uint16_t[pSample->mNumberOfSamples];
379     pSample->Read(pSampleBuf, pSample->mNumberOfSamples);
380    
381     String filename = outPath + String("/") + DirEntry.mName + ".wav";
382     int res = writeWav(pSample,
383     filename.c_str(),
384     pSampleBuf,
385     pSample->mNumberOfSamples);
386     if (res < 0) {
387     printf("couldn't write sample data\n");
388     errorOccured = true;
389     }
390     else printf("ok\n");
391     delete[] pSampleBuf;
392     }
393     else {
394     printf("failed to load sample data\n");
395     errorOccured = true;
396     }
397     #else // no disk streaming
398     if (pSample->LoadSampleData()) {
399    
400     String filename = outPath + String("/") + DirEntry.mName + ".wav";
401     int res = writeWav(pSample,
402     filename.c_str(),
403     pSample->mpSamples,
404     pSample->mNumberOfSamples);
405     if (res < 0) {
406     printf("couldn't write sample data\n");
407     errorOccured = true;
408     }
409     else printf("ok\n");
410     pSample->ReleaseSampleData();
411     }
412     else {
413     printf("failed to load sample data\n");
414     errorOccured = true;
415     }
416     #endif // USE_DISK_STREAMING
417     pSample->Release();
418     samp++;
419     }
420     pVolume->Release();
421     }
422     vol++;
423     }
424     pPartition->Release();
425     }
426     }
427     }
428     if (errorOccured) printf("Extraction failed\n");
429     }
430    
431     #if !HAVE_SNDFILE // use libaudiofile
432     closeAFlib();
433     #endif // !HAVE_SNDFILE
434    
435     #if 0
436     // this is just for debugging purpose - it converts the whole image to ascii
437     printf("Converting...\n");
438     FILE* f = fopen("akai_converted_to_ascii.iso", "w");
439     if (f) {
440     char* buf = (char*) malloc(AKAI_BLOCK_SIZE);
441     pImage->SetPos(0);
442     while (pImage->Available(1)) {
443     int readbytes = pImage->Read(buf,1,AKAI_BLOCK_SIZE);
444     if (readbytes != AKAI_BLOCK_SIZE) printf("Block incomplete (read: %d)\n",readbytes);
445     ConvertAkaiToAscii(buf, readbytes);
446     fwrite(buf, AKAI_BLOCK_SIZE, 1, f);
447     }
448     free(buf);
449     fclose(f);
450     }
451     else printf("Could not open file\n");
452     #endif
453    
454     delete pAkai;
455     delete pImage;
456 schoenebeck 2574 #if WIN32
457 schoenebeck 2572 while(!_kbhit());
458     #endif
459     }
460    
461     // only for debugging
462     void ConvertAkaiToAscii(char * buffer, int length)
463     {
464     int i;
465    
466     for (i = 0; i < length; i++)
467     {
468     if (buffer[i]>=0 && buffer[i]<=9)
469     buffer[i] +=48;
470     else if (buffer[i]==10)
471     buffer[i] = 32;
472     else if (buffer[i]>=11 && buffer[i]<=36)
473     buffer[i] = 64+(buffer[i]-10);
474     else
475     buffer[i] = 32;
476     }
477     buffer[length] = '\0';
478     while (length-- > 0 && buffer[length] == 32)
479     {
480     // This block intentionaly left blank :)
481     }
482     buffer[length+1] = '\0';
483     }
484    
485     int writeWav(AkaiSample* sample, const char* filename, void* samples, long samplecount, int channels, int bitdepth, long rate) {
486     #if HAVE_SNDFILE
487     SNDFILE* hfile;
488     SF_INFO sfinfo;
489     SF_INSTRUMENT instr;
490     int format = SF_FORMAT_WAV;
491     switch (bitdepth) {
492     case 8:
493     format |= SF_FORMAT_PCM_S8;
494     break;
495     case 16:
496     format |= SF_FORMAT_PCM_16;
497     break;
498     case 24:
499     format |= SF_FORMAT_PCM_24;
500     break;
501     case 32:
502     format |= SF_FORMAT_PCM_32;
503     break;
504     default:
505     cerr << "Error: Bithdepth " << ToString(bitdepth) << " not supported by libsndfile, ignoring sample!\n" << flush;
506     return -1;
507     }
508     memset(&sfinfo, 0, sizeof (sfinfo));
509     memset(&instr, 0, sizeof (instr));
510     sfinfo.samplerate = rate;
511     sfinfo.frames = samplecount;
512     sfinfo.channels = channels;
513     sfinfo.format = format;
514     if (!(hfile = sf_open(filename, SFM_WRITE, &sfinfo))) {
515     cerr << "Error: Unable to open output file \'" << filename << "\'.\n" << flush;
516     return -1;
517     }
518     instr.basenote = sample->mMidiRootNote;
519     instr.detune = sample->mTuneCents;
520     for (int i = 0; i < sample->mActiveLoops; ++i) {
521     instr.loop_count = sample->mActiveLoops;
522     instr.loops[i].mode = SF_LOOP_FORWARD;
523     instr.loops[i].start = sample->mLoops[i].mMarker;
524     instr.loops[i].end = sample->mLoops[i].mCoarseLength;
525     instr.loops[i].count = 0; // infinite
526     }
527     sf_command(hfile, SFC_SET_INSTRUMENT, &instr, sizeof(instr));
528     sf_count_t res = bitdepth == 24 ?
529     sf_write_int(hfile, static_cast<int*>(samples), channels * samplecount) :
530     sf_write_short(hfile, static_cast<short*>(samples), channels * samplecount);
531     if (res != channels * samplecount) {
532     cerr << sf_strerror(hfile) << endl << flush;
533     sf_close(hfile);
534     return -1;
535     }
536     sf_close(hfile);
537     #else // use libaudiofile
538     AFfilesetup setup = _afNewFileSetup();
539     _afInitFileFormat(setup, AF_FILE_WAVE);
540     _afInitChannels(setup, AF_DEFAULT_TRACK, channels);
541     _afInitSampleFormat(setup, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP, bitdepth);
542     _afInitRate(setup, AF_DEFAULT_TRACK, rate);
543     if (setup == AF_NULL_FILESETUP) return -1;
544     AFfilehandle hFile = _afOpenFile(filename, "w", setup);
545     if (hFile == AF_NULL_FILEHANDLE) return -1;
546     if (_afWriteFrames(hFile, AF_DEFAULT_TRACK, samples, samplecount) < 0) return -1;
547     _afCloseFile(hFile);
548     _afFreeFileSetup(setup);
549     #endif // HAVE_SNDFILE
550    
551     return 0; // success
552     }
553    
554     #if !HAVE_SNDFILE // use libaudiofile
555     void openAFlib() {
556     hAFlib = dlopen("libaudiofile.so", RTLD_NOW);
557     if (!hAFlib) {
558     cout << "Unable to load library libaudiofile.so: " << dlerror() << endl;
559     return;
560     }
561     _afNewFileSetup = (AFfilesetup(*)(void)) dlsym(hAFlib, "afNewFileSetup");
562     _afFreeFileSetup = (void(*)(AFfilesetup)) dlsym(hAFlib, "afFreeFileSetup");
563     _afInitChannels = (void(*)(AFfilesetup,int,int)) dlsym(hAFlib, "afInitChannels");
564     _afInitSampleFormat = (void(*)(AFfilesetup,int,int,int)) dlsym(hAFlib, "afInitSampleFormat");
565     _afInitFileFormat = (void(*)(AFfilesetup,int)) dlsym(hAFlib, "afInitFileFormat");
566     _afInitRate = (void(*)(AFfilesetup,int,double)) dlsym(hAFlib, "afInitRate");
567     _afWriteFrames = (int(*)(AFfilehandle,int,const void*,int)) dlsym(hAFlib, "afWriteFrames");
568     _afOpenFile = (AFfilehandle(*)(const char*,const char*,AFfilesetup)) dlsym(hAFlib, "afOpenFile");
569     _afCloseFile = (int(*)(AFfilehandle file)) dlsym(hAFlib, "afCloseFile");
570     if (dlerror()) cout << "Failed to load function from libaudiofile.so: " << dlerror() << endl;
571     }
572    
573     void closeAFlib() {
574     if (hAFlib) dlclose(hAFlib);
575     }
576     #endif // !HAVE_SNDFILE

  ViewVC Help
Powered by ViewVC