/* * vim: softtabstop=4 shiftwidth=4 cindent foldmethod=marker expandtab * * $LastChangedDate$ * $Revision$ * $LastChangedBy$ * $URL$ * * Copyright 2009-2011 Eric Connell * Copyright 2010-2011 Roman Tetelman * * This file is part of Mangler. * * Mangler is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Mangler is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Mangler. If not, see . */ #include "mangler.h" #ifdef HAVE_ALSA #include "mangleraudio.h" #include "mangleralsa.h" ManglerAlsa::ManglerAlsa(uint32_t rate, uint8_t channels, uint32_t pcm_framesize) {/*{{{*/ alsa_stream = NULL; this->pcm_framesize = pcm_framesize; this->channels = channels; }/*}}}*/ ManglerAlsa::~ManglerAlsa() {/*{{{*/ close(); }/*}}}*/ bool ManglerAlsa::open(int type, Glib::ustring device, int rate, int channels) {/*{{{*/ if ((alsa_error = snd_pcm_open( &alsa_stream, (device == "") ? "default" : device.c_str(), (type >= AUDIO_OUTPUT) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE, 0)) < 0) { fprintf(stderr, "alsa: snd_pcm_open() failed: %s\n", snd_strerror(alsa_error)); alsa_stream = NULL; return false; } if ((alsa_error = snd_pcm_set_params( alsa_stream, // pcm handle SND_PCM_FORMAT_S16_LE, // format SND_PCM_ACCESS_RW_INTERLEAVED, // access channels, // channels rate, // rate true, // soft_resample 150000)) < 0) { // latency in usec (0.15 sec) fprintf(stderr, "alsa: snd_pcm_set_params() failed: %s\n", snd_strerror(alsa_error)); close(); return false; } if ((alsa_error = snd_pcm_prepare(alsa_stream)) < 0) { fprintf(stderr, "alsa: snd_pcm_prepare() failed: %s\n", snd_strerror(alsa_error)); close(); return false; } if (type == AUDIO_INPUT && (alsa_error = snd_pcm_start(alsa_stream)) < 0) { fprintf(stderr, "alsa: snd_pcm_start() failed: %s\n", snd_strerror(alsa_error)); close(); return false; } return true; }/*}}}*/ void ManglerAlsa::close(bool drain) {/*{{{*/ if (alsa_stream) { if (drain) { snd_pcm_drain(alsa_stream); } snd_pcm_close(alsa_stream); alsa_stream = NULL; } }/*}}}*/ bool ManglerAlsa::write(uint8_t *sample, uint32_t length, int channels) {/*{{{*/ uint32_t buflen; uint32_t pcmlen = length; uint8_t *pcmptr = sample; while ((buflen = pcmlen >= ALSA_BUF ? ALSA_BUF : pcmlen)) { if ((alsa_frames = snd_pcm_writei(alsa_stream, pcmptr, buflen / (sizeof(int16_t) * channels))) < 0) { if (alsa_frames == -EPIPE) { snd_pcm_prepare(alsa_stream); } else if ((alsa_error = snd_pcm_recover(alsa_stream, alsa_frames, 0)) < 0) { fprintf(stderr, "alsa: snd_pcm_writei() failed: %s\n", snd_strerror(alsa_error)); return false; } } pcmlen -= buflen; pcmptr += buflen; } return true; }/*}}}*/ bool ManglerAlsa::read(uint8_t *buf) {/*{{{*/ if ((alsa_frames = snd_pcm_readi(alsa_stream, buf, pcm_framesize / (sizeof(int16_t) * channels))) < 0) { if (alsa_frames == -EPIPE) { snd_pcm_prepare(alsa_stream); } else if ((alsa_error = snd_pcm_recover(alsa_stream, alsa_frames, 0)) < 0) { fprintf(stderr, "alsa: snd_pcm_readi() failed: %s\n", snd_strerror(alsa_error)); return false; } } return true; }/*}}}*/ Glib::ustring ManglerAlsa::getAudioSubsystem(void) {/*{{{*/ return Glib::ustring("alsa"); }/*}}}*/ void ManglerAlsa::getDeviceList(std::vector& inputDevices, std::vector& outputDevices) {/*{{{*/ snd_pcm_stream_t stream[2] = { SND_PCM_STREAM_PLAYBACK, SND_PCM_STREAM_CAPTURE }; int ctr; for (ctr = 0; ctr < 2; ctr++) { // the rest is just copypasta, with bad code from alsa snd_ctl_t *handle; int card, err, dev, idx_p = 0, idx_c = 0; snd_ctl_card_info_t *info; snd_pcm_info_t *pcminfo; card = -1; snd_ctl_card_info_alloca(&info); snd_pcm_info_alloca(&pcminfo); if (snd_card_next(&card) < 0 || card < 0) { fputs("alsa: no sound cards found!\n", stderr); return; } while (card >= 0) { char hw[256] = ""; snprintf(hw, 255, "hw:%i", card); if ((err = snd_ctl_open(&handle, hw, 0)) < 0) { fprintf(stderr, "alsa: control open (%i): %s\n", card, snd_strerror(err)); if (snd_card_next(&card) < 0) { fprintf(stderr, "alsa: snd_ctl_open: snd_card_next\n"); break; } continue; } if ((err = snd_ctl_card_info(handle, info)) < 0) { fprintf(stderr, "alsa: control hardware info (%i): %s\n", card, snd_strerror(err)); snd_ctl_close(handle); if (snd_card_next(&card) < 0) { fprintf(stderr, "alsa: snd_ctl_card_info: snd_card_next\n"); break; } continue; } dev = -1; for (;;) { if (snd_ctl_pcm_next_device(handle, &dev) < 0) { fprintf(stderr, "alsa: snd_ctl_pcm_next_device\n"); } if (dev < 0) { break; } snd_pcm_info_set_device(pcminfo, dev); snd_pcm_info_set_subdevice(pcminfo, 0); snd_pcm_info_set_stream(pcminfo, stream[ctr]); if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) { if (err != -ENOENT) { fprintf(stderr, "alsa: control digital audio info (%i): %s\n", card, snd_strerror(err)); } continue; } char name[256] = "", desc[512] = ""; snprintf(name, 255, "hw:%i,%i", card, dev); snprintf(desc, 511, "%s: %s (%s)", snd_ctl_card_info_get_name(info), snd_pcm_info_get_name(pcminfo), name ); switch (stream[ctr]) { case SND_PCM_STREAM_PLAYBACK: outputDevices.push_back( new ManglerAudioDevice( idx_p++, name, desc) ); break; case SND_PCM_STREAM_CAPTURE: inputDevices.push_back( new ManglerAudioDevice( idx_c++, name, desc) ); break; } } snd_ctl_close(handle); if (snd_card_next(&card) < 0) { fprintf(stderr, "alsa: snd_card_next\n"); break; } } } }/*}}}*/ #endif