root/sweep/trunk/src/driver_alsa.c

Revision 688, 11.7 kB (checked in by erikd, 2 years ago)

Clean up large number of '#if 0' and '#if 1' code blocks.

Remove code chunks surrounded by '#if 0' .. '#endif'.
Remove '#if 1' and '#endif' around code chunks that are actually being used.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 /*
2  * Sweep, a sound wave editor.
3  *
4  * Copyright (C) 2000 Conrad Parker
5  *
6  * This program 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  * This program 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 this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 /*
22  * ALSA 0.6 support by Paul Davis
23  * ALSA 0.9 updates by Zenaan Harkness
24  * ALSA 1.0 updates by Daniel Dreschers
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #  include <config.h>
29 #endif
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/time.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <math.h>
40 #include <sys/ioctl.h>
41 #include <pthread.h>
42
43 #include <sweep/sweep_types.h>
44 #include <sweep/sweep_sample.h>
45
46 #include "driver.h"
47 #include "pcmio.h"
48 #include "question_dialogs.h"
49
50 #ifdef DRIVER_ALSA
51
52 #include <alsa/asoundlib.h>
53
54 // shamelessly ripped from alsaplayer alsa-final driver:
55 #ifndef timersub
56 #define timersub(a, b, result) \
57 do { \
58         (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
59   (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
60   if ((result)->tv_usec < 0) { \
61                 --(result)->tv_sec; \
62                 (result)->tv_usec += 1000000; \
63         } \
64 } while (0)
65 #endif
66
67 static sw_handle alsa_handle = {
68  0, -1, 0, 0, NULL
69 };
70
71
72 void print_pcm_state (snd_pcm_t * pcm)
73 {
74   switch (snd_pcm_state(pcm)) {
75     case SND_PCM_STATE_OPEN:
76       fprintf (stderr, "sweep: print_pcm_state: state is OPEN\n");
77       break;
78     case SND_PCM_STATE_SETUP:
79       fprintf (stderr, "sweep: print_pcm_state: state is SETUP\n");
80       break;
81     case SND_PCM_STATE_PREPARED:
82       fprintf (stderr, "sweep: print_pcm_state: state is PREPARED\n");
83       break;
84     case SND_PCM_STATE_RUNNING:
85       fprintf (stderr, "sweep: print_pcm_state: state is RUNNING\n");
86       break;
87     case SND_PCM_STATE_XRUN:
88       fprintf (stderr, "sweep: print_pcm_state: state is XRUN\n");
89       break;
90     case SND_PCM_STATE_DRAINING:
91       fprintf (stderr, "sweep: print_pcm_state: state is DRAINING\n");
92       break;
93     case SND_PCM_STATE_PAUSED:
94       fprintf (stderr, "sweep: print_pcm_state: state is PAUSED\n");
95       break;
96     case SND_PCM_STATE_SUSPENDED:
97       fprintf (stderr, "sweep: print_pcm_state: state is SUSPENDED\n");
98       break;
99     default:
100       fprintf (stderr, "sweep: print_pcm_state: state is unknown! THIS SHOULD NEVER HAPPEN!\n");
101   }
102 }
103
104 static GList *
105 alsa_get_names (void)
106 {
107   GList * names = NULL;
108   char * name;
109
110   if ((name = getenv ("SWEEP_ALSA_PCM")) != 0) {
111     names = g_list_append (names, name);
112   }
113
114   /* The standard command line options for this are -D or --device.
115    * The default fallback should be plughw.
116    */
117   names = g_list_append (names, "plughw:0,0");
118   names = g_list_append (names, "plughw:0,1");
119   names = g_list_append (names, "plughw:1,0");
120   names = g_list_append (names, "plughw:1,1");
121
122   return names;
123 }
124
125 static sw_handle *
126 alsa_device_open (int monitoring, int flags)
127 {
128   int err;
129   char * alsa_pcm_name;
130   snd_pcm_t * pcm_handle;
131   sw_handle * handle = &alsa_handle;
132   snd_pcm_stream_t stream;
133
134   if (monitoring) {
135     if (pcmio_get_use_monitor())
136       alsa_pcm_name = pcmio_get_monitor_dev ();
137     else
138       return NULL;
139   } else {
140     alsa_pcm_name = pcmio_get_main_dev ();
141   }
142
143   if (flags == O_RDONLY) {
144     stream = SND_PCM_STREAM_CAPTURE;
145   } else if (flags == O_WRONLY) {
146     stream = SND_PCM_STREAM_PLAYBACK;
147   } else {
148     return NULL;
149   }
150
151   if ((err = snd_pcm_open(&pcm_handle, alsa_pcm_name, stream, 0)) < 0) {
152     sweep_perror (errno,
153                   "Error opening ALSA device %s",
154                   alsa_pcm_name /*, snd_strerror (err)*/);
155     return NULL;
156   }
157
158   handle->driver_flags = flags;
159   handle->custom_data = pcm_handle;
160
161   return handle;
162 }
163
164   // /src/alsa/alsaplayer-0.99.72/output/alsa-final/alsa.c
165   // /src/alsa/alsa-lib-0.9.0rc3/test/pcm.c
166 static void
167 alsa_device_setup (sw_handle * handle, sw_format * format)
168 {
169   int err;
170   snd_pcm_t * pcm_handle = (snd_pcm_t *)handle->custom_data;
171   snd_pcm_hw_params_t * hwparams;
172   int dir;
173   unsigned int rate = format->rate;
174   unsigned int channels = format->channels;
175   unsigned int periods;
176   snd_pcm_uframes_t period_size = PBUF_SIZE/format->channels;
177
178   if (handle->driver_flags == O_RDONLY) {
179     dir = (int)SND_PCM_STREAM_CAPTURE;
180   } else if (handle->driver_flags == O_WRONLY) {
181     dir = (int)SND_PCM_STREAM_PLAYBACK;
182   } else {
183     return;
184   }
185
186   snd_pcm_hw_params_alloca (&hwparams);
187
188   if ((err = snd_pcm_hw_params_any (pcm_handle, hwparams)) < 0) {
189     fprintf(stderr,
190             "sweep: alsa_setup: can't get PCM hw params (%s)\n",
191             snd_strerror(err));
192     return;
193   }
194
195   if ((err = snd_pcm_hw_params_set_access
196        (pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
197     fprintf(stderr,
198             "sweep: alsa_setup: can't set interleaved access (%s)\n",
199             snd_strerror(err));
200     return;
201   }
202
203   if ((err = snd_pcm_hw_params_set_format
204        (pcm_handle, hwparams, SND_PCM_FORMAT_FLOAT)) < 0) {
205     fprintf (stderr,
206              "sweep: alsa_setup: audio interface does not support "
207              "host endian 32 bit float samples (%s)\n",
208              snd_strerror(err));
209     return;
210   }
211
212   if ((err = snd_pcm_hw_params_set_rate_near
213        (pcm_handle, hwparams, &rate, 0 /* dir */)) < 0) {
214     fprintf (stderr,
215              "sweep: alsa_setup: audio interface does not support "
216              "sample rate of %d (%s)\n",
217              format->rate, snd_strerror (err));
218     /*return;*/
219   }
220
221   if ((err = snd_pcm_hw_params_set_channels_near
222        (pcm_handle, hwparams, &channels)) < 0) {
223     fprintf (stderr,
224              "sweep: alsa_setup: audio interface does not support "
225              "%d channels (%s)\n",
226              format->channels, snd_strerror (err));
227     /*return;*/
228   }
229
230   if ((err = snd_pcm_hw_params_set_period_size_near
231        (pcm_handle, hwparams, &period_size, 0)) < 0) {
232     fprintf (stderr,
233              "sweep: alsa_setup: audio interface does not support "
234              "period size of %ld (%s)\n", period_size, snd_strerror (err));
235     return;
236   }
237
238   periods = LOGFRAGS_TO_FRAGS(pcmio_get_log_frags());
239
240   if ((err = snd_pcm_hw_params_set_periods_near
241        (pcm_handle, hwparams, &periods, 0)) < 0) {
242     fprintf (stderr,
243              "sweep: alsa_setup: audio interface does not support "
244              "period size of %d (%s) - suprising that we get this err!\n",
245              periods, snd_strerror (err));
246     return;
247   }
248
249   // see alsa-lib html docs (may have to build them) for methods doco
250   // The following is old alsa 0.6 code, which may need including somehow:
251   //params.ready_mode = SND_PCM_READY_FRAGMENT;
252   //params.start_mode = SND_PCM_START_DATA;
253   //params.xrun_mode = SND_PCM_XRUN_FRAGMENT;
254   //params.frag_size = PBUF_SIZE / params.format.channels;
255   //params.avail_min = params.frag_size;
256   // params.buffer_size = 3 * params.frag_size;
257
258   if ((err = snd_pcm_hw_params (pcm_handle, hwparams)) < 0) {
259     fprintf (stderr,
260              "sweep: alsa_setup: audio interface could not be configured "
261              "with specified parameters\n");
262     return;
263   }
264   //printf ("sweep: alsa_setup 9\n");
265
266   {
267     unsigned int c, r;
268     int dir = 0;
269
270     if ((err = snd_pcm_hw_params_get_rate (hwparams, &r, &dir)) < 0) {
271       fprintf (stderr,
272                "sweep: alsa_setup: error getting PCM rate (%s)\n",
273                snd_strerror (err));
274     }
275
276     if ((err = snd_pcm_hw_params_get_channels (hwparams, &c)) < 0) {
277       fprintf (stderr,
278                "sweep: alsa_setup: error getting PCM channels (%s)\n",
279                snd_strerror (err));
280     }
281
282 #ifdef DEBUG
283     fprintf (stderr, "alsa got rate %i, channels %i, dir %d\n", r, c, dir);
284 #endif
285
286     handle->driver_rate = r;
287     handle->driver_channels = c;
288        
289     if (c < 1) {
290       fprintf (stderr, "sweep: alsa_setup: alsa says channels == %i\n", c);
291       return;
292     }
293   }
294
295   if (snd_pcm_prepare (pcm_handle) < 0) {
296     fprintf (stderr, "audio interface could not be prepared for playback\n");
297     return;
298   }
299 }
300
301 static int
302 alsa_device_wait (sw_handle * handle)
303 {
304   snd_pcm_t * pcm_handle = (snd_pcm_t *)handle->custom_data;
305
306   if (snd_pcm_wait (pcm_handle, 1000) < 0) {
307     fprintf (stderr, "poll failed (%s)\n", strerror (errno));
308   }
309
310   return 0;
311 }
312
313 #define PLAYBACK_SCALE (32768 / SW_AUDIO_T_MAX)
314
315 static ssize_t
316 alsa_device_read (sw_handle * handle, sw_audio_t * buf, size_t count)
317 {
318   snd_pcm_t * pcm_handle = (snd_pcm_t *)handle->custom_data;
319   snd_pcm_uframes_t uframes;
320   int err;
321
322   uframes = handle->driver_channels > 0 ? count / handle->driver_channels : 0;
323
324   err = snd_pcm_readi (pcm_handle, buf, uframes);
325
326   return err;
327 }
328
329 static ssize_t
330 alsa_device_write (sw_handle * handle, sw_audio_t * buf, size_t count)
331 {
332   snd_pcm_t * pcm_handle = (snd_pcm_t *)handle->custom_data;
333   snd_pcm_uframes_t uframes;
334   snd_pcm_status_t * status;
335   int err;
336
337   /*printf ("sweep: alsa_write \n");*/
338
339   uframes = handle->driver_channels > 0 ? count / handle->driver_channels : 0;
340   //printf ("sweep: alsa_write 1\n");
341
342   // this basicaly ripped straight out of alsaplayer alsa-final driver:
343   err = snd_pcm_writei(pcm_handle, buf, uframes);
344
345   if (err == -EPIPE) {
346     snd_pcm_status_alloca(&status);
347     if ((err = snd_pcm_status(pcm_handle, status))<0) {
348       fprintf(stderr, "sweep: alsa_write: xrun. can't determine length\n");
349     } else {
350       if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
351         struct timeval now, diff, tstamp;
352         gettimeofday(&now, 0);
353         snd_pcm_status_get_trigger_tstamp(status, &tstamp);
354         timersub(&now, &tstamp, &diff);
355         fprintf(stderr, "sweep: alsa_write: xrun of at least %.3f msecs. "
356             "resetting stream\n", diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
357       } else {
358         fprintf(stderr, "sweep: alsa_write: xrun. can't determine length\n");
359       }
360     } 
361     snd_pcm_prepare(pcm_handle);
362     err = snd_pcm_writei(pcm_handle, buf, uframes);
363     if (err != uframes) {
364       fprintf(stderr, "sweep: alsa_write: %s\n", snd_strerror(err));
365       return 0;
366     } else if (err < 0) {
367       fprintf(stderr, "sweep: alsa_write: %s\n", snd_strerror(err));
368       return 0;
369     }
370   }
371
372   return 1;
373 }
374
375 sw_framecount_t
376 alsa_device_offset (sw_handle * handle)
377 {
378   /*printf ("sweep: alsa_offset\n");*/
379   return -1;
380 }
381
382 static void
383 alsa_device_reset (sw_handle * handle)
384 {
385   /*printf ("sweep: alsa_reset\n");*/
386 }
387
388 static void
389 alsa_device_flush (sw_handle * handle)
390 {
391   /*printf ("sweep: alsa_flush\n");*/
392 }
393
394 /*
395  * alsa lib provides:
396  * int snd_pcm_drop (snd_pcm_t *pcm) // Stop a PCM dropping pending frames.
397  * int snd_pcm_drain (snd_pcm_t *pcm) // Stop a PCM preserving pending frames.
398  */
399 static void
400 alsa_device_drain (sw_handle * handle)
401 {
402   snd_pcm_t * pcm_handle = (snd_pcm_t *)handle->custom_data;
403
404   if (snd_pcm_drop (pcm_handle) < 0) {
405         fprintf (stderr, "audio interface could not be stopped\n");
406         return;
407   }
408   if (snd_pcm_prepare (pcm_handle) < 0) {
409         fprintf (stderr, "audio interface could not be re-prepared\n");
410         return;
411   }
412 }
413
414 static void
415 alsa_device_close (sw_handle * handle)
416 {
417   snd_pcm_t * pcm_handle = (snd_pcm_t *)handle->custom_data;
418  
419   snd_pcm_close (pcm_handle);
420   handle->custom_data = NULL;
421 }
422
423 static sw_driver _driver_alsa = {
424   "ALSA",
425   alsa_get_names,
426   alsa_device_open,
427   alsa_device_setup,
428   alsa_device_wait,
429   alsa_device_read,
430   alsa_device_write,
431   alsa_device_offset,
432   alsa_device_reset,
433   alsa_device_flush,
434   alsa_device_drain,
435   alsa_device_close,
436   "alsa_primary_device",
437   "alsa_monitor_device",
438   "alsa_log_frags"
439 };
440
441 #else
442
443 static sw_driver _driver_alsa = {
444   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
445 };
446
447 #endif
448
449 sw_driver * driver_alsa = &_driver_alsa;
Note: See TracBrowser for help on using the browser.