diff --git a/include/audio/error.hpp b/include/audio/error.hpp new file mode 100644 index 0000000..0e4cb40 --- /dev/null +++ b/include/audio/error.hpp @@ -0,0 +1,68 @@ +/** + This file is a part of the WyZ project + Copyright (C) 2020 rexy712 + + This program 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. + + 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef OUR_DICK_AUDIO_ERROR_HPP +#define OUR_DICK_AUDIO_ERROR_HPP + +namespace audio{ + + class error + { + public: + enum err{ + NONE = 0, + UNSUPPORTED_FORMAT, + SYSTEM, + BAD_FILE, + UNSUPPORTED_ENCODING, + INVALID_CHANNEL_COUNT, + INVALID_SAMPLE_RATE, + INVALID_DEVICE, + INVALID_FLAG, + BAD_DEVICE_COMBO, + OUT_OF_MEMORY, + BUFFER_TOO_BIG, + BUFFER_TOO_SMALL, + BAD_CALLBACK, + BAD_STREAM, + TIMED_OUT, + STREAM_STOPPED, + STREAM_NOT_STOPPED, + INPUT_OVERFLOW, + OUTPUT_UNDERFLOW, + BAD_BUFFER, + }; + private: + int m_actual; + int m_homog; + + public: + error(int actual); + error(const error&) = default; + error(error&&) = default; + ~error() = default; + error& operator=(const error&) = default; + error& operator=(error&&) = default; + + operator int()const; + int get()const; + int get_raw()const; + }; +} + +#endif diff --git a/include/audio/frame_fmt.hpp b/include/audio/frame_fmt.hpp new file mode 100644 index 0000000..3d0574b --- /dev/null +++ b/include/audio/frame_fmt.hpp @@ -0,0 +1,32 @@ +/** + 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_FRAME_FMT_HPP +#define OUR_DICK_AUDIO_FRAME_FMT_HPP + +#include + +namespace audio{ + + enum class frame_fmt{ + float32 = paFloat32, + }; + +} + +#endif diff --git a/include/audio/init.hpp b/include/audio/init.hpp index dafc9ef..6e273c8 100644 --- a/include/audio/init.hpp +++ b/include/audio/init.hpp @@ -23,9 +23,12 @@ namespace audio{ class pa_system { - public: + private: pa_system(); ~pa_system(); + + public: + static pa_system& instance(); }; } diff --git a/include/audio/sndrd.hpp b/include/audio/sndrd.hpp new file mode 100644 index 0000000..00af015 --- /dev/null +++ b/include/audio/sndrd.hpp @@ -0,0 +1,90 @@ +/** + 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_SNDRD_HPP +#define OUR_DICK_SNDRD_HPP + +#include +#include //size_t + +#include + +#include "error.hpp" + +namespace audio{ + + class sndrd + { + private: + SF_INFO m_info = {}; + SNDFILE* m_fp = nullptr; + + public: + enum class mode : int{ + r = SFM_READ, + w = SFM_WRITE, + rw = SFM_RDWR, + }; + public: + constexpr sndrd(void)noexcept = default; + sndrd(const char* f, mode m = mode::r)noexcept; + sndrd(const sndrd&) = delete; + sndrd(sndrd&& f)noexcept; + ~sndrd(void)noexcept; + sndrd& operator=(const sndrd&) = delete; + sndrd& operator=(sndrd&& f)noexcept; + + SNDFILE* release(void)noexcept; + + //Returns length in frames + size_t length(void)noexcept; + //Returns position in frames + size_t position(void)const noexcept; + void set_pos(size_t newpos)noexcept; + void seek(long int offset)noexcept; + + operator SNDFILE*(void)noexcept; + operator const SNDFILE*(void)const noexcept; + SNDFILE* get(void)noexcept; + const SNDFILE* get(void)const noexcept; + bool valid()const noexcept; + + size_t read(float* dest, size_t items)noexcept; + size_t read_frames(float* dest, size_t frames)noexcept; + //TODO other formats + + size_t write(const float* src, size_t items)noexcept; + size_t write_frames(const float* src, size_t frames)noexcept; + //TODO other formats + + void close()noexcept; + + //getters + size_t frames()const noexcept; + int samplerate()const noexcept; + int channels()const noexcept; + int file_format()const noexcept; + int sections()const noexcept; + bool seekable()const noexcept; + + error last_error()const noexcept; + }; + +} + +#endif diff --git a/include/audio/stream.hpp b/include/audio/stream.hpp index 90c73b4..e49523e 100644 --- a/include/audio/stream.hpp +++ b/include/audio/stream.hpp @@ -24,44 +24,41 @@ #include "detail/utility.hpp" #include "detail.hpp" +#include "error.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; + mutable 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): + stream(int in_c, int out_c, 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)){} + m_err(open_default_stream(&m_stream, in_c, out_c, paFloat32, samplerate, buffersize, m_cb)){} ~stream(); - int start(); - int stop(); - int abort(); - int close(); + error start(); + error stop(); + error abort(); + error close(); bool is_stopped()const; bool is_active()const; - int last_error()const; + error 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, + int 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*/, diff --git a/makefile b/makefile index cd64199..5e5ebc6 100644 --- a/makefile +++ b/makefile @@ -40,7 +40,7 @@ ifneq ($(WINDOWS),1) CC::=gcc CXX::=g++ LDLIBS::= - LDFLAGS::= -lglfw -lgl3w -ldl -lm -lportaudio -lasound + LDFLAGS::= -lglfw -lgl3w -ldl -lm -lportaudio -lasound -lsndfile STRIP::=strip RANLIB::=ranlib AR::=ar diff --git a/src/audio/error.cpp b/src/audio/error.cpp new file mode 100644 index 0000000..3221cbc --- /dev/null +++ b/src/audio/error.cpp @@ -0,0 +1,105 @@ +/** + This file is a part of the WyZ project + Copyright (C) 2020 rexy712 + + This program 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. + + 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "audio/error.hpp" + +#include +#include + +namespace audio{ + + static int map_lib_errors(int liberr){ + //portaudio and sndfile have no error overlaps, so a switch covers both + switch(liberr){ + default: + case SF_ERR_NO_ERROR: + //case paNoError: //same value as SF_ERR_NO_ERROR + return error::NONE; + case paSampleFormatNotSupported: + case SF_ERR_UNRECOGNISED_FORMAT: + return error::UNSUPPORTED_FORMAT; + case SF_ERR_MALFORMED_FILE: + return error::BAD_FILE; + case SF_ERR_UNSUPPORTED_ENCODING: + return error::UNSUPPORTED_ENCODING; + + case paInvalidChannelCount: + return error::INVALID_CHANNEL_COUNT; + case paInvalidSampleRate: + return error::INVALID_SAMPLE_RATE; + case paDeviceUnavailable: + case paInvalidDevice: + return error::INVALID_DEVICE; + case paInvalidFlag: + return error::INVALID_FLAG; + case paBadIODeviceCombination: + return error::BAD_DEVICE_COMBO; + case paInsufficientMemory: + return error::OUT_OF_MEMORY; + case paBufferTooBig: + return error::BUFFER_TOO_BIG; + case paBufferTooSmall: + return error::BUFFER_TOO_SMALL; + case paNullCallback: + return error::BAD_CALLBACK; + case paCanNotReadFromACallbackStream: + case paCanNotWriteToACallbackStream: + case paCanNotReadFromAnOutputOnlyStream: + case paCanNotWriteToAnInputOnlyStream: + case paBadStreamPtr: + return error::BAD_STREAM; + case paTimedOut: + return error::TIMED_OUT; + case paStreamIsStopped: + return error::STREAM_STOPPED; + case paStreamIsNotStopped: + return error::STREAM_NOT_STOPPED; + case paInputOverflowed: + return error::INPUT_OVERFLOW; + case paOutputUnderflowed: + return error::OUTPUT_UNDERFLOW; + case paBadBufferPtr: + return error::BAD_BUFFER; + + case paIncompatibleStreamHostApi: + case paInvalidHostApi: + case paHostApiNotFound: + case paIncompatibleHostApiSpecificStreamInfo: + case paInternalError: + case paNotInitialized: + case paUnanticipatedHostError: + case SF_ERR_SYSTEM: + return error::SYSTEM; + } + } + + error::error(int actual): + m_actual(actual), + m_homog(map_lib_errors(actual)){} + + error::operator int()const{ + return m_homog; + } + int error::get()const{ + return m_homog; + } + int error::get_raw()const{ + return m_actual; + } + +} diff --git a/src/audio/init.cpp b/src/audio/init.cpp index 13735b2..4dd4be3 100644 --- a/src/audio/init.cpp +++ b/src/audio/init.cpp @@ -41,4 +41,8 @@ namespace audio{ Pa_Terminate(); } + pa_system& pa_system::instance(){ + static pa_system inst; + return inst; + } } diff --git a/src/audio/sndrd.cpp b/src/audio/sndrd.cpp new file mode 100644 index 0000000..0d6e495 --- /dev/null +++ b/src/audio/sndrd.cpp @@ -0,0 +1,119 @@ +/** + 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/sndrd.hpp" +#include "audio/error.hpp" + +#include //exchange, swap + +namespace audio{ + + sndrd::sndrd(const char* f, mode m)noexcept: + m_info(), + m_fp(sf_open(f, static_cast(m), &m_info)){} + sndrd::sndrd(sndrd&& f)noexcept: + m_info(f.m_info), + m_fp(std::exchange(f.m_fp, nullptr)){} + sndrd::~sndrd()noexcept{ + close(); + } + sndrd& sndrd::operator=(sndrd&& f)noexcept{ + std::swap(m_fp, f.m_fp); + m_info = f.m_info; + return *this; + } + + SNDFILE* sndrd::release()noexcept{ + return std::exchange(m_fp, nullptr); + } + size_t sndrd::length()noexcept{ + size_t pos = position(); + size_t len = sf_seek(m_fp, 0, SEEK_END); + sf_seek(m_fp, pos, SEEK_SET); + return len; + } + size_t sndrd::position()const noexcept{ + return sf_seek(m_fp, 0, SEEK_CUR); + } + void sndrd::set_pos(size_t newpos)noexcept{ + sf_seek(m_fp, newpos, SEEK_SET); + } + void sndrd::seek(long int offset)noexcept{ + size_t pos = position(); + sf_seek(m_fp, pos + offset, SEEK_SET); + } + + sndrd::operator SNDFILE*()noexcept{ + return m_fp; + } + sndrd::operator const SNDFILE*()const noexcept{ + return m_fp; + } + SNDFILE* sndrd::get()noexcept{ + return m_fp; + } + const SNDFILE* sndrd::get()const noexcept{ + return m_fp; + } + bool sndrd::valid()const noexcept{ + return m_fp && !last_error(); + } + + size_t sndrd::read(float* dest, size_t items)noexcept{ + return sf_read_float(m_fp, dest, items); + } + size_t sndrd::read_frames(float* dest, size_t frames)noexcept{ + return sf_readf_float(m_fp, dest, frames); + } + + size_t sndrd::write(const float* src, size_t items)noexcept{ + return sf_write_float(m_fp, src, items); + } + size_t sndrd::write_frames(const float* src, size_t frames)noexcept{ + return sf_writef_float(m_fp, src, frames); + } + + void sndrd::close()noexcept{ + if(m_fp) + sf_close(m_fp); + m_fp = nullptr; + } + + size_t sndrd::frames()const noexcept{ + return m_info.frames; + } + int sndrd::samplerate()const noexcept{ + return m_info.samplerate; + } + int sndrd::channels()const noexcept{ + return m_info.channels; + } + int sndrd::file_format()const noexcept{ + return m_info.format; + } + int sndrd::sections()const noexcept{ + return m_info.sections; + } + bool sndrd::seekable()const noexcept{ + return m_info.seekable; + } + + error sndrd::last_error()const noexcept{ + return error(sf_error(m_fp)); + } +} diff --git a/src/audio/stream.cpp b/src/audio/stream.cpp index 1d16ee7..ce96f35 100644 --- a/src/audio/stream.cpp +++ b/src/audio/stream.cpp @@ -18,6 +18,7 @@ #include "audio/stream.hpp" #include "audio/init.hpp" +#include "audio/error.hpp" #include @@ -27,45 +28,45 @@ namespace audio{ close(); delete m_cb; } - int stream::start(){ - return m_err = Pa_StartStream(m_stream); + error stream::start(){ + return error(m_err = Pa_StartStream(m_stream)); } - int stream::stop(){ - return m_err = Pa_StopStream(m_stream); + error stream::stop(){ + return error(m_err = Pa_StopStream(m_stream)); } - int stream::abort(){ - return m_err = Pa_AbortStream(m_stream); + error stream::abort(){ + return error(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(){ + error stream::close(){ if(m_stream){ m_err = Pa_CloseStream(m_stream); m_stream = nullptr; - return m_err; + return error(m_err); } - return paNotInitialized; + return error(paNotInitialized); } - int stream::last_error()const{ - return m_err; + bool stream::is_stopped()const{ + return (m_err = Pa_IsStreamStopped(m_stream)) == 1; + } + bool stream::is_active()const{ + return (m_err = Pa_IsStreamActive(m_stream)) == 1; + } + + + error stream::last_error()const{ + return error(m_err); } PaStream* stream::initialize_global_instance(){ - static pa_system sys; + pa_system::instance(); return nullptr; } int stream::open_default_stream(PaStream** stream, int in_c, int out_c, - stream_fmt fmt, double samplerate, + int 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); + return Pa_OpenDefaultStream(stream, in_c, out_c, fmt, samplerate, bufsize, callback, cb); } int stream::callback(const void* input, void* output, unsigned long framecount, const PaStreamCallbackTimeInfo* /*timeInfo*/, PaStreamCallbackFlags /*statusFlags*/,