~ubuntu-branches/ubuntu/karmic/muse/karmic-proposed

« back to all changes in this revision

Viewing changes to synti/stklib/RtAudio.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Kobras
  • Date: 2002-04-23 17:28:23 UTC
  • Revision ID: james.westby@ubuntu.com-20020423172823-w8yplzr81a759xa3
Tags: upstream-0.5.2
ImportĀ upstreamĀ versionĀ 0.5.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/******************************************/
 
2
/*
 
3
  RtAudio.cpp
 
4
  Realtime Sound I/O Object for STK
 
5
  by Gary P. Scavone, 1998-2000.
 
6
 
 
7
  The sound output sections of this object
 
8
  were originally based in part on code by
 
9
  Doug Scott (SGI), Tim Stilson (Linux),
 
10
  and R. Marsanyi (DirectSound).  The latest
 
11
  DirectSound code was re-written by Dave
 
12
  Chisholm at CCRMA.
 
13
 
 
14
  This object provides a standard API
 
15
  across all platforms for STK realtime
 
16
  audio input/output.  Multi-channel
 
17
  support is supported when provided by
 
18
  the soundcard.
 
19
 
 
20
  Only 16-bit integer input/output
 
21
  routines are written for the moment
 
22
  though it would be simple to overload
 
23
  the methods for other data types.
 
24
*/
 
25
/******************************************/
 
26
 
 
27
#include "RtAudio.h"
 
28
 
 
29
#if (defined(__STK_REALTIME_) && defined(__OS_IRIX_))
 
30
 
 
31
#define NUM_FRAGMENTS 4
 
32
 
 
33
RtAudio :: RtAudio(int channels, MY_FLOAT srate, const char *mode, int device)
 
34
{
 
35
  ALconfig audio_port_config;
 
36
  long queue_size;
 
37
  ALpv pvs[2];
 
38
  char msg[256];
 
39
 
 
40
  // initialize resources
 
41
  audio_port_out = 0;
 
42
  audio_port_in = 0;
 
43
 
 
44
  // check mode string
 
45
  if (strcmp(mode,"play") && strcmp(mode,"record") && strcmp(mode,"duplex")) {
 
46
    sprintf(msg, "RtAudio: constructor parameter 'mode' must be play, record, or duplex only.\n");
 
47
    throw StkError(msg, StkError::FUNCTION_SYNTAX);
 
48
  }
 
49
 
 
50
  /* Create ALconfig structure */
 
51
  audio_port_config = alNewConfig();
 
52
  if (!audio_port_config) {
 
53
    sprintf(msg,"RtAudio: Couldn't create SGI ALconfig: %s\n",
 
54
            alGetErrorString(oserror()));
 
55
    throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
56
  }
 
57
 
 
58
  /* Configure channels */
 
59
  stk_chans = channels;
 
60
  if(alSetChannels(audio_port_config, stk_chans) < 0) {
 
61
    sprintf(msg,"RtAudio: SGI error configuring %d channels: %s\n",
 
62
            channels, alGetErrorString(oserror()));
 
63
    throw StkError(msg, StkError::SOUNDCARD_CAPS);
 
64
  }
 
65
 
 
66
  /* Size the output queue */
 
67
  queue_size = RT_BUFFER_SIZE * NUM_FRAGMENTS; // in sample frames
 
68
  if(alSetQueueSize(audio_port_config, queue_size) < 0) {
 
69
    sprintf(msg,"RtAudio: SGI error configuring output queue size: %s\n",
 
70
            alGetErrorString(oserror()));
 
71
    throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
72
  }
 
73
 
 
74
  if ( !strcmp(mode,"play") || !strcmp(mode,"duplex") ) { // playback only
 
75
 
 
76
    /* Open the output audio port */
 
77
    audio_port_out = alOpenPort("STK output port", "w", audio_port_config);
 
78
    if(!audio_port_out) {
 
79
      sprintf(msg,"RtAudio: SGI error ... cannot initialize output audio port: %s\n",
 
80
              alGetErrorString(oserror()));
 
81
      throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
82
    }
 
83
 
 
84
    /* Set sample rate parameters */
 
85
    pvs[0].param = AL_MASTER_CLOCK;
 
86
    pvs[0].value.i = AL_CRYSTAL_MCLK_TYPE;
 
87
    pvs[1].param = AL_RATE;
 
88
    pvs[1].value.ll = alDoubleToFixed((double)srate);
 
89
    if (alSetParams(AL_DEFAULT_OUTPUT, pvs, 2) < 0) { /* set output SR */
 
90
      sprintf(msg,"RtAudio: SGI error ... cannot set sample rate parameters: %s\n",
 
91
              alGetErrorString(oserror()));
 
92
      throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
93
    }
 
94
 
 
95
    /* Tell port to accept refill at buffers - 1 */
 
96
    alSetFillPoint(audio_port_out, RT_BUFFER_SIZE * (NUM_FRAGMENTS - 1));
 
97
  }
 
98
  else if ( !strcmp(mode,"record")  || !strcmp(mode,"duplex") ) { // record only
 
99
 
 
100
    /* Open the input audio port */
 
101
    audio_port_in = alOpenPort("STK input port", "r", audio_port_config);
 
102
    if(!audio_port_in) {
 
103
      sprintf(msg,"RtAudio: SGI error ... cannot initialize input audio port: %s\n",
 
104
              alGetErrorString(oserror()));
 
105
      throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
106
    }
 
107
 
 
108
    /* Set sample rate parameters */
 
109
    pvs[0].param = AL_MASTER_CLOCK;
 
110
    pvs[0].value.i = AL_CRYSTAL_MCLK_TYPE;
 
111
    pvs[1].param = AL_RATE;
 
112
    pvs[1].value.ll = alDoubleToFixed((double)srate);
 
113
    if (alSetParams(AL_DEFAULT_INPUT, pvs, 2) < 0) { /* set input SR */
 
114
      sprintf(msg,"RtAudio: SGI error ... cannot set sample rate parameters: %s\n",
 
115
              alGetErrorString(oserror()));
 
116
      throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
117
    }
 
118
 
 
119
    /* tell port to accept refill at buffers - 1 */
 
120
    alSetFillPoint(audio_port_in, 0);  
 
121
  }
 
122
 
 
123
  alFreeConfig(audio_port_config);
 
124
  audio_port_config = 0;
 
125
}
 
126
 
 
127
RtAudio :: ~RtAudio()
 
128
{
 
129
  if (audio_port_out) alClosePort(audio_port_out);
 
130
  audio_port_out=0;
 
131
 
 
132
  if (audio_port_in) alClosePort(audio_port_in);
 
133
  audio_port_in=0;
 
134
}
 
135
 
 
136
int RtAudio :: playBuffer(INT16 *buf, int bufsize)
 
137
{
 
138
    alWriteFrames(audio_port_out, buf, bufsize/stk_chans);
 
139
    return 0;
 
140
}
 
141
 
 
142
int RtAudio :: recordBuffer(INT16 *buf, int bufsize)
 
143
{
 
144
    alReadFrames(audio_port_in, buf, bufsize/stk_chans);
 
145
    return 0;
 
146
}
 
147
 
 
148
 
 
149
/* Linux ALSA Sound API here */
 
150
 
 
151
#elif (defined(__STK_REALTIME_) && defined(__ALSA_API_))
 
152
 
 
153
RtAudio :: RtAudio(int channels, MY_FLOAT srate, const char *mode, int device)
 
154
{
 
155
  int card, dev, err, nChoices = 0;
 
156
  int data_format, default_card;
 
157
  unsigned int mask;
 
158
  snd_pcm_channel_params_t params;
 
159
        struct snd_ctl_hw_info info;
 
160
        snd_pcm_info_t pcminfo;
 
161
        snd_pcm_channel_info_t chninfo;
 
162
  snd_pcm_channel_setup_t setup;
 
163
  snd_ctl_t *chandle;
 
164
  bool print_list = FALSE;
 
165
  char msg[256];
 
166
 
 
167
  // check mode string
 
168
  if (strcmp(mode,"play") && strcmp(mode,"record") && strcmp(mode,"duplex")) {
 
169
    sprintf(msg, "RtAudio: constructor parameter 'mode' must be play, record, or duplex only.\n");
 
170
    throw StkError(msg, StkError::FUNCTION_SYNTAX);
 
171
  }
 
172
 
 
173
  if (channels < 1) {
 
174
    sprintf(msg, "RtAudio: number of channels = %d not supported!\n", channels);
 
175
    throw StkError(msg, StkError::FUNCTION_SYNTAX);
 
176
  }
 
177
 
 
178
  // check to make sure we have card(s) and/or ALSA drivers available
 
179
  mask = snd_cards_mask();
 
180
  if (!mask) {
 
181
    sprintf(msg, "RtAudio: no ALSA soundcards reported available.\n");
 
182
    throw StkError(msg, StkError::SOUNDCARD_NOT_FOUND);
 
183
  }
 
184
 
 
185
  ohandle = 0;
 
186
  ihandle = 0;
 
187
  outbuf = 0;
 
188
  inbuf = 0;
 
189
 
 
190
  if (!strcmp(mode, "play"))
 
191
    direction = SND_PCM_INFO_PLAYBACK;
 
192
  else if (!strcmp(mode, "record"))
 
193
    direction = SND_PCM_INFO_CAPTURE;
 
194
  else
 
195
    direction = SND_PCM_INFO_DUPLEX;
 
196
 
 
197
  // The proliferation of multichannel soundcards carries with it a wide range
 
198
  // of special requirements. Often, a card will need to be fed a special data
 
199
  // or channel format.  So, we need to probe the available card(s) to determine
 
200
  // these requirements.  If no device is specified as a constructor argument,
 
201
  // I'll start probing at the default card number (which can be set with the
 
202
  // ALSA_CARD environment variable) and then any other existing cards until I
 
203
  // find a device that will meet our needs (or not).  If a specified device 
 
204
  // does not exist or will not work, I'll print a list of available audio
 
205
  // devices and let the user select one.
 
206
  if (device == -1) {
 
207
    default_card = snd_defaults_pcm_card();
 
208
  }
 
209
  else { // check device specified as argument
 
210
    if (!(mask & (1<<device))) {
 
211
      default_card = 0;
 
212
      print_list = TRUE;
 
213
      printf("\n");
 
214
    }
 
215
    else {
 
216
      default_card = device;
 
217
    }
 
218
  }
 
219
 
 
220
  card = default_card;
 
221
  while (card<SND_CARDS) {
 
222
    if (mask & (1<<card)) {
 
223
      if ((err = snd_ctl_open(&chandle, card)) < 0) {
 
224
        fprintf(stderr,"RtAudio: ALSA error on control open (%d): %s\n",
 
225
                card, snd_strerror(err));
 
226
        continue;
 
227
      }
 
228
      if ((err = snd_ctl_hw_info(chandle, &info)) < 0) {
 
229
        fprintf(stderr,"RtAudio: ALSA error on control hardware info (%d): %s\n",
 
230
                card, snd_strerror(err));
 
231
        snd_ctl_close(chandle);
 
232
        continue;
 
233
      }
 
234
      for (dev=0; dev<(int)info.pcmdevs; dev++) {
 
235
        /* get information for each device */
 
236
        if ((err = snd_ctl_pcm_info(chandle, dev, &pcminfo)) < 0) {
 
237
          fprintf(stderr,"RtAudio: ALSA error on control PCM info (%d): %s\n",
 
238
                  card, snd_strerror(err));
 
239
          continue;
 
240
        }
 
241
        if (pcminfo.flags & direction) { // a device exists for the given mode
 
242
          if ( (direction == SND_PCM_INFO_PLAYBACK) ||
 
243
               (direction == SND_PCM_INFO_DUPLEX) ) {
 
244
            if ((err=snd_pcm_open(&ohandle, card, dev, SND_PCM_OPEN_PLAYBACK))!=0) {
 
245
              fprintf(stderr,"RtAudio: ALSA PCM playback open error (%d): %s\n",
 
246
                      card, snd_strerror(err));
 
247
              continue;
 
248
            }
 
249
            // we have a device open ... get the channel information (direction specific)
 
250
            memset(&chninfo, 0, sizeof(chninfo));
 
251
            chninfo.channel = SND_PCM_CHANNEL_PLAYBACK;
 
252
            if ((err = snd_pcm_channel_info(ohandle, &chninfo)) < 0) {
 
253
              fprintf(stderr,"RtAudio: ALSA error on PCM playback info (%d): %s\n",
 
254
                      card, snd_strerror(err));
 
255
              snd_pcm_close(ohandle);
 
256
              continue;
 
257
            }
 
258
            // check number of channels and sample rate
 
259
            if ((chninfo.max_voices >= channels) && (chninfo.min_rate <= (int)srate)
 
260
                && (chninfo.max_rate >= (int)srate)) { // this card will work 
 
261
              dev_ochans = (channels >= chninfo.min_voices) ? channels : chninfo.min_voices;
 
262
              if (direction == SND_PCM_INFO_PLAYBACK) {
 
263
                // playback only ... jump out of loop and proceed
 
264
                // duplex case continue to capture check
 
265
                if (print_list) {
 
266
                  printf("Audio Card %d, Device %d: %s\n", card, dev, info.name);
 
267
                  nChoices++;
 
268
                  snd_pcm_close(ohandle);
 
269
                  continue;
 
270
                }
 
271
                goto have_good_device;
 
272
              }
 
273
            } else { // this device won't work
 
274
              snd_pcm_close(ohandle);
 
275
              continue;
 
276
            }
 
277
          }
 
278
          if ( (direction == SND_PCM_INFO_CAPTURE) ||
 
279
              (direction == SND_PCM_INFO_DUPLEX) ) {
 
280
            if ((err=snd_pcm_open(&ihandle, card, dev, SND_PCM_OPEN_CAPTURE))!=0) {
 
281
              fprintf(stderr,"RtAudio: ALSA PCM capture open error (%d): %s\n",
 
282
                      card, snd_strerror(err));
 
283
              if (direction == SND_PCM_INFO_DUPLEX)
 
284
                snd_pcm_close(ohandle);
 
285
              continue;
 
286
            }
 
287
            // we have a device open ... get the channel information (direction specific)
 
288
            memset(&chninfo, 0, sizeof(chninfo));
 
289
            chninfo.channel = SND_PCM_CHANNEL_CAPTURE;
 
290
            if ((err = snd_pcm_channel_info(ihandle, &chninfo)) < 0) {
 
291
              fprintf(stderr,"RtAudio: ALSA error on PCM capture info (%d): %s\n",
 
292
                      card, snd_strerror(err));
 
293
              snd_pcm_close(ihandle);
 
294
              if (direction == SND_PCM_INFO_DUPLEX)
 
295
                snd_pcm_close(ohandle);
 
296
              continue;
 
297
            }
 
298
            // check number of channels and sample rate
 
299
            if ((chninfo.max_voices >= channels) && (chninfo.min_rate <= (int)srate)
 
300
                && (chninfo.max_rate >= (int)srate)) { // this card will work 
 
301
              dev_ichans = (channels >= chninfo.min_voices) ? channels : chninfo.min_voices;
 
302
              if (print_list) {
 
303
                printf("Audio Card %d, Device %d: %s\n", card, dev, info.name);
 
304
                nChoices++;
 
305
                snd_pcm_close(ihandle);
 
306
                if (direction == SND_PCM_INFO_DUPLEX)
 
307
                  snd_pcm_close(ohandle);
 
308
                continue;
 
309
              }
 
310
              goto have_good_device; // jump out of loop and proceed
 
311
            } else { // this device won't work
 
312
              snd_pcm_close(ihandle);
 
313
              if (direction == SND_PCM_INFO_DUPLEX)
 
314
                snd_pcm_close(ohandle);
 
315
            }
 
316
          }
 
317
        }
 
318
      }
 
319
    }
 
320
    if (default_card == 0) card++;
 
321
    else { // default card != 0, now start with card 0 and keep searching
 
322
      if (card == default_card) card = 0; // first time only
 
323
      else {
 
324
        card++;
 
325
        if (card == default_card) card++; // skip over default card
 
326
      }
 
327
    }
 
328
  }
 
329
 
 
330
  if (print_list && nChoices) {
 
331
    char choice[16];
 
332
    printf("\nType an audio card number from above: ");
 
333
    fgets(choice, 16, stdin);
 
334
    card = atoi(choice);
 
335
    printf("Select a device for the same card: ");
 
336
    fgets(choice, 16, stdin);
 
337
    printf("\n");
 
338
    dev = atoi(choice);
 
339
    // re-open the device(s)
 
340
    if ( (direction == SND_PCM_INFO_PLAYBACK) ||
 
341
         (direction == SND_PCM_INFO_DUPLEX) ) {
 
342
      if ((err=snd_pcm_open(&ohandle, card, dev, SND_PCM_OPEN_PLAYBACK))!=0) {
 
343
        sprintf(msg, "RtAudio: ALSA PCM playback open error (%d:%d): %s\n",
 
344
                card, dev, snd_strerror(err));
 
345
        throw StkError(msg, StkError::SOUNDCARD_CAPS);
 
346
      }
 
347
    }
 
348
    if ( (direction == SND_PCM_INFO_CAPTURE) ||
 
349
        (direction == SND_PCM_INFO_DUPLEX) ) {
 
350
      if ((err=snd_pcm_open(&ihandle, card, dev, SND_PCM_OPEN_CAPTURE))!=0) {
 
351
        sprintf(msg, "RtAudio: ALSA PCM capture open error (%d:%d): %s\n",
 
352
                card, dev, snd_strerror(err));
 
353
        if (direction == SND_PCM_INFO_DUPLEX)
 
354
          snd_pcm_close(ohandle);
 
355
        throw StkError(msg, StkError::SOUNDCARD_CAPS);
 
356
      }
 
357
    }
 
358
    goto have_good_device;
 
359
  }
 
360
 
 
361
  // if we got here, no devices were found to meet the requested functionality
 
362
  sprintf(msg, "RtAudio: no ALSA device found for requested service!\n");
 
363
  throw StkError(msg, StkError::SOUNDCARD_CAPS);
 
364
 
 
365
 have_good_device: // the current value of card and dev are what we will use
 
366
 
 
367
  // The hardware sometimes requires more channels of data than we necessarily
 
368
  // want to work with in STK.  In fact, sometimes the hardware requires a
 
369
  // different number of output channels than input channels.  Thus, we need to
 
370
  // remember all values.
 
371
  stk_chans = channels;
 
372
 
 
373
  // If duplex mode, the format will come from the capture device info.  I'm assuming
 
374
  // that both directions will have the same data format.
 
375
  if (chninfo.formats & SND_PCM_FMT_S16_LE) {
 
376
    data_format = SND_PCM_SFMT_S16_LE;
 
377
    bytes_per_sample = 2;
 
378
  }
 
379
  else if (chninfo.formats & SND_PCM_FMT_S32_LE) {
 
380
    data_format = SND_PCM_SFMT_S32_LE;
 
381
    bytes_per_sample = 4;
 
382
  }
 
383
  else {
 
384
    sprintf(msg, "RtAudio: only ALSA S16_LE and S32_LE data formats at the moment!\n");
 
385
    throw StkError(msg);
 
386
  }
 
387
 
 
388
  // global channel parameters (not direction specific)
 
389
  memset(&params, 0, sizeof(params));
 
390
  params.mode=SND_PCM_MODE_BLOCK;
 
391
  params.stop_mode=SND_PCM_STOP_ROLLOVER; 
 
392
  params.buf.block.frags_max=3;
 
393
  params.buf.block.frags_min=1;
 
394
  params.format.interleave=1;
 
395
  params.format.format=data_format; // this should depend on the card, unless we use the plugin
 
396
  params.format.rate=(int)srate;
 
397
 
 
398
  // We need to set the channel parameters, flush, and prepare for each direction of use.
 
399
  // Thus, if doing duplex, we need to do these for both directions.  First do it for
 
400
  // playback and duplex.
 
401
  if ((direction == SND_PCM_INFO_PLAYBACK) || (direction == SND_PCM_INFO_DUPLEX)) {
 
402
    params.channel=SND_PCM_CHANNEL_PLAYBACK;
 
403
    params.start_mode=SND_PCM_START_FULL;
 
404
    params.format.voices=dev_ochans;
 
405
    // The fragsize is in bytes per frame = RT_BUFFER_SIZE * dev_chans * bytes_per_sample.
 
406
    // ALSA requires that all reads and write be done in fragsize increments.
 
407
    ofragsize = (int) (dev_ochans * bytes_per_sample * RT_BUFFER_SIZE);
 
408
    params.buf.block.frag_size=ofragsize;
 
409
    if ((err=snd_pcm_channel_params(ohandle, &params))!=0) {
 
410
      // try to close what was opened!
 
411
      snd_pcm_close(ohandle);
 
412
      sprintf(msg, "RtAudio: Cannot set ALSA audio device parameters for playback: %s\n",
 
413
              snd_strerror(err));
 
414
      throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
415
    }
 
416
 
 
417
    // now check the actual values, because sometimes they are not what we requested
 
418
    memset(&setup, 0, sizeof(setup));
 
419
    setup.channel=SND_PCM_CHANNEL_PLAYBACK;
 
420
    if ((err=snd_pcm_channel_setup(ohandle, &setup))<0) {
 
421
      snd_pcm_close(ohandle);
 
422
      sprintf(msg, "RtAudio: Cannot get ALSA audio device setup info: %s\n",
 
423
              snd_strerror(err));
 
424
      throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
425
    } else {
 
426
      if (setup.format.rate != (int) srate) {
 
427
        // try to close what was opened!
 
428
        snd_pcm_close(ohandle);
 
429
        sprintf(msg, "RtAudio: Soundcard doesn't seem to support requested sample rate: %.2f!\n",
 
430
                srate);
 
431
        throw StkError(msg, StkError::SOUNDCARD_CAPS);
 
432
      }
 
433
      if (setup.buf.block.frag_size != ofragsize) ofragsize = setup.buf.block.frag_size;
 
434
    }
 
435
 
 
436
    // allocate and clear the output buffer
 
437
    outbuf = (unsigned char*) new char[ofragsize];
 
438
    memset(outbuf, 0, ofragsize);
 
439
 
 
440
    // I'm not sure what this does and I'm not sure it actually helps.
 
441
    if ((err=snd_pcm_playback_flush(ohandle))!=0) {
 
442
      // try to free and close what was opened!
 
443
      snd_pcm_close(ohandle);
 
444
      if (outbuf) delete [] outbuf;
 
445
      sprintf(msg, "RtAudio: Cannot flush ALSA channel buffers for playback!\n");
 
446
      throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
447
    }
 
448
 
 
449
    if ((err=snd_pcm_channel_prepare(ohandle, params.channel))!=0) {
 
450
      // try to close what was opened!
 
451
      snd_pcm_close(ohandle);
 
452
      if (outbuf) delete [] outbuf;
 
453
      sprintf(msg, "RtAudio: Cannot prepare ALSA channel for playback!\n");
 
454
      throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
455
    }
 
456
  }
 
457
 
 
458
  // Now do it for capture and duplex.
 
459
  if ((direction == SND_PCM_INFO_CAPTURE) || (direction == SND_PCM_INFO_DUPLEX)) {
 
460
    params.channel=SND_PCM_CHANNEL_CAPTURE;
 
461
    params.start_mode=SND_PCM_START_DATA;
 
462
    params.format.voices=dev_ichans;
 
463
    // The fragsize is in bytes per frame = RT_BUFFER_SIZE * dev_chans * bytes_per_sample.
 
464
    // ALSA requires that all reads and write be done in fragsize increments.
 
465
    ifragsize = (int) (dev_ichans * bytes_per_sample * RT_BUFFER_SIZE);
 
466
    params.buf.block.frag_size=ifragsize;
 
467
    if ((err=snd_pcm_channel_params(ihandle, &params))!=0) {
 
468
      // try to close what was opened!
 
469
      snd_pcm_close(ihandle);
 
470
      if (direction == SND_PCM_INFO_DUPLEX) {
 
471
        if (outbuf) delete [] outbuf;
 
472
        snd_pcm_close(ohandle);
 
473
      }
 
474
      sprintf(msg, "RtAudio: Cannot set ALSA audio device parameters for capture: %s\n",
 
475
              snd_strerror(err));
 
476
      throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
477
    }
 
478
 
 
479
    // now check the actual values, because sometimes they are not what we requested
 
480
    memset(&setup, 0, sizeof(setup));
 
481
    setup.channel=SND_PCM_CHANNEL_CAPTURE;
 
482
    if ((err=snd_pcm_channel_setup(ihandle, &setup))<0) {
 
483
      // try to close what was opened!
 
484
      snd_pcm_close(ihandle);
 
485
      if (direction == SND_PCM_INFO_DUPLEX) {
 
486
        if (outbuf) delete [] outbuf;
 
487
        snd_pcm_close(ohandle);
 
488
      }
 
489
      sprintf(msg, "RtAudio: Cannot get ALSA audio device setup info: %s\n",
 
490
              snd_strerror(err));
 
491
      throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
492
    } else {
 
493
      if (setup.format.rate != (int) srate) {
 
494
        // try to close what was opened!
 
495
        snd_pcm_close(ihandle);
 
496
        if (direction == SND_PCM_INFO_DUPLEX) {
 
497
          if (outbuf) delete [] outbuf;
 
498
          snd_pcm_close(ohandle);
 
499
        }
 
500
        sprintf(msg, "RtAudio: Soundcard doesn't seem to support requested sample rate: %.2f!\n",
 
501
                srate);
 
502
        throw StkError(msg, StkError::SOUNDCARD_CAPS);
 
503
      }
 
504
 
 
505
      if (setup.buf.block.frag_size != ifragsize) ifragsize = setup.buf.block.frag_size;
 
506
    }
 
507
 
 
508
    // allocate and clear the input buffer
 
509
    inbuf = (unsigned char*) new char[ifragsize];
 
510
    memset(inbuf, 0, ifragsize);
 
511
 
 
512
    if ((err=snd_pcm_capture_flush(ihandle))!=0) {
 
513
      // try to close what was opened!
 
514
      snd_pcm_close(ihandle);
 
515
      if (inbuf) delete [] inbuf;
 
516
      if (direction == SND_PCM_INFO_DUPLEX) {
 
517
        if (outbuf) delete [] outbuf;
 
518
        snd_pcm_close(ohandle);
 
519
      }
 
520
      sprintf(msg, "RtAudio: Cannot flush ALSA channel buffers for capture!\n");
 
521
      throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
522
    }
 
523
 
 
524
    if ((err=snd_pcm_channel_prepare(ihandle, params.channel))!=0) {
 
525
      // try to close what was opened!
 
526
      snd_pcm_close(ihandle);
 
527
      if (inbuf) delete [] inbuf;
 
528
      if (direction == SND_PCM_INFO_DUPLEX) {
 
529
        if (outbuf) delete [] outbuf;
 
530
        snd_pcm_close(ohandle);
 
531
      }
 
532
      sprintf(msg, "RtAudio: Cannot prepare ALSA channel for capture!\n");
 
533
      throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
534
    }
 
535
  }
 
536
}
 
537
 
 
538
RtAudio :: ~RtAudio()
 
539
{
 
540
  if ((direction == SND_PCM_INFO_PLAYBACK) || (direction == SND_PCM_INFO_DUPLEX)) {
 
541
    snd_pcm_playback_drain(ohandle);
 
542
    snd_pcm_close(ohandle);
 
543
  }
 
544
  if ((direction == SND_PCM_INFO_CAPTURE) || (direction == SND_PCM_INFO_DUPLEX)) {
 
545
    snd_pcm_close(ihandle);
 
546
  }
 
547
  if (outbuf) delete [] outbuf;
 
548
  if (inbuf) delete [] inbuf;
 
549
}
 
550
 
 
551
int RtAudio :: playBuffer(INT16 *buf, int bufsize)
 
552
{
 
553
  // The argument bufsize is the number of audio samples (INT16s) in buf.
 
554
  int err, i;
 
555
  static char msg[256];
 
556
  unsigned char *temp;
 
557
  static int channel = 1;
 
558
  static int counter = 0;
 
559
  static int extra_chans = dev_ochans - stk_chans;
 
560
 
 
561
  // performance optimization occurs when the following conditions are met
 
562
  if ((extra_chans == 0) && (bytes_per_sample == 2) && (bufsize * 2 == ofragsize)) {
 
563
    // we don't need to use outbuf
 
564
    if ((err=snd_pcm_write(ohandle, buf, ofragsize))!=ofragsize) {
 
565
      sprintf(msg, "RtAudio: ALSA audio write error!\n");
 
566
      throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
567
    }
 
568
  } else {
 
569
    for (i=0; i<bufsize; i++) {
 
570
      // write the i-th sample to outbuf
 
571
      temp = (unsigned char *) &buf[i];
 
572
      // shift data to the two highest bytes of each output sample
 
573
      counter += bytes_per_sample-2;
 
574
      outbuf[counter++] = temp[0];
 
575
      outbuf[counter++] = temp[1];
 
576
      channel++;
 
577
 
 
578
      if (channel > stk_chans) {
 
579
        // because we created outbuf with calloc(), we shouldn't have to write zeros here
 
580
        counter += bytes_per_sample * extra_chans;
 
581
        channel = 1;
 
582
      }
 
583
 
 
584
      if (counter >= ofragsize) {
 
585
        if ((err=snd_pcm_write(ohandle, outbuf, ofragsize))!=ofragsize) {
 
586
          sprintf(msg, "RtAudio: ALSA audio write error!\n");
 
587
          throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
588
        }
 
589
        counter = 0;
 
590
      }
 
591
    }
 
592
  }
 
593
 
 
594
  return 0;
 
595
}
 
596
 
 
597
int RtAudio :: recordBuffer(INT16 *buf, int bufsize)
 
598
{
 
599
  // The argument bufsize is the number of audio samples (INT16s) in buf.
 
600
  int err, i;
 
601
  static char msg[256];
 
602
  unsigned char *temp;
 
603
  static int channel = 1;
 
604
  static int counter = 0;
 
605
  static int extra_chans = dev_ichans - stk_chans;
 
606
 
 
607
  // performance optimization occurs when the following conditions are met
 
608
  if ((extra_chans == 0) && (bytes_per_sample == 2) && (bufsize * 2 == ifragsize)) {
 
609
    // we don't need to use inbuf
 
610
    if ((err=snd_pcm_read(ihandle, buf, ifragsize))!=ifragsize) {
 
611
      sprintf(msg, "RtAudio: ALSA audio read error!\n");
 
612
      throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
613
    }
 
614
  } else {
 
615
    for (i=0; i<bufsize; i++) {
 
616
      if (counter == 0) {
 
617
        if ((err=snd_pcm_read(ihandle, inbuf, ifragsize))!=ifragsize) {
 
618
          sprintf(msg, "RtAudio: ALSA audio read error!\n");
 
619
          throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
620
        }
 
621
      }
 
622
 
 
623
      temp = (unsigned char *) &buf[i];
 
624
      // take data from the two highest bytes of each input sample
 
625
      counter += bytes_per_sample-2;
 
626
      temp[0] = inbuf[counter++];
 
627
      temp[1] = inbuf[counter++];
 
628
      channel++;
 
629
 
 
630
      if (channel > stk_chans) {
 
631
        // just skip over the extra channels
 
632
        counter += bytes_per_sample * extra_chans;
 
633
        channel = 1;
 
634
      }
 
635
      if (counter >= ifragsize) counter = 0;
 
636
    }
 
637
  }
 
638
 
 
639
  return 0;
 
640
}
 
641
 
 
642
 
 
643
/* Linux OSS Sound API here */
 
644
 
 
645
#elif (defined(__STK_REALTIME_) && defined(__OSS_API_))
 
646
 
 
647
// Define the maximum number of dsp devices that we'll attempt
 
648
// to probe before giving up.
 
649
#define MAX_DSP_DEVS 8
 
650
#define DAC_NAME "/dev/dsp"
 
651
// The number of fragments can be made larger than 2 if the sound
 
652
// system performance is poor.
 
653
#define NUM_FRAGMENTS 4
 
654
 
 
655
RtAudio :: RtAudio(int channels, MY_FLOAT srate, const char *mode, int device)
 
656
{
 
657
  char device_name[16];
 
658
  int fragsize;
 
659
  int fragment_size_log;
 
660
  int format;
 
661
  int chans = channels;
 
662
  int speed;
 
663
  int i, nChoices = 0;
 
664
  bool print_list = FALSE;
 
665
  char msg[256];
 
666
 
 
667
  // check mode string
 
668
  if (strcmp(mode,"play") && strcmp(mode,"record") && strcmp(mode,"duplex")) {
 
669
    sprintf(msg, "RtAudio: constructor parameter 'mode' must be play, record, or duplex only.\n");
 
670
    throw StkError(msg, StkError::FUNCTION_SYNTAX);
 
671
  }
 
672
 
 
673
  if (channels < 1) {
 
674
    sprintf(msg, "RtAudio: number of channels = %d not supported!\n", channels);
 
675
    throw StkError(msg, StkError::FUNCTION_SYNTAX);
 
676
  }
 
677
 
 
678
  fragsize = RT_BUFFER_SIZE * channels * 2; // in bytes
 
679
  fragment_size_log = (int)(log10((double)fragsize)/log10(2.0));
 
680
  long temp = ((long) NUM_FRAGMENTS << 16) + fragment_size_log;
 
681
 
 
682
  // /dev/dsp should be a link to the default pcm device under OSS
 
683
  strcpy(device_name, DAC_NAME);
 
684
 
 
685
  // The OSS API doesn't really give us a means for probing the
 
686
  // capabilities of devices.  Thus, we'll just pursue a brute
 
687
  // force method of opening devices until we either find something
 
688
  // that doesn't complain or we have to give up.  We'll start with
 
689
  // the default device, then try /dev/dsp0, /dev/dsp1, etc...
 
690
 
 
691
  if (device != -1) {
 
692
    // start with device specified as argument
 
693
    sprintf(device_name, "%s%d", DAC_NAME, device);
 
694
  }
 
695
 
 
696
  for (i=0; i<=MAX_DSP_DEVS; i++) {
 
697
 
 
698
    // if the default device doesn't work, try some others
 
699
    if (i > 0) sprintf(device_name, "%s%d", DAC_NAME, i-1);
 
700
 
 
701
    if (device != -1 && i == 1) {
 
702
      // the specified device didn't work ... now print other options
 
703
      print_list = TRUE;
 
704
      printf("\n");
 
705
    }
 
706
 
 
707
    if (!strcmp(mode,"play")) { // playback only
 
708
      if ((audio_fd = open(device_name, O_WRONLY, 0)) == -1) {
 
709
        // Open device failed ... either busy or doesn't exist
 
710
        if (errno == EBUSY)
 
711
          fprintf(stderr,"RtAudio: OSS PCM playback device (%s) is busy and cannot be opened.\n",
 
712
                  device_name);
 
713
        continue;
 
714
      }
 
715
    }
 
716
    else if (!strcmp(mode,"record")) { // record only
 
717
      if ((audio_fd = open(device_name, O_RDONLY, 0)) == -1) {
 
718
        // Open device failed ... either busy or doesn't exist
 
719
        if (errno == EBUSY)
 
720
          fprintf(stderr,"RtAudio: OSS PCM record device (%s) is busy and cannot be opened.\n",
 
721
                  device_name);
 
722
        continue;
 
723
      }
 
724
    }
 
725
    else if (!strcmp(mode,"duplex")) { // duplex mode
 
726
      if ((audio_fd = open(device_name, O_RDWR, 0)) == -1) {
 
727
        // Open device failed ... either busy or doesn't exist
 
728
        if (errno == EBUSY)
 
729
          fprintf(stderr,"RtAudio: OSS PCM device (%s) is busy and cannot be opened for duplex operation.\n",
 
730
                  device_name);
 
731
        continue;
 
732
      }
 
733
      int caps;
 
734
      if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps)) {
 
735
        close(audio_fd);
 
736
        fprintf(stderr,"RtAudio: OSS error getting device (%s) capabilities for duplex operation.\n",
 
737
                device_name);
 
738
        continue;
 
739
      }
 
740
      if (!(caps & DSP_CAP_DUPLEX)) {
 
741
        close(audio_fd);
 
742
        fprintf(stderr,"RtAudio: OSS reports device (%s) does not support duplex operation.\n",
 
743
                device_name);
 
744
        continue;
 
745
      }
 
746
      if (ioctl(audio_fd, SNDCTL_DSP_SETDUPLEX, 0)) {
 
747
        close(audio_fd);
 
748
        fprintf(stderr,"RtAudio: OSS error setting device (%s) for duplex operation.\n",
 
749
                device_name);
 
750
        continue;
 
751
      }
 
752
    }
 
753
 
 
754
    // Setup the number and size of the fragments
 
755
    if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &temp)) {
 
756
      close(audio_fd);
 
757
      fprintf(stderr,"RtAudio: OSS error setting fragment size for device (%s).\n",
 
758
              device_name);
 
759
      continue;
 
760
    }
 
761
 
 
762
    // Setup the data format ... we're only supporting 16-bit little-endian data for now
 
763
    format = AFMT_S16_LE;
 
764
    if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format) == -1) {
 
765
      close(audio_fd);
 
766
      fprintf(stderr,"RtAudio: OSS error setting data format for device (%s).\n",
 
767
              device_name);
 
768
      continue;
 
769
    }
 
770
 
 
771
    // Check the see whether the device supports the requested format
 
772
    if (format != AFMT_S16_LE) {
 
773
      close(audio_fd);
 
774
      fprintf(stderr,"RtAudio: OSS error ... audio device (%s) doesn't support 16-bit signed LE format.\n",
 
775
              device_name);
 
776
      continue;
 
777
    }
 
778
 
 
779
    // Setup the number of channels
 
780
    if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &chans) == -1) {
 
781
      close(audio_fd);
 
782
      fprintf(stderr,"RtAudio: OSS error setting %d channels on  device (%s).\n",
 
783
              channels, device_name);
 
784
      continue;
 
785
    }
 
786
 
 
787
    // Check to see whether the device supports the requested number of channels
 
788
    if (chans != channels ) {
 
789
      close(audio_fd);
 
790
      fprintf(stderr,"RtAudio: OSS error ... audio device (%s) doesn't support %d channels.\n",
 
791
              device_name, channels);
 
792
      continue;
 
793
    }
 
794
 
 
795
    // Setup the sampling rate
 
796
    speed = (int) srate;
 
797
    if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &speed)==-1) {
 
798
      close(audio_fd);
 
799
      fprintf(stderr,"RtAudio: OSS error setting sample rate = %f on  device (%s).\n",
 
800
              srate, device_name);
 
801
      continue;
 
802
    }
 
803
    
 
804
    // Check to see whether the device supports the requested sample rate
 
805
    if (abs(speed - (int)srate) > 100) {
 
806
      close(audio_fd);
 
807
      fprintf(stderr,"RtAudio: OSS error ... audio device (%s) doesn't support sample rate of %f.\n",
 
808
              device_name, srate);
 
809
      continue;
 
810
    }
 
811
 
 
812
    if (print_list) {
 
813
      close(audio_fd);
 
814
      printf("Audio Device %d: %s\n", i-1, device_name);
 
815
      nChoices++;
 
816
      continue;
 
817
    }
 
818
 
 
819
    // If we got here, we found a device that meets our needs.  Return to the caller.
 
820
    return;
 
821
  }
 
822
 
 
823
  // If we got here and print_list = TRUE, then we need to ask the user to specify
 
824
  // a device from the printed list.  Then we have to go through the whole process
 
825
  // of opening and checking the device capabilities again, as we did above.  We
 
826
  // can't assume the user always types a valid device number.  We can thank OSS
 
827
  // for the kludgy-ness of this whole process.
 
828
  if (print_list && nChoices ) {
 
829
    char choice[16];
 
830
    printf("\nType an audio device number from above: ");
 
831
    fgets(choice, 16, stdin);
 
832
    i = atoi(choice);
 
833
    printf("\n");
 
834
    sprintf(device_name, "%s%d", DAC_NAME, i);
 
835
 
 
836
    // Now redo the above with the specified card.
 
837
    if (!strcmp(mode,"play")) { // playback only
 
838
      if ((audio_fd = open(device_name, O_WRONLY, 0)) == -1) {
 
839
        // Open device failed ... either busy or doesn't exist
 
840
        sprintf(msg, "RtAudio: OSS PCM playback device (%s) cannot be opened.\n",
 
841
                device_name);
 
842
        throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
843
      }
 
844
    }
 
845
    else if (!strcmp(mode,"record")) { // record only
 
846
      if ((audio_fd = open(device_name, O_RDONLY, 0)) == -1) {
 
847
        // Open device failed ... either busy or doesn't exist
 
848
        sprintf(msg, "RtAudio: OSS PCM record device (%s) cannot be opened.\n",
 
849
                device_name);
 
850
        throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
851
      }
 
852
    }
 
853
    else if (!strcmp(mode,"duplex")) { // duplex mode
 
854
      if ((audio_fd = open(device_name, O_RDWR, 0)) == -1) {
 
855
        // Open device failed ... either busy or doesn't exist
 
856
        sprintf(msg, "RtAudio: OSS PCM device (%s) cannot be opened for duplex operation.\n",
 
857
                device_name);
 
858
        throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
859
      }
 
860
      int caps;
 
861
      if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps)) {
 
862
        close(audio_fd);
 
863
        sprintf(msg, "RtAudio: OSS error getting device (%s) capabilities for duplex operation.\n",
 
864
                device_name);
 
865
        throw StkError(msg, StkError::SOUNDCARD_CAPS);
 
866
      }
 
867
      if (!(caps & DSP_CAP_DUPLEX)) {
 
868
        close(audio_fd);
 
869
        sprintf(msg, "RtAudio: OSS reports device (%s) does not support duplex operation.\n",
 
870
                device_name);
 
871
        throw StkError(msg, StkError::SOUNDCARD_CAPS);
 
872
      }
 
873
      if (ioctl(audio_fd, SNDCTL_DSP_SETDUPLEX, 0)) {
 
874
        close(audio_fd);
 
875
        sprintf(msg, "RtAudio: OSS error setting device (%s) for duplex operation.\n",
 
876
                device_name);
 
877
        throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
878
      }
 
879
    }
 
880
 
 
881
    // Setup the number and size of the fragments
 
882
    if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &temp)) {
 
883
      close(audio_fd);
 
884
      sprintf(msg, "RtAudio: OSS error setting fragment size for device (%s).\n",
 
885
              device_name);
 
886
      throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
887
    }
 
888
 
 
889
    // Setup the data format ... we're only supporting 16-bit little-endian data for now
 
890
    format = AFMT_S16_LE;
 
891
    if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format) == -1) {
 
892
      close(audio_fd);
 
893
      sprintf(msg,"RtAudio: OSS error setting data format for device (%s).\n",
 
894
              device_name);
 
895
      throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
896
    }
 
897
 
 
898
    // Check the see whether the device supports the requested format
 
899
    if (format != AFMT_S16_LE) {
 
900
      close(audio_fd);
 
901
      sprintf(msg,"RtAudio: OSS error ... audio device (%s) doesn't support 16-bit signed LE format.\n",
 
902
              device_name);
 
903
      throw StkError(msg, StkError::SOUNDCARD_CAPS);
 
904
    }
 
905
 
 
906
    // Setup the number of channels
 
907
    if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &chans) == -1) {
 
908
      close(audio_fd);
 
909
      sprintf(msg, "RtAudio: OSS error setting %d channels on  device (%s).\n",
 
910
              channels, device_name);
 
911
      throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
912
    }
 
913
 
 
914
    // Check to see whether the device supports the requested number of channels
 
915
    if (chans != channels ) {
 
916
      close(audio_fd);
 
917
      sprintf(msg, "RtAudio: OSS error ... audio device (%s) doesn't support %d channels.\n",
 
918
              device_name, channels);
 
919
      throw StkError(msg, StkError::SOUNDCARD_CAPS);
 
920
    }
 
921
 
 
922
    // Setup the sampling rate
 
923
    speed = (int) srate;
 
924
    if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &speed)==-1) {
 
925
      close(audio_fd);
 
926
      sprintf(msg, "RtAudio: OSS error setting sample rate = %f on  device (%s).\n",
 
927
              srate, device_name);
 
928
      throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
929
    }
 
930
    
 
931
    // Check to see whether the device supports the requested sample rate
 
932
    if (abs(speed - (int)srate) > 100) {
 
933
      close(audio_fd);
 
934
      sprintf(msg,"RtAudio: OSS error ... audio device (%s) doesn't support sample rate of %f.\n",
 
935
              device_name, srate);
 
936
      throw StkError(msg, StkError::SOUNDCARD_CAPS);
 
937
    }
 
938
 
 
939
    // If we got here, the specified device works ... return to the caller.
 
940
    return;
 
941
  }
 
942
 
 
943
  // If we got here, no device was found to meet the requested functionality
 
944
  sprintf(msg, "RtAudio: No OSS device found for requested service!\n");
 
945
  throw StkError(msg, StkError::SOUNDCARD_CAPS);
 
946
}
 
947
 
 
948
RtAudio :: ~RtAudio()
 
949
{
 
950
  if (audio_fd) close(audio_fd);
 
951
  audio_fd=0;
 
952
}
 
953
 
 
954
int RtAudio :: playBuffer(INT16 *buf, int bufsize)
 
955
{
 
956
  // The argument bufsize is the number of audio samples (INT16s) in buf.
 
957
  // The OSS write() routine takes its buffer size in bytes, thus the
 
958
  // multiplication by two.  While it is not necessary to call this function
 
959
  // with fragsize buffers (which should equal RT_BUFFER_SIZE * channels * 2),
 
960
  // its behavior is apparently optimized for such.
 
961
  static char msg[256];
 
962
 
 
963
  if (write(audio_fd, buf, 2*bufsize) == -1) {
 
964
    sprintf(msg, "RtAudio: OSS audio write error!\n");
 
965
    throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
966
  }
 
967
  return 0;
 
968
}
 
969
 
 
970
int RtAudio :: recordBuffer(INT16 *buf, int bufsize)
 
971
{
 
972
  // The argument bufsize is the number of audio samples (INT16s) in buf.
 
973
  // The OSS read() routine takes its buffer size in bytes, thus the
 
974
  // multiplication by two.  While it is not necessary to call this function
 
975
  // with fragsize buffers (which should equal RT_BUFFER_SIZE * channels * 2),
 
976
  // its behavior is apparently optimized for such.
 
977
  static char msg[256];
 
978
 
 
979
  if (read(audio_fd, buf, 2*bufsize) == -1) {
 
980
    sprintf(msg, "RtAudio: OSS audio read error!\n");
 
981
    throw StkError(msg, StkError::SOUNDCARD_CONTROL);
 
982
  }
 
983
  return 0;
 
984
}
 
985
 
 
986
 
 
987
#elif (defined(__STK_REALTIME_) && defined(__OS_Win_) )
 
988
/*
 
989
  WINDOZE:
 
990
 
 
991
  If anyone needed substantiation as to why Microsoft doesn't deserve
 
992
  to exist, just read through the following code and compare it to
 
993
  any of the other supported APIs in this file.  Dave, thanks for
 
994
  your hard work to get things running better ... it is appreciated.
 
995
  But personally, I'm disgusted with Microsoft and their pure
 
996
  incompetence (GPS, 10/2000).
 
997
 
 
998
  AUDIO OUTPUT:
 
999
 
 
1000
  There are several ways to go about handling sound output
 
1001
  under DirectSound.  We choose to always write new buffers of
 
1002
  data behind the read pointer of the DS play buffer. This is
 
1003
  safe (compared to trying to lead the pointer) but inherently
 
1004
  produces a delay equivalent to the entire sound buffer (plus
 
1005
  any other delays that Windows provides). Our default parameters
 
1006
  cause an inherent delay of about 30 ms b/c of this. To change
 
1007
  this, adjust the NUM_FRAGMENTS definition in RtAudio.h
 
1008
 
 
1009
  AUDIO INPUT:
 
1010
 
 
1011
  We chose to use a complex but stable scheme of keeping our own internal
 
1012
  buffer separate from the DS input buffer. This take up more CPU time
 
1013
  but is safer than using the Sleep() function for critical parts of
 
1014
  code. Users are then given copies of this data (or pointers into our
 
1015
  actual internal buffer, if they use the overloaded version of
 
1016
  recordBuffer(INT16**) A separate thread (under control of the WinMM
 
1017
  timer) takes care of transfering data from the DS buffer to our own buffer.
 
1018
  You can expect a total delay of about 100 - 150 ms for fully duplexed
 
1019
  audio - it seems impossible to get directx to perform any better than this.
 
1020
 
 
1021
  In addition, the windows version has stopPlay, startPlay, startRecord
 
1022
  and stopRecord methods b/c DirectX does not automatically stop I/O if
 
1023
  no fresh data is sent.
 
1024
 
 
1025
  The DirectSoundCapture API is only available with DirectX versions
 
1026
  5.0 and higher.
 
1027
 */
 
1028
 
 
1029
RtAudio :: RtAudio(int channels, MY_FLOAT srate, const char *mode, int device)
 
1030
{
 
1031
  HRESULT result;
 
1032
  BYTE* audioPtr;
 
1033
  DWORD dataLen;
 
1034
  HWND hWnd = GetForegroundWindow();
 
1035
        WAVEFORMATEX waveFormat;
 
1036
        LPGUID directSoundGuid = NULL;
 
1037
        int i, devNum = 0;
 
1038
 
 
1039
  // Initialize the DirectSound object and buffer pointers to NULL
 
1040
  directSoundObject = NULL;
 
1041
  directSoundBuffer = NULL;
 
1042
  directSoundCaptureObject = NULL;
 
1043
  directSoundCaptureBuffer = NULL;
 
1044
  inputBuffer = NULL;
 
1045
 
 
1046
        // Some basic initialization stuff
 
1047
        sampleRate = srate;
 
1048
        nextRecordRead = 0;
 
1049
        nextRecordWrite = 0;
 
1050
        internalError = false;
 
1051
        playing = false;
 
1052
        recording = false;
 
1053
        numDevices = 0;
 
1054
        nextWritePos = 0;
 
1055
 
 
1056
  // Define the wave format structure (16-bit PCM, srate, channels)
 
1057
  ZeroMemory(&waveFormat, sizeof(WAVEFORMATEX));
 
1058
  waveFormat.wFormatTag = WAVE_FORMAT_PCM;
 
1059
  waveFormat.nChannels = channels;
 
1060
  waveFormat.nSamplesPerSec = (unsigned long) srate;
 
1061
  waveFormat.wBitsPerSample = 16;
 
1062
  waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
 
1063
  waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
 
1064
 
 
1065
  // check mode string
 
1066
  if (strcmp(mode,"play") && strcmp(mode,"record") && strcmp(mode,"duplex")) {
 
1067
    sprintf(errormsg, "RtAudio: constructor parameter 'mode' must be play, record, or duplex only.\n");
 
1068
    throw StkError(errormsg, StkError::FUNCTION_SYNTAX);
 
1069
  }
 
1070
 
 
1071
  if (channels < 1) {
 
1072
    sprintf(errormsg, "RtAudio: number of channels = %d not supported!\n", channels);
 
1073
    throw StkError(errormsg, StkError::FUNCTION_SYNTAX);
 
1074
  }
 
1075
 
 
1076
        // PLAYBACK INITIALIZATION
 
1077
        if ( (!strcmp(mode,"play")) || (!strcmp(mode,"duplex")) ) {
 
1078
 
 
1079
    DSBUFFERDESC directSoundBufferDescription;
 
1080
 
 
1081
    if (device == -1) {
 
1082
      // use default device
 
1083
      if (FAILED(result = DirectSoundCreate(NULL, &directSoundObject, NULL))) {
 
1084
        sprintf(errormsg,"RtAudio: Unable to create default direct sound object: %s\n", getErrorMessage(result));
 
1085
        throw StkError(errormsg,StkError::SOUNDCARD_NOT_FOUND);
 
1086
      }
 
1087
      goto have_good_output_device;
 
1088
    } else {
 
1089
      // Enumerate through the devices, trying the user specified device first.
 
1090
      // If that fails, prompt for action.
 
1091
      if (FAILED(result = DirectSoundEnumerate((LPDSENUMCALLBACK)SoundDeviceEnumCallback, this))) {
 
1092
        sprintf(errormsg,"RtAudio: Unable to enumerate through sound devices: %s\n", getErrorMessage(result));
 
1093
        throw StkError(errormsg,StkError::SOUNDCARD_NOT_FOUND);
 
1094
      }
 
1095
      if (device >=0 && device < numDevices) {
 
1096
        // try user specified device if within bounds
 
1097
        directSoundGuid = devices[device].guid;
 
1098
        result = DirectSoundCreate(directSoundGuid, &directSoundObject, NULL);
 
1099
        if (result == DS_OK) goto have_good_output_device;
 
1100
      }
 
1101
    }
 
1102
 
 
1103
    printf("\n");
 
1104
    for (i=0; i<numDevices; i++) {
 
1105
      printf("Playback Device %d:  %s\n", i, devices[i].description);
 
1106
    }
 
1107
    printf("\nPlease type a sound device number from above: ");
 
1108
    char choice[16];
 
1109
    fgets(choice, 16, stdin);
 
1110
    devNum = atoi(choice);
 
1111
    if (devNum >=0 && devNum < numDevices) {
 
1112
      directSoundGuid = devices[devNum].guid;
 
1113
      if (FAILED(result = DirectSoundCreate(directSoundGuid, &directSoundObject, NULL))) {
 
1114
        sprintf(errormsg,"RtAudio: Unable to create direct sound object: %s\n", getErrorMessage(result));
 
1115
        throw StkError(errormsg,StkError::SOUNDCARD_CAPS);
 
1116
      }
 
1117
    } else {
 
1118
      throw StkError("RtAudio: Invalid device number specified!",StkError::SOUNDCARD_NOT_FOUND);
 
1119
    }
 
1120
 
 
1121
    have_good_output_device:
 
1122
 
 
1123
                // Set cooperative level
 
1124
                if (FAILED(result = directSoundObject->SetCooperativeLevel(hWnd, DSSCL_PRIORITY))) {
 
1125
                        sprintf(errormsg,"RtAudio: Unable to set cooperative level: %s\n", getErrorMessage(result));
 
1126
                        throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1127
                }
 
1128
 
 
1129
    // Get DS device capabilites
 
1130
    DSCAPS directSoundCapabilities;
 
1131
    ZeroMemory(&directSoundCapabilities, sizeof(DSCAPS));
 
1132
    directSoundCapabilities.dwSize = sizeof(DSCAPS);
 
1133
    if (FAILED(result = directSoundObject->GetCaps(&directSoundCapabilities))) {
 
1134
                        sprintf(errormsg,"RtAudio: Unable to get DS capabilities: %s\n", getErrorMessage(result));
 
1135
      throw StkError(errormsg,StkError::SOUNDCARD_CAPS);
 
1136
    }
 
1137
 
 
1138
                /* Even though we will write to the secondary buffer, we need
 
1139
       to access the primary buffer to set the correct output format.
 
1140
       The default is 8-bit, 22 kHz!
 
1141
    */
 
1142
                LPDIRECTSOUNDBUFFER directSoundPrimaryBuffer;
 
1143
                // Setup the DS primary buffer description.
 
1144
                ZeroMemory(&directSoundBufferDescription, sizeof(DSBUFFERDESC));
 
1145
                directSoundBufferDescription.dwSize = sizeof(DSBUFFERDESC);
 
1146
                directSoundBufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
 
1147
                // Obtain the primary buffer
 
1148
                if (FAILED(result = directSoundObject->CreateSoundBuffer(&directSoundBufferDescription,
 
1149
                                                             &directSoundPrimaryBuffer, NULL))) {
 
1150
                        sprintf(errormsg,"RtAudio: Unable to access DS primary buffer: %s\n", getErrorMessage(result));
 
1151
                        throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1152
                }
 
1153
 
 
1154
    // Set the primary DS buffer sound format.
 
1155
                if (FAILED(result = directSoundPrimaryBuffer->SetFormat(&waveFormat))) {
 
1156
                        sprintf(errormsg,"RtAudio: Unable to set DS primary buffer format: %s\n", getErrorMessage(result));
 
1157
                        throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1158
                }
 
1159
 
 
1160
                // Setup the secondary DS buffer description. Try to do it in hardware
 
1161
                directSoundBufferSize = channels * RT_BUFFER_SIZE * NUM_FRAGMENTS * sizeof(INT16);
 
1162
                ZeroMemory(&directSoundBufferDescription, sizeof(DSBUFFERDESC));
 
1163
                directSoundBufferDescription.dwSize = sizeof(DSBUFFERDESC);
 
1164
                directSoundBufferDescription.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCHARDWARE;
 
1165
                directSoundBufferDescription.dwBufferBytes = directSoundBufferSize;
 
1166
                directSoundBufferDescription.lpwfxFormat = &waveFormat;
 
1167
 
 
1168
                // Try to create the secondary DS buffer. If that doesn't work, try for
 
1169
    // software - if that doesn't work, throw an exception.
 
1170
                if (FAILED(result = directSoundObject->CreateSoundBuffer(&directSoundBufferDescription, &directSoundBuffer, NULL))) {
 
1171
                        directSoundBufferDescription.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE;
 
1172
                        if (FAILED(result = directSoundObject->CreateSoundBuffer(&directSoundBufferDescription, &directSoundBuffer, NULL))) {
 
1173
                                sprintf(errormsg,"RtAudio: Unable to create secondary DS buffer: %s\n", getErrorMessage(result));
 
1174
                                throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1175
                        }
 
1176
                }
 
1177
 
 
1178
                // Get the buffer size
 
1179
                DSBCAPS dsbcaps;
 
1180
                dsbcaps.dwSize = sizeof(DSBCAPS);
 
1181
                directSoundBuffer->GetCaps(&dsbcaps);
 
1182
                directSoundBufferSize = dsbcaps.dwBufferBytes;
 
1183
 
 
1184
    // Lock the DS buffer
 
1185
                if (FAILED(result = directSoundBuffer->Lock(0, directSoundBufferSize, (LPLPVOID) &audioPtr, &dataLen, NULL, NULL, 0))) {
 
1186
                        sprintf(errormsg,"RtAudio: Unable to lock DS buffer: %s\n", getErrorMessage(result));
 
1187
                        throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1188
                }
 
1189
 
 
1190
    // Zero the DS buffer
 
1191
    ZeroMemory(audioPtr, dataLen);
 
1192
 
 
1193
    // Unlock the DS buffer
 
1194
                if (FAILED(result = directSoundBuffer->Unlock(audioPtr, dataLen, NULL, 0))) {
 
1195
                        sprintf(errormsg,"RtAudio: Unable to unlock DS buffer: %s\n", getErrorMessage(result));
 
1196
                        throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1197
                }
 
1198
        } // end of play initialization
 
1199
 
 
1200
 
 
1201
        // RECORDING INITIALIZATION
 
1202
  if ( (!strcmp(mode,"record")) || (!strcmp(mode,"duplex")) ) {
 
1203
 
 
1204
    DSCBUFFERDESC directSoundCaptureDescription;
 
1205
 
 
1206
    if (device == -1) {
 
1207
      // use default device
 
1208
      if (FAILED(result = DirectSoundCaptureCreate(NULL, &directSoundCaptureObject, NULL))) {
 
1209
        sprintf(errormsg,"RtAudio: Unable to create default direct sound capture object: %s\n", getErrorMessage(result));
 
1210
        throw StkError(errormsg,StkError::SOUNDCARD_NOT_FOUND);
 
1211
      }
 
1212
      goto have_good_input_device;
 
1213
    } else {
 
1214
      // Enumerate through the devices, trying the user specified device first.
 
1215
      // If that fails, prompt for action.
 
1216
      if (FAILED(result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)SoundDeviceEnumCallback, this))) {
 
1217
        sprintf(errormsg,"RtAudio: Unable to enumerate through sound capture devices: %s\n", getErrorMessage(result));
 
1218
        throw StkError(errormsg,StkError::SOUNDCARD_NOT_FOUND);
 
1219
      }
 
1220
      if (device >=0 && device < numDevices) {
 
1221
        // try user specified device if within bounds
 
1222
        directSoundGuid = devices[device].guid;
 
1223
        result = DirectSoundCaptureCreate(directSoundGuid, &directSoundCaptureObject, NULL);
 
1224
        if (result == DS_OK) goto have_good_input_device;
 
1225
      }
 
1226
    }
 
1227
 
 
1228
    printf("\n");
 
1229
    for (i=0; i<numDevices; i++) {
 
1230
      printf("Capture Device %d:  %s\n", i, devices[i].description);
 
1231
    }
 
1232
    printf("\nPlease type a sound device number from above: ");
 
1233
    char choice[16];
 
1234
    fgets(choice, 16, stdin);
 
1235
    devNum = atoi(choice);
 
1236
    if (devNum >=0 && devNum < numDevices) {
 
1237
      directSoundGuid = devices[devNum].guid;
 
1238
      if (FAILED(result = DirectSoundCaptureCreate(directSoundGuid, &directSoundCaptureObject, NULL))) {
 
1239
        sprintf(errormsg,"RtAudio: Unable to create direct sound capture object: %s\n",getErrorMessage(result));
 
1240
        throw StkError(errormsg,StkError::SOUNDCARD_CAPS);
 
1241
      }
 
1242
    } else {
 
1243
      throw StkError("RtAudio: Invalid device number specified!\n", StkError::SOUNDCARD_NOT_FOUND);
 
1244
    }
 
1245
 
 
1246
    have_good_input_device:
 
1247
 
 
1248
    // Setup the DS Capture buffer description
 
1249
    ZeroMemory(&directSoundCaptureDescription, sizeof(DSCBUFFERDESC));
 
1250
    directSoundCaptureDescription.dwSize = sizeof(DSCBUFFERDESC);
 
1251
    directSoundCaptureDescription.dwFlags = 0;
 
1252
    directSoundCaptureBufferSize = (DWORD)(waveFormat.nAvgBytesPerSec * DS_CAPTURE_BUFFER_SIZE);
 
1253
    directSoundCaptureDescription.dwBufferBytes = directSoundCaptureBufferSize;
 
1254
    directSoundCaptureDescription.dwReserved = 0;
 
1255
    directSoundCaptureDescription.lpwfxFormat = &waveFormat;
 
1256
 
 
1257
    // Create the DS Capture buffer
 
1258
    if (FAILED(result = directSoundCaptureObject->CreateCaptureBuffer(&directSoundCaptureDescription, &directSoundCaptureBuffer, NULL))) {
 
1259
                        sprintf(errormsg,"RtAudio: Unable to create DS capture buffer: %s\n", getErrorMessage(result));
 
1260
                        throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1261
    }
 
1262
 
 
1263
    // Lock the DS Capture buffer
 
1264
    if (FAILED(result = directSoundCaptureBuffer->Lock(0, directSoundCaptureBufferSize, (LPLPVOID) &audioPtr, &dataLen, NULL, NULL, 0))) {
 
1265
                        sprintf(errormsg,"RtAudio: Unable to lock DS capture buffer: %s\n", getErrorMessage(result));
 
1266
      throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1267
    }
 
1268
 
 
1269
    // Zero the DS Capture buffer
 
1270
    ZeroMemory(audioPtr, dataLen);
 
1271
 
 
1272
    // Size and allocate our inputBuffer
 
1273
    inputBufferSize = (long) (sampleRate * channels * 2 * sizeof(INT16));
 
1274
    inputBuffer = (BYTE *) new BYTE[inputBufferSize];
 
1275
 
 
1276
    //Zero our internal input buffer
 
1277
    ZeroMemory(inputBuffer, inputBufferSize);
 
1278
 
 
1279
    // Unlock the DS Capture buffer
 
1280
    if (FAILED(result = directSoundCaptureBuffer->Unlock(audioPtr, dataLen, NULL, 0))) {
 
1281
                        sprintf(errormsg,"RtAudio: Unable to unlock DS capture buffer: %s\n", getErrorMessage(result));
 
1282
                        throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1283
    }
 
1284
 
 
1285
    // Start the DS Capture buffer input
 
1286
    if (FAILED(result = directSoundCaptureBuffer->Start(DSCBSTART_LOOPING))) {
 
1287
                        sprintf(errormsg,"RtAudio: Unable to start DS capture buffer looping: %s\n", getErrorMessage(result));
 
1288
                        throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1289
    }
 
1290
 
 
1291
    //CALLBACK STUFF HERE
 
1292
    if ((timerID = timeSetEvent (TIMER_PERIOD, TIMER_RESOLUTION, PeriodicCallbackFn, (DWORD) this, TIME_PERIODIC)) == NULL) {
 
1293
                        throw StkError("RtAudio: Couldn't start periodic timer callback function for audio input.\n",StkError::PROCESS_THREAD);
 
1294
    }
 
1295
                recording = true;
 
1296
 
 
1297
  } // end of record initialization
 
1298
 
 
1299
  if ( (!strcmp(mode,"play")) || (!strcmp(mode,"duplex")) ) {
 
1300
    // Start the DS buffer playback
 
1301
    if (FAILED(result = directSoundBuffer->Play(0, 0, DSBPLAY_LOOPING ))) {
 
1302
                        sprintf(errormsg,"RtAudio: Unable to start DS buffer looping: %s\n", getErrorMessage(result));
 
1303
                        throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1304
    }
 
1305
                playing = true;
 
1306
  }
 
1307
}
 
1308
 
 
1309
RtAudio :: ~RtAudio()
 
1310
{
 
1311
        // Cleanup the DS buffers
 
1312
  if (directSoundBuffer)        {
 
1313
          directSoundBuffer->Stop();
 
1314
          directSoundBuffer->Release();
 
1315
          directSoundBuffer = NULL;
 
1316
        }
 
1317
  if (directSoundCaptureBuffer) {
 
1318
          directSoundCaptureBuffer->Stop();
 
1319
          directSoundCaptureBuffer->Release();
 
1320
          directSoundCaptureBuffer = NULL;
 
1321
        }
 
1322
 
 
1323
  // Cleanup the DS objects
 
1324
  if (directSoundObject) {
 
1325
          directSoundObject->Release();
 
1326
          directSoundObject = NULL;
 
1327
        }
 
1328
  if (directSoundCaptureObject) {
 
1329
          directSoundCaptureObject->Release();
 
1330
          directSoundCaptureObject = NULL;
 
1331
        }
 
1332
        if (timerID) {
 
1333
                timeKillEvent (timerID);
 
1334
  }
 
1335
  if (inputBuffer) {
 
1336
    delete [] inputBuffer;
 
1337
    inputBuffer = 0;
 
1338
  }
 
1339
}
 
1340
 
 
1341
int RtAudio :: playBuffer(short *buf, int bufsize)
 
1342
{
 
1343
        if (!playing) startPlay();
 
1344
 
 
1345
        HRESULT result;
 
1346
  LPVOID buffer1 = NULL;
 
1347
  LPVOID buffer2 = NULL;
 
1348
  DWORD bufferSize1 = 0;
 
1349
  DWORD bufferSize2 = 0;
 
1350
  DWORD playPos, safePos;
 
1351
        int samplesToWrite;
 
1352
 
 
1353
        do {
 
1354
    // The user may send us a buffer of data that is too large
 
1355
    // for us to stream in at once, so set up a loop and check
 
1356
    // the size of their buffer.
 
1357
                samplesToWrite = bufsize;
 
1358
                if (samplesToWrite > RT_BUFFER_SIZE  ) {
 
1359
                        samplesToWrite = RT_BUFFER_SIZE ;
 
1360
                }
 
1361
 
 
1362
                // Find out where the read and "safe write" pointers are.
 
1363
                if (FAILED(result = directSoundBuffer->GetCurrentPosition(&playPos, &safePos))) {
 
1364
                        sprintf(errormsg,"RtAudio: Unable to get current DS position: %s\n", getErrorMessage(result));
 
1365
                        throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1366
                }
 
1367
 
 
1368
                if( playPos < nextWritePos ) playPos += directSoundBufferSize; // unwrap offset
 
1369
                DWORD endWrite = nextWritePos + samplesToWrite * sizeof(INT16);
 
1370
 
 
1371
    //printf("bufsize = %d ", bufsize);
 
1372
                // Check whether the entire write region is behind the play pointer.
 
1373
                while ( playPos < endWrite ) {
 
1374
                        // If we are here, then we must wait until the play pointer
 
1375
                        // gets beyond the write region.  To do so, we can use the
 
1376
      // Sleep() function (which has questionable accuracy).
 
1377
                        // Sleep until safePos catches up. Calculate number of
 
1378
                        // milliseconds to wait as:
 
1379
                        //   time = distance * (milliseconds/second) * fudgefactor /
 
1380
                        //          ((bytes/sample) * (samples/second))
 
1381
                        // A "fudgefactor" less than 1 is used because it was found
 
1382
                        // that sleeping too long was MUCH worse than sleeping for
 
1383
                        // several shorter periods.
 
1384
                        DWORD millis = (DWORD) (((endWrite - playPos) * 900.0) / ( sizeof(INT16) * sampleRate));
 
1385
      //printf(". ");
 
1386
                        Sleep( millis );
 
1387
 
 
1388
                        // Wake up, find out where we are now
 
1389
                        result = directSoundBuffer->GetCurrentPosition( &playPos, &safePos );
 
1390
                        if( result != DS_OK ) return -1;
 
1391
                        if( playPos < nextWritePos ) playPos += directSoundBufferSize; // unwrap offset
 
1392
                }
 
1393
 
 
1394
          // Lock free space in the buffer
 
1395
                result = directSoundBuffer->Lock (nextWritePos, samplesToWrite * sizeof(INT16), &buffer1, &bufferSize1, &buffer2, &bufferSize2, 0);
 
1396
                if (SUCCEEDED(result)) {
 
1397
                        // Copy the buffer into the DS
 
1398
                        CopyMemory(buffer1, buf, bufferSize1);
 
1399
                        if(NULL != buffer2) CopyMemory(buffer2, buf+(bufferSize1/sizeof(INT16)), bufferSize2);
 
1400
 
 
1401
                        // Update our buffer offset and unlock sound buffer
 
1402
                        if (FAILED(directSoundBuffer->Unlock (buffer1, bufferSize1, buffer2, bufferSize2))) {
 
1403
                                throw StkError("RtAudio: Unable to unlock DS buffer\n",StkError::SOUNDCARD_CONTROL);
 
1404
                        }
 
1405
                        nextWritePos = (nextWritePos + bufferSize1 + bufferSize2) % directSoundBufferSize;
 
1406
                }       else {
 
1407
                        sprintf(errormsg,"RtAudio: Unable to lock DS buffer: %s\n", getErrorMessage(result));
 
1408
                        throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1409
                }
 
1410
 
 
1411
                //update how many samples still need to be written - possibly no more
 
1412
                bufsize -= samplesToWrite;
 
1413
                buf += samplesToWrite;
 
1414
        } while (bufsize > 0);
 
1415
 
 
1416
        return 0;
 
1417
}
 
1418
 
 
1419
int RtAudio :: recordBuffer(short *buf, int bufsize)
 
1420
{
 
1421
        if (!recording) startRecord();
 
1422
 
 
1423
        if (internalError) {
 
1424
                throw StkError("RtAudio: Read failure in windoze capture thread.\n",
 
1425
                   StkError::SOUNDCARD_CONTROL);
 
1426
        }
 
1427
  else {
 
1428
                UINT length; // length of data to copy
 
1429
                while (bufsize > 0) {
 
1430
                        if (nextRecordWrite > nextRecordRead) {
 
1431
                                length = nextRecordWrite - nextRecordRead;
 
1432
                                if (length > bufsize * sizeof(INT16)) {
 
1433
          // there may be more data available than our user wants
 
1434
                                        length = bufsize * sizeof(INT16);
 
1435
                                }
 
1436
                                CopyMemory(buf, inputBuffer+nextRecordRead, length);
 
1437
                                nextRecordRead += length;
 
1438
                                bufsize -= length / sizeof(INT16);
 
1439
                                buf += length / sizeof(INT16);
 
1440
                        }
 
1441
      else if (nextRecordWrite == nextRecordRead) {
 
1442
        // no data is currently available to return to the user -
 
1443
                                // so we sleep for the time necesary to produce the desired
 
1444
        // number of samples
 
1445
                                Sleep((DWORD) (bufsize / sampleRate * 1000));
 
1446
                        }
 
1447
      else {
 
1448
        // this means that our write pointer must have wrapped around -
 
1449
        // in this case we return the last part ofthe buffer and save the
 
1450
        // readable beginning of the buffer to be copied next time around
 
1451
        // the loop, rather than do two copies here - just simpler code
 
1452
                                length = inputBufferSize - nextRecordRead;
 
1453
                                if (length > bufsize * sizeof(INT16)) {
 
1454
          // there may be more data available than our user wants
 
1455
                                        length = bufsize * sizeof(INT16);
 
1456
                                        CopyMemory(buf, inputBuffer+nextRecordRead, length);
 
1457
          // dont fully wrap around - we didn't use all data
 
1458
                                        nextRecordRead += length;
 
1459
                                }
 
1460
        else {
 
1461
                                        CopyMemory(buf, inputBuffer+nextRecordRead, length);
 
1462
                                        nextRecordRead = 0; // we wrap around
 
1463
                                }
 
1464
                                bufsize -= length / sizeof(INT16);
 
1465
                                buf += length / sizeof(INT16);
 
1466
                        }
 
1467
                }
 
1468
        }
 
1469
        return 0;
 
1470
}
 
1471
 
 
1472
/**** Windoze specific overload of recordBuffer ****/
 
1473
int RtAudio :: recordBuffer(INT16** buf)
 
1474
{
 
1475
        if (!recording) startRecord();
 
1476
 
 
1477
  if (internalError) {
 
1478
                throw StkError("RtAudio: Read failure in windoze capture thread.\n",StkError::SOUNDCARD_CONTROL);
 
1479
 
 
1480
        } else if (nextRecordRead == nextRecordWrite) {
 
1481
    // no new data
 
1482
                return 0;
 
1483
 
 
1484
        } else {
 
1485
                BYTE* temp = inputBuffer + nextRecordRead;
 
1486
                *buf = (INT16*) temp;
 
1487
                UINT length;
 
1488
 
 
1489
                if (nextRecordWrite > nextRecordRead) {
 
1490
                        length = nextRecordWrite - nextRecordRead;
 
1491
                        nextRecordRead = nextRecordWrite;
 
1492
                } else {
 
1493
      /* This means that our write pointer must have wrapped around -
 
1494
         in this case we return the last part ofthe buffer and save the
 
1495
         readable begining of the buffer to be returned next call to
 
1496
         recordBuffer, rather than have some complex two pointer wrap
 
1497
         around scheme.
 
1498
      */
 
1499
                        length = inputBufferSize - nextRecordRead;
 
1500
                        nextRecordRead = 0;
 
1501
                }
 
1502
 
 
1503
    // We divide by two so that we return number of samples, not bytes
 
1504
                return (length/2);
 
1505
        }
 
1506
}
 
1507
 
 
1508
/**** Windows specific start and stop functions ****/
 
1509
void RtAudio :: stopPlay()
 
1510
{
 
1511
        if (!playing) return;
 
1512
 
 
1513
        HRESULT result;
 
1514
        BYTE* audioPtr;
 
1515
  DWORD dataLen;
 
1516
 
 
1517
        //stop the buffer
 
1518
  if (FAILED(result = directSoundBuffer->Stop())) {
 
1519
                sprintf(errormsg,"RtAudio: Unable to stop DS buffer looping: %s\n", getErrorMessage(result));
 
1520
                throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1521
  }
 
1522
 
 
1523
        // Lock the DS buffer and clear it so if we start to play again, we won't have old data playing
 
1524
        if (FAILED(result = directSoundBuffer->Lock(0, directSoundBufferSize, (LPLPVOID) &audioPtr, &dataLen, NULL, NULL, 0))) {
 
1525
                sprintf(errormsg,"RtAudio: Unable to lock DS buffer: %s\n", getErrorMessage(result));
 
1526
                throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1527
        }
 
1528
 
 
1529
        // Zero the DS buffer
 
1530
        ZeroMemory(audioPtr, dataLen);
 
1531
 
 
1532
        // Unlock the DS buffer
 
1533
        if (FAILED(result = directSoundBuffer->Unlock(audioPtr, dataLen, NULL, 0))) {
 
1534
                sprintf(errormsg,"RtAudio: Unable to unlock DS buffer: %s\n", getErrorMessage(result));
 
1535
                throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1536
        }
 
1537
        //if we start playing again, we must begin at beginning of buffer
 
1538
        nextWritePos = 0;
 
1539
 
 
1540
        playing = false;
 
1541
}
 
1542
 
 
1543
void RtAudio :: startPlay()
 
1544
{
 
1545
        if (playing) return;
 
1546
 
 
1547
        HRESULT result;
 
1548
  if (FAILED(result = directSoundBuffer->Play(0, 0, DSBPLAY_LOOPING ))) {
 
1549
                sprintf(errormsg,"RtAudio: Unable to restart DS buffer: %s\n", getErrorMessage(result));
 
1550
                throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1551
  }
 
1552
        playing = true;
 
1553
}
 
1554
 
 
1555
void RtAudio :: stopRecord()
 
1556
{
 
1557
        if (!recording) return;
 
1558
 
 
1559
        HRESULT result;
 
1560
        BYTE* audioPtr;
 
1561
  DWORD dataLen;
 
1562
 
 
1563
        // stop the buffer
 
1564
  if (FAILED(result = directSoundCaptureBuffer->Stop())) {
 
1565
                sprintf(errormsg,"RtAudio: Unable to stop DS capture buffer looping: %s\n", getErrorMessage(result));
 
1566
                throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1567
  }
 
1568
 
 
1569
        // Lock the DS buffer and clear it so if we start to play again, we won't have old data playing
 
1570
        if (FAILED(result = directSoundCaptureBuffer->Lock(0, directSoundCaptureBufferSize, (LPLPVOID) &audioPtr, &dataLen, NULL, NULL, 0))) {
 
1571
                sprintf(errormsg,"RtAudio: Unable to lock DS capture buffer: %s\n", getErrorMessage(result));
 
1572
                throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1573
        }
 
1574
 
 
1575
        // Zero the DS buffer
 
1576
        ZeroMemory(audioPtr, dataLen);
 
1577
 
 
1578
        // Unlock the DS buffer
 
1579
        if (FAILED(result = directSoundCaptureBuffer->Unlock(audioPtr, dataLen, NULL, 0))) {
 
1580
                sprintf(errormsg,"RtAudio: Unable to unlock DS capture buffer: %s\n", getErrorMessage(result));
 
1581
                throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1582
        }
 
1583
        // If we start playing again, we must begin at beginning of buffer
 
1584
        recording = false;
 
1585
}
 
1586
 
 
1587
void RtAudio :: startRecord()
 
1588
{
 
1589
        if (recording) return;
 
1590
 
 
1591
        HRESULT result;
 
1592
        if (FAILED(result = directSoundCaptureBuffer->Start(DSCBSTART_LOOPING))) {
 
1593
                sprintf(errormsg,"RtAudio: Unable to restart DS capture buffer looping: %s\n", getErrorMessage(result));
 
1594
                throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
 
1595
  }
 
1596
 
 
1597
        recording = true;
 
1598
}
 
1599
 
 
1600
 
 
1601
/**** Internal Utilities ****/
 
1602
 
 
1603
void RtAudio::getInputSamples()
 
1604
{
 
1605
  /* This function is called periodically to get samples from
 
1606
     the direct sound capture buffer and transfer them into our
 
1607
     own buffer.
 
1608
  */
 
1609
  HRESULT result;
 
1610
  LPVOID buffer1 = NULL;
 
1611
  LPVOID buffer2 = NULL;
 
1612
  DWORD size1 = 0;
 
1613
  DWORD size2 = 0;
 
1614
  DWORD capturePos, safePos;
 
1615
  static UINT nextDirectSoundReadPosition = 0;
 
1616
  int numBytesToLock;
 
1617
 
 
1618
  // Find out where the write and "safe read" pointers are.
 
1619
  if (FAILED(result = directSoundCaptureBuffer->GetCurrentPosition(&capturePos, &safePos))) {
 
1620
                internalError = true;
 
1621
                return;
 
1622
        }
 
1623
 
 
1624
        if (!recording) {
 
1625
    // The user doesn't want us to continue recording, so we must
 
1626
    // skip over info until they call startRecord().
 
1627
          if (safePos < nextDirectSoundReadPosition)
 
1628
      safePos += directSoundCaptureBufferSize; //unwrap offset
 
1629
                numBytesToLock = safePos - nextDirectSoundReadPosition; //or in this case, num bytes to skip...
 
1630
                nextDirectSoundReadPosition = (nextDirectSoundReadPosition + numBytesToLock) % directSoundCaptureBufferSize;
 
1631
                nextRecordWrite = nextRecordRead;
 
1632
                return;
 
1633
        }
 
1634
 
 
1635
        if (safePos == nextDirectSoundReadPosition) {
 
1636
    // If this case, no input samples are yet ready so we must wait until
 
1637
    // some are. If this happens alot, we should increase the period of
 
1638
    // our callback function.
 
1639
                return;
 
1640
        }
 
1641
 
 
1642
  if ( safePos < nextDirectSoundReadPosition )
 
1643
    safePos += directSoundCaptureBufferSize; //unwrap offset
 
1644
        numBytesToLock = safePos - nextDirectSoundReadPosition;
 
1645
        UINT availableSpace = inputBufferSize - nextRecordWrite;
 
1646
 
 
1647
        /* This code is rather long and complex, b/c it must handle the data
 
1648
     transfer from the direct sound capture buffer to our own internal
 
1649
     buffer, and both, one, or neither buffer may wrap around.
 
1650
  */
 
1651
  if (SUCCEEDED(result = directSoundCaptureBuffer->Lock(nextDirectSoundReadPosition, numBytesToLock, &buffer1, &size1, &buffer2, &size2, 0))) {
 
1652
                if (availableSpace > size1) {
 
1653
      // we have more than enough space in our internal buffer to hold the data
 
1654
        CopyMemory((inputBuffer + nextRecordWrite), buffer1, size1);
 
1655
        availableSpace -= size1;
 
1656
        nextRecordWrite += size1;
 
1657
 
 
1658
                } else if (availableSpace == size1) {
 
1659
      // we have exactly enough space
 
1660
                        CopyMemory((inputBuffer + nextRecordWrite), buffer1, size1);
 
1661
                        availableSpace = inputBufferSize;
 
1662
        nextRecordWrite = 0;
 
1663
 
 
1664
                } else {
 
1665
      // we need to wrap around to the begining of the buffer, ie availableSpace < size1
 
1666
                        CopyMemory((inputBuffer + nextRecordWrite), buffer1, availableSpace);
 
1667
                        size1 -= availableSpace;
 
1668
                        CopyMemory(inputBuffer,(BYTE*)buffer1+availableSpace, size1);
 
1669
                        availableSpace = inputBufferSize - size1;
 
1670
                        nextRecordWrite = size1;
 
1671
                }
 
1672
 
 
1673
    if (buffer2 != NULL) {
 
1674
      // this means that the DS buffer wrapped around, and we need to
 
1675
      // deal with second data portion
 
1676
                        if (availableSpace > size2) {
 
1677
        // we have more than enough space in our internal buffer to hold data
 
1678
                                CopyMemory((inputBuffer + nextRecordWrite), buffer2, size2);
 
1679
                                nextRecordWrite += size2;
 
1680
 
 
1681
                        } else if (availableSpace == size2) {
 
1682
        // we have exactly enough space
 
1683
                                CopyMemory((inputBuffer + nextRecordWrite), buffer2, size2);
 
1684
                                nextRecordWrite = 0;
 
1685
 
 
1686
                        } else {
 
1687
        // we need to wrap around to the begining of the buffer, ie availableSpace < size2
 
1688
                                CopyMemory((inputBuffer + nextRecordWrite), buffer2, availableSpace);
 
1689
                                size2 -= availableSpace;
 
1690
                                CopyMemory(inputBuffer,(BYTE*)buffer2+availableSpace, size2);
 
1691
                                nextRecordWrite = size2;
 
1692
                        }
 
1693
                }
 
1694
 
 
1695
                // Update our buffer offset and unlock sound buffer
 
1696
                directSoundCaptureBuffer->Unlock (buffer1, size1, buffer2, size2);
 
1697
                nextDirectSoundReadPosition = (nextDirectSoundReadPosition + size1 + size2) % directSoundCaptureBufferSize;
 
1698
        } else {
 
1699
    // we were unable to lock the buffer
 
1700
                internalError = true;
 
1701
                return;
 
1702
        }
 
1703
}
 
1704
 
 
1705
//This static callback function is used by the windows multimedia timer for recording
 
1706
void CALLBACK RtAudio::PeriodicCallbackFn(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
 
1707
{
 
1708
  // dwUser contains ptr to our RtAudio object
 
1709
  RtAudio * soundIO = (RtAudio *) dwUser;
 
1710
  (soundIO->getInputSamples)();
 
1711
}
 
1712
 
 
1713
//Windows forces you to use a callback function to enumerate through sound devices...
 
1714
bool CALLBACK RtAudio::SoundDeviceEnumCallback(LPGUID lpguid,LPCSTR lpcstrDescription,LPCSTR lpcstrModule, LPVOID lpContext)
 
1715
//bool CALLBACK RtAudio::SoundDeviceEnumCallback(LPGUID guid, LPCSTR desc, LPCSTR module, LPVOID context)
 
1716
{
 
1717
        ((RtAudio *) lpContext)->addDevice(lpguid,(char*) lpcstrDescription,(char*) lpcstrModule);
 
1718
 
 
1719
  /*
 
1720
        if (numDevices < MAX_DEVICES) {
 
1721
                if (guid != NULL) {
 
1722
                        // make our own copy of the device ID, although probably not necesary
 
1723
                        devices[numDevices].guid = (LPGUID) LocalAlloc(LPTR, sizeof(GUID));
 
1724
                        memcpy(devices[numDevices].guid, guid, sizeof(GUID));
 
1725
                        devices[numDevices].moduleName = module;
 
1726
                        devices[numDevices].description = (char *)desc;
 
1727
                }
 
1728
    else {
 
1729
                        devices[numDevices].guid = NULL;
 
1730
                        devices[numDevices].description = "Default Device";
 
1731
                        devices[numDevices].moduleName = "";
 
1732
                }
 
1733
                numDevices++;
 
1734
        }
 
1735
  */
 
1736
        return true;
 
1737
}
 
1738
 
 
1739
void RtAudio::addDevice(LPGUID guid, char* description, char* moduleName)
 
1740
{
 
1741
        if (numDevices < MAX_DEVICES) {
 
1742
                if (guid != NULL) {
 
1743
                        // make our own copy of the device ID, although probably not necesary
 
1744
                        devices[numDevices].guid = (LPGUID) LocalAlloc(LPTR, sizeof(GUID));
 
1745
                        memcpy(devices[numDevices].guid,guid,sizeof(GUID));
 
1746
                        devices[numDevices].moduleName = moduleName;
 
1747
                        devices[numDevices].description = description;
 
1748
                }
 
1749
    else {
 
1750
                        devices[numDevices].guid = NULL;
 
1751
                        devices[numDevices].description = "Default Device";
 
1752
                        devices[numDevices].moduleName = "";
 
1753
                }
 
1754
                numDevices++;
 
1755
        }
 
1756
}
 
1757
 
 
1758
char* RtAudio::getErrorMessage(int code) {
 
1759
        switch (code) {
 
1760
  case DSERR_ALLOCATED:
 
1761
    return "Direct Sound already allocated";
 
1762
 
 
1763
  case DSERR_CONTROLUNAVAIL:
 
1764
    return "Direct Sound control unavailable";
 
1765
 
 
1766
  case DSERR_INVALIDPARAM:
 
1767
    return "Direct Sound invalid parameter";
 
1768
 
 
1769
  case DSERR_INVALIDCALL:
 
1770
    return "Direct Sound invalid call";
 
1771
 
 
1772
  case DSERR_GENERIC:
 
1773
    return "Direct Sound generic error";
 
1774
 
 
1775
  case DSERR_PRIOLEVELNEEDED:
 
1776
    return "Direct Sound Priority level needed";
 
1777
 
 
1778
  case DSERR_OUTOFMEMORY:
 
1779
    return "Direct Sound out of memory";
 
1780
 
 
1781
  case DSERR_BADFORMAT:
 
1782
    return "Direct Sound bad format";
 
1783
 
 
1784
  case DSERR_UNSUPPORTED:
 
1785
    return "Direct Sound unsupported error";
 
1786
 
 
1787
  case DSERR_NODRIVER:
 
1788
    return "Direct Sound no driver error";
 
1789
 
 
1790
  case DSERR_ALREADYINITIALIZED:
 
1791
    return "Direct Sound already initialized";
 
1792
 
 
1793
  case DSERR_NOAGGREGATION:
 
1794
    return "Direct Sound no aggregation";
 
1795
 
 
1796
  case DSERR_BUFFERLOST:
 
1797
    return "Direct Sound buffer lost";
 
1798
 
 
1799
  case DSERR_OTHERAPPHASPRIO:
 
1800
    return "Direct Sound other app has priority";
 
1801
 
 
1802
  case DSERR_UNINITIALIZED:
 
1803
    return "Direct Sound uninitialized";
 
1804
 
 
1805
  default:
 
1806
    return "Direct Sound unknown error";
 
1807
        }
 
1808
}
 
1809
 
 
1810
#endif