xref: /btstack/test/avdtp/portaudio_test.c (revision 6897da5c53aac5b1f90f41b5b15d0bd43d61dfff)
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     int16_t sine[TABLE_SIZE];
62     int left_phase;
63     int right_phase;
64 } paTestData;
65 
66 static uint8_t ring_buffer_storage[3*FRAMES_PER_BUFFER*BYTES_PER_FRAME];
67 static btstack_ring_buffer_t ring_buffer;
68 
69 static int total_num_samples = 0;
70 static const char * wav_filename = "portaudio_sine.wav";
71 
72 static void write_wav_data(int16_t * data, int num_frames, int num_channels, int sample_rate){
73     (void)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 static void fill_ring_buffer(void *userData){
80     paTestData *data = (paTestData*)userData;
81 
82     while (btstack_ring_buffer_bytes_free(&ring_buffer) > BYTES_PER_FRAME){
83         uint8_t write_data[BYTES_PER_FRAME];
84         *(int16_t*)&write_data[0] = data->sine[data->left_phase];
85         *(int16_t*)&write_data[2] = data->sine[data->right_phase];
86 
87         btstack_ring_buffer_write(&ring_buffer, write_data, BYTES_PER_FRAME);
88         write_wav_data((int16_t*)write_data, 1, NUM_CHANNELS, SAMPLE_RATE);
89 
90         data->left_phase += 1;
91         if (data->left_phase >= TABLE_SIZE){
92             data->left_phase -= TABLE_SIZE;
93         }
94         data->right_phase += 2; /* higher pitch so we can distinguish left and right. */
95         if (data->right_phase >= TABLE_SIZE){
96             data->right_phase -= TABLE_SIZE;
97         }
98     }
99 }
100 
101 static int patestCallback( const void *inputBuffer, void *outputBuffer,
102                            unsigned long framesPerBuffer,
103                            const PaStreamCallbackTimeInfo* timeInfo,
104                            PaStreamCallbackFlags statusFlags,
105                            void *userData ) {
106     (void) timeInfo; /* Prevent unused variable warnings. */
107     (void) statusFlags;
108     (void) inputBuffer;
109     (void) userData;
110 
111     uint32_t bytes_read = 0;
112     int bytes_per_buffer = framesPerBuffer * BYTES_PER_FRAME;
113 
114     if (btstack_ring_buffer_bytes_available(&ring_buffer) >= bytes_per_buffer){
115         btstack_ring_buffer_read(&ring_buffer, (uint8_t *) outputBuffer, bytes_per_buffer, &bytes_read);
116     } else {
117         printf("NOT ENOUGH DATA!\n");
118         memset(outputBuffer, 0, bytes_per_buffer);
119     }
120     return paContinue;
121 }
122 
123 
124 int main(int argc, const char * argv[]){
125     (void) argc;
126     (void) 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] = sin(((double)i/(double)TABLE_SIZE) * M_PI * 2.)*32767;
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     outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
147     outputParameters.channelCount = NUM_CHANNELS;
148     outputParameters.sampleFormat = PA_SAMPLE_TYPE;
149     outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
150     outputParameters.hostApiSpecificStreamInfo = NULL;
151 
152     /* -- setup stream -- */
153     err = Pa_OpenStream(
154            &stream,
155            NULL,                /* &inputParameters */
156            &outputParameters,
157            SAMPLE_RATE,
158            FRAMES_PER_BUFFER,
159            paClipOff,           /* we won't output out of range samples so don't bother clipping them */
160            patestCallback,      /* use callback */
161            &data );             /* callback userData */
162 
163     memset(ring_buffer_storage, 0, sizeof(ring_buffer_storage));
164     btstack_ring_buffer_init(&ring_buffer, ring_buffer_storage, sizeof(ring_buffer_storage));
165 
166     wav_writer_open(wav_filename, NUM_CHANNELS, SAMPLE_RATE);
167 
168     if (err != paNoError){
169         printf("Error opening default stream: \"%s\"\n",  Pa_GetErrorText(err));
170         return paNoError;
171     }
172 
173     err = Pa_StartStream(stream);
174     if (err != paNoError){
175         printf("Error starting the stream: \"%s\"\n",  Pa_GetErrorText(err));
176         return paNoError;
177     }
178 
179     /* Sleep for several seconds. */
180     while (1){
181         fill_ring_buffer(&data);
182         Pa_Sleep(1);
183     }
184 
185     err = Pa_StopStream(stream);
186     if (err != paNoError){
187         printf("Error stopping the stream: \"%s\"\n",  Pa_GetErrorText(err));
188         return paNoError;
189     }
190 
191     err = Pa_CloseStream(stream);
192     if (err != paNoError){
193         printf("Error closing the stream: \"%s\"\n",  Pa_GetErrorText(err));
194         return paNoError;
195     }
196 
197     err = Pa_Terminate();
198     if (err != paNoError){
199         printf("Error terminating portaudio: \"%s\"\n",  Pa_GetErrorText(err));
200         return paNoError;
201     }
202     return 0;
203 }
204