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*/,