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/Makefile.am | 33 ++++++++--- drumgizmo/enginefactory.cc | 25 ++++++++ drumgizmo/output/alsa.cc | 141 +++++++++++++++++++++++++++++++++++++++++++++ drumgizmo/output/alsa.h | 62 ++++++++++++++++++++ 4 files changed, 253 insertions(+), 8 deletions(-) create mode 100644 drumgizmo/output/alsa.cc create mode 100644 drumgizmo/output/alsa.h diff --git a/drumgizmo/Makefile.am b/drumgizmo/Makefile.am index 7044303..654ec77 100644 --- a/drumgizmo/Makefile.am +++ b/drumgizmo/Makefile.am @@ -8,9 +8,11 @@ SUBDIRS = input output bin_PROGRAMS = drumgizmo -drumgizmo_LDADD = $(DRUMGIZMO_LIBS) $(PTHREAD_LIBS) $(SMF_LIBS) -ldl $(JACK_LIBS) +drumgizmo_LDADD = $(DRUMGIZMO_LIBS) $(PTHREAD_LIBS) -ldl $(JACK_LIBS) -drumgizmo_CXXFLAGS = $(SNDFILE_CXXFLAGS) $(PTHREAD_CFLAGS) $(SMF_CFLAGS) $(EXPAT_CFLAGS) \ +drumgizmo_LDFLAGS = + +drumgizmo_CXXFLAGS = $(SNDFILE_CXXFLAGS) $(PTHREAD_CFLAGS) $(EXPAT_CFLAGS) \ -I$(top_srcdir)/include -I$(top_srcdir)/src \ -I$(top_srcdir)/hugin -DWITH_HUG_MUTEX -DWITH_HUG_FILTER \ $(JACK_CFLAGS) $(SSEFLAGS) \ @@ -23,17 +25,32 @@ drumgizmo_SOURCES = \ drumgizmoc.cc \ jackclient.cc \ enginefactory.cc \ - input/midifile.cc \ - output/wavfile.cc \ $(DRUMGIZMO_SOURCES) \ $(top_srcdir)/hugin/hugin.c \ $(top_srcdir)/hugin/hugin_filter.c +#if HAVE_INPUT_MIDIFILE +drumgizmo_CXXFLAGS += $(SMF_CFLAGS) +drumgizmo_LDADD += $(SMF_LIBS) +drumgizmo_SOURCES += input/midifile.cc +#drumgizmo_CXXFLAGS += HAVE_INPUT_MIDIFILE +#endif # HAVE_INPUT_MIDIFILE + +#if HAVE_OUTPUT_WAVFILE +drumgizmo_SOURCES += output/wavfile.cc +#drumgizmo_CXXFLAGS += HAVE_OUTPUT_WAVFILE +#endif # HAVE_OUTPUT_WAVFILE + +#if HAVE_OUTPUT_ALSA +drumgizmo_CXXFLAGS += $(ALSA_CFLAGS) +drumgizmo_LDFLAGS += $(ALSA_LIBS) +drumgizmo_SOURCES += output/alsa.cc +#drumgizmo_CXXFLAGS += HAVE_OUTPUT_ALSA +#endif # HAVE_OUTPUT_ALSA + EXTRA_DIST = \ drumgizmoc.h \ jackclient.h \ - enginefactory.h \ - input/midifile.h \ - output/wavfile.h + enginefactory.h -endif +endif # ENABLE_CLI diff --git a/drumgizmo/enginefactory.cc b/drumgizmo/enginefactory.cc index b6569e0..4e9bc62 100644 --- a/drumgizmo/enginefactory.cc +++ b/drumgizmo/enginefactory.cc @@ -26,13 +26,30 @@ */ #include "enginefactory.h" #include "jackclient.h" + +#define HAVE_INPUT_MIDIFILE 1 +#define HAVE_OUTPUT_WAVFILE 1 +#define HAVE_OUTPUT_ALSA 1 + +#ifdef HAVE_INPUT_MIDIFILE #include "input/midifile.h" +#endif + +#ifdef HAVE_OUTPUT_WAVFILE #include "output/wavfile.h" +#endif + +#ifdef HAVE_OUTPUT_ALSA +#include "output/alsa.h" +#endif InputEnginePtr createInputEngine(std::string const & name) { +#ifdef HAVE_INPUT_MIDIFILE if (name == "midifile") { return std::make_unique(); } +#endif + // todo: add more engines printf("Unsupported input engine: %s\n", name.c_str()); @@ -40,9 +57,17 @@ InputEnginePtr createInputEngine(std::string const & name) { } OutputEnginePtr createOutputEngine(std::string const & name) { +#ifdef HAVE_OUTPUT_WAVFILE if (name == "wavfile") { return std::make_unique(); } +#endif +#ifdef HAVE_OUTPUT_ALSA + if (name == "alsa") { + return std::make_unique(); + } +#endif + // todo: add more engines printf("Unsupported output engine: %s\n", name.c_str()); 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