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