/[svn]/linuxsampler/trunk/src/drivers/audio/AudioOutputDeviceAsio.cpp
ViewVC logotype

Annotation of /linuxsampler/trunk/src/drivers/audio/AudioOutputDeviceAsio.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1888 - (hide annotations) (download)
Sat Apr 18 09:26:45 2009 UTC (15 years ago) by persson
File size: 41796 byte(s)
* applied old fixes from Benno to the ASIO driver that were included
  in the previous binary release but accidentally never committed to
  CVS (fixes #117)

1 senoner 1500 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5     * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 persson 1644 * Copyright (C) 2005 - 2008 Christian Schoenebeck *
7 senoner 1500 * *
8     * This program is free software; you can redistribute it and/or modify *
9     * it under the terms of the GNU General Public License as published by *
10     * the Free Software Foundation; either version 2 of the License, or *
11     * (at your option) any later version. *
12     * *
13     * This program is distributed in the hope that it will be useful, *
14     * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16     * GNU General Public License for more details. *
17     * *
18     * You should have received a copy of the GNU General Public License *
19     * along with this program; if not, write to the Free Software *
20     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21     * MA 02111-1307 USA *
22     ***************************************************************************/
23    
24     #include "AudioOutputDeviceAsio.h"
25     #include "AudioOutputDeviceFactory.h"
26    
27     #define kMaxInputChannels 32
28     #define kMaxOutputChannels 32
29     #define ASIO_MAX_DEVICE_INFO 32
30    
31     #define NUM_STANDARDSAMPLINGRATES 3 /* 11.025, 22.05, 44.1 */
32     #define NUM_CUSTOMSAMPLINGRATES 9 /* must be the same number of elements as in the array below */
33     #define MAX_NUMSAMPLINGRATES (NUM_STANDARDSAMPLINGRATES+NUM_CUSTOMSAMPLINGRATES)
34    
35     // internal data storage
36     typedef struct DriverInfo
37     {
38     // ASIOInit()
39     ASIODriverInfo driverInfo;
40    
41     // ASIOGetChannels()
42     long numInputChannels;
43     long numOutputChannels;
44    
45     // ASIOGetBufferSize()
46     long minBufSize;
47     long maxBufSize;
48     long preferredBufSize;
49     long bufGranularity;
50     long chosenBufSize;
51    
52     // ASIOGetSampleRate()
53     ASIOSampleRate sampleRate;
54     int numSampleRates;
55     std::vector<int> sampleRates;
56    
57     // ASIOOutputReady()
58     bool ASIOOutputReadySupported;
59    
60     // ASIOGetLatencies ()
61     long inputLatency;
62     long outputLatency;
63    
64     // ASIOCreateBuffers ()
65     long numInputBuffers; // becomes number of actual created input buffers
66     long numOutputBuffers; // becomes number of actual created output buffers
67     ASIOBufferInfo bufferInfos[kMaxInputChannels + kMaxOutputChannels]; // buffer info's
68    
69     // ASIOGetChannelInfo()
70     ASIOChannelInfo channelInfos[kMaxInputChannels + kMaxOutputChannels]; // channel info's
71     // The above two arrays share the same indexing, as the data in them are linked together
72    
73     // Information from ASIOGetSamplePosition()
74     // data is converted to double floats for easier use, however 64 bit integer can be used, too
75     double nanoSeconds;
76     double samples;
77     double tcSamples; // time code samples
78    
79     // bufferSwitchTimeInfo()
80     ASIOTime tInfo; // time info state
81     unsigned long sysRefTime; // system reference time, when bufferSwitch() was called
82    
83     // Signal the end of processing in this example
84     bool stopped;
85     } DriverInfo;
86    
87     extern AsioDrivers* asioDrivers;
88     extern bool loadAsioDriver(char *name);
89     static ASIODriverInfo MyAsioDriverInfo;
90     static DriverInfo asioDriverInfo = {0};
91    
92 persson 1888 static bool asioDriverOpened = false;
93 senoner 1500 static bool AudioOutputDeviceAsioInstantiated = false;
94     static String currentAsioDriverName;
95     static std::vector<String> asioDriverList;
96     static bool asioDriverListLoaded = false;
97    
98     namespace LinuxSampler {
99    
100     // callback prototypes
101    
102     ASIOCallbacks asioCallbacks;
103     ASIOBufferInfo bufferInfos[2];
104     AudioOutputDeviceAsio *GlobalAudioOutputDeviceAsioThisPtr;
105    
106 persson 1888 template<int bitres>
107     static void floatToASIOSTInt32LSBXX(float *in, void *dest, int numSamples) {
108     double pos_max_value = (1 << bitres) -1.0;
109     double neg_max_value = -pos_max_value;
110     int32_t *out = (int32_t *)dest;
111     for(int i=0; i < numSamples ; i++) {
112     double sample_point = in[i] * pos_max_value;
113     if (sample_point < neg_max_value) sample_point = neg_max_value;
114     if (sample_point > pos_max_value) sample_point = pos_max_value;
115     out[i] = (int16_t)sample_point;
116     }
117     }
118    
119     static void floatToASIOSTInt16LSB(float *in, void *dest, int numSamples) {
120 senoner 1500 int16_t *out = (int16_t *)dest;
121     for(int i=0; i < numSamples ; i++) {
122     float sample_point = in[i] * 32768.0f;
123     if (sample_point < -32768.0) sample_point = -32768.0;
124     if (sample_point > 32767.0) sample_point = 32767.0;
125     out[i] = (int16_t)sample_point;
126     }
127     }
128    
129 persson 1888 static void floatToASIOSTInt32LSB(float *in, void *dest, int numSamples) {
130 senoner 1500 int32_t *out = (int32_t *)dest;
131     for(int i=0; i < numSamples ; i++) {
132     double sample_point = in[i] * 2147483648.0;
133     if (sample_point < - 2147483648.0) sample_point = -2147483648.0;
134     if (sample_point > 2147483647.0) sample_point = 2147483647.0;
135     out[i] = (int32_t)(sample_point);
136     }
137     }
138    
139 persson 1888 std::vector<String> getAsioDriverNames();
140    
141 senoner 1500 static bool ASIO_loadAsioDriver(char *name)
142     {
143 persson 1888 dmsg(2,("ASIO_loadAsioDriver: trying to load '%s'\n",name));
144 senoner 1500 #ifdef WINDOWS
145     CoInitialize(0);
146     #endif
147     return loadAsioDriver(name);
148     }
149    
150     int ASIO_OpenAndQueryDeviceInfo(char *driverName, DriverInfo *driverInfo, ASIODriverInfo *asioDriverInfo)
151     {
152     ASIOSampleRate possibleSampleRates[]
153     = {8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0, 44100.0, 48000.0, 88200.0, 96000.0};
154    
155     ASIOChannelInfo channelInfos;
156     ASIOError asioError;
157    
158 persson 1888 // call this function in order to fill the internal vector containing the list of ASIO cards
159     // since the reading of the list is performed only one time subsequent calls to this function will be automatically ignored
160     // calling getAsioDriverNames() is needed in the case of a LinuxSampler client application first creating an ASIO device
161     // which calls ASIO_OpenAndQueryDeviceInfo() and afterwards retrieve the list of available ASIO cards
162     // since getAsioDriverNames() unloads the current driver and loads the driver 'dummy' this would cause
163     // the audio stopping and forcing the create the audio output device again
164     getAsioDriverNames();
165    
166     dmsg(2,("ASIO_OpenAndQueryDeviceInfo driverName='%s' current='%s'\n",driverName, currentAsioDriverName.c_str() ));
167 senoner 1500 if(!strcmp(driverName, currentAsioDriverName.c_str() )) {
168     // if the given ASIO driver name == current ASIO driver name and the driver
169     // was already opened then do nothing
170     if(asioDriverOpened) {
171 persson 1888 dmsg(2,("asioDriver ALREADY OPENED, DOING NOTHING !\n"));
172 senoner 1500 return 0;
173     }
174     }
175     else {
176 persson 1888 dmsg(2,("driverName != currentAsioDriverName , new asio driver specified, opening device ....\n"));
177 senoner 1500 // if a new ASIO driver name was specified then first check if we need to close
178     // the old one
179     if(asioDriverOpened) {
180 persson 1888 dmsg(2,("different asioDriver ALREADY OPENED, closing old one !\n"));
181 senoner 1500 asioDriverOpened = false;
182     ASIOExit(); // close the old ASIO driver
183     }
184     }
185     currentAsioDriverName = driverName;
186    
187     memset((void *)asioDriverInfo,0,sizeof(ASIODriverInfo));
188     asioDriverInfo->asioVersion=1;
189     asioDriverInfo->sysRef = NULL;
190    
191     /* MUST BE CHECKED : to force fragments loading on Mac */
192     //ASIO_loadAsioDriver("dummy");
193    
194 persson 1888 dmsg(2,("Before ASIO_loadAsioDriver('%s')\n",driverName));
195 senoner 1500 if ( !ASIO_loadAsioDriver(driverName) ) {
196 persson 1888 dmsg(2,("ASIO_OpenAndQueryDeviceInfo could not loadAsioDriver %s\n", driverName));
197 senoner 1500 return -1;
198     }
199 persson 1888 dmsg(2,("Before ASIOInit()\n"));
200 senoner 1500 if( (asioError = ASIOInit(asioDriverInfo)) != ASE_OK ) {
201 persson 1888 dmsg(2,("ASIO_OpenAndQueryDeviceInfo: ASIOInit returned %d for %s\n", asioError, driverName));
202 senoner 1500 return asioError;
203     }
204 persson 1888 dmsg(2,("Before ASIOGetChannels()\n"));
205 senoner 1500 if( (asioError = ASIOGetChannels(&driverInfo->numInputChannels, &driverInfo->numOutputChannels) != ASE_OK)) {
206 persson 1888 dmsg(2,("ASIO_OpenAndQueryDeviceInfo could not ASIOGetChannels for %s\n", driverName));
207 senoner 1500 return asioError;
208     }
209    
210 persson 1888 dmsg(2,("Before ASIOGetBufferSize()\n"));
211 senoner 1500 if( (asioError = ASIOGetBufferSize(&driverInfo->minBufSize,&driverInfo->maxBufSize,&driverInfo->preferredBufSize,&driverInfo->bufGranularity) != ASE_OK)) {
212 persson 1888 dmsg(2,("ASIO_OpenAndQueryDeviceInfo could not ASIOGetBufferSize for %s\n", driverName));
213 senoner 1500 return asioError;
214     }
215    
216 persson 1888 dmsg(2,("ASIO_OpenAndQueryDeviceInfo: InputChannels = %d\n", driverInfo->numInputChannels ));
217     dmsg(2,("ASIO_OpenAndQueryDeviceInfo: OutputChannels = %d\n", driverInfo->numOutputChannels ));
218 senoner 1500
219     /* Loop through the possible sampling rates and check each to see if the device supports it. */
220     driverInfo->numSampleRates = 0;
221     driverInfo->sampleRates.clear();
222     for (int index = 0; index < MAX_NUMSAMPLINGRATES; index++) {
223     if (ASIOCanSampleRate(possibleSampleRates[index]) != ASE_NoClock) {
224 persson 1888 dmsg(2,("ASIOCanSampleRate: possible sample rate = %d\n", (long)possibleSampleRates[index]));
225 senoner 1500 driverInfo->sampleRates.push_back( (int)possibleSampleRates[index] );
226     driverInfo->numSampleRates++;
227     }
228     }
229    
230     /* get the channel infos for each output channel (including sample format) */
231     for(int i=0 ; i < driverInfo->numOutputChannels ; i++) {
232     driverInfo->channelInfos[i].channel = i;
233     driverInfo->channelInfos[i].isInput = ASIOFalse;
234     ASIOGetChannelInfo(&driverInfo->channelInfos[i]);
235 persson 1888 dmsg(2,("channelInfos[%d].type (sampleformat) = %d\n", i, driverInfo->channelInfos[i].type));
236 senoner 1500 }
237    
238 persson 1888 dmsg(2,("ASIO_OpenAndQueryDeviceInfo: driver opened.\n"));
239 senoner 1500 asioDriverOpened = true;
240     return ASE_OK;
241     }
242    
243    
244     std::vector<String> getAsioDriverNames() {
245    
246     char* names[ASIO_MAX_DEVICE_INFO];
247     int numDrivers;
248    
249     if(asioDriverListLoaded) {
250 persson 1888 dmsg(2,("getAsioDriverNames: ASIO driver list already loaded, doing returning cached list.\n"));
251 senoner 1500 return asioDriverList;
252     }
253    
254     /* MUST BE CHECKED : to force fragments loading on Mac */
255     ASIO_loadAsioDriver("dummy");
256    
257     #if MAC
258     numDrivers = asioDrivers->getNumFragments();
259     #elif WINDOWS
260     numDrivers = asioDrivers->asioGetNumDev();
261     #endif
262    
263     for (int i = 0 ; i < ASIO_MAX_DEVICE_INFO ; i++) {
264     names[i] = new char[32];
265     memset(names[i],0,32);
266     }
267    
268     /* Get names of all available ASIO drivers */
269     asioDrivers->getDriverNames(names,ASIO_MAX_DEVICE_INFO);
270 persson 1888 dmsg(2,("getAsioDriverNames: numDrivers=%d\n",numDrivers));
271 senoner 1500
272     for (int i = 0 ; i < numDrivers ; i++) {
273 persson 1888 dmsg(2,("ASIO DRIVERLIST: i=%d name='%s'\n",i,names[i]));
274 senoner 1500
275     #if 1
276     asioDriverList.push_back(names[i]);
277     #else
278     int asioError;
279     // FIXME: we currently try what is the best methode to exclude not connected devices or not working drivers
280     // the code below is for testing only, it tried to load each ASIO driver and if it gives an error it is not included
281     // in the list of available drivers (asioDriverList)
282     if ( ASIO_loadAsioDriver(names[i]) ) {
283     if( (asioError = ASIOInit(&MyAsioDriverInfo)) == ASE_OK ) {
284     asioDriverList.push_back(names[i]);
285     }
286     else {
287 persson 1888 dmsg(2,("getDriverList: ASIOInit of driver %s gave Error %d ! ignoring it.\n", names[i],asioError));
288 senoner 1500 }
289     }
290     else {
291 persson 1888 dmsg(2,("getDriverList: load driver %s failed! ignoring it.\n", names[i]));
292 senoner 1500 }
293     // FIXME: we need to check this ASIOExit is needed (gave a crash on a ASIO ADSP24(WDM) card so we commented it out)
294     //ASIOExit();
295     #endif
296 persson 1888 //currentAsioDriverName="";
297 senoner 1500
298     }
299    
300     for (int i = 0 ; i < ASIO_MAX_DEVICE_INFO ; i++) {
301 persson 1644 delete[] names[i];
302 senoner 1500 }
303    
304 persson 1888 dmsg(2,("getAsioDriverNames: returing from function. asioDriverList.size()=%d\n", asioDriverList.size() ));
305 senoner 1500 asioDriverListLoaded = true;
306     return asioDriverList;
307     }
308    
309     unsigned long get_sys_reference_time()
310     {
311     // get the system reference time
312     #if WINDOWS
313     return timeGetTime();
314     #elif MAC
315     static const double twoRaisedTo32 = 4294967296.;
316     UnsignedWide ys;
317     Microseconds(&ys);
318     double r = ((double)ys.hi * twoRaisedTo32 + (double)ys.lo);
319     return (unsigned long)(r / 1000.);
320     #endif
321     }
322    
323    
324     //----------------------------------------------------------------------------------
325     // conversion from 64 bit ASIOSample/ASIOTimeStamp to double float
326     #if NATIVE_INT64
327     #define ASIO64toDouble(a) (a)
328     #else
329     const double twoRaisedTo32 = 4294967296.;
330     #define ASIO64toDouble(a) ((a).lo + (a).hi * twoRaisedTo32)
331     #endif
332    
333    
334     ASIOTime* AudioOutputDeviceAsio::bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow)
335     {
336    
337     static long processedSamples = 0;
338     // store the timeInfo for later use
339     asioDriverInfo.tInfo = *timeInfo;
340    
341     // get the time stamp of the buffer, not necessary if no
342     // synchronization to other media is required
343     if (timeInfo->timeInfo.flags & kSystemTimeValid)
344     asioDriverInfo.nanoSeconds = ASIO64toDouble(timeInfo->timeInfo.systemTime);
345     else
346     asioDriverInfo.nanoSeconds = 0;
347    
348     if (timeInfo->timeInfo.flags & kSamplePositionValid)
349     asioDriverInfo.samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition);
350     else
351     asioDriverInfo.samples = 0;
352    
353     if (timeInfo->timeCode.flags & kTcValid)
354     asioDriverInfo.tcSamples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples);
355     else
356     asioDriverInfo.tcSamples = 0;
357    
358 persson 1888 // TODO: ignore for now. get the system reference time
359     // asioDriverInfo.sysRefTime = get_sys_reference_time();
360 senoner 1500
361     // buffer size in samples
362     long buffSize = asioDriverInfo.chosenBufSize;
363     int32_t *p;
364    
365     // tell LinuxSampler to render a fragment of buffSize samples
366     GlobalAudioOutputDeviceAsioThisPtr->RenderAudio(buffSize);
367    
368     // now write and convert the samples to the ASIO buffer
369     for (int i = 0; i < asioDriverInfo.numOutputBuffers; i++)
370     {
371     // do processing for the outputs only
372     switch (asioDriverInfo.channelInfos[i].type)
373     {
374     case ASIOSTInt16LSB:
375 persson 1888 floatToASIOSTInt16LSB(GlobalAudioOutputDeviceAsioThisPtr->Channels[i]->Buffer(),
376     (int16_t *)asioDriverInfo.bufferInfos[i].buffers[index], buffSize);
377 senoner 1500 break;
378     case ASIOSTInt24LSB: // used for 20 bits as well
379     memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 3);
380     break;
381     case ASIOSTInt32LSB:
382 persson 1888 floatToASIOSTInt32LSB(GlobalAudioOutputDeviceAsioThisPtr->Channels[i]->Buffer(),
383     (int32_t *)asioDriverInfo.bufferInfos[i].buffers[index], buffSize);
384 senoner 1500 break;
385     case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
386     throw AudioOutputException(String("ASIO Error: ASIOSTFloat32LSB not yet supported! report to LinuxSampler developers.") );
387     break;
388     case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
389     throw AudioOutputException(String("ASIO Error: ASIOSTFloat64LSB not yet supported! report to LinuxSampler developers.") );
390     break;
391    
392     // these are used for 32 bit data buffer, with different alignment of the data inside
393     // 32 bit PCI bus systems can more easily used with these
394 persson 1888 case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
395     floatToASIOSTInt32LSBXX<16>( GlobalAudioOutputDeviceAsioThisPtr->Channels[i]->Buffer(),
396     (int32_t *)asioDriverInfo.bufferInfos[i].buffers[index], buffSize);
397 senoner 1500 break;
398     case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
399 persson 1888 floatToASIOSTInt32LSBXX<18>( GlobalAudioOutputDeviceAsioThisPtr->Channels[i]->Buffer(),
400     (int32_t *)asioDriverInfo.bufferInfos[i].buffers[index], buffSize);
401 senoner 1500 break;
402     case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
403 persson 1888 floatToASIOSTInt32LSBXX<20>( GlobalAudioOutputDeviceAsioThisPtr->Channels[i]->Buffer(),
404     (int32_t *)asioDriverInfo.bufferInfos[i].buffers[index], buffSize);
405 senoner 1500 break;
406     case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
407 persson 1888 floatToASIOSTInt32LSBXX<24>( GlobalAudioOutputDeviceAsioThisPtr->Channels[i]->Buffer(),
408     (int32_t *)asioDriverInfo.bufferInfos[i].buffers[index], buffSize);
409 senoner 1500 break;
410     case ASIOSTInt16MSB:
411     throw AudioOutputException(String("ASIO Error: ASIOSTInt16MSB not yet supported! report to LinuxSampler developers.") );
412     break;
413     case ASIOSTInt24MSB: // used for 20 bits as well
414     throw AudioOutputException(String("ASIO Error: ASIOSTInt24MSB not yet supported! report to LinuxSampler developers.") );
415     break;
416     case ASIOSTInt32MSB:
417     throw AudioOutputException(String("ASIO Error: ASIOSTInt32MSB not yet supported! report to LinuxSampler developers.") );
418     break;
419     case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on bigendian architecture
420     throw AudioOutputException(String("ASIO Error: ASIOSTFloat32MSB not yet supported! report to LinuxSampler developers.") );
421     break;
422     case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on bigendian architecture
423     throw AudioOutputException(String("ASIO Error: ASIOSTFloat64MSB not yet supported! report to LinuxSampler developers.") );
424     break;
425    
426     // these are used for 32 bit data buffer, with different alignment of the data inside
427     // 32 bit PCI bus systems can more easily used with these
428     case ASIOSTInt32MSB16: // 32 bit data with 18 bit alignment
429     throw AudioOutputException(String("ASIO Error: ASIOSTInt32MSB16 not yet supported! report to LinuxSampler developers.") );
430     break;
431     case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
432     throw AudioOutputException(String("ASIO Error: ASIOSTInt32MSB18 not yet supported! report to LinuxSampler developers.") );
433     break;
434     case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
435     throw AudioOutputException(String("ASIO Error: ASIOSTInt32MSB20 not yet supported! report to LinuxSampler developers.") );
436     break;
437     case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
438     throw AudioOutputException(String("ASIO Error: ASIOSTInt32MSB24 not yet supported! report to LinuxSampler developers.") );
439     break;
440     default:
441     throw AudioOutputException(String("ASIO Error: unknown ASIOST sample format. report error.") );
442     break;
443     }
444     }
445    
446     // finally if the driver supports the ASIOOutputReady() optimization, do it here, all data are in place
447     if (asioDriverInfo.ASIOOutputReadySupported)
448     ASIOOutputReady();
449    
450     processedSamples += buffSize;
451     return 0L;
452     }
453    
454     void AudioOutputDeviceAsio::bufferSwitch(long index, ASIOBool processNow)
455     {
456     // the actual processing callback.
457     // as this is a "back door" into the bufferSwitchTimeInfo a timeInfo needs to be created
458     // though it will only set the timeInfo.samplePosition and timeInfo.systemTime fields and the according flags
459     ASIOTime timeInfo;
460     memset (&timeInfo, 0, sizeof (timeInfo));
461    
462     // get the time stamp of the buffer, not necessary if no
463     // synchronization to other media is required
464     if(ASIOGetSamplePosition(&timeInfo.timeInfo.samplePosition, &timeInfo.timeInfo.systemTime) == ASE_OK)
465     timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid;
466    
467     bufferSwitchTimeInfo (&timeInfo, index, processNow);
468     }
469    
470     void sampleRateChanged(ASIOSampleRate sRate)
471     {
472     // do whatever you need to do if the sample rate changed
473     // usually this only happens during external sync.
474     // Audio processing is not stopped by the driver, actual sample rate
475     // might not have even changed, maybe only the sample rate status of an
476     // AES/EBU or S/PDIF digital input at the audio device.
477     // You might have to update time/sample related conversion routines, etc.
478     }
479    
480     long asioMessages(long selector, long value, void* message, double* opt)
481     {
482 persson 1888 dmsg(2,("asioMessages selector=%d value=%d\n",selector,value));
483 senoner 1500 // currently the parameters "value", "message" and "opt" are not used.
484     long ret = 0;
485     switch(selector)
486     {
487     case kAsioSelectorSupported:
488     if(value == kAsioResetRequest
489     || value == kAsioEngineVersion
490     || value == kAsioResyncRequest
491     || value == kAsioLatenciesChanged
492     // the following three were added for ASIO 2.0, we don't necessarily have to support them
493     || value == kAsioSupportsTimeInfo
494     || value == kAsioSupportsTimeCode
495     || value == kAsioSupportsInputMonitor)
496     ret = 1L;
497     break;
498     case kAsioResetRequest:
499     // defer the task and perform the reset of the driver during the next "safe" situation
500     // You cannot reset the driver right now, as this code is called from the driver.
501     // Reset the driver is done by completely destruct is. I.e. ASIOStop(), ASIODisposeBuffers(), Destruction
502     // Afterwards you initialize the driver again.
503     //GlobalAudioOutputDeviceAsioThisPtr->asioDriverInfo.stopped; // In this sample the processing will just stop
504     ret = 1L;
505     break;
506     case kAsioResyncRequest:
507     // This informs the application, that the driver encountered some non fatal data loss.
508     // It is used for synchronization purposes of different media.
509     // Added mainly to work around the Win16Mutex problems in Windows 95/98 with the
510     // Windows Multimedia system, which could loose data because the Mutex was hold too long
511     // by another thread.
512     // However a driver can issue it in other situations, too.
513     ret = 1L;
514     break;
515     case kAsioLatenciesChanged:
516     // This will inform the host application that the drivers were latencies changed.
517     // Beware, it this does not mean that the buffer sizes have changed!
518     // You might need to update internal delay data.
519     ret = 1L;
520     break;
521     case kAsioEngineVersion:
522     // return the supported ASIO version of the host application
523     // If a host applications does not implement this selector, ASIO 1.0 is assumed
524     // by the driver
525     ret = 2L;
526     break;
527     case kAsioSupportsTimeInfo:
528     // informs the driver wether the asioCallbacks.bufferSwitchTimeInfo() callback
529     // is supported.
530     // For compatibility with ASIO 1.0 drivers the host application should always support
531     // the "old" bufferSwitch method, too.
532     ret = 1;
533     break;
534     case kAsioSupportsTimeCode:
535     // informs the driver wether application is interested in time code info.
536     // If an application does not need to know about time code, the driver has less work
537     // to do.
538     ret = 0;
539     break;
540     }
541     return ret;
542     }
543    
544    
545     // *************** ParameterCard ***************
546     // *
547    
548     AudioOutputDeviceAsio::ParameterCard::ParameterCard() : DeviceCreationParameterString() {
549     InitWithDefault(); // use default card
550     }
551    
552     AudioOutputDeviceAsio::ParameterCard::ParameterCard(String s) throw (Exception) : DeviceCreationParameterString(s) {
553     }
554    
555     String AudioOutputDeviceAsio::ParameterCard::Description() {
556     return "Sound card to be used";
557     }
558    
559     bool AudioOutputDeviceAsio::ParameterCard::Fix() {
560     return true;
561     }
562    
563     bool AudioOutputDeviceAsio::ParameterCard::Mandatory() {
564     return false;
565     }
566    
567     std::map<String,DeviceCreationParameter*> AudioOutputDeviceAsio::ParameterCard::DependsAsParameters() {
568     return std::map<String,DeviceCreationParameter*>(); // no dependencies
569     }
570    
571     optional<String> AudioOutputDeviceAsio::ParameterCard::DefaultAsString(std::map<String,String> Parameters) {
572    
573     std::vector<String> cards = PossibilitiesAsString(Parameters);
574     if (cards.empty()) throw Exception("AudioOutputDeviceAsio: Can't find any card");
575 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterCard::DefaultAsString='%s'\n",cards[0].c_str() ));
576    
577     // if currentAsioDriverName is empty then return the first card
578     // otherwise return the currentAsioDriverName. this avoids closing the current ASIO driver
579     // when the LSCP client calls commands like GET AUDIO_OUTPUT_DRIVER_PARAMETER INFO ASIO CARD
580     // which would load the default card (without this check it would always be the same which would cause the above problem)
581     if( currentAsioDriverName == "" ) {
582     return cards[0]; // first card by default
583     }
584     else {
585     return currentAsioDriverName;
586     }
587    
588 senoner 1500 }
589    
590     std::vector<String> AudioOutputDeviceAsio::ParameterCard::PossibilitiesAsString(std::map<String,String> Parameters) {
591 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterCard::PossibilitiesAsString:\n"));
592 senoner 1500 return getAsioDriverNames();
593     }
594    
595     void AudioOutputDeviceAsio::ParameterCard::OnSetValue(String s) throw (Exception) {
596     // not posssible, as parameter is fix
597     }
598    
599     String AudioOutputDeviceAsio::ParameterCard::Name() {
600     return "CARD";
601     }
602    
603    
604    
605     // *************** ParameterSampleRate ***************
606     // *
607    
608     AudioOutputDeviceAsio::ParameterSampleRate::ParameterSampleRate() : AudioOutputDevice::ParameterSampleRate::ParameterSampleRate() {
609     }
610    
611     AudioOutputDeviceAsio::ParameterSampleRate::ParameterSampleRate(String s) : AudioOutputDevice::ParameterSampleRate::ParameterSampleRate(s) {
612     }
613    
614     std::map<String,DeviceCreationParameter*> AudioOutputDeviceAsio::ParameterSampleRate::DependsAsParameters() {
615     static ParameterCard card;
616     std::map<String,DeviceCreationParameter*> dependencies;
617     dependencies[card.Name()] = &card;
618     return dependencies;
619     }
620    
621     optional<int> AudioOutputDeviceAsio::ParameterSampleRate::DefaultAsInt(std::map<String,String> Parameters) {
622     if (!Parameters.count("CARD")) {
623 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterSampleRate::DefaultAsInt returning optional<int>::nothing (parameter CARD not supplied)\n"));
624 senoner 1500 return optional<int>::nothing;
625     }
626    
627     // return the default samplerate. first try 44100 then 48000, then the first samplerate found in the list
628     ParameterCard card(Parameters["CARD"]);
629     if( ASIO_OpenAndQueryDeviceInfo((char *)card.ValueAsString().c_str(), &asioDriverInfo, &MyAsioDriverInfo) != ASE_OK) {
630     throw AudioOutputException(String("Error: ASIO Initalization Error") );
631     }
632     for(int i = 0 ; i < asioDriverInfo.sampleRates.size() ; i++) {
633     if(asioDriverInfo.sampleRates[i] == 44100) {
634     return 44100;
635 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterSampleRate::DefaultAsInt returning 44100\n"));
636 senoner 1500 }
637     if(asioDriverInfo.sampleRates[i] == 48000) {
638 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterSampleRate::DefaultAsInt returning 48000\n"));
639 senoner 1500 return 48000;
640     }
641     }
642 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterSampleRate::DefaultAsInt returning %d\n",asioDriverInfo.sampleRates[0]));
643 senoner 1500 return asioDriverInfo.sampleRates[0];
644     }
645    
646     std::vector<int> AudioOutputDeviceAsio::ParameterSampleRate::PossibilitiesAsInt(std::map<String,String> Parameters) {
647 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterSampleRate::PossibilitiesAsInt\n"));
648 senoner 1500 if (!Parameters.count("CARD")) {
649 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterSampleRate::PossibilitiesAsInt returning empty vector (parameter CARD not supplied)\n"));
650 senoner 1500 return std::vector<int>();
651     }
652    
653     ParameterCard card(Parameters["CARD"]);
654     if( ASIO_OpenAndQueryDeviceInfo((char *)card.ValueAsString().c_str(), &asioDriverInfo, &MyAsioDriverInfo) != ASE_OK) {
655     throw AudioOutputException(String("Error: ASIO Initalization Error") );
656     }
657    
658     for(int i=0;i<asioDriverInfo.sampleRates.size();i++) {
659 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterSampleRate::PossibilitiesAsInt samplerate[%d]=%d\n",i,asioDriverInfo.sampleRates[i]));
660 senoner 1500 }
661    
662     return asioDriverInfo.sampleRates;
663     }
664    
665    
666    
667     // *************** ParameterChannels ***************
668     // *
669    
670     AudioOutputDeviceAsio::ParameterChannels::ParameterChannels() : AudioOutputDevice::ParameterChannels::ParameterChannels() {
671     //InitWithDefault();
672     // could not determine default value? ...
673     //if (ValueAsInt() == 0) SetValue(2); // ... then (try) a common value
674     }
675    
676     AudioOutputDeviceAsio::ParameterChannels::ParameterChannels(String s) : AudioOutputDevice::ParameterChannels::ParameterChannels(s) {
677     }
678    
679     std::map<String,DeviceCreationParameter*> AudioOutputDeviceAsio::ParameterChannels::DependsAsParameters() {
680     static ParameterCard card;
681     std::map<String,DeviceCreationParameter*> dependencies;
682     dependencies[card.Name()] = &card;
683     return dependencies;
684     }
685    
686     optional<int> AudioOutputDeviceAsio::ParameterChannels::DefaultAsInt(std::map<String,String> Parameters) {
687 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterChannels::DefaultAsInt\n"));
688 senoner 1500 if (!Parameters.count("CARD")) {
689 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterChannels::DefaultAsInt returning optional<int>::nothing (CARD parameter not supplied)\n"));
690 senoner 1500 return optional<int>::nothing;
691     }
692    
693     ParameterCard card(Parameters["CARD"]);
694     if( ASIO_OpenAndQueryDeviceInfo((char *)card.ValueAsString().c_str(), &asioDriverInfo, &MyAsioDriverInfo) != ASE_OK) {
695     throw AudioOutputException(String("Error: ASIO Initialization Error") );
696     }
697 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterChannels::DefaultAsInt returning %d\n",asioDriverInfo.numOutputChannels));
698 senoner 1500 return asioDriverInfo.numOutputChannels;
699     }
700    
701     optional<int> AudioOutputDeviceAsio::ParameterChannels::RangeMinAsInt(std::map<String,String> Parameters) {
702 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterChannels::RangeMinAsInt!\n"));
703 senoner 1500 if (!Parameters.count("CARD")) {
704 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterChannels::RangeMinAsInt returning optional<int>::nothing (CARD parameter not supplied)\n"));
705 senoner 1500 return optional<int>::nothing;
706     }
707 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterChannels::RangeMinAsInt returning 1\n"));
708 senoner 1500 return 1;
709     }
710    
711     optional<int> AudioOutputDeviceAsio::ParameterChannels::RangeMaxAsInt(std::map<String,String> Parameters) {
712 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterChannels::RangeMaxAsInt!\n"));
713 senoner 1500 if (!Parameters.count("CARD")) {
714 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterChannels::RangeMaxAsInt returning optional<int>::nothing (CARD parameter not supplied)\n"));
715 senoner 1500 return optional<int>::nothing;
716     }
717    
718     ParameterCard card(Parameters["CARD"]);
719     if( ASIO_OpenAndQueryDeviceInfo((char *)card.ValueAsString().c_str(), &asioDriverInfo, &MyAsioDriverInfo) != ASE_OK) {
720     throw AudioOutputException(String("Error: ASIO Init Error") );
721     }
722 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterChannels::RangeMaxAsInt returning %d\n",asioDriverInfo.numOutputChannels));
723 senoner 1500 return asioDriverInfo.numOutputChannels;
724     }
725    
726    
727    
728     // *************** ParameterFragmentSize ***************
729     // *
730    
731     AudioOutputDeviceAsio::ParameterFragmentSize::ParameterFragmentSize() : DeviceCreationParameterInt() {
732     InitWithDefault();
733     }
734    
735     AudioOutputDeviceAsio::ParameterFragmentSize::ParameterFragmentSize(String s) throw (Exception) : DeviceCreationParameterInt(s) {
736     }
737    
738     String AudioOutputDeviceAsio::ParameterFragmentSize::Description() {
739     return "Size of each buffer fragment";
740     }
741    
742     bool AudioOutputDeviceAsio::ParameterFragmentSize::Fix() {
743     return true;
744     }
745    
746     bool AudioOutputDeviceAsio::ParameterFragmentSize::Mandatory() {
747     return false;
748     }
749    
750     std::map<String,DeviceCreationParameter*> AudioOutputDeviceAsio::ParameterFragmentSize::DependsAsParameters() {
751     static ParameterCard card;
752     std::map<String,DeviceCreationParameter*> dependencies;
753     dependencies[card.Name()] = &card;
754     return dependencies;
755     }
756    
757     optional<int> AudioOutputDeviceAsio::ParameterFragmentSize::DefaultAsInt(std::map<String,String> Parameters) {
758 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::DefaultAsInt!\n"));
759 senoner 1500 if (!Parameters.count("CARD")) {
760 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::DefaultAsInt returning optional<int>::nothing (no CARD parameter supplied\n"));
761 senoner 1500 return optional<int>::nothing;
762     }
763    
764     ParameterCard card(Parameters["CARD"]);
765     if( ASIO_OpenAndQueryDeviceInfo((char *)card.ValueAsString().c_str(), &asioDriverInfo, &MyAsioDriverInfo) != ASE_OK) {
766     throw AudioOutputException(String("Error: ASIO Initialization Error") );
767     }
768 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::DefaultAsInt returning %d\n",asioDriverInfo.preferredBufSize));
769 senoner 1500 return asioDriverInfo.preferredBufSize;
770     }
771    
772     optional<int> AudioOutputDeviceAsio::ParameterFragmentSize::RangeMinAsInt(std::map<String,String> Parameters) {
773 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::RangeMinAsInt!\n"));
774 senoner 1500 if (!Parameters.count("CARD")) {
775 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::RangeMinAsInt returning optional<int>::nothing (no CARD parameter supplied\n"));
776 senoner 1500 return optional<int>::nothing;
777     }
778    
779     ParameterCard card(Parameters["CARD"]);
780     if( ASIO_OpenAndQueryDeviceInfo((char *)card.ValueAsString().c_str(), &asioDriverInfo, &MyAsioDriverInfo) != ASE_OK) {
781     throw AudioOutputException(String("Error: ASIO Initalization Error") );
782     }
783 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::RangeMinAsInt returning %d\n",asioDriverInfo.minBufSize));
784 senoner 1500 return asioDriverInfo.minBufSize;
785     }
786    
787     optional<int> AudioOutputDeviceAsio::ParameterFragmentSize::RangeMaxAsInt(std::map<String,String> Parameters) {
788 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::RangeMaxAsInt!\n"));
789 senoner 1500 if (!Parameters.count("CARD")) {
790 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::RangeMaxAsInt returning optional<int>::nothing (no CARD parameter supplied\n"));
791 senoner 1500 return optional<int>::nothing;
792     }
793    
794     ParameterCard card(Parameters["CARD"]);
795     if( ASIO_OpenAndQueryDeviceInfo((char *)card.ValueAsString().c_str(), &asioDriverInfo, &MyAsioDriverInfo) != ASE_OK) {
796     throw AudioOutputException(String("Error: ASIO Initialization Error") );
797     }
798 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::RangeMaxAsInt returning %d\n",asioDriverInfo.maxBufSize));
799 senoner 1500 return asioDriverInfo.maxBufSize;
800     }
801    
802     std::vector<int> AudioOutputDeviceAsio::ParameterFragmentSize::PossibilitiesAsInt(std::map<String,String> Parameters) {
803 persson 1888 dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::PossibilitiesAsInt!\n"));
804 senoner 1500 return std::vector<int>();
805     }
806    
807     void AudioOutputDeviceAsio::ParameterFragmentSize::OnSetValue(int i) throw (Exception) {
808     // not posssible, as parameter is fix
809     }
810    
811     String AudioOutputDeviceAsio::ParameterFragmentSize::Name() {
812     return "FRAGMENTSIZE";
813     }
814    
815    
816    
817     // *************** AudioOutputDeviceAsio ***************
818     // *
819    
820     /**
821     * Create and initialize Asio audio output device with given parameters.
822     *
823     * @param Parameters - optional parameters
824     * @throws AudioOutputException if output device cannot be opened
825     */
826     AudioOutputDeviceAsio::AudioOutputDeviceAsio(std::map<String,DeviceCreationParameter*> Parameters) : AudioOutputDevice(Parameters) {
827     if(AudioOutputDeviceAsioInstantiated) throw Exception("AudioOutputDeviceAsio: Sorry, only one ASIO card at time can be opened");
828     AudioOutputDeviceAsioInstantiated = true;
829     asioDriverOpened = false;
830     this->uiAsioChannels = ((DeviceCreationParameterInt*)Parameters["CHANNELS"])->ValueAsInt();
831     this->uiSamplerate = ((DeviceCreationParameterInt*)Parameters["SAMPLERATE"])->ValueAsInt();
832     this->FragmentSize = ((DeviceCreationParameterInt*)Parameters["FRAGMENTSIZE"])->ValueAsInt();
833     String Card = ((DeviceCreationParameterString*)Parameters["CARD"])->ValueAsString();
834    
835    
836 persson 1888 dmsg(2,("AudioOutputDeviceAsio::AudioOutputDeviceAsio constructor\n"));
837 senoner 1500
838     asioIsPlaying = false;
839    
840     ASIO_OpenAndQueryDeviceInfo((char *)Card.c_str(), &asioDriverInfo, &MyAsioDriverInfo);
841 persson 1888 dmsg(2,("AudioOutputDeviceAsio::AudioOutputDeviceAsio: after ASIO_OpenAndQueryDeviceInfo\n"));
842 senoner 1500
843     if( ASIOSetSampleRate(uiSamplerate) != ASE_OK ) {
844     throw AudioOutputException(String("Error: ASIOSetSampleRate: cannot set samplerate. ") );
845     }
846 persson 1888 dmsg(2,("AudioOutputDeviceAsio::AudioOutputDeviceAsio: after ASIOSetSampleRate\n"));
847 senoner 1500
848     if(ASIOOutputReady() == ASE_OK) asioDriverInfo.ASIOOutputReadySupported = true;
849     else asioDriverInfo.ASIOOutputReadySupported = false;
850 persson 1888 dmsg(2,("AudioOutputDeviceAsio::AudioOutputDeviceAsio: after ASIOOutputReady\n"));
851 senoner 1500
852     asioDriverInfo.numInputBuffers = 0;
853     asioDriverInfo.numOutputBuffers = uiAsioChannels;
854     asioDriverInfo.chosenBufSize = FragmentSize;
855    
856     for( int i = 0 ; i < uiAsioChannels ; i++) {
857     asioDriverInfo.bufferInfos[i].isInput = ASIOFalse;
858     asioDriverInfo.bufferInfos[i].channelNum = i;
859     }
860    
861     asioCallbacks.bufferSwitch = &bufferSwitch;
862     asioCallbacks.sampleRateDidChange = &sampleRateChanged;
863     asioCallbacks.asioMessage = &asioMessages;
864     asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo;
865    
866     if ( ASIOCreateBuffers(asioDriverInfo.bufferInfos, asioDriverInfo.numOutputBuffers, asioDriverInfo.chosenBufSize = FragmentSize, &asioCallbacks) != ASE_OK ){
867     throw AudioOutputException(String("AudioOutputDeviceAsio: Error: ASIOCreateBuffers failed.") );
868     }
869 persson 1888 dmsg(2,("AudioOutputDeviceAsio::AudioOutputDeviceAsio: after ASIOCreateBuffers\n"));
870 senoner 1500
871     // create audio channels for this audio device to which the sampler engines can write to
872     for (int i = 0; i < uiAsioChannels; i++) this->Channels.push_back(new AudioChannel(i, FragmentSize));
873    
874     // FIXME: temporary global variable used to store the this pointer for the ASIO callbacks wanting to access the AudioOutputDeviceAsio methods
875     GlobalAudioOutputDeviceAsioThisPtr = this;
876    
877     if (((DeviceCreationParameterBool*)Parameters["ACTIVE"])->ValueAsBool()) {
878     Play();
879     }
880     }
881    
882     AudioOutputDeviceAsio::~AudioOutputDeviceAsio() {
883     ASIOExit();
884     asioDriverOpened = false;
885     AudioOutputDeviceAsioInstantiated = false;
886     }
887    
888     void AudioOutputDeviceAsio::Play() {
889 persson 1888 dmsg(2,("AudioOutputDeviceAsio::Play() !\n"));
890 senoner 1500 if ( ASIOStart() != ASE_OK ){
891     asioIsPlaying = false;
892     throw AudioOutputException(String("AudioOutputDeviceAsio: Error: ASIOStart failed.") );
893     }
894     else asioIsPlaying = true;
895     }
896    
897     bool AudioOutputDeviceAsio::IsPlaying() {
898     return asioIsPlaying;
899     }
900    
901     void AudioOutputDeviceAsio::Stop() {
902 persson 1888 dmsg(2,("AudioOutputDeviceAsio::Stop() !\n"));
903 senoner 1500 ASIOStop();
904     asioIsPlaying = false;
905     }
906    
907     AudioChannel* AudioOutputDeviceAsio::CreateChannel(uint ChannelNr) {
908 persson 1888 dmsg(2,("AudioOutputDeviceAsio::CreateChannel value=%d uiAsioChannels=%d\n",ChannelNr,uiAsioChannels));
909 senoner 1500 // just create a mix channel
910     return new AudioChannel(ChannelNr, Channel(ChannelNr % uiAsioChannels));
911     }
912    
913     uint AudioOutputDeviceAsio::MaxSamplesPerCycle() {
914 persson 1888 dmsg(2,("AudioOutputDeviceAsio::MaxSamplesPerCycle value=%d\n",FragmentSize));
915 senoner 1500 return FragmentSize;
916     }
917    
918     uint AudioOutputDeviceAsio::SampleRate() {
919 persson 1888 dmsg(2,("AudioOutputDeviceAsio::SampleRate value=%d\n",uiSamplerate)); fflush(stdout);
920 senoner 1500 return uiSamplerate;
921     }
922    
923     String AudioOutputDeviceAsio::Name() {
924     return "ASIO";
925     }
926    
927     String AudioOutputDeviceAsio::Driver() {
928     return Name();
929     }
930    
931     String AudioOutputDeviceAsio::Description() {
932     return "Audio Streaming Input Output 2.2";
933     }
934    
935     String AudioOutputDeviceAsio::Version() {
936 persson 1888 String s = "$Revision: 1.3 $";
937 senoner 1500 return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword
938     }
939    
940     } // namespace LinuxSampler

  ViewVC Help
Powered by ViewVC