xref: /btstack/test/avdtp/portaudio_test.c (revision 4a7e70d324ee17b34ba489c52cbaac93503d9e1f)
1 /*
2  * Copyright (C) 2016 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #include <stdio.h>
39 #include <math.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <portaudio.h>
43 
44 #include "btstack_ring_buffer.h"
45 #include "wav_util.h"
46 
47 #define NUM_CHANNELS        2
48 #define NUM_SECONDS         1
49 #define PA_SAMPLE_TYPE      paInt16
50 #define SAMPLE_RATE         44100
51 #define FRAMES_PER_BUFFER   400
52 #define BYTES_PER_FRAME     (2*NUM_CHANNELS)
53 
54 #ifndef M_PI
55 #define M_PI  3.14159265
56 #endif
57 
58 #define TABLE_SIZE   100
59 
60 typedef struct {
61     float sine[TABLE_SIZE];
62     int left_phase;
63     int right_phase;
64     char message[20];
65 } paTestData;
66 
67 static uint8_t ring_buffer_storage[3*FRAMES_PER_BUFFER*BYTES_PER_FRAME];
68 static btstack_ring_buffer_t ring_buffer;
69 
70 static int total_num_samples = 0;
71 static char * wav_filename = "portaudio_sine.wav";
72 
73 static void write_wav_data(int16_t * data, int num_frames, int num_channels, int sample_rate){
74     wav_writer_write_int16(num_frames*num_channels, data);
75     total_num_samples+=num_frames*num_channels;
76     if (total_num_samples>5*SAMPLE_RATE) wav_writer_close();
77 }
78 
79 
80 static void fill_ring_buffer(void *userData){
81     paTestData *data = (paTestData*)userData;
82 
83     while (btstack_ring_buffer_bytes_free(&ring_buffer) > BYTES_PER_FRAME){
84         int16_t left = data->sine[data->left_phase] * 32767;
85         int16_t right = data->sine[data->right_phase] * 32767;
86 
87         uint8_t write_data[BYTES_PER_FRAME];
88         *(int16_t*)&write_data[0] = left;
89         *(int16_t*)&write_data[2] = right;
90         btstack_ring_buffer_write(&ring_buffer, write_data, BYTES_PER_FRAME);
91         write_wav_data((int16_t*)write_data, 1, NUM_CHANNELS, SAMPLE_RATE);
92 
93         data->left_phase += 1;
94         if (data->left_phase >= TABLE_SIZE){
95             data->left_phase -= TABLE_SIZE;
96         }
97         data->right_phase += 2; /* higher pitch so we can distinguish left and right. */
98         if (data->right_phase >= TABLE_SIZE){
99             data->right_phase -= TABLE_SIZE;
100         }
101     }
102 }
103 
104 static int patestCallback( const void *inputBuffer, void *outputBuffer,
105                            unsigned long framesPerBuffer,
106                            const PaStreamCallbackTimeInfo* timeInfo,
107                            PaStreamCallbackFlags statusFlags,
108                            void *userData ) {
109     (void) timeInfo; /* Prevent unused variable warnings. */
110     (void) statusFlags;
111     (void) inputBuffer;
112 
113     uint16_t bytes_read = 0;
114     int bytes_per_buffer = framesPerBuffer * BYTES_PER_FRAME;
115 
116     if (btstack_ring_buffer_bytes_available(&ring_buffer) >= bytes_per_buffer){
117         btstack_ring_buffer_read(&ring_buffer, outputBuffer, bytes_per_buffer, &bytes_read);
118     } else {
119         printf("NOT ENGOUGH DAT!\n");
120         memset(outputBuffer, 0, bytes_per_buffer);
121     }
122     return paContinue;
123 }
124 
125 
126 int main(int argc, const char * argv[]){
127 
128     PaError err;
129     static paTestData data;
130     static PaStream * stream;
131 
132     /* initialise sinusoidal wavetable */
133     int i;
134     for (i=0; i<TABLE_SIZE; i++){
135         data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
136     }
137     data.left_phase = data.right_phase = 0;
138 
139     err = Pa_Initialize();
140     if (err != paNoError){
141         printf("Error initializing portaudio: \"%s\"\n",  Pa_GetErrorText(err));
142         return paNoError;
143     }
144 
145     PaStreamParameters outputParameters;
146 
147     /* -- setup input and output -- */
148     outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
149     outputParameters.channelCount = NUM_CHANNELS;
150     outputParameters.sampleFormat = PA_SAMPLE_TYPE;
151     outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
152     outputParameters.hostApiSpecificStreamInfo = NULL;
153     /* -- setup stream -- */
154     err = Pa_OpenStream(
155            &stream,
156            NULL,                /* &inputParameters */
157            &outputParameters,
158            SAMPLE_RATE,
159            FRAMES_PER_BUFFER,
160            paClipOff,           /* we won't output out of range samples so don't bother clipping them */
161            patestCallback,      /* use callback */
162            &data );              /* no callback userData yet */
163 
164     memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage));
165     btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage));
166 
167     wav_writer_open(wav_filename, NUM_CHANNELS, SAMPLE_RATE);
168 
169     if (err != paNoError){
170         printf("Error opening default stream: \"%s\"\n",  Pa_GetErrorText(err));
171         return paNoError;
172     }
173 
174     err = Pa_StartStream(stream);
175     if (err != paNoError){
176         printf("Error starting the stream: \"%s\"\n",  Pa_GetErrorText(err));
177         return paNoError;
178     }
179 
180     /* Sleep for several seconds. */
181     while (1){
182         fill_ring_buffer(&data);
183         Pa_Sleep(1);
184     }
185 
186     err = Pa_StopStream(stream);
187     if (err != paNoError){
188         printf("Error stopping the stream: \"%s\"\n",  Pa_GetErrorText(err));
189         return paNoError;
190     }
191 
192     err = Pa_CloseStream(stream);
193     if (err != paNoError){
194         printf("Error closing the stream: \"%s\"\n",  Pa_GetErrorText(err));
195         return paNoError;
196     }
197 
198     err = Pa_Terminate();
199     if (err != paNoError){
200         printf("Error terminating portaudio: \"%s\"\n",  Pa_GetErrorText(err));
201         return paNoError;
202     }
203     return 0;
204 }
205