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