Add sound file decoding support with libsndfile
This commit is contained in:
parent
09ba2d297c
commit
03693c6d06
68
include/audio/error.hpp
Normal file
68
include/audio/error.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
||||
32
include/audio/frame_fmt.hpp
Normal file
32
include/audio/frame_fmt.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OUR_DICK_AUDIO_FRAME_FMT_HPP
|
||||
#define OUR_DICK_AUDIO_FRAME_FMT_HPP
|
||||
|
||||
#include <portaudio.h>
|
||||
|
||||
namespace audio{
|
||||
|
||||
enum class frame_fmt{
|
||||
float32 = paFloat32,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -23,9 +23,12 @@ namespace audio{
|
||||
|
||||
class pa_system
|
||||
{
|
||||
public:
|
||||
private:
|
||||
pa_system();
|
||||
~pa_system();
|
||||
|
||||
public:
|
||||
static pa_system& instance();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
90
include/audio/sndrd.hpp
Normal file
90
include/audio/sndrd.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OUR_DICK_SNDRD_HPP
|
||||
#define OUR_DICK_SNDRD_HPP
|
||||
|
||||
#include <sndfile.h>
|
||||
#include <cstddef> //size_t
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#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
|
||||
@ -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<typename Callback, typename... Args>
|
||||
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<Callback,Args...>(std::forward<Callback>(cb), std::forward<Args>(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*/,
|
||||
|
||||
2
makefile
2
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
|
||||
|
||||
105
src/audio/error.cpp
Normal file
105
src/audio/error.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "audio/error.hpp"
|
||||
|
||||
#include <portaudio.h>
|
||||
#include <sndfile.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -41,4 +41,8 @@ namespace audio{
|
||||
Pa_Terminate();
|
||||
}
|
||||
|
||||
pa_system& pa_system::instance(){
|
||||
static pa_system inst;
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
|
||||
119
src/audio/sndrd.cpp
Normal file
119
src/audio/sndrd.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "audio/sndrd.hpp"
|
||||
#include "audio/error.hpp"
|
||||
|
||||
#include <utility> //exchange, swap
|
||||
|
||||
namespace audio{
|
||||
|
||||
sndrd::sndrd(const char* f, mode m)noexcept:
|
||||
m_info(),
|
||||
m_fp(sf_open(f, static_cast<int>(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));
|
||||
}
|
||||
}
|
||||
@ -18,6 +18,7 @@
|
||||
|
||||
#include "audio/stream.hpp"
|
||||
#include "audio/init.hpp"
|
||||
#include "audio/error.hpp"
|
||||
|
||||
#include <portaudio.h>
|
||||
|
||||
@ -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<int>(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*/,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user