From e3a89822bef7342580f90f21689fb0b2dd8c425e Mon Sep 17 00:00:00 2001 From: rexy712 Date: Mon, 17 Aug 2020 20:04:43 -0700 Subject: [PATCH] Start work on audio stuff --- include/audio/detail.hpp | 61 +++++++++++++++++++++++++++++ include/audio/init.hpp | 33 ++++++++++++++++ include/audio/stream.hpp | 73 +++++++++++++++++++++++++++++++++++ include/detail/utility.hpp | 36 ++++++++++++++++++ makefile | 6 +-- src/audio/init.cpp | 44 +++++++++++++++++++++ src/audio/stream.cpp | 78 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 328 insertions(+), 3 deletions(-) create mode 100644 include/audio/detail.hpp create mode 100644 include/audio/init.hpp create mode 100644 include/audio/stream.hpp create mode 100644 include/detail/utility.hpp create mode 100644 src/audio/init.cpp create mode 100644 src/audio/stream.cpp diff --git a/include/audio/detail.hpp b/include/audio/detail.hpp new file mode 100644 index 0000000..8c29d6a --- /dev/null +++ b/include/audio/detail.hpp @@ -0,0 +1,61 @@ +/** + This file is a part of our_dick + Copyright (C) 2020 rexy712 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +#ifndef OUR_DICK_AUDIO_DETAIL_HPP +#define OUR_DICK_AUDIO_DETAIL_HPP + +#include //tuple, get +#include //forward, integer_sequence + +namespace audio::detail{ + template + struct callback_helper{ + Func&& m_f; + std::tuple m_args; + + template + int operator()(const void* input, void* output, unsigned long frame_count, std::integer_sequence){ + return std::forward(m_f)(input, output, frame_count, std::get(m_args)...); + } + }; + + class callback_iface + { + public: + callback_iface() = default; + + virtual ~callback_iface() = default; + virtual int operator()(const void* input, void* output, unsigned long frame_count) = 0; + }; + + template + class callback_impl : public callback_iface + { + private: + callback_helper m_ch; + public: + template + callback_impl(Fn&& f, Ts&&... ts): + m_ch{std::forward(f), {std::forward(ts)...}}{} + int operator()(const void* input, void* output, unsigned long frame_count)override{ + return m_ch(input, output, frame_count, typename ::detail::sequence_gen::type{}); + } + }; +} + +#endif diff --git a/include/audio/init.hpp b/include/audio/init.hpp new file mode 100644 index 0000000..dafc9ef --- /dev/null +++ b/include/audio/init.hpp @@ -0,0 +1,33 @@ +/** + This file is a part of our_dick + Copyright (C) 2020 rexy712 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +#ifndef OUR_DICK_AUDIO_INIT_HPP +#define OUR_DICK_AUDIO_INIT_HPP + +namespace audio{ + + class pa_system + { + public: + pa_system(); + ~pa_system(); + }; + +} + +#endif diff --git a/include/audio/stream.hpp b/include/audio/stream.hpp new file mode 100644 index 0000000..8a1f893 --- /dev/null +++ b/include/audio/stream.hpp @@ -0,0 +1,73 @@ +/** + This file is a part of our_dick + Copyright (C) 2020 rexy712 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +#ifndef OUR_DICK_AUDIO_STREAM_HPP +#define OUR_DICK_AUDIO_STREAM_HPP + +#include //forward +#include + +#include "detail/utility.hpp" +#include "detail.hpp" + +namespace audio{ + + enum class stream_fmt{ + float32 = paFloat32, + }; + + class stream + { + private: + detail::callback_iface* m_cb = nullptr; + PaStream* m_stream = nullptr; + PaError m_err = paNoError; + + public: + template + stream(int in_c, int out_c, stream_fmt fmt, double samplerate, size_t buffersize, Callback&& cb, Args&&... cbargs): + m_cb(new detail::callback_impl(std::forward(cb), std::forward(cbargs)...)), + m_stream(initialize_global_instance()), + m_err(open_default_stream(&m_stream, in_c, out_c, fmt, samplerate, buffersize, m_cb)){} + + ~stream(); + + int start(); + int stop(); + int abort(); + int close(); + + bool is_stopped()const; + bool is_active()const; + + int last_error()const; + + private: + static PaStream* initialize_global_instance(); + //to avoid using portaudio functions in the header + static int open_default_stream(PaStream*, int in_c, int out_c, + stream_fmt fmt, double samplerate, + size_t bufsize, detail::callback_iface* cb); + static int callback(const void* input, void* output, unsigned long framecount, + const PaStreamCallbackTimeInfo* /*timeInfo*/, PaStreamCallbackFlags /*statusFlags*/, + void* userdata); + }; + +} + +#endif diff --git a/include/detail/utility.hpp b/include/detail/utility.hpp new file mode 100644 index 0000000..d0fb782 --- /dev/null +++ b/include/detail/utility.hpp @@ -0,0 +1,36 @@ +/** + This file is a part of our_dick + Copyright (C) 2020 rexy712 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +#ifndef OUR_DICK_UTILITY_HPP +#define OUR_DICK_UTILITY_HPP + +#include //integer_sequence + +namespace detail{ + template + struct sequence_gen{ + using type = typename sequence_gen::type; + }; + template + struct sequence_gen<0,Is...>{ + using type = std::integer_sequence; + }; + +} + +#endif diff --git a/makefile b/makefile index 0b87169..cd64199 100644 --- a/makefile +++ b/makefile @@ -19,7 +19,7 @@ ifeq ($(OS),Windows_NT) WINDOWS::=1 endif -SOURCE_DIRS::=src +SOURCE_DIRS::=src src/audio SOURCES::= OBJDIR::=obj DEPDIR::=$(OBJDIR)/dep @@ -40,7 +40,7 @@ ifneq ($(WINDOWS),1) CC::=gcc CXX::=g++ LDLIBS::= - LDFLAGS::= -lglfw -lgl3w -ldl -lm + LDFLAGS::= -lglfw -lgl3w -ldl -lm -lportaudio -lasound STRIP::=strip RANLIB::=ranlib AR::=ar @@ -51,7 +51,7 @@ else #windows MINGW_PREFIX::=x86_64-w64-mingw32- CC::=$(MINGW_PREFIX)gcc CXX::=$(MINGW_PREFIX)g++ - LDLIBS::= + LDLIBS::=-lglfw -lgl3w -ldl -lm -lportaudio LDFLAGS::= STRIP::=$(MINGW_PREFIX)strip RANLIB::=$(MINGW_PREFIX)ranlib diff --git a/src/audio/init.cpp b/src/audio/init.cpp new file mode 100644 index 0000000..13735b2 --- /dev/null +++ b/src/audio/init.cpp @@ -0,0 +1,44 @@ +/** + This file is a part of our_dick + Copyright (C) 2020 rexy712 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +#include "audio/init.hpp" + +#include + +#ifdef __gnu_linux__ +#include //va_arg (missing include in alsa headers) +#include //snd_lib_error_set_handler +namespace audio::detail{ + void linux_alsa_error_handler(const char* /*file*/, int /*line*/, const char* /*function*/, int /*err*/, const char* /*fmt*/,...){} +} +#endif + +namespace audio{ + + pa_system::pa_system(){ +#ifdef __gnu_linux__ + //silence excessive stderr warnings from alsa + snd_lib_error_set_handler(detail::linux_alsa_error_handler); +#endif + Pa_Initialize(); + } + pa_system::~pa_system(){ + Pa_Terminate(); + } + +} diff --git a/src/audio/stream.cpp b/src/audio/stream.cpp new file mode 100644 index 0000000..27d737c --- /dev/null +++ b/src/audio/stream.cpp @@ -0,0 +1,78 @@ +/** + This file is a part of our_dick + Copyright (C) 2020 rexy712 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +#include "audio/stream.hpp" +#include "audio/init.hpp" + +#include + +namespace audio{ + + stream::~stream(){ + close(); + delete m_cb; + } + int stream::start(){ + return m_err = Pa_StartStream(m_stream); + } + int stream::stop(){ + return m_err = Pa_StopStream(m_stream); + } + int stream::abort(){ + return m_err = Pa_AbortStream(m_stream); + } + + bool stream::is_stopped()const{ + return Pa_IsStreamStopped(m_stream); + } + bool stream::is_active()const{ + return Pa_IsStreamActive(m_stream); + } + + int stream::close(){ + if(m_stream){ + m_err = Pa_CloseStream(m_stream); + m_stream = nullptr; + return m_err; + } + return paNotInitialized; + } + + int stream::last_error()const{ + return m_err; + } + + PaStream* stream::initialize_global_instance(){ + static pa_system sys; + return nullptr; + } + int stream::open_default_stream(PaStream* stream, int in_c, int out_c, + stream_fmt fmt, double samplerate, + size_t bufsize, detail::callback_iface* cb) + { + return Pa_OpenDefaultStream(&stream, in_c, out_c, static_cast(fmt), samplerate, bufsize, callback, cb); + } + int stream::callback(const void* input, void* output, unsigned long framecount, + const PaStreamCallbackTimeInfo* /*timeInfo*/, PaStreamCallbackFlags /*statusFlags*/, + void* userdata) + { + detail::callback_iface* cb = static_cast(userdata); + return (*cb)(input, output, framecount); + } + +}