1 /*
2  * $Id: $
3  * Portable Audio I/O Library
4  * Windows DirectSound surround sound output test
5  *
6  * Copyright (c) 2007 Ross Bencina
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining
9  * a copy of this software and associated documentation files
10  * (the "Software"), to deal in the Software without restriction,
11  * including without limitation the rights to use, copy, modify, merge,
12  * publish, distribute, sublicense, and/or sell copies of the Software,
13  * and to permit persons to whom the Software is furnished to do so,
14  * subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be
17  * included in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
23  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  */
27 
28 /*
29  * The text above constitutes the entire PortAudio license; however,
30  * the PortAudio community also makes the following non-binding requests:
31  *
32  * Any person wishing to distribute modifications to the Software is
33  * requested to send the modifications to the original developer so that
34  * they can be incorporated into the canonical version. It is also
35  * requested that these non-binding requests be included along with the
36  * license above.
37  */
38 
39 #include <stdio.h>
40 #include <math.h>
41 
42 #include <windows.h>    /* required when using pa_win_wmme.h */
43 #include <mmsystem.h>   /* required when using pa_win_wmme.h */
44 
45 #include "portaudio.h"
46 #include "pa_win_ds.h"
47 
48 #define NUM_SECONDS         (12)
49 #define SAMPLE_RATE         (44100)
50 #define FRAMES_PER_BUFFER   (64)
51 
52 #ifndef M_PI
53 #define M_PI  (3.14159265)
54 #endif
55 
56 #define TABLE_SIZE          (100)
57 
58 #define CHANNEL_COUNT       (6)
59 
60 
61 
62 typedef struct
63 {
64     float sine[TABLE_SIZE];
65 	int phase;
66 	int currentChannel;
67 	int cycleCount;
68 }
69 paTestData;
70 
71 /* This routine will be called by the PortAudio engine when audio is needed.
72 ** It may called at interrupt level on some machines so don't do anything
73 ** that could mess up the system like calling malloc() or free().
74 */
patestCallback(const void * inputBuffer,void * outputBuffer,unsigned long framesPerBuffer,const PaStreamCallbackTimeInfo * timeInfo,PaStreamCallbackFlags statusFlags,void * userData)75 static int patestCallback( const void *inputBuffer, void *outputBuffer,
76                             unsigned long framesPerBuffer,
77                             const PaStreamCallbackTimeInfo* timeInfo,
78                             PaStreamCallbackFlags statusFlags,
79                             void *userData )
80 {
81     paTestData *data = (paTestData*)userData;
82     float *out = (float*)outputBuffer;
83     unsigned long i,j;
84 
85     (void) timeInfo; /* Prevent unused variable warnings. */
86     (void) statusFlags;
87     (void) inputBuffer;
88 
89     for( i=0; i<framesPerBuffer; i++ )
90     {
91 		for( j = 0; j < CHANNEL_COUNT; ++j ){
92 			if( j == data->currentChannel && data->cycleCount < 4410 ){
93 				*out++ = data->sine[data->phase];
94 				data->phase += 1 + j;	// play each channel at a different pitch so they can be distinguished
95 				if( data->phase >= TABLE_SIZE ){
96 					data->phase -= TABLE_SIZE;
97 				}
98 			}else{
99 				*out++ = 0;
100 			}
101 		}
102 
103 		data->cycleCount++;
104 		if( data->cycleCount > 44100 ){
105 			data->cycleCount = 0;
106 
107 			++data->currentChannel;
108 			if( data->currentChannel >= CHANNEL_COUNT )
109 				data->currentChannel -= CHANNEL_COUNT;
110 		}
111 	}
112 
113     return paContinue;
114 }
115 
116 /*******************************************************************/
main(int argc,char * argv[])117 int main(int argc, char* argv[])
118 {
119     PaStreamParameters outputParameters;
120     PaWinDirectSoundStreamInfo directSoundStreamInfo;
121     PaStream *stream;
122     PaError err;
123     paTestData data;
124     int i;
125     int deviceIndex;
126 
127     printf("PortAudio Test: output a sine blip on each channel. SR = %d, BufSize = %d, Chans = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER, CHANNEL_COUNT);
128 
129     err = Pa_Initialize();
130     if( err != paNoError ) goto error;
131 
132 	deviceIndex = Pa_GetHostApiInfo( Pa_HostApiTypeIdToHostApiIndex( paDirectSound ) )->defaultOutputDevice;
133 	if( argc == 2 ){
134 		sscanf( argv[1], "%d", &deviceIndex );
135 	}
136 
137 	printf( "using device id %d (%s)\n", deviceIndex, Pa_GetDeviceInfo(deviceIndex)->name );
138 
139     /* initialise sinusoidal wavetable */
140     for( i=0; i<TABLE_SIZE; i++ )
141     {
142         data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
143     }
144 
145 	data.phase = 0;
146 	data.currentChannel = 0;
147 	data.cycleCount = 0;
148 
149     outputParameters.device = deviceIndex;
150     outputParameters.channelCount = CHANNEL_COUNT;
151     outputParameters.sampleFormat = paFloat32; /* 32 bit floating point processing */
152     outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
153     outputParameters.hostApiSpecificStreamInfo = NULL;
154 
155     /* it's not strictly necessary to provide a channelMask for surround sound
156        output. But if you want to be sure which channel mask PortAudio will use
157        then you should supply one */
158     directSoundStreamInfo.size = sizeof(PaWinDirectSoundStreamInfo);
159     directSoundStreamInfo.hostApiType = paDirectSound;
160     directSoundStreamInfo.version = 1;
161     directSoundStreamInfo.flags = paWinDirectSoundUseChannelMask;
162     directSoundStreamInfo.channelMask = PAWIN_SPEAKER_5POINT1; /* request 5.1 output format */
163     outputParameters.hostApiSpecificStreamInfo = &directSoundStreamInfo;
164 
165 	if( Pa_IsFormatSupported( 0, &outputParameters, SAMPLE_RATE ) == paFormatIsSupported  ){
166 		printf( "Pa_IsFormatSupported reports device will support %d channels.\n", CHANNEL_COUNT );
167 	}else{
168 		printf( "Pa_IsFormatSupported reports device will not support %d channels.\n", CHANNEL_COUNT );
169 	}
170 
171     err = Pa_OpenStream(
172               &stream,
173               NULL, /* no input */
174               &outputParameters,
175               SAMPLE_RATE,
176               FRAMES_PER_BUFFER,
177               paClipOff,      /* we won't output out of range samples so don't bother clipping them */
178               patestCallback,
179               &data );
180     if( err != paNoError ) goto error;
181 
182     err = Pa_StartStream( stream );
183     if( err != paNoError ) goto error;
184 
185     printf("Play for %d seconds.\n", NUM_SECONDS );
186     Pa_Sleep( NUM_SECONDS * 1000 );
187 
188     err = Pa_StopStream( stream );
189     if( err != paNoError ) goto error;
190 
191     err = Pa_CloseStream( stream );
192     if( err != paNoError ) goto error;
193 
194     Pa_Terminate();
195     printf("Test finished.\n");
196 
197     return err;
198 error:
199     Pa_Terminate();
200     fprintf( stderr, "An error occured while using the portaudio stream\n" );
201     fprintf( stderr, "Error number: %d\n", err );
202     fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
203     return err;
204 }
205 
206