/** * Record */ void *record_alsa(struct bat *bat) { int err = 0; FILE *fp = NULL; struct snd_pcm_container sndpcm; struct wav_container wav; int size, offset, count, frames; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); printf("Entering capture thread (ALSA).\n"); memset(&sndpcm, 0, sizeof(sndpcm)); if (NULL != bat->capture.device) { err = snd_pcm_open(&sndpcm.handle, bat->capture.device, SND_PCM_STREAM_CAPTURE, 0); if (err < 0) { loge(E_OPENPCMC, "%s(%d)", snd_strerror(err), err); goto fail_exit; } } else { loge(E_OPENPCMC, "exit"); goto fail_exit; } err = set_snd_pcm_params(bat, &sndpcm); if (err != 0) goto fail_exit; prepare_wav_info(&wav, bat); remove(bat->capture.file); fp = fopen(bat->capture.file, "w+"); if (NULL == fp) { loge(E_OPENFILEC, "%s", bat->capture.file); goto fail_exit; } pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); pthread_cleanup_push(close_handle, sndpcm.handle); pthread_cleanup_push(destroy_mem, sndpcm.buffer); pthread_cleanup_push((void *)close_file, fp); if (fwrite(&wav.header, 1, sizeof(wav.header), fp) != sizeof(wav.header)) { loge(E_WRITEFILE, "header %s(%d)", snd_strerror(err), err); goto fail_exit; } if (fwrite(&wav.format, 1, sizeof(wav.format), fp) != sizeof(wav.format)) { loge(E_WRITEFILE, "format %s(%d)", snd_strerror(err), err); goto fail_exit; } if (fwrite(&wav.chunk, 1, sizeof(wav.chunk), fp) != sizeof(wav.chunk)) { loge(E_WRITEFILE, "chunk %s(%d)", snd_strerror(err), err); goto fail_exit; } count = wav.chunk.length; printf("Recording ...\n"); while (count > 0) { size = (count <= sndpcm.period_bytes) ? count : sndpcm.period_bytes; frames = size * 8 / sndpcm.frame_bits; offset = 0; while (frames > 0) { err = snd_pcm_readi(sndpcm.handle, sndpcm.buffer + offset, frames); if (err == -EAGAIN || (err >= 0 && err < frames)) { snd_pcm_wait(sndpcm.handle, 500); } else if (err == -EPIPE) { snd_pcm_prepare(sndpcm.handle); loge(E_READPCM S_OVERRUN, "%s(%d)", snd_strerror(err), err); } else if (err < 0) { loge(E_READPCM, "%s(%d)", snd_strerror(err), err); goto fail_exit; } if (err > 0) { frames -= err; offset += err * sndpcm.frame_bits / 8; } } if (fwrite(sndpcm.buffer, 1, size, fp) != size) { loge(E_WRITEFILE, "%s(%d)", snd_strerror(err), err); goto fail_exit; } count -= size; bat->periods_played++; if (bat->period_limit && bat->periods_played >= bat->periods_total) break; } /* Normally we will never reach this part of code (before fail_exit) as this thread will be cancelled by end of play thread. */ pthread_cleanup_pop(0); pthread_cleanup_pop(0); pthread_cleanup_pop(0); snd_pcm_drain(sndpcm.handle); fclose(fp); free(sndpcm.buffer); snd_pcm_close(sndpcm.handle); retval_record = 0; pthread_exit(&retval_record); fail_exit: if (fp) fclose(fp); if (sndpcm.buffer) free(sndpcm.buffer); if (sndpcm.handle) snd_pcm_close(sndpcm.handle); retval_record = 1; pthread_exit(&retval_record); }
/** * Record */ void *record_alsa(struct bat *bat) { int err = 0; FILE *fp = NULL; struct pcm_container sndpcm; struct wav_container wav; int count; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); fprintf(bat->log, _("Entering capture thread (ALSA).\n")); retval_record = 0; memset(&sndpcm, 0, sizeof(sndpcm)); if (bat->capture.device == NULL) { fprintf(bat->err, _("No PCM device for capture: exit\n")); retval_record = 1; goto exit1; } err = snd_pcm_open(&sndpcm.handle, bat->capture.device, SND_PCM_STREAM_CAPTURE, 0); if (err != 0) { fprintf(bat->err, _("Cannot open PCM capture device: ")); fprintf(bat->err, _("%s(%d)\n"), snd_strerror(err), err); retval_record = 1; goto exit1; } err = set_snd_pcm_params(bat, &sndpcm); if (err != 0) { retval_record = 1; goto exit2; } remove(bat->capture.file); fp = fopen(bat->capture.file, "w+"); if (fp == NULL) { fprintf(bat->err, _("Cannot open file for capture: %s %d\n"), bat->capture.file, -errno); retval_record = 1; goto exit3; } prepare_wav_info(&wav, bat); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); pthread_cleanup_push(snd_pcm_close, sndpcm.handle); pthread_cleanup_push(free, sndpcm.buffer); pthread_cleanup_push(fclose, fp); err = write_wav_header(fp, &wav, bat); if (err != 0) { retval_record = 1; goto exit4; } count = wav.chunk.length; fprintf(bat->log, _("Recording ...\n")); err = read_from_pcm_loop(fp, count, &sndpcm, bat); if (err != 0) { retval_record = 1; goto exit4; } /* Normally we will never reach this part of code (before fail_exit) as this thread will be cancelled by end of play thread. */ pthread_cleanup_pop(0); pthread_cleanup_pop(0); pthread_cleanup_pop(0); snd_pcm_drain(sndpcm.handle); exit4: fclose(fp); exit3: free(sndpcm.buffer); exit2: snd_pcm_close(sndpcm.handle); exit1: pthread_exit(&retval_record); }
/** * Play */ void *playback_alsa(struct bat *bat) { int err = 0; struct snd_pcm_container sndpcm; int size, offset, count; printf("Entering playback thread (ALSA).\n"); memset(&sndpcm, 0, sizeof(sndpcm)); if (NULL != bat->playback.device) { err = snd_pcm_open(&sndpcm.handle, bat->playback.device, SND_PCM_STREAM_PLAYBACK, 0); if (err < 0) { loge(E_OPENPCMP, "%s(%d)", snd_strerror(err), err); goto fail_exit; } } else { loge(E_NOPCMP, "exit"); goto fail_exit; } err = set_snd_pcm_params(bat, &sndpcm); if (err != 0) goto fail_exit; if (bat->playback.file == NULL) { printf("Playing generated audio sine wave"); bat->sinus_duration == 0 ? printf(" endlessly\n") : printf("\n"); } else { printf("Playing input audio file: %s\n", bat->playback.file); bat->fp = fopen(bat->playback.file, "rb"); if (bat->fp == NULL) { loge(E_OPENFILEC, "%s", bat->playback.file); goto fail_exit; } } count = sndpcm.period_bytes; /* playback buffer size */ #ifdef DEBUG FILE *sin_file; sin_file = fopen("/tmp/sin.wav", "wb"); #endif while (1) { offset = 0; size = count * 8 / sndpcm.frame_bits; err = generate_input_data(sndpcm, count, bat); if (err < 0) goto fail_exit; else if (err > 0) break; #ifdef DEBUG fwrite(sndpcm.buffer, count * 8 / sndpcm.frame_bits, 4, sin_file); #endif if (bat->period_limit && bat->periods_played >= bat->periods_total) break; err = write_to_pcm(size, &sndpcm, offset); if (err == -1) goto fail_exit; } #ifdef DEBUG fclose(sin_file); #endif snd_pcm_drain(sndpcm.handle); if (bat->fp) fclose(bat->fp); free(sndpcm.buffer); snd_pcm_close(sndpcm.handle); retval_play = 0; pthread_exit(&retval_play); fail_exit: if (bat->fp) fclose(bat->fp); if (sndpcm.buffer) free(sndpcm.buffer); if (sndpcm.handle) snd_pcm_close(sndpcm.handle); retval_play = 1; pthread_exit(&retval_play); }
/** * Play */ void *playback_alsa(struct bat *bat) { int err = 0; struct pcm_container sndpcm; fprintf(bat->log, _("Entering playback thread (ALSA).\n")); retval_play = 0; memset(&sndpcm, 0, sizeof(sndpcm)); if (bat->playback.device == NULL) { fprintf(bat->err, _("No PCM device for playback: exit\n")); retval_play = 1; goto exit1; } err = snd_pcm_open(&sndpcm.handle, bat->playback.device, SND_PCM_STREAM_PLAYBACK, 0); if (err != 0) { fprintf(bat->err, _("Cannot open PCM playback device: ")); fprintf(bat->err, _("%s(%d)\n"), snd_strerror(err), err); retval_play = 1; goto exit1; } err = set_snd_pcm_params(bat, &sndpcm); if (err != 0) { retval_play = 1; goto exit2; } if (bat->playback.file == NULL) { fprintf(bat->log, _("Playing generated audio sine wave")); bat->sinus_duration == 0 ? fprintf(bat->log, _(" endlessly\n")) : fprintf(bat->log, _("\n")); } else { fprintf(bat->log, _("Playing input audio file: %s\n"), bat->playback.file); bat->fp = fopen(bat->playback.file, "rb"); if (bat->fp == NULL) { fprintf(bat->err, _("Cannot open file for capture: ")); fprintf(bat->err, _("%s %d\n"), bat->playback.file, -errno); retval_play = 1; goto exit3; } /* Skip header */ err = read_wav_header(bat, bat->playback.file, bat->fp, true); if (err != 0) { retval_play = 1; goto exit4; } } err = write_to_pcm_loop(&sndpcm, bat); if (err != 0) { retval_play = 1; goto exit4; } exit4: if (bat->playback.file) fclose(bat->fp); exit3: free(sndpcm.buffer); exit2: snd_pcm_close(sndpcm.handle); exit1: pthread_exit(&retval_play); }