/* * Might be called from signal handlers. */ int alsa_hook_writen(alsa_hook_t alsa_hook, snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size) { struct alsa_hook_stream_s *stream; int c, ret = 0; int savedErrno = errno; if (!(alsa_hook->flags & ALSA_HOOK_CAPTURING)) goto leave; alsa_hook_get_stream(alsa_hook, pcm, &stream); if (unlikely(!stream->initialized)) { ret = EINVAL; goto leave; } if (unlikely((ret = alsa_hook_lock_write(alsa_hook, stream)))) goto leave; if (unlikely(stream->flags & GLC_AUDIO_INTERLEAVED)) { if (!(stream->mode & SND_PCM_ASYNC)) glc_log(alsa_hook->glc, GLC_ERROR, "alsa_hook", "stream format (interleaved) incompatible with snd_pcm_writen()"); ret = EINVAL; goto unlock; } if (unlikely((ret = alsa_hook_wait_for_thread(alsa_hook, stream)))) goto unlock; if (unlikely((ret = alsa_hook_set_data_size(stream, snd_pcm_frames_to_bytes(pcm, size))))) goto unlock; stream->capture_time = glc_state_time(alsa_hook->glc); for (c = 0; c < stream->channels; c++) memcpy(&stream->capture_data[c * snd_pcm_samples_to_bytes(pcm, size)], bufs[c], snd_pcm_samples_to_bytes(pcm, size)); sem_post(&stream->capture_full); unlock: alsa_hook_unlock_write(alsa_hook, stream); leave: errno = savedErrno; return ret; }
static int write_device(AudioDevice *ad, unsigned char *data, int size) { ALSA_data *alsa = (ALSA_data *)ad->private_data; snd_pcm_sframes_t r; ssize_t unit = snd_pcm_samples_to_bytes(alsa->fd, 1) * ad->channels; snd_pcm_uframes_t count = size / unit; while (count > 0) { if ((r = snd_pcm_writei(alsa->fd, data, count)) == -EAGAIN) { //debug_message_fnc(" EAGAIN\n"); snd_pcm_wait(alsa->fd, 1000); } else if (r > 0) { //debug_message_fnc(" wrote %d bytes\n", (int)(r * unit)); ad->bytes_written += r * unit; count -= r; data += r * unit; } else if (r == -EPIPE) { debug_message_fnc("EPIPE: "); if (snd_pcm_state(alsa->fd) == SND_PCM_STATE_XRUN) { if ((r = snd_pcm_prepare(alsa->fd)) < 0) { debug_message("failed\n"); warning_fnc("snd_pcm_prepare() failed."); } else { debug_message("OK\n"); } } } else { warning_fnc(" r = %d < 0...\n", (int)r); } } return 1; }
static int bytes_written(AudioDevice *ad) { ALSA_data *alsa = (ALSA_data *)ad->private_data; snd_pcm_sframes_t delay; snd_pcm_state_t state; if (!alsa) return -1; state = snd_pcm_state(alsa->fd); if (state == SND_PCM_STATE_OPEN || state == SND_PCM_STATE_SETUP) return -1; if (snd_pcm_delay(alsa->fd, &delay) < 0) warning_fnc("snd_pcm_delay() failed.\n"); return ad->bytes_written - snd_pcm_samples_to_bytes(alsa->fd, delay) * ad->channels; }
int alsa_hook_complex_to_interleaved(struct alsa_hook_stream_s *stream, const snd_pcm_channel_area_t *areas, snd_pcm_uframes_t offset, snd_pcm_uframes_t frames, char *to) { /** \todo test this... :D */ /** \note this is quite expensive operation */ unsigned int c; size_t s, off, add, ssize; add = snd_pcm_frames_to_bytes(stream->pcm, 1); ssize = snd_pcm_samples_to_bytes(stream->pcm, 1); for (c = 0; c < stream->channels; c++) { off = add * c; for (s = 0; s < frames; s++) { memcpy(&to[off], alsa_hook_mmap_pos(&areas[c], offset + s), ssize); off += add; } } return 0; }
int alsa_play_play(alsa_play_t alsa_play, glc_audio_data_header_t *audio_hdr, char *data) { snd_pcm_uframes_t frames, rem; snd_pcm_sframes_t ret = 0; unsigned int c; if (audio_hdr->id != alsa_play->id) return 0; if (!alsa_play->pcm) { glc_log(alsa_play->glc, GLC_ERROR, "alsa_play", "broken stream %d", alsa_play->id); return EINVAL; } frames = snd_pcm_bytes_to_frames(alsa_play->pcm, audio_hdr->size); glc_utime_t time = glc_state_time(alsa_play->glc); glc_utime_t duration = ((glc_utime_t) 1000000000 * (glc_utime_t) frames) / (glc_utime_t) alsa_play->rate; if (time + alsa_play->silence_threshold + duration < audio_hdr->time) { struct timespec ts = { .tv_sec = (audio_hdr->time - time - duration - alsa_play->silence_threshold)/1000000000, .tv_nsec = (audio_hdr->time - time - duration - alsa_play->silence_threshold)%1000000000 }; nanosleep(&ts,NULL); } /* * This condition determine what will be the initial audio packet. * it is preferable to be ahead by < duration/2 than behind * the video by > duration/2 */ else if (time > audio_hdr->time + duration/2) { glc_log(alsa_play->glc, GLC_DEBUG, "alsa_play", "dropped packet. now %" PRId64 " ts %" PRId64, time, audio_hdr->time); return 0; } rem = frames; while (rem > 0) { /* alsa is horrible... */ /*snd_pcm_wait(alsa_play->pcm, duration);*/ if (alsa_play->flags & GLC_AUDIO_INTERLEAVED) ret = snd_pcm_writei(alsa_play->pcm, &data[snd_pcm_frames_to_bytes(alsa_play->pcm, frames - rem)], rem); else { for (c = 0; c < alsa_play->channels; c++) alsa_play->bufs[c] = &data[snd_pcm_samples_to_bytes(alsa_play->pcm, frames) * c + snd_pcm_samples_to_bytes(alsa_play->pcm, frames - rem)]; ret = snd_pcm_writen(alsa_play->pcm, alsa_play->bufs, rem); } if (ret == 0) break; if ((ret == -EBUSY) || (ret == -EAGAIN)) break; else if (ret < 0) { if ((ret = alsa_play_xrun(alsa_play, ret))) { glc_log(alsa_play->glc, GLC_ERROR, "alsa_play", "xrun recovery failed: %s", snd_strerror(-ret)); return ret; } } else rem -= ret; } return 0; }
/* * Might be called from signal handlers. */ int alsa_hook_mmap_commit(alsa_hook_t alsa_hook, snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t frames) { struct alsa_hook_stream_s *stream; unsigned int c; int ret = 0; int savedErrno = errno; if (!(alsa_hook->flags & ALSA_HOOK_CAPTURING)) goto leave; alsa_hook_get_stream(alsa_hook, pcm, &stream); if (unlikely((ret = alsa_hook_lock_write(alsa_hook, stream)))) goto leave; if (unlikely(stream->channels == 0)) goto unlock; /* 0 channels :P */ if (unlikely(!stream->mmap_areas)) { /* this might actually happen */ if (!(stream->mode & SND_PCM_ASYNC)) glc_log(alsa_hook->glc, GLC_WARN, "alsa_hook", "snd_pcm_mmap_commit() before snd_pcm_mmap_begin()"); goto unlock; } if (unlikely(offset != stream->offset)) if (!(stream->mode & SND_PCM_ASYNC)) glc_log(alsa_hook->glc, GLC_WARN, "alsa_hook", "offset=%lu != stream->offset=%lu", offset, stream->offset); if (unlikely((ret = alsa_hook_wait_for_thread(alsa_hook, stream)))) goto unlock; if (unlikely((ret = alsa_hook_set_data_size(stream, snd_pcm_frames_to_bytes(pcm, frames))))) goto unlock; stream->capture_time = glc_state_time(alsa_hook->glc); if (stream->flags & GLC_AUDIO_INTERLEAVED) { memcpy(stream->capture_data, alsa_hook_mmap_pos(stream->mmap_areas, offset), stream->capture_size); } else if (stream->complex) { alsa_hook_complex_to_interleaved(stream, stream->mmap_areas, offset, frames, stream->capture_data); } else { for (c = 0; c < stream->channels; c++) memcpy(&stream->capture_data[c * snd_pcm_samples_to_bytes(stream->pcm, frames)], alsa_hook_mmap_pos(&stream->mmap_areas[c], offset), snd_pcm_samples_to_bytes(stream->pcm, frames)); } sem_post(&stream->capture_full); unlock: alsa_hook_unlock_write(alsa_hook, stream); leave: errno = savedErrno; return ret; }
static int set_params(AudioDevice *ad, AudioFormat *format_p, int *ch_p, unsigned int *rate_p) { ALSA_data *alsa = (ALSA_data *)ad->private_data; int r, err; snd_pcm_format_t f; AudioFormat format = *format_p; snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; int ch = *ch_p; unsigned int rate = *rate_p; unsigned int buffer, period; snd_pcm_hw_params_alloca(&hwparams); snd_pcm_sw_params_alloca(&swparams); ad->format = _AUDIO_FORMAT_UNSET; *format_p = _AUDIO_FORMAT_UNSET; *ch_p = 0; *rate_p = 0; /* format part */ switch (format) { case _AUDIO_FORMAT_MU_LAW: f = SND_PCM_FORMAT_MU_LAW; break; case _AUDIO_FORMAT_A_LAW: f = SND_PCM_FORMAT_A_LAW; break; case _AUDIO_FORMAT_ADPCM: f = SND_PCM_FORMAT_IMA_ADPCM; break; case _AUDIO_FORMAT_U8: f = SND_PCM_FORMAT_U8; break; case _AUDIO_FORMAT_S8: f = SND_PCM_FORMAT_S8; break; case _AUDIO_FORMAT_U16_LE: f = SND_PCM_FORMAT_U16_LE; break; case _AUDIO_FORMAT_U16_BE: f = SND_PCM_FORMAT_U16_BE; break; case _AUDIO_FORMAT_S16_LE: f = SND_PCM_FORMAT_S16_LE; break; case _AUDIO_FORMAT_S16_BE: f = SND_PCM_FORMAT_S16_BE; break; #if 0 case _AUDIO_FORMAT_S32_LE: f = SND_PCM_FORMAT_U32_LE; break; case _AUDIO_FORMAT_S32_BE: f = SND_PCM_FORMAT_S32_BE; break; #endif default: show_message_fnc("format %d is invalid.\n", format); return 0; } if ((err = snd_pcm_hw_params_any(alsa->fd, hwparams)) < 0) { show_message_fnc("snd_pcm_hw_params_any() failed.\n"); return 0; } if ((err = snd_pcm_hw_params_set_access(alsa->fd, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { show_message_fnc("snd_pcm_hw_params_set_access() failed.\n"); return 0; } if ((err = snd_pcm_hw_params_set_format(alsa->fd, hwparams, f)) < 0) { show_message_fnc("snd_pcm_hw_params_set_format() failed.\n"); return 0; } ad->format = format; *format_p = format; /* channel part */ if ((err = snd_pcm_hw_params_set_channels(alsa->fd, hwparams, ch)) < 0) { show_message_fnc("snd_pcm_hw_params_set_channels() failed.\n"); return 0; } *ch_p = ch; /* rate part */ if ((err = snd_pcm_hw_params_set_rate_near(alsa->fd, hwparams, &rate, 0)) < 0) { show_message_fnc("snd_pcm_hw_params_set_rate_near() failed.\n"); return 0; } *rate_p = rate; /* buffer & period */ buffer = 500000; period = 500000 / 4; if ((err = snd_pcm_hw_params_set_buffer_time_near(alsa->fd, hwparams, &buffer, 0)) < 0) { show_message_fnc("snd_pcm_hw_params_set_buffer_near() failed.\n"); return 0; } r = snd_pcm_hw_params_get_buffer_size(hwparams, &alsa->buffer_size); if ((err = snd_pcm_hw_params_set_period_time_near(alsa->fd, hwparams, &period, 0)) < 0) { show_message_fnc("snd_pcm_hw_params_set_period_near() failed.\n"); return 0; } r = snd_pcm_hw_params_get_period_size(hwparams, &alsa->period_size, 0); if ((err = snd_pcm_hw_params(alsa->fd, hwparams)) < 0) { perror("snd_pcm_hw_params"); err_message_fnc("snd_pcm_hw_params() failed.\n"); snd_pcm_hw_params_dump(hwparams, alsa->log); return 0; } r = snd_pcm_hw_params_get_channels(hwparams, &ad->channels); r = snd_pcm_hw_params_get_rate(hwparams, &ad->speed, 0); r = r; // dummy #ifdef DEBUG { snd_pcm_format_t form; debug_message_fnc("format "); r = snd_pcm_hw_params_get_format(hwparams, &form); switch (form) { case SND_PCM_FORMAT_MU_LAW: debug_message("MU_LAW "); break; case SND_PCM_FORMAT_A_LAW: debug_message("A_LAW "); break; case SND_PCM_FORMAT_IMA_ADPCM: debug_message("ADPCM "); break; case SND_PCM_FORMAT_U8: debug_message("U8 "); break; case SND_PCM_FORMAT_S8: debug_message("S8 "); break; case SND_PCM_FORMAT_U16_LE: debug_message("U16LE "); break; case SND_PCM_FORMAT_U16_BE: debug_message("U16BE "); break; case SND_PCM_FORMAT_S16_LE: debug_message("S16LE "); break; case SND_PCM_FORMAT_S16_BE: debug_message("S16BE "); break; #if 0 case SND_PCM_FORMAT_U32_LE: debug_message("U32LE "); break; case SND_PCM_FORMAT_S32_BE: debug_message("S32BE "); break; #endif default: debug_message("UNKNOWN "); break; } debug_message("%d ch %d Hz buffer %ld period %ld OK\n", ad->channels, ad->speed, alsa->buffer_size, alsa->period_size); } #endif /* sw_params */ if ((err = snd_pcm_sw_params_current(alsa->fd, swparams)) < 0) { show_message_fnc("snd_pcm_sw_params_any() failed.\n"); return 0; } if ((err = snd_pcm_sw_params(alsa->fd, swparams)) < 0) { show_message_fnc("snd_pcm_sw_params() failed.\n"); return 0; } debug_message_fnc("1 sample -> %ld bytes\n", snd_pcm_samples_to_bytes(alsa->fd, 1)); return 1; }