00001
00043
00044
00045
00046
00047
00048
00049
00050 #include <sent/stddefs.h>
00051 #include <sent/adin.h>
00052
00053 #include <sys/ioctl.h>
00054 #include <sys/types.h>
00055 #include <sys/stat.h>
00056 #include <fcntl.h>
00057
00058 #include <alsa/asoundlib.h>
00059
00060 static snd_pcm_t *handle;
00061 static snd_pcm_hw_params_t *hwparams;
00062 static char *pcm_name = "hw:0,0";
00063
00064 static boolean need_swap;
00065 static int latency = 50;
00066
00067 static struct pollfd *ufds;
00068 static int count;
00069
00070
00079 boolean
00080 adin_mic_standby(int sfreq, void *dummy)
00081 {
00082 int err;
00083 #if (SND_LIB_MAJOR == 0)
00084 int actual_rate;
00085 #else
00086 unsigned int actual_rate;
00087 #endif
00088 int dir;
00089
00090
00091 snd_pcm_hw_params_alloca(&hwparams);
00092
00093
00094 if ((err = snd_pcm_open(&handle, pcm_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) {
00095 j_printerr("Error: cannot open PCM device %s (%s)\n", pcm_name, snd_strerror(err));
00096 return(FALSE);
00097 }
00098
00099
00100 if ((err = snd_pcm_nonblock(handle, 0)) < 0) {
00101 j_printerr("Error: cannot set PCM device to block mode\n");
00102 return(FALSE);
00103 }
00104
00105
00106 if ((err = snd_pcm_hw_params_any(handle, hwparams)) < 0) {
00107 j_printerr("Error: cannot initialize PCM device parameter structure (%s)\n", snd_strerror(err));
00108 return(FALSE);
00109 }
00110
00111
00112 if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
00113 j_printerr("Error: cannot set PCM device access mode (%s)\n", snd_strerror(err));
00114 return(FALSE);
00115 }
00116
00117
00118 #ifdef WORDS_BIGENDIAN
00119
00120 if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_BE)) >= 0) {
00121 need_swap = FALSE;
00122 } else if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE)) >= 0) {
00123 need_swap = TRUE;
00124 } else {
00125 j_printerr("Error: cannot set PCM device format to 16bit-signed (%s)\n", snd_strerror(err));
00126 return(FALSE);
00127 }
00128 #else
00129
00130 if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE)) >= 0) {
00131 need_swap = FALSE;
00132 } else if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_BE)) >= 0) {
00133 need_swap = TRUE;
00134 } else {
00135 j_printerr("Error: cannot set PCM device format to 16bit-signed (%s)\n", snd_strerror(err));
00136 return(FALSE);
00137 }
00138 #endif
00139
00140
00141 #if (SND_LIB_MAJOR == 0)
00142 actual_rate = snd_pcm_hw_params_set_rate_near(handle, hwparams, sfreq, &dir);
00143 if (actual_rate < 0) {
00144 j_printerr("Error: cannot set PCM device sample rate to %d (%s)\n", sfreq, snd_strerror(actual_rate));
00145 return(FALSE);
00146 }
00147 #else
00148 actual_rate = sfreq;
00149 err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &actual_rate, &dir);
00150 if (err < 0) {
00151 j_printerr("Error: cannot set PCM device sample rate to %d (%s)\n", sfreq, snd_strerror(err));
00152 return(FALSE);
00153 }
00154 #endif
00155 if (actual_rate != sfreq) {
00156 j_printerr("Warning: the rate %d Hz is not supported by your PCM hardware.\n ==> Using %d Hz instead.\n", sfreq, actual_rate);
00157 }
00158
00159
00160 if ((err = snd_pcm_hw_params_set_channels(handle, hwparams, 1)) < 0) {
00161 j_printerr("Error: cannot set PCM monoral channel (%s)\n", snd_strerror(err));
00162 return(FALSE);
00163 }
00164
00165
00166 {
00167 #if (SND_LIB_MAJOR == 0)
00168 int periodsize;
00169 int actual_size;
00170 int maxsize, minsize;
00171 #else
00172 snd_pcm_uframes_t periodsize;
00173 snd_pcm_uframes_t actual_size;
00174 snd_pcm_uframes_t maxsize, minsize;
00175 #endif
00176
00177
00178 dir = 0;
00179 #if (SND_LIB_MAJOR == 0)
00180 if ((maxsize = snd_pcm_hw_params_get_period_size_max(hwparams, &dir)) < 0) {
00181 j_printerr("Error: cannot get maximum period size\n");
00182 return(FALSE);
00183 }
00184 if ((minsize = snd_pcm_hw_params_get_period_size_min(hwparams, &dir)) < 0) {
00185 j_printerr("Error: cannot get minimum period size\n");
00186 return(FALSE);
00187 }
00188 #else
00189 if ((err = snd_pcm_hw_params_get_period_size_max(hwparams, &maxsize, &dir)) < 0) {
00190 j_printerr("Error: cannot get maximum period size\n");
00191 return(FALSE);
00192 }
00193 if ((err = snd_pcm_hw_params_get_period_size_min(hwparams, &minsize, &dir)) < 0) {
00194 j_printerr("Error: cannot get minimum period size\n");
00195 return(FALSE);
00196 }
00197 #endif
00198
00199
00200 periodsize = actual_rate * latency / 1000 * sizeof(SP16);
00201 if (periodsize < minsize) {
00202 j_printerr("Warning: PCM latency of %d ms (%d bytes) too small, use device minimum %d bytes\n", latency, periodsize, minsize);
00203 periodsize = minsize;
00204 } else if (periodsize > maxsize) {
00205 j_printerr("Warning: PCM latency of %d ms (%d bytes) too large, use device maximum %d bytes\n", latency, periodsize, maxsize);
00206 periodsize = maxsize;
00207 }
00208
00209
00210 #if (SND_LIB_MAJOR == 0)
00211 actual_size = snd_pcm_hw_params_set_period_size_near(handle, hwparams, periodsize, &dir);
00212 if (actual_size < 0) {
00213 j_printerr("Error: cannot set PCM record period size to %d (%s)\n", periodsize, snd_strerror(actual_size));
00214 return(FALSE);
00215 }
00216 #else
00217 actual_size = periodsize;
00218 err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &actual_size, &dir);
00219 if (err < 0) {
00220 j_printerr("Error: cannot set PCM record period size to %d (%s)\n", periodsize, snd_strerror(err));
00221 return(FALSE);
00222 }
00223 #endif
00224 if (actual_size != periodsize) {
00225 j_printerr("Warning: PCM period size: %d bytes (%d ms) -> %d bytes\n", periodsize, latency, actual_size);
00226 }
00227
00228 if ((err = snd_pcm_hw_params_set_periods(handle, hwparams, 2, 0)) < 0) {
00229 j_printerr("Error: cannot set PCM number of periods to %d (%s)\n", 1, snd_strerror(err));
00230 return(FALSE);
00231 }
00232 }
00233
00234
00235 if ((err = snd_pcm_hw_params(handle, hwparams)) < 0) {
00236 j_printerr("Error: cannot set PCM hardware parameters (%s)\n", snd_strerror(err));
00237 return(FALSE);
00238 }
00239
00240
00241 if ((err = snd_pcm_prepare(handle)) < 0) {
00242 j_printerr("Error: cannot prepare audio interface (%s)\n", snd_strerror(err));
00243 }
00244
00245
00246 count = snd_pcm_poll_descriptors_count(handle);
00247 if (count <= 0) {
00248 j_printerr("Error: invalid PCM poll descriptors count\n");
00249 return(FALSE);
00250 }
00251 ufds = mymalloc(sizeof(struct pollfd) * count);
00252
00253 if ((err = snd_pcm_poll_descriptors(handle, ufds, count)) < 0) {
00254 j_printerr("Error: unable to obtain poll descriptors for PCM recording (%s)\n", snd_strerror(err));
00255 return(FALSE);
00256 }
00257
00258 return(TRUE);
00259 }
00260
00269 static int
00270 xrun_recovery(snd_pcm_t *handle, int err)
00271 {
00272 if (err == -EPIPE) {
00273 err = snd_pcm_prepare(handle);
00274 if (err < 0)
00275 j_printerr("Can't recovery from PCM buffer underrun, prepare failed: %s\n", snd_strerror(err));
00276 return 0;
00277 } else if (err == -ESTRPIPE) {
00278 while ((err = snd_pcm_resume(handle)) == -EAGAIN)
00279 sleep(1);
00280 if (err < 0) {
00281 err = snd_pcm_prepare(handle);
00282 if (err < 0)
00283 j_printerr("Can't recovery from PCM buffer suspend, prepare failed: %s\n", snd_strerror(err));
00284 }
00285 return 0;
00286 }
00287 return err;
00288 }
00289
00295 boolean
00296 adin_mic_start()
00297 {
00298 int err;
00299 snd_pcm_state_t status;
00300
00301
00302 while(1) {
00303 status = snd_pcm_state(handle);
00304 switch(status) {
00305 case SND_PCM_STATE_PREPARED:
00306 if ((err = snd_pcm_start(handle)) < 0) {
00307 j_printerr("Error: cannot start PCM (%s)\n", snd_strerror(err));
00308 return (FALSE);
00309 }
00310 return(TRUE);
00311 break;
00312 case SND_PCM_STATE_RUNNING:
00313 if ((err = snd_pcm_drop(handle)) < 0) {
00314 j_printerr("Error: cannot drop PCM (%s)\n", snd_strerror(err));
00315 return (FALSE);
00316 }
00317 break;
00318 case SND_PCM_STATE_XRUN:
00319 if ((err = xrun_recovery(handle, -EPIPE)) < 0) {
00320 j_printerr("Error: PCM XRUN recovery failed (%s)\n", snd_strerror(err));
00321 return(FALSE);
00322 }
00323 break;
00324 case SND_PCM_STATE_SUSPENDED:
00325 if ((err = xrun_recovery(handle, -ESTRPIPE)) < 0) {
00326 j_printerr("Error: PCM XRUN recovery failed (%s)\n", snd_strerror(err));
00327 return(FALSE);
00328 }
00329 break;
00330 }
00331 }
00332
00333 return(TRUE);
00334 }
00335
00341 boolean
00342 adin_mic_stop()
00343 {
00344 return(TRUE);
00345 }
00346
00359 int
00360 adin_mic_read(SP16 *buf, int sampnum)
00361 {
00362 int cnt;
00363 snd_pcm_sframes_t avail;
00364
00365 while ((avail = snd_pcm_avail_update(handle)) <= 0) {
00366 usleep(latency * 1000);
00367 }
00368 if (avail < sampnum) {
00369 cnt = snd_pcm_readi(handle, buf, avail);
00370 } else {
00371 cnt = snd_pcm_readi(handle, buf, sampnum);
00372 }
00373
00374 if (cnt < 0) {
00375 j_printerr("Error: PCM read failed (%s)\n", snd_strerror(cnt));
00376 return(-2);
00377 }
00378
00379 if (need_swap) {
00380 swap_sample_bytes(buf, cnt);
00381 }
00382 return(cnt);
00383 }