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
write_wav_data(int16_t * data,int num_frames,int num_channels,int sample_rate)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
fill_ring_buffer(void * userData)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
patestCallback(const void * inputBuffer,void * outputBuffer,unsigned long framesPerBuffer,const PaStreamCallbackTimeInfo * timeInfo,PaStreamCallbackFlags statusFlags,void * userData)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
main(int argc,const char * argv[])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