some random improvements
[pcsx_rearmed.git] / plugins / dfsound / alsa.c
1 /***************************************************************************
2                             alsa.c  -  description
3                              -------------------
4     begin                : Sat Mar 01 2003
5     copyright            : (C) 2002 by Pete Bernert
6     email                : BlackDove@addcom.de
7  ***************************************************************************/
8 /***************************************************************************
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version. See also the license.txt file for *
14  *   additional informations.                                              *
15  *                                                                         *
16  ***************************************************************************/
17
18 #include <stdio.h>
19 #include <string.h>
20 #define ALSA_PCM_NEW_HW_PARAMS_API
21 #define ALSA_PCM_NEW_SW_PARAMS_API
22 #include <alsa/asoundlib.h>
23 #include "out.h"
24
25 static snd_pcm_t *handle = NULL;
26 static snd_pcm_uframes_t buffer_size;
27
28 static void alsa_finish(void);
29
30 // SETUP SOUND
31 static int alsa_init(void)
32 {
33  snd_pcm_hw_params_t *hwparams;
34  snd_pcm_status_t *status;
35  snd_ctl_t *ctl_handle = NULL;
36  snd_ctl_card_info_t *info;
37  unsigned int pspeed;
38  int pchannels;
39  int format;
40  unsigned int buffer_time = 100000;
41  unsigned int period_time = buffer_time / 4;
42  const char *alsa_name = "default";
43  const char *name;
44  int retval = -1;
45  int err;
46
47  name = getenv("ALSA_NAME");
48  if (name != NULL)
49   alsa_name = name;
50
51  snd_ctl_card_info_alloca(&info);
52  if ((err = snd_ctl_open(&ctl_handle, alsa_name, 0)) < 0) {
53   printf("control open: %s\n", snd_strerror(err));
54  }
55  else if ((err = snd_ctl_card_info(ctl_handle, info)) < 0) {
56   printf("control info: %s\n", snd_strerror(err));
57   snd_ctl_card_info_clear(info);
58  }
59  if (ctl_handle != NULL)
60   snd_ctl_close(ctl_handle);
61
62  name = snd_ctl_card_info_get_name(info);
63  if (name != NULL) {
64   if (strcasecmp(name, "PulseAudio") == 0) {
65     // PulseAudio's ALSA emulation is known to be broken..
66     printf("alsa: refusing to run under PulseAudio's emulation\n");
67     return -1;
68   }
69   else {
70     printf("alsa: using '%s', set ALSA_NAME to change\n", name);
71   }
72  }
73
74  pchannels=2;
75
76  pspeed = 44100;
77  format = SND_PCM_FORMAT_S16;
78
79  if ((err = snd_pcm_open(&handle, alsa_name, 
80                       SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0)
81   {
82    printf("Audio open error: %s\n", snd_strerror(err));
83    return -1;
84   }
85
86  if((err = snd_pcm_nonblock(handle, 0))<0)
87   {
88    printf("Can't set blocking moded: %s\n", snd_strerror(err));
89    goto out;
90   }
91
92  snd_pcm_hw_params_alloca(&hwparams);
93
94  if((err=snd_pcm_hw_params_any(handle, hwparams))<0)
95   {
96    printf("Broken configuration for this PCM: %s\n", snd_strerror(err));
97    goto out;
98   }
99
100  if((err=snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED))<0)
101   {
102    printf("Access type not available: %s\n", snd_strerror(err));
103    goto out;
104   }
105
106  if((err=snd_pcm_hw_params_set_format(handle, hwparams, format))<0)
107   {
108    printf("Sample format not available: %s\n", snd_strerror(err));
109    goto out;
110   }
111
112  if((err=snd_pcm_hw_params_set_channels(handle, hwparams, pchannels))<0)
113   {
114    printf("Channels count not available: %s\n", snd_strerror(err));
115    goto out;
116   }
117
118  if((err=snd_pcm_hw_params_set_rate_near(handle, hwparams, &pspeed, 0))<0)
119   {
120    printf("Rate not available: %s\n", snd_strerror(err));
121    goto out;
122   }
123
124  if((err=snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, 0))<0)
125   {
126    printf("Buffer time error: %s\n", snd_strerror(err));
127    goto out;
128   }
129
130  if((err=snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, 0))<0)
131   {
132    printf("Period time error: %s\n", snd_strerror(err));
133    goto out;
134   }
135
136  if((err=snd_pcm_hw_params(handle, hwparams))<0)
137   {
138    printf("Unable to install hw params: %s\n", snd_strerror(err));
139    goto out;
140   }
141
142  snd_pcm_status_alloca(&status);
143  if((err=snd_pcm_status(handle, status))<0)
144   {
145    printf("Unable to get status: %s\n", snd_strerror(err));
146    goto out;
147   }
148
149  buffer_size = snd_pcm_status_get_avail(status);
150  retval = 0;
151
152 out:
153  if (retval != 0)
154   alsa_finish();
155  return retval;
156 }
157
158 // REMOVE SOUND
159 static void alsa_finish(void)
160 {
161  if(handle != NULL)
162   {
163    snd_pcm_drop(handle);
164    snd_pcm_close(handle);
165    handle = NULL;
166   }
167 }
168
169 // GET BYTES BUFFERED
170 static int alsa_busy(void)
171 {
172  int l;
173
174  if (handle == NULL)                                 // failed to open?
175   return 1;
176  l = snd_pcm_avail(handle);
177  if (l < 0) return 0;
178  if (l < buffer_size / 2)                            // can we write in at least the half of fragments?
179       l = 1;                                         // -> no? wait
180  else l = 0;                                         // -> else go on
181
182  return l;
183 }
184
185 // FEED SOUND DATA
186 static void alsa_feed(void *pSound, int lBytes)
187 {
188  char sbuf[4096];
189
190  if (handle == NULL) return;
191
192  if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN)
193   {
194    memset(sbuf, 0, sizeof(sbuf));
195    snd_pcm_prepare(handle);
196    snd_pcm_writei(handle, sbuf, sizeof(sbuf) / 4);
197    snd_pcm_writei(handle, sbuf, sizeof(sbuf) / 4);
198    snd_pcm_writei(handle, sbuf, sizeof(sbuf) / 4);
199   }
200  else
201   {
202    int l = snd_pcm_avail(handle);
203    if (l < lBytes / 4)
204     {
205      if (l == 0)
206       return;
207
208      lBytes = l * 4;
209     }
210   }
211
212  snd_pcm_writei(handle, pSound, lBytes / 4);
213 }
214
215 void out_register_alsa(struct out_driver *drv)
216 {
217         drv->name = "alsa";
218         drv->init = alsa_init;
219         drv->finish = alsa_finish;
220         drv->busy = alsa_busy;
221         drv->feed = alsa_feed;
222 }