1
/******************************************/
4
Realtime Sound I/O Object for STK
5
by Gary P. Scavone, 1998-2000.
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
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
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.
25
/******************************************/
29
#if (defined(__STK_REALTIME_) && defined(__OS_IRIX_))
31
#define NUM_FRAGMENTS 4
33
RtAudio :: RtAudio(int channels, MY_FLOAT srate, const char *mode, int device)
35
ALconfig audio_port_config;
40
// initialize resources
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);
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);
58
/* Configure 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);
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);
74
if ( !strcmp(mode,"play") || !strcmp(mode,"duplex") ) { // playback only
76
/* Open the output audio port */
77
audio_port_out = alOpenPort("STK output port", "w", audio_port_config);
79
sprintf(msg,"RtAudio: SGI error ... cannot initialize output audio port: %s\n",
80
alGetErrorString(oserror()));
81
throw StkError(msg, StkError::SOUNDCARD_CONTROL);
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);
95
/* Tell port to accept refill at buffers - 1 */
96
alSetFillPoint(audio_port_out, RT_BUFFER_SIZE * (NUM_FRAGMENTS - 1));
98
else if ( !strcmp(mode,"record") || !strcmp(mode,"duplex") ) { // record only
100
/* Open the input audio port */
101
audio_port_in = alOpenPort("STK input port", "r", audio_port_config);
103
sprintf(msg,"RtAudio: SGI error ... cannot initialize input audio port: %s\n",
104
alGetErrorString(oserror()));
105
throw StkError(msg, StkError::SOUNDCARD_CONTROL);
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);
119
/* tell port to accept refill at buffers - 1 */
120
alSetFillPoint(audio_port_in, 0);
123
alFreeConfig(audio_port_config);
124
audio_port_config = 0;
127
RtAudio :: ~RtAudio()
129
if (audio_port_out) alClosePort(audio_port_out);
132
if (audio_port_in) alClosePort(audio_port_in);
136
int RtAudio :: playBuffer(INT16 *buf, int bufsize)
138
alWriteFrames(audio_port_out, buf, bufsize/stk_chans);
142
int RtAudio :: recordBuffer(INT16 *buf, int bufsize)
144
alReadFrames(audio_port_in, buf, bufsize/stk_chans);
149
/* Linux ALSA Sound API here */
151
#elif (defined(__STK_REALTIME_) && defined(__ALSA_API_))
153
RtAudio :: RtAudio(int channels, MY_FLOAT srate, const char *mode, int device)
155
int card, dev, err, nChoices = 0;
156
int data_format, default_card;
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;
164
bool print_list = FALSE;
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);
174
sprintf(msg, "RtAudio: number of channels = %d not supported!\n", channels);
175
throw StkError(msg, StkError::FUNCTION_SYNTAX);
178
// check to make sure we have card(s) and/or ALSA drivers available
179
mask = snd_cards_mask();
181
sprintf(msg, "RtAudio: no ALSA soundcards reported available.\n");
182
throw StkError(msg, StkError::SOUNDCARD_NOT_FOUND);
190
if (!strcmp(mode, "play"))
191
direction = SND_PCM_INFO_PLAYBACK;
192
else if (!strcmp(mode, "record"))
193
direction = SND_PCM_INFO_CAPTURE;
195
direction = SND_PCM_INFO_DUPLEX;
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.
207
default_card = snd_defaults_pcm_card();
209
else { // check device specified as argument
210
if (!(mask & (1<<device))) {
216
default_card = device;
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));
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);
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));
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));
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);
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
266
printf("Audio Card %d, Device %d: %s\n", card, dev, info.name);
268
snd_pcm_close(ohandle);
271
goto have_good_device;
273
} else { // this device won't work
274
snd_pcm_close(ohandle);
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);
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);
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;
303
printf("Audio Card %d, Device %d: %s\n", card, dev, info.name);
305
snd_pcm_close(ihandle);
306
if (direction == SND_PCM_INFO_DUPLEX)
307
snd_pcm_close(ohandle);
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);
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
325
if (card == default_card) card++; // skip over default card
330
if (print_list && nChoices) {
332
printf("\nType an audio card number from above: ");
333
fgets(choice, 16, stdin);
335
printf("Select a device for the same card: ");
336
fgets(choice, 16, stdin);
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);
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);
358
goto have_good_device;
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);
365
have_good_device: // the current value of card and dev are what we will use
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;
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;
379
else if (chninfo.formats & SND_PCM_FMT_S32_LE) {
380
data_format = SND_PCM_SFMT_S32_LE;
381
bytes_per_sample = 4;
384
sprintf(msg, "RtAudio: only ALSA S16_LE and S32_LE data formats at the moment!\n");
388
// global channel parameters (not direction specific)
389
memset(¶ms, 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;
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, ¶ms))!=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",
414
throw StkError(msg, StkError::SOUNDCARD_CONTROL);
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",
424
throw StkError(msg, StkError::SOUNDCARD_CONTROL);
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",
431
throw StkError(msg, StkError::SOUNDCARD_CAPS);
433
if (setup.buf.block.frag_size != ofragsize) ofragsize = setup.buf.block.frag_size;
436
// allocate and clear the output buffer
437
outbuf = (unsigned char*) new char[ofragsize];
438
memset(outbuf, 0, ofragsize);
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);
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);
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, ¶ms))!=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);
474
sprintf(msg, "RtAudio: Cannot set ALSA audio device parameters for capture: %s\n",
476
throw StkError(msg, StkError::SOUNDCARD_CONTROL);
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);
489
sprintf(msg, "RtAudio: Cannot get ALSA audio device setup info: %s\n",
491
throw StkError(msg, StkError::SOUNDCARD_CONTROL);
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);
500
sprintf(msg, "RtAudio: Soundcard doesn't seem to support requested sample rate: %.2f!\n",
502
throw StkError(msg, StkError::SOUNDCARD_CAPS);
505
if (setup.buf.block.frag_size != ifragsize) ifragsize = setup.buf.block.frag_size;
508
// allocate and clear the input buffer
509
inbuf = (unsigned char*) new char[ifragsize];
510
memset(inbuf, 0, ifragsize);
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);
520
sprintf(msg, "RtAudio: Cannot flush ALSA channel buffers for capture!\n");
521
throw StkError(msg, StkError::SOUNDCARD_CONTROL);
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);
532
sprintf(msg, "RtAudio: Cannot prepare ALSA channel for capture!\n");
533
throw StkError(msg, StkError::SOUNDCARD_CONTROL);
538
RtAudio :: ~RtAudio()
540
if ((direction == SND_PCM_INFO_PLAYBACK) || (direction == SND_PCM_INFO_DUPLEX)) {
541
snd_pcm_playback_drain(ohandle);
542
snd_pcm_close(ohandle);
544
if ((direction == SND_PCM_INFO_CAPTURE) || (direction == SND_PCM_INFO_DUPLEX)) {
545
snd_pcm_close(ihandle);
547
if (outbuf) delete [] outbuf;
548
if (inbuf) delete [] inbuf;
551
int RtAudio :: playBuffer(INT16 *buf, int bufsize)
553
// The argument bufsize is the number of audio samples (INT16s) in buf.
555
static char msg[256];
557
static int channel = 1;
558
static int counter = 0;
559
static int extra_chans = dev_ochans - stk_chans;
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);
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];
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;
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);
597
int RtAudio :: recordBuffer(INT16 *buf, int bufsize)
599
// The argument bufsize is the number of audio samples (INT16s) in buf.
601
static char msg[256];
603
static int channel = 1;
604
static int counter = 0;
605
static int extra_chans = dev_ichans - stk_chans;
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);
615
for (i=0; i<bufsize; i++) {
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);
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++];
630
if (channel > stk_chans) {
631
// just skip over the extra channels
632
counter += bytes_per_sample * extra_chans;
635
if (counter >= ifragsize) counter = 0;
643
/* Linux OSS Sound API here */
645
#elif (defined(__STK_REALTIME_) && defined(__OSS_API_))
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
655
RtAudio :: RtAudio(int channels, MY_FLOAT srate, const char *mode, int device)
657
char device_name[16];
659
int fragment_size_log;
661
int chans = channels;
664
bool print_list = FALSE;
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);
674
sprintf(msg, "RtAudio: number of channels = %d not supported!\n", channels);
675
throw StkError(msg, StkError::FUNCTION_SYNTAX);
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;
682
// /dev/dsp should be a link to the default pcm device under OSS
683
strcpy(device_name, DAC_NAME);
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...
692
// start with device specified as argument
693
sprintf(device_name, "%s%d", DAC_NAME, device);
696
for (i=0; i<=MAX_DSP_DEVS; i++) {
698
// if the default device doesn't work, try some others
699
if (i > 0) sprintf(device_name, "%s%d", DAC_NAME, i-1);
701
if (device != -1 && i == 1) {
702
// the specified device didn't work ... now print other options
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
711
fprintf(stderr,"RtAudio: OSS PCM playback device (%s) is busy and cannot be opened.\n",
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
720
fprintf(stderr,"RtAudio: OSS PCM record device (%s) is busy and cannot be opened.\n",
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
729
fprintf(stderr,"RtAudio: OSS PCM device (%s) is busy and cannot be opened for duplex operation.\n",
734
if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps)) {
736
fprintf(stderr,"RtAudio: OSS error getting device (%s) capabilities for duplex operation.\n",
740
if (!(caps & DSP_CAP_DUPLEX)) {
742
fprintf(stderr,"RtAudio: OSS reports device (%s) does not support duplex operation.\n",
746
if (ioctl(audio_fd, SNDCTL_DSP_SETDUPLEX, 0)) {
748
fprintf(stderr,"RtAudio: OSS error setting device (%s) for duplex operation.\n",
754
// Setup the number and size of the fragments
755
if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &temp)) {
757
fprintf(stderr,"RtAudio: OSS error setting fragment size for device (%s).\n",
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) {
766
fprintf(stderr,"RtAudio: OSS error setting data format for device (%s).\n",
771
// Check the see whether the device supports the requested format
772
if (format != AFMT_S16_LE) {
774
fprintf(stderr,"RtAudio: OSS error ... audio device (%s) doesn't support 16-bit signed LE format.\n",
779
// Setup the number of channels
780
if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &chans) == -1) {
782
fprintf(stderr,"RtAudio: OSS error setting %d channels on device (%s).\n",
783
channels, device_name);
787
// Check to see whether the device supports the requested number of channels
788
if (chans != channels ) {
790
fprintf(stderr,"RtAudio: OSS error ... audio device (%s) doesn't support %d channels.\n",
791
device_name, channels);
795
// Setup the sampling rate
797
if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &speed)==-1) {
799
fprintf(stderr,"RtAudio: OSS error setting sample rate = %f on device (%s).\n",
804
// Check to see whether the device supports the requested sample rate
805
if (abs(speed - (int)srate) > 100) {
807
fprintf(stderr,"RtAudio: OSS error ... audio device (%s) doesn't support sample rate of %f.\n",
814
printf("Audio Device %d: %s\n", i-1, device_name);
819
// If we got here, we found a device that meets our needs. Return to the caller.
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 ) {
830
printf("\nType an audio device number from above: ");
831
fgets(choice, 16, stdin);
834
sprintf(device_name, "%s%d", DAC_NAME, i);
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",
842
throw StkError(msg, StkError::SOUNDCARD_CONTROL);
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",
850
throw StkError(msg, StkError::SOUNDCARD_CONTROL);
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",
858
throw StkError(msg, StkError::SOUNDCARD_CONTROL);
861
if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps)) {
863
sprintf(msg, "RtAudio: OSS error getting device (%s) capabilities for duplex operation.\n",
865
throw StkError(msg, StkError::SOUNDCARD_CAPS);
867
if (!(caps & DSP_CAP_DUPLEX)) {
869
sprintf(msg, "RtAudio: OSS reports device (%s) does not support duplex operation.\n",
871
throw StkError(msg, StkError::SOUNDCARD_CAPS);
873
if (ioctl(audio_fd, SNDCTL_DSP_SETDUPLEX, 0)) {
875
sprintf(msg, "RtAudio: OSS error setting device (%s) for duplex operation.\n",
877
throw StkError(msg, StkError::SOUNDCARD_CONTROL);
881
// Setup the number and size of the fragments
882
if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &temp)) {
884
sprintf(msg, "RtAudio: OSS error setting fragment size for device (%s).\n",
886
throw StkError(msg, StkError::SOUNDCARD_CONTROL);
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) {
893
sprintf(msg,"RtAudio: OSS error setting data format for device (%s).\n",
895
throw StkError(msg, StkError::SOUNDCARD_CONTROL);
898
// Check the see whether the device supports the requested format
899
if (format != AFMT_S16_LE) {
901
sprintf(msg,"RtAudio: OSS error ... audio device (%s) doesn't support 16-bit signed LE format.\n",
903
throw StkError(msg, StkError::SOUNDCARD_CAPS);
906
// Setup the number of channels
907
if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &chans) == -1) {
909
sprintf(msg, "RtAudio: OSS error setting %d channels on device (%s).\n",
910
channels, device_name);
911
throw StkError(msg, StkError::SOUNDCARD_CONTROL);
914
// Check to see whether the device supports the requested number of channels
915
if (chans != channels ) {
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);
922
// Setup the sampling rate
924
if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &speed)==-1) {
926
sprintf(msg, "RtAudio: OSS error setting sample rate = %f on device (%s).\n",
928
throw StkError(msg, StkError::SOUNDCARD_CONTROL);
931
// Check to see whether the device supports the requested sample rate
932
if (abs(speed - (int)srate) > 100) {
934
sprintf(msg,"RtAudio: OSS error ... audio device (%s) doesn't support sample rate of %f.\n",
936
throw StkError(msg, StkError::SOUNDCARD_CAPS);
939
// If we got here, the specified device works ... return to the caller.
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);
948
RtAudio :: ~RtAudio()
950
if (audio_fd) close(audio_fd);
954
int RtAudio :: playBuffer(INT16 *buf, int bufsize)
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];
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);
970
int RtAudio :: recordBuffer(INT16 *buf, int bufsize)
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];
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);
987
#elif (defined(__STK_REALTIME_) && defined(__OS_Win_) )
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).
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
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.
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.
1025
The DirectSoundCapture API is only available with DirectX versions
1029
RtAudio :: RtAudio(int channels, MY_FLOAT srate, const char *mode, int device)
1034
HWND hWnd = GetForegroundWindow();
1035
WAVEFORMATEX waveFormat;
1036
LPGUID directSoundGuid = NULL;
1039
// Initialize the DirectSound object and buffer pointers to NULL
1040
directSoundObject = NULL;
1041
directSoundBuffer = NULL;
1042
directSoundCaptureObject = NULL;
1043
directSoundCaptureBuffer = NULL;
1046
// Some basic initialization stuff
1049
nextRecordWrite = 0;
1050
internalError = false;
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;
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);
1072
sprintf(errormsg, "RtAudio: number of channels = %d not supported!\n", channels);
1073
throw StkError(errormsg, StkError::FUNCTION_SYNTAX);
1076
// PLAYBACK INITIALIZATION
1077
if ( (!strcmp(mode,"play")) || (!strcmp(mode,"duplex")) ) {
1079
DSBUFFERDESC directSoundBufferDescription;
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);
1087
goto have_good_output_device;
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);
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;
1104
for (i=0; i<numDevices; i++) {
1105
printf("Playback Device %d: %s\n", i, devices[i].description);
1107
printf("\nPlease type a sound device number from above: ");
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);
1118
throw StkError("RtAudio: Invalid device number specified!",StkError::SOUNDCARD_NOT_FOUND);
1121
have_good_output_device:
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);
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);
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!
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);
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);
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;
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);
1178
// Get the buffer size
1180
dsbcaps.dwSize = sizeof(DSBCAPS);
1181
directSoundBuffer->GetCaps(&dsbcaps);
1182
directSoundBufferSize = dsbcaps.dwBufferBytes;
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);
1190
// Zero the DS buffer
1191
ZeroMemory(audioPtr, dataLen);
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);
1198
} // end of play initialization
1201
// RECORDING INITIALIZATION
1202
if ( (!strcmp(mode,"record")) || (!strcmp(mode,"duplex")) ) {
1204
DSCBUFFERDESC directSoundCaptureDescription;
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);
1212
goto have_good_input_device;
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);
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;
1229
for (i=0; i<numDevices; i++) {
1230
printf("Capture Device %d: %s\n", i, devices[i].description);
1232
printf("\nPlease type a sound device number from above: ");
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);
1243
throw StkError("RtAudio: Invalid device number specified!\n", StkError::SOUNDCARD_NOT_FOUND);
1246
have_good_input_device:
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;
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);
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);
1269
// Zero the DS Capture buffer
1270
ZeroMemory(audioPtr, dataLen);
1272
// Size and allocate our inputBuffer
1273
inputBufferSize = (long) (sampleRate * channels * 2 * sizeof(INT16));
1274
inputBuffer = (BYTE *) new BYTE[inputBufferSize];
1276
//Zero our internal input buffer
1277
ZeroMemory(inputBuffer, inputBufferSize);
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);
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);
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);
1297
} // end of record initialization
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);
1309
RtAudio :: ~RtAudio()
1311
// Cleanup the DS buffers
1312
if (directSoundBuffer) {
1313
directSoundBuffer->Stop();
1314
directSoundBuffer->Release();
1315
directSoundBuffer = NULL;
1317
if (directSoundCaptureBuffer) {
1318
directSoundCaptureBuffer->Stop();
1319
directSoundCaptureBuffer->Release();
1320
directSoundCaptureBuffer = NULL;
1323
// Cleanup the DS objects
1324
if (directSoundObject) {
1325
directSoundObject->Release();
1326
directSoundObject = NULL;
1328
if (directSoundCaptureObject) {
1329
directSoundCaptureObject->Release();
1330
directSoundCaptureObject = NULL;
1333
timeKillEvent (timerID);
1336
delete [] inputBuffer;
1341
int RtAudio :: playBuffer(short *buf, int bufsize)
1343
if (!playing) startPlay();
1346
LPVOID buffer1 = NULL;
1347
LPVOID buffer2 = NULL;
1348
DWORD bufferSize1 = 0;
1349
DWORD bufferSize2 = 0;
1350
DWORD playPos, safePos;
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 ;
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);
1368
if( playPos < nextWritePos ) playPos += directSoundBufferSize; // unwrap offset
1369
DWORD endWrite = nextWritePos + samplesToWrite * sizeof(INT16);
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));
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
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);
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);
1405
nextWritePos = (nextWritePos + bufferSize1 + bufferSize2) % directSoundBufferSize;
1407
sprintf(errormsg,"RtAudio: Unable to lock DS buffer: %s\n", getErrorMessage(result));
1408
throw StkError(errormsg,StkError::SOUNDCARD_CONTROL);
1411
//update how many samples still need to be written - possibly no more
1412
bufsize -= samplesToWrite;
1413
buf += samplesToWrite;
1414
} while (bufsize > 0);
1419
int RtAudio :: recordBuffer(short *buf, int bufsize)
1421
if (!recording) startRecord();
1423
if (internalError) {
1424
throw StkError("RtAudio: Read failure in windoze capture thread.\n",
1425
StkError::SOUNDCARD_CONTROL);
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);
1436
CopyMemory(buf, inputBuffer+nextRecordRead, length);
1437
nextRecordRead += length;
1438
bufsize -= length / sizeof(INT16);
1439
buf += length / sizeof(INT16);
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));
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;
1461
CopyMemory(buf, inputBuffer+nextRecordRead, length);
1462
nextRecordRead = 0; // we wrap around
1464
bufsize -= length / sizeof(INT16);
1465
buf += length / sizeof(INT16);
1472
/**** Windoze specific overload of recordBuffer ****/
1473
int RtAudio :: recordBuffer(INT16** buf)
1475
if (!recording) startRecord();
1477
if (internalError) {
1478
throw StkError("RtAudio: Read failure in windoze capture thread.\n",StkError::SOUNDCARD_CONTROL);
1480
} else if (nextRecordRead == nextRecordWrite) {
1485
BYTE* temp = inputBuffer + nextRecordRead;
1486
*buf = (INT16*) temp;
1489
if (nextRecordWrite > nextRecordRead) {
1490
length = nextRecordWrite - nextRecordRead;
1491
nextRecordRead = nextRecordWrite;
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
1499
length = inputBufferSize - nextRecordRead;
1503
// We divide by two so that we return number of samples, not bytes
1508
/**** Windows specific start and stop functions ****/
1509
void RtAudio :: stopPlay()
1511
if (!playing) return;
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);
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);
1529
// Zero the DS buffer
1530
ZeroMemory(audioPtr, dataLen);
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);
1537
//if we start playing again, we must begin at beginning of buffer
1543
void RtAudio :: startPlay()
1545
if (playing) return;
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);
1555
void RtAudio :: stopRecord()
1557
if (!recording) return;
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);
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);
1575
// Zero the DS buffer
1576
ZeroMemory(audioPtr, dataLen);
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);
1583
// If we start playing again, we must begin at beginning of buffer
1587
void RtAudio :: startRecord()
1589
if (recording) return;
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);
1601
/**** Internal Utilities ****/
1603
void RtAudio::getInputSamples()
1605
/* This function is called periodically to get samples from
1606
the direct sound capture buffer and transfer them into our
1610
LPVOID buffer1 = NULL;
1611
LPVOID buffer2 = NULL;
1614
DWORD capturePos, safePos;
1615
static UINT nextDirectSoundReadPosition = 0;
1618
// Find out where the write and "safe read" pointers are.
1619
if (FAILED(result = directSoundCaptureBuffer->GetCurrentPosition(&capturePos, &safePos))) {
1620
internalError = true;
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;
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.
1642
if ( safePos < nextDirectSoundReadPosition )
1643
safePos += directSoundCaptureBufferSize; //unwrap offset
1644
numBytesToLock = safePos - nextDirectSoundReadPosition;
1645
UINT availableSpace = inputBufferSize - nextRecordWrite;
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.
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;
1658
} else if (availableSpace == size1) {
1659
// we have exactly enough space
1660
CopyMemory((inputBuffer + nextRecordWrite), buffer1, size1);
1661
availableSpace = inputBufferSize;
1662
nextRecordWrite = 0;
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;
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;
1681
} else if (availableSpace == size2) {
1682
// we have exactly enough space
1683
CopyMemory((inputBuffer + nextRecordWrite), buffer2, size2);
1684
nextRecordWrite = 0;
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;
1695
// Update our buffer offset and unlock sound buffer
1696
directSoundCaptureBuffer->Unlock (buffer1, size1, buffer2, size2);
1697
nextDirectSoundReadPosition = (nextDirectSoundReadPosition + size1 + size2) % directSoundCaptureBufferSize;
1699
// we were unable to lock the buffer
1700
internalError = true;
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)
1708
// dwUser contains ptr to our RtAudio object
1709
RtAudio * soundIO = (RtAudio *) dwUser;
1710
(soundIO->getInputSamples)();
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)
1717
((RtAudio *) lpContext)->addDevice(lpguid,(char*) lpcstrDescription,(char*) lpcstrModule);
1720
if (numDevices < MAX_DEVICES) {
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;
1729
devices[numDevices].guid = NULL;
1730
devices[numDevices].description = "Default Device";
1731
devices[numDevices].moduleName = "";
1739
void RtAudio::addDevice(LPGUID guid, char* description, char* moduleName)
1741
if (numDevices < MAX_DEVICES) {
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;
1750
devices[numDevices].guid = NULL;
1751
devices[numDevices].description = "Default Device";
1752
devices[numDevices].moduleName = "";
1758
char* RtAudio::getErrorMessage(int code) {
1760
case DSERR_ALLOCATED:
1761
return "Direct Sound already allocated";
1763
case DSERR_CONTROLUNAVAIL:
1764
return "Direct Sound control unavailable";
1766
case DSERR_INVALIDPARAM:
1767
return "Direct Sound invalid parameter";
1769
case DSERR_INVALIDCALL:
1770
return "Direct Sound invalid call";
1773
return "Direct Sound generic error";
1775
case DSERR_PRIOLEVELNEEDED:
1776
return "Direct Sound Priority level needed";
1778
case DSERR_OUTOFMEMORY:
1779
return "Direct Sound out of memory";
1781
case DSERR_BADFORMAT:
1782
return "Direct Sound bad format";
1784
case DSERR_UNSUPPORTED:
1785
return "Direct Sound unsupported error";
1787
case DSERR_NODRIVER:
1788
return "Direct Sound no driver error";
1790
case DSERR_ALREADYINITIALIZED:
1791
return "Direct Sound already initialized";
1793
case DSERR_NOAGGREGATION:
1794
return "Direct Sound no aggregation";
1796
case DSERR_BUFFERLOST:
1797
return "Direct Sound buffer lost";
1799
case DSERR_OTHERAPPHASPRIO:
1800
return "Direct Sound other app has priority";
1802
case DSERR_UNINITIALIZED:
1803
return "Direct Sound uninitialized";
1806
return "Direct Sound unknown error";