1 /*
2 * Copyright (C) 2002 2003 2004 2005 2009 2012, Magnus Hjorth
3 *
4 * This file is part of mhWaveEdit.
5 *
6 * mhWaveEdit is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * mhWaveEdit is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with mhWaveEdit; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include <stdio.h>
22 #include <sys/time.h>
23
24 #include <gtk/gtk.h>
25
26 #include <portaudio.h>
27
28 #include "ringbuf.h"
29 #include "sound.h"
30 #include "main.h"
31 #include "gettext.h"
32
33 static struct {
34 gboolean delay_time_set;
35 gfloat delay_time;
36 off_t played_bytes;
37 guint32 samplerate;
38 gint samplesize;
39 guint framesize;
40
41 GTimeVal start_time;
42
43 Ringbuf *output_buffer, *input_buffer;
44 int input_overrun_count;
45
46 PaDeviceID out, in;
47 PortAudioStream *outstream, *instream;
48 } portaudio_data = { 0 };
49
50 // Anropas innan n�gra andra output_-anrop och f�re gtk_init.
portaudio_init(gboolean silent)51 static gboolean portaudio_init(gboolean silent)
52 {
53 Pa_Initialize();
54 portaudio_data.output_buffer = ringbuf_new(
55 inifile_get_guint32(INI_SETTING_SOUNDBUFSIZE,
56 INI_SETTING_SOUNDBUFSIZE_DEFAULT) );
57 portaudio_data.input_buffer = ringbuf_new(65536);
58 portaudio_data.out = Pa_GetDefaultOutputDeviceID();
59 portaudio_data.in = Pa_GetDefaultInputDeviceID();
60 if (portaudio_data.out == paNoDevice)
61 g_warning ( _("sound-portaudio: No output devices available!") );
62 if (portaudio_data.in == paNoDevice)
63 g_warning ( _("sound-portaudio: No input devices available!") );
64 return TRUE;
65 }
66
67 // Anropas till sist
portaudio_quit(void)68 static void portaudio_quit(void)
69 {
70 ringbuf_free(portaudio_data.output_buffer);
71 Pa_Terminate();
72 }
73
portaudio_samplerate_supported(const PaDeviceInfo * i,guint32 samplerate)74 static gboolean portaudio_samplerate_supported(const PaDeviceInfo *i,
75 guint32 samplerate)
76 {
77 int x;
78 if (i->numSampleRates == -1) {
79 return (samplerate >= i->sampleRates[0] &&
80 samplerate <= i->sampleRates[1]);
81 } else {
82 for (x=0; x<i->numSampleRates; x++)
83 if (i->sampleRates[x] == samplerate) return TRUE;
84 return FALSE;
85 }
86 }
87
portaudio_fmtparse(Dataformat * format)88 static PaSampleFormat portaudio_fmtparse(Dataformat *format)
89 {
90 if (format->type == DATAFORMAT_FLOAT) {
91 if (format->samplesize == sizeof(float))
92 return paFloat32;
93 else
94 return 0;
95 }
96 if (XOR(format->bigendian,IS_BIGENDIAN)) return 0;
97 switch (format->samplesize) {
98 case 1:
99 return format->sign ? paInt8 : paUInt8;
100 case 2:
101 return format->sign ? paInt16 : 0;
102 case 3:
103 return format->sign ? paPackedInt24 : 0;
104 case 4:
105 if (format->packing == 0)
106 return format->sign ? paInt32 : 0;
107 else if (format->packing == 1)
108 return format->sign ? paInt24 : 0;
109 else
110 return 0;
111 default:
112 g_assert_not_reached();
113 return 0;
114 }
115 }
116
portaudio_samplesize_supported(const PaDeviceInfo * i,Dataformat * format)117 static gboolean portaudio_samplesize_supported(const PaDeviceInfo *i,
118 Dataformat *format)
119 {
120 return ((i->nativeSampleFormats & portaudio_fmtparse(format))!=0);
121 }
122
portaudio_output_supports_format(Dataformat * format)123 static gboolean portaudio_output_supports_format(Dataformat *format)
124 {
125 const PaDeviceInfo *i = NULL;
126 if (portaudio_data.out != paNoDevice)
127 i = Pa_GetDeviceInfo(portaudio_data.out);
128 return (i && format->channels <= i->maxOutputChannels &&
129 portaudio_samplerate_supported(i,format->samplerate) &&
130 portaudio_samplesize_supported(i,format));
131 }
132
133
portaudio_output_stop(gboolean must_flush)134 static gboolean portaudio_output_stop(gboolean must_flush)
135 {
136 if (!portaudio_data.outstream) return TRUE;
137 if (must_flush) {
138 /* Note: We must check the ringbuffer after checking if the
139 * stream is inactive to avoid races */
140 if (Pa_StreamActive(portaudio_data.outstream)==0 &&
141 !ringbuf_isempty(portaudio_data.output_buffer))
142 Pa_StartStream(portaudio_data.outstream);
143 while (!ringbuf_isempty(portaudio_data.output_buffer))
144 do_yield(TRUE);
145 }
146 Pa_StopStream(portaudio_data.outstream);
147 Pa_CloseStream(portaudio_data.outstream);
148 portaudio_data.outstream = NULL;
149 ringbuf_drain(portaudio_data.output_buffer);
150 return must_flush;
151 }
152
portaudio_output_clear_buffers(void)153 static void portaudio_output_clear_buffers(void)
154 {
155 Pa_AbortStream(portaudio_data.outstream);
156 ringbuf_drain(portaudio_data.output_buffer);
157 }
158
portaudio_output_want_data(void)159 static gboolean portaudio_output_want_data(void)
160 {
161 return !ringbuf_isfull(portaudio_data.output_buffer);
162 }
163
portaudio_output_callback(void * inputBuffer,void * outputBuffer,unsigned long framesPerBuffer,PaTimestamp outTime,void * userData)164 static int portaudio_output_callback(void *inputBuffer, void *outputBuffer,
165 unsigned long framesPerBuffer,
166 PaTimestamp outTime, void *userData)
167 {
168 guint32 i,j;
169 i = ringbuf_dequeue(portaudio_data.output_buffer,outputBuffer,
170 framesPerBuffer*portaudio_data.framesize);
171 j = framesPerBuffer * portaudio_data.framesize - i;
172 if (j>0) memset(((gchar *)outputBuffer)+i, 0, j);
173 portaudio_data.played_bytes += i;
174 return 0;
175 }
176
177 // output_select_format anropas alltid innan output_play
portaudio_output_select_format(Dataformat * format,gboolean silent)178 static gint portaudio_output_select_format(Dataformat *format, gboolean silent)
179 {
180 PaError err;
181 if (!portaudio_output_supports_format(format)) return -1;
182 portaudio_data.played_bytes = 0;
183 err = Pa_OpenStream(&portaudio_data.outstream,
184 paNoDevice, 0, 0, NULL, /* input */
185 portaudio_data.out, format->channels,
186 portaudio_fmtparse(format),
187 NULL,/*out*/
188 (double)format->samplerate, 256, 0,
189 paClipOff|paDitherOff, portaudio_output_callback,NULL);
190 if (err != 0) {
191 printf(_("Pa_OpenStream failed: %s\n"),Pa_GetErrorText(err));
192 return -1;
193 }
194 portaudio_data.samplerate = format->samplerate;
195 portaudio_data.samplesize = format->samplesize;
196 portaudio_data.framesize = format->samplesize * format->channels;
197 return 0;
198 }
199
portaudio_output_play(gchar * buffer,guint bufsize)200 static guint portaudio_output_play(gchar *buffer, guint bufsize)
201 {
202 guint32 i;
203 GTimeVal nowtime,r;
204 PaTimestamp p;
205 if (!bufsize) return ringbuf_available(portaudio_data.output_buffer);
206 i = ringbuf_enqueue(portaudio_data.output_buffer,buffer,bufsize);
207 /* To avoid buffer underruns, wait until there is some data in the output
208 * buffer... */
209 if (Pa_StreamActive(portaudio_data.outstream)==0 &&
210 ringbuf_available(portaudio_data.output_buffer)>4096) {
211 Pa_StartStream(portaudio_data.outstream);
212 g_get_current_time(&portaudio_data.start_time);
213 portaudio_data.delay_time_set = FALSE;
214 }
215 if (!portaudio_data.delay_time_set) {
216 p = Pa_StreamTime( portaudio_data.outstream );
217 if (p>0) {
218 g_get_current_time(&nowtime);
219 timeval_subtract(&r,&portaudio_data.start_time,&nowtime);
220 portaudio_data.delay_time =
221 (gfloat)r.tv_sec + (gfloat)r.tv_usec/1000000.0 -
222 p / (gfloat)portaudio_data.samplerate;
223 portaudio_data.delay_time_set = TRUE;
224 }
225 }
226 return i;
227 }
228
portaudio_input_supported_formats(gboolean * complete)229 static GList *portaudio_input_supported_formats(gboolean *complete)
230 {
231 *complete = (portaudio_data.in == paNoDevice);
232 return NULL;
233 }
234
portaudio_input_supports_format(Dataformat * format)235 static gboolean portaudio_input_supports_format(Dataformat *format)
236 {
237 const PaDeviceInfo *i = NULL;
238 i = Pa_GetDeviceInfo(portaudio_data.in);
239 return (i && format->channels <= i->maxOutputChannels &&
240 portaudio_samplerate_supported(i,format->samplerate) &&
241 portaudio_samplesize_supported(i,format));
242 }
243
portaudio_input_callback(void * inputBuffer,void * outputBuffer,unsigned long framesPerBuffer,PaTimestamp outTime,void * userData)244 static int portaudio_input_callback(void *inputBuffer, void *outputBuffer,
245 unsigned long framesPerBuffer,
246 PaTimestamp outTime, void *userData)
247 {
248 // puts("input_callback");
249 ringbuf_enqueue(portaudio_data.input_buffer,inputBuffer,
250 framesPerBuffer*portaudio_data.framesize);
251 if (ringbuf_isfull(portaudio_data.input_buffer)) {
252 puts(_("Buffer overrun!"));
253 portaudio_data.input_overrun_count ++;
254 }
255 memset(outputBuffer,0,framesPerBuffer*portaudio_data.framesize); /* hack */
256 return 0;
257 }
258
portaudio_input_select_format(Dataformat * format,gboolean silent)259 static gint portaudio_input_select_format(Dataformat *format, gboolean silent)
260 {
261 PaError err;
262 /*printf("in = %d, numInputChannels = %d, inputSampleFormat = %d\n",in,
263 format->channels,fmtparse(format->samplesize,format->sign)); */
264 if (!portaudio_input_supports_format(format)) return -1;
265
266 portaudio_data.input_overrun_count = 0;
267 /* PortAudio seems to act strangely when no output device is specified */
268
269 err = Pa_OpenStream(
270 &portaudio_data.instream,
271 portaudio_data.in, format->channels,
272 portaudio_fmtparse(format), NULL, /*in*/
273 portaudio_data.out, format->channels,
274 portaudio_fmtparse(format), NULL, /*out*/
275 (double)format->samplerate, 64, 0,
276 paClipOff, portaudio_input_callback, NULL);
277 if (err != 0) {
278 printf(_("Pa_OpenStream failed: %s\n"),Pa_GetErrorText(err));
279 return -1;
280 }
281 portaudio_data.samplerate = format->samplerate;
282 portaudio_data.framesize = format->samplesize * format->channels;
283 Pa_StartStream(portaudio_data.instream);
284 return 0;
285 }
286
portaudio_input_stop(void)287 static void portaudio_input_stop(void)
288 {
289 if (!portaudio_data.instream) return;
290 Pa_AbortStream(portaudio_data.instream);
291 Pa_CloseStream(portaudio_data.instream);
292 portaudio_data.instream = NULL;
293 ringbuf_drain(portaudio_data.input_buffer);
294 }
295
portaudio_input_store(Ringbuf * buffer)296 static void portaudio_input_store(Ringbuf *buffer)
297 {
298 ringbuf_transfer(portaudio_data.input_buffer,buffer);
299 }
300
portaudio_input_overrun_count(void)301 static int portaudio_input_overrun_count(void)
302 {
303 return portaudio_data.input_overrun_count;
304 }
305