From 6253e37c2f0219d61193d0d405e7f23a4bae3287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Gl=C3=B6ckner?= Date: Thu, 21 Jan 2016 18:00:28 +0100 Subject: Added AlsaOutputEngine --- drumgizmo/output/alsa.cc | 141 +++++++++++++++++++++++++++++++++++++++++++++++ drumgizmo/output/alsa.h | 62 +++++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 drumgizmo/output/alsa.cc create mode 100644 drumgizmo/output/alsa.h (limited to 'drumgizmo/output') diff --git a/drumgizmo/output/alsa.cc b/drumgizmo/output/alsa.cc new file mode 100644 index 0000000..d6a2f19 --- /dev/null +++ b/drumgizmo/output/alsa.cc @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * alsa.cc + * + * Do 21. Jan 16:48:32 CET 2016 + * Copyright 2016 Christian Glöckner + * cgloeckner@freenet.de + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo 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 2 of the License, or + * (at your option) any later version. + * + * DrumGizmo 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 DrumGizmo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "alsa.h" + +int const BUFFER_SIZE = 40960; + +struct AlsaInitError { + int const code; + std::string const msg; + + AlsaInitError(int op_code, std::string const & msg) + : code{code} + , msg{msg} { + } + + static inline void test(int code, std::string const & msg) { + if (code < 0) { + throw AlsaInitError(code, msg); + } + } +}; + +AlsaOutputEngine::AlsaOutputEngine() + : handle{nullptr} + , params{nullptr} + , data{} + , num_channels{0u} + , dev{"default"} + , srate{44100} + , frames{32} { +} + +AlsaOutputEngine::~AlsaOutputEngine() { + if (params) { + // snd_pcm_hw_params_alloca uses std alloc + free(params); + } + if (handle != nullptr) { + snd_pcm_close(handle); + } +} + +bool AlsaOutputEngine::init(Channels channels) { + // try to initialize alsa + try { + int value = snd_pcm_open(&handle, dev.c_str(), SND_PCM_STREAM_PLAYBACK, 0); + AlsaInitError::test(value, "snd_pcm_open"); + num_channels = channels.size(); + if (handle == nullptr) { + printf("No handle!\n"); + return false; + } + // Allocate and init a hardware parameters object + snd_pcm_hw_params_alloca(¶ms); + value = snd_pcm_hw_params_any(handle, params); + AlsaInitError::test(value, "snd_pcm_hw_params_any"); + + value = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); + AlsaInitError::test(value, "snd_pcm_hw_params_set_access"); + value = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_FLOAT); + AlsaInitError::test(value, "snd_pcm_hw_params_set_format"); + value = snd_pcm_hw_params_set_channels(handle, params, num_channels); + AlsaInitError::test(value, "snd_pcm_hw_params_set_channels"); + value = snd_pcm_hw_params_set_rate_near(handle, params, &srate, 0); + AlsaInitError::test(value, "snd_pcm_hw_params_set_rate_near"); + value = snd_pcm_hw_params_set_period_size_near(handle, params, &frames, 0); + AlsaInitError::test(value, "snd_pcm_hw_params_set_period_size_near"); + value = snd_pcm_hw_params(handle, params); + AlsaInitError::test(value, "snd_pcm_hw_params"); + + } catch (AlsaInitError const & error) { + printf("%s failed: %s\n", error.msg.c_str(), snd_strerror(error.code)); + fflush(stdout); + return false; + } + + data.clear(); + data.resize(BUFFER_SIZE * num_channels); + + return true; +} + +void AlsaOutputEngine::setParm(std::string parm, std::string value) { + if (parm == "dev") { + dev = value; + } else if (parm == "frames") { + frames = std::stoi(value); + } else if (parm == "srate") { + srate = std::stoi(value); + } +} + +bool AlsaOutputEngine::start() { + return true; +} + +void AlsaOutputEngine::stop() { +} + +void AlsaOutputEngine::pre(size_t nsamples) { +} + +void AlsaOutputEngine::run(int ch, sample_t* samples, size_t nsamples) { + // Write channel data in interleaved buffer + for (auto i = 0u; i < nsamples; ++i) { + data[i * num_channels + ch] = samples[i]; + } +} + +void AlsaOutputEngine::post(size_t nsamples) { + // Write the interleaved buffer to the soundcard + snd_pcm_writei(handle, data.data(), nsamples); +} + +size_t AlsaOutputEngine::samplerate() { + return srate; +} diff --git a/drumgizmo/output/alsa.h b/drumgizmo/output/alsa.h new file mode 100644 index 0000000..c7585e0 --- /dev/null +++ b/drumgizmo/output/alsa.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * alsa.h + * + * Do 21. Jan 16:48:32 CET 2016 + * Copyright 2016 Christian Glöckner + * cgloeckner@freenet.de + ****************************************************************************/ + +/* + * This file is part of DrumGizmo. + * + * DrumGizmo 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 2 of the License, or + * (at your option) any later version. + * + * DrumGizmo 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 DrumGizmo; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#pragma once + +// Use the newer ALSA API +#define ALSA_PCM_NEW_HW_PARAMS_API + +#include +#include + +#include "audiooutputengine.h" + +class AlsaOutputEngine + : public AudioOutputEngine { + public: + AlsaOutputEngine(); + ~AlsaOutputEngine(); + + // based on AudioOutputEngine + bool init(Channels chan) override; + void setParm(std::string parm, std::string value) override; + bool start() override; + void stop() override; + void pre(size_t nsamples) override; + void run(int ch, sample_t* samples, size_t nsamples) override; + void post(size_t nsamples) override; + size_t samplerate() override; + + private: + snd_pcm_t* handle; + snd_pcm_hw_params_t* params; + std::vector data; + size_t num_channels; + + std::string dev; + unsigned int srate; // samplerate + snd_pcm_uframes_t frames; +}; -- cgit v1.2.3