Fix wayland build and merge fixes from other project using audio

This commit is contained in:
rexy712 2021-08-01 13:27:13 -07:00
parent 835f190aa9
commit 59b6cd65fd
29 changed files with 483 additions and 328 deletions

BIN
class diagram.dia Normal file

Binary file not shown.

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -22,43 +22,53 @@
#include <cstdlib> //size_t
#include "mixdata.hpp"
#include "impl/channel.hpp"
namespace sfx{
namespace impl{
class channel;
}
//Playback for one simultaneos audio source
//needs to be thread safe, lock free, and avoid false sharing
class channel
{
private:
impl::channel* m_impl;
public:
using native_chunk = impl::channel::chunk;
public:
channel(impl::channel& c);
channel(const channel& c) = default;
channel(channel&& c) = default;
~channel() = default;
~channel(void) = default;
channel& operator=(const channel&) = default;
channel& operator=(channel&&) = default;
void resize_buffer(size_t newsize);
void play(const mixchunk& m);
void play(const mixdata& m);
template<size_t Size>
void play(const mixrawdata<Size>& m);
template<size_t Size>
bool try_play(const mixrawdata<Size>& m);
void pause();
void resume();
void stop();
void pause(void);
void resume(void);
void stop(void);
bool is_paused()const;
bool is_playing()const;
bool is_stopped()const;
bool is_paused(void)const;
bool is_playing(void)const;
bool is_stopped(void)const;
};
template<size_t Size>
void channel::play(const mixrawdata<Size>& m){
m_impl->play(m);
}
template<size_t Size>
bool channel::try_play(const mixrawdata<Size>& m){
return m_impl->try_play(m);
}
}
#endif

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -21,6 +21,7 @@
#include <tuple> //tuple, get
#include <utility> //forward, integer_sequence
#include <rexy/meta.hpp> //sequence_gen
namespace sfx::detail{
template<typename Func, typename... Args>
@ -30,7 +31,7 @@ namespace sfx::detail{
template<int... Indices>
int operator()(const void* input, void* output, unsigned long frame_count,
unsigned long flags, std::integer_sequence<int,Indices...>)
unsigned long flags, rexy::sequence_tuple<Indices...>)
{
return std::forward<Func>(m_f)(input, output, frame_count, flags, std::get<Indices>(m_args)...);
}
@ -39,9 +40,9 @@ namespace sfx::detail{
class callback_iface
{
public:
callback_iface() = default;
callback_iface(void) = default;
virtual ~callback_iface() = default;
virtual ~callback_iface(void) = default;
virtual int operator()(const void* input, void* output, unsigned long frame_count, unsigned long flags) = 0;
};
@ -55,7 +56,7 @@ namespace sfx::detail{
callback_impl(Fn&& f, Ts&&... ts):
m_ch{std::forward<Fn>(f), {std::forward<Ts>(ts)...}}{}
int operator()(const void* input, void* output, unsigned long frame_count, unsigned long flags)override{
return m_ch(input, output, frame_count, flags, typename ::util::sequence_gen<sizeof...(Args)>::type{});
return m_ch(input, output, frame_count, flags, typename rexy::sequence_gen<sizeof...(Args)>::type{});
}
};
}

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -21,7 +21,6 @@
namespace sfx{
//Combine errors from multiple libraries into one to make for easier end usage
class error
{
public:
@ -49,20 +48,20 @@ namespace sfx{
BAD_BUFFER,
};
private:
int m_actual; //actual error generated by a library
int m_homog; //homogenized error (from the above enum)
int m_actual;
int m_homog;
public:
error(int actual);
error(const error&) = default;
error(error&&) = default;
~error() = default;
~error(void) = default;
error& operator=(const error&) = default;
error& operator=(error&&) = default;
operator int()const;
int get()const;
int get_raw()const;
operator int(void)const;
int get(void)const;
int get_raw(void)const;
};
}

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -23,7 +23,6 @@
namespace sfx{
//supported audio frame data formats
enum class frame_fmt{
float32 = paFloat32,
};

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -21,22 +21,26 @@
#include <atomic>
#include <cstdlib> //size_t
#include "util/ring_buffer.hpp"
#include <rexy/mpmc_queue.hpp>
#include "audio/mixdata.hpp"
namespace sfx::impl{
class channel
{
public:
static constexpr size_t chunk_size = 256;
using chunk = mixrawdata<chunk_size>;
private:
util::mpmc_ring_buffer<mixdata> m_data;
rexy::mpmc_queue<mixrawdata<chunk_size>> m_data;
std::atomic_bool m_paused;
std::atomic_bool m_active;
bool m_stopped;
//audio thread access only!
struct alignas(64){
mixdata data = {};
chunk data = {};
size_t offset = 0;
}m_playing_chunk;
@ -50,12 +54,15 @@ namespace sfx::impl{
channel& operator=(const channel& c);
channel& operator=(channel&& c);
mixdata pop();
bool try_pop(mixdata& m);
chunk pop();
bool try_pop(chunk& m);
void resize_buffer(size_t newsize);
void play(const mixdata& m);
template<size_t Size>
void play(const mixrawdata<Size>& m);
template<size_t Size>
bool try_play(const mixrawdata<Size>& m);
void pause();
void resume();
@ -65,8 +72,8 @@ namespace sfx::impl{
bool is_playing()const;
bool is_stopped()const;
mixdata& playing_chunk();
const mixdata& playing_chunk()const;
chunk& playing_chunk();
const chunk& playing_chunk()const;
size_t& playing_offset();
const size_t& playing_offset()const;
@ -77,6 +84,32 @@ namespace sfx::impl{
void wait_for_consumer();
};
template<size_t Size>
void channel::play(const mixrawdata<Size>& m){
if constexpr(Size > chunk_size){
size_t chunk_frames = chunk_size / m.channels;
for(size_t i = 0;i < m.frames;i += chunk_frames){
m_data.emplace(m, i);
}
}else{
m_data.emplace(m);
}
}
template<size_t Size>
bool channel::try_play(const mixrawdata<Size>& m){
if constexpr(Size > chunk_size){
size_t chunk_frames = chunk_size / m.channels;
for(size_t i = 0;i < m.frames;i += chunk_frames){
if(!m_data.try_emplace(m, i))
return false;
}
return true;
}else{
return m_data.try_emplace(m);
}
}
}
#endif

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -66,13 +66,13 @@ namespace sfx::impl{
void resume();
bool should_exit()const;
void exit();
size_t output_channels()const;
size_t output_count()const;
size_t get_samplerate()const;
size_t samplerate()const;
size_t channel_count()const;
private:
static int callback(const void* /*input*/, void* output, unsigned long frame_count, unsigned long flags, mixer& mix);
static int callback(const void* /*input*/, void* output, unsigned long frame_count, unsigned long flags, const stream_info&, mixer& mix);
};
}

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -21,20 +21,14 @@
namespace sfx{
//initializer/deinitializer for portaudio
class pa_system
{
private:
pa_system();
~pa_system();
pa_system(void);
~pa_system(void);
public:
int status()const;
void try_init()const;
public:
static pa_system& instance();
static inline int s_status = 0;
static pa_system& instance(void);
};
}

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -20,51 +20,111 @@
#define OUR_DICK_AUDIO_MIXDATA_HPP
#include <cstdlib> //size_t
#include <cstring> //memcpy
#include <initializer_list>
#include <algorithm>
#include <rexy/buffer.hpp>
namespace sfx{
//raw chunk of mixdata used by mixer
struct mixdata {
float* data = nullptr;
template<size_t Size>
struct mixrawdata{
static constexpr size_t max_size = Size;
float data[Size];
size_t frames = 0;
size_t channels = 0;
size_t samplerate = 0;
float volume = 1;
bool allocated = true;
constexpr mixrawdata(void);
constexpr mixrawdata(std::initializer_list<float> l, size_t channels, size_t samplerate);
template<size_t Other>
mixrawdata(const mixrawdata<Other>&, size_t offset_frames = 0);
mixrawdata(const mixrawdata&);
~mixrawdata(void) = default;
mixrawdata& operator=(const mixrawdata&);
};
//managed chunk of mixdata used by the end user
class mixchunk
{
private:
mixdata m_data;
rexy::buffer<float> m_buf;
size_t m_channels = 1;
size_t m_samplerate = 48000;
float m_volume = 1;
public:
constexpr mixchunk() = default;
constexpr mixchunk(void);
mixchunk(size_t frames, size_t channels, size_t samplerate);
mixchunk(const mixdata&);
mixchunk(mixdata&&);
mixchunk(const mixchunk&);
mixchunk(mixchunk&&);
~mixchunk();
~mixchunk(void) = default;
mixchunk& operator=(const mixchunk&);
mixchunk& operator=(mixchunk&&);
const float* data()const;
float* data();
const float* data(void)const;
float* data(void);
size_t floats()const;
size_t frames()const;
size_t channels()const;
size_t samplerate()const;
float volume()const;
size_t floats(void)const;
size_t frames(void)const;
size_t channels(void)const;
size_t samplerate(void)const;
float volume(void)const;
void set_samplerate(size_t s);
void set_volume(float v);
const mixdata& raw()const;
};
constexpr mixchunk::mixchunk(void):
m_buf(){}
template<size_t Size>
constexpr mixrawdata<Size>::mixrawdata(void){}
template<size_t Size>
constexpr mixrawdata<Size>::mixrawdata(std::initializer_list<float> l, size_t ch, size_t sr):
frames(l.size() / ch),
channels(ch),
samplerate(sr)
{
size_t i = 0;
for(auto it = l.begin();it != l.end();++it){
data[i++] = *it;
}
}
template<size_t Size>
template<size_t Other>
mixrawdata<Size>::mixrawdata(const mixrawdata<Other>& o, size_t offset_frames):
frames(std::min(Size / o.channels, o.frames - offset_frames)),
channels(o.channels),
samplerate(o.samplerate),
volume(o.volume)
{
memcpy(data, o.data + (offset_frames * o.channels), frames * channels * sizeof(float));
}
template<size_t Size>
mixrawdata<Size>::mixrawdata(const mixrawdata& m):
frames(m.frames),
channels(m.channels),
samplerate(m.samplerate),
volume(m.volume)
{
memcpy(data, m.data, m.frames * sizeof(float) * m.channels);
}
template<size_t Size>
mixrawdata<Size>& mixrawdata<Size>::operator=(const mixrawdata& m){
frames = m.frames;
channels = m.channels;
samplerate = m.samplerate;
volume = m.volume;
memcpy(data, m.data, m.frames * sizeof(float) * m.channels);
return *this;
}
}

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -28,7 +28,6 @@ namespace sfx{
class mixer;
}
//Handle playback of multiple simultaneous audio streams
class mixer
{
public:
@ -42,24 +41,26 @@ namespace sfx{
mixer(mode m, size_t channel_count, size_t samplerate = 48000);
mixer(const mixer&) = delete;
mixer(mixer&&) = delete;
~mixer();
~mixer(void);
mixer& operator=(const mixer&) = delete;
mixer& operator=(mixer&&) = delete;
void reserve_channels(size_t count);
channel get_channel(size_t index);
channel get_channel();
channel get_channel(void);
bool is_paused()const;
bool is_terminated()const;
bool is_paused(void)const;
bool is_terminated(void)const;
void pause();
void resume();
void terminate();
void pause(void);
void resume(void);
void terminate(void);
size_t get_samplerate()const;
size_t channel_count()const;
size_t samplerate(void)const;
size_t channel_count(void)const;
size_t output_count(void)const;
};
}

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -29,7 +29,6 @@
namespace sfx{
//Sound file reader. Reads directly into mixchunks which can be passed directly to a mixer channel
class sndrd
{
private:
@ -64,9 +63,9 @@ namespace sfx{
operator const SNDFILE*(void)const noexcept;
SNDFILE* get(void)noexcept;
const SNDFILE* get(void)const noexcept;
bool valid()const noexcept;
bool valid(void)const noexcept;
mixchunk read_all()noexcept;
mixchunk read_all(void)noexcept;
mixchunk read(size_t items)noexcept;
size_t read(float* dest, size_t items)noexcept;
mixchunk read_frames(size_t frames)noexcept;
@ -77,17 +76,17 @@ namespace sfx{
size_t write_frames(const float* src, size_t frames)noexcept;
//TODO other formats
void close()noexcept;
void close(void)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;
size_t frames(void)const noexcept;
int samplerate(void)const noexcept;
int channels(void)const noexcept;
int file_format(void)const noexcept;
int sections(void)const noexcept;
bool seekable(void)const noexcept;
error last_error()const noexcept;
error last_error(void)const noexcept;
};
}

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -22,23 +22,26 @@
#include <utility> //forward
#include <portaudio.h>
#include "util/sequence.hpp"
#include "detail.hpp"
#include "error.hpp"
#include "config.hpp"
#include "stream_info.hpp"
#include <cstdlib>
namespace sfx{
//Represents an audio device under the control of portaudio
class stream
{
private:
detail::callback_iface* m_cb = nullptr;
PaStream* m_stream = nullptr;
mutable PaError m_err = paNoError;
double m_samplerate;
int m_in_channels;
int m_out_channels;
stream_info m_info;
public:
enum state{
STOP = paComplete,
CONTINUE = paContinue,
};
public:
//buffersize of 0 means to automatically pick a good size
@ -46,44 +49,34 @@ namespace sfx{
//TODO: allow choosing device by index
template<typename Callback, typename... Args>
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_cb(new detail::callback_impl<Callback,const stream_info&, Args...>(std::forward<Callback>(cb), m_info, std::forward<Args>(cbargs)...)),
m_stream(initialize_global_instance()),
m_err(open_default_stream(&m_stream, in_c, out_c, paFloat32, samplerate, buffersize, m_cb)),
m_samplerate(samplerate),
m_in_channels(in_c),
m_out_channels(out_c)
{
if(m_err != paNoError){
debug_print_error("Failed to open audio stream\n");
}
}
m_info{out_c, in_c, samplerate}{}
~stream();
~stream(void);
error start();
error stop();
error abort();
error close();
error start(void);
error stop(void);
error abort(void);
error close(void);
bool is_stopped()const;
bool is_active()const;
bool is_stopped(void)const;
bool is_active(void)const;
int input_count()const;
int output_count()const;
int input_count(void)const;
int output_count(void)const;
double get_samplerate()const;
double samplerate(void)const;
error last_error()const;
error last_error(void)const;
private:
static PaStream* initialize_global_instance();
static PaStream* initialize_global_instance(void);
//to avoid using portaudio functions in the header
static int open_default_stream(PaStream**, int in_c, int out_c,
long unsigned 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,
void* userdata);
};
}

View File

@ -0,0 +1,32 @@
/**
This file is a part of our_dick
Copyright (C) 2021 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_STREAM_INFO_HPP
#define OUR_DICK_AUDIO_STREAM_INFO_HPP
namespace sfx{
struct stream_info{
int out_channels;
int in_channels;
double samplerate;
};
}
#endif

View File

@ -0,0 +1,23 @@
/**
This file is a part of our_dick
Copyright (C) 2021 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/>.
*/
namespace gfx::backend{
bool is_wayland_display();
}

View File

@ -19,24 +19,22 @@
#ifndef OUR_DICK_GRAPHICS_INIT_HPP
#define OUR_DICK_GRAPHICS_INIT_HPP
#include <mutex>
namespace gfx{
//initializer/deinitializer for glfw
class glfw_system
{
private:
static inline int s_status = 0;
static inline std::mutex s_ref_count_mtx = {};
static inline int s_ref_count = 0;
public:
glfw_system();
~glfw_system();
public:
int status()const;
void try_init()const;
public:
static glfw_system& instance();
private:
static inline int s_status = 0;
};
}

View File

@ -20,6 +20,7 @@
#define OUR_DICK_GRAPHICS_WINDOW_HPP
#include "gl_include.hpp"
#include "init.hpp"
#include "math/math.hpp"
#include "util/init_constants.hpp"
#include "fbo.hpp"
@ -44,6 +45,7 @@ namespace gfx{
private:
GLFWwindow* m_window = nullptr;
glfw_system m_glfw_handle;
fbo m_root_fbo{util::no_initialize};
char* m_title = nullptr;
int m_antialias_level = 0;

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -25,30 +25,23 @@ namespace sfx{
channel::channel(impl::channel& c):
m_impl(&c){}
void channel::play(const mixchunk& m){
m_impl->play(m.raw());
}
void channel::play(const mixdata& m){
m_impl->play(m);
}
void channel::pause(){
void channel::pause(void){
m_impl->pause();
}
void channel::resume(){
void channel::resume(void){
m_impl->resume();
}
void channel::stop(){
void channel::stop(void){
m_impl->stop();
}
bool channel::is_paused()const{
bool channel::is_paused(void)const{
return m_impl->is_paused();
}
bool channel::is_playing()const{
bool channel::is_playing(void)const{
return m_impl->is_playing();
}
bool channel::is_stopped()const{
bool channel::is_stopped(void)const{
return m_impl->is_stopped();
}

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -92,13 +92,13 @@ namespace sfx{
m_actual(actual),
m_homog(map_lib_errors(actual)){}
error::operator int()const{
error::operator int(void)const{
return m_homog;
}
int error::get()const{
int error::get(void)const{
return m_homog;
}
int error::get_raw()const{
int error::get_raw(void)const{
return m_actual;
}

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -55,28 +55,22 @@ namespace sfx::impl{
return *this;
}
mixdata channel::pop(){
mixdata m;
auto channel::pop() -> chunk{
chunk m;
m_data.pop(m);
return m;
}
bool channel::try_pop(mixdata& m){
bool channel::try_pop(chunk& m){
return m_data.try_pop(m);
}
void channel::resize_buffer(size_t newsize){
debug_print_warn("Resizing audio channel buffer size. Pausing channel in audio thread\n");
bool was_paused = m_paused.load(std::memory_order_acquire);
pause();
wait_for_consumer();
m_data.resize(newsize);
if(!was_paused)
resume();
debug_print("Audio channel resuming\n");
}
void channel::play(const mixdata& m){
m_data.push(m);
}
void channel::pause(){
@ -103,10 +97,10 @@ namespace sfx::impl{
return m_stopped;
}
mixdata& channel::playing_chunk(){
auto channel::playing_chunk() -> chunk&{
return m_playing_chunk.data;
}
const mixdata& channel::playing_chunk()const{
auto channel::playing_chunk()const -> const chunk&{
return m_playing_chunk.data;
}
size_t& channel::playing_offset(){

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -19,10 +19,15 @@
#include "audio/impl/mixer.hpp"
#include "audio/impl/channel.hpp"
#include "audio/mixdata.hpp"
#include "audio/error.hpp"
namespace sfx::impl{
//prevent inclusion of <algorithm> since that kills compile times
template<typename T>
static constexpr T& min(T& left, T& right){
return (left < right) ? left : right;
}
mixer::mixer(int output_channels, size_t channel_count, size_t samplerate):
m_paused(false),
m_exit(false),
@ -31,10 +36,7 @@ namespace sfx::impl{
m_lk(m_copy_lock, std::defer_lock),
m_output_sink(0, output_channels, samplerate, 0, callback, *this)
{
if(m_output_sink.last_error() != sfx::error::NONE)
m_output_sink.start();
else
exit();
m_output_sink.start();
}
mixer::~mixer(){
exit();
@ -59,10 +61,8 @@ namespace sfx::impl{
}
void mixer::lock(){
debug_print_warn("Locking mixer\n");
m_lk.lock();
consumer_lock();
debug_print("Unlocking mixer\n");
}
void mixer::consumer_lock(){
pause();
@ -97,23 +97,23 @@ namespace sfx::impl{
m_exit.store(true, std::memory_order_release);
while(is_active()){}
}
size_t mixer::output_channels()const{
size_t mixer::output_count()const{
return m_output_sink.output_count();
}
size_t mixer::get_samplerate()const{
return m_output_sink.get_samplerate();
size_t mixer::samplerate()const{
return m_output_sink.samplerate();
}
size_t mixer::channel_count()const{
return m_channels.size();
}
int mixer::callback(const void* /*input*/, void* output, unsigned long frame_count, unsigned long flags, mixer& mix){
int mixer::callback(const void* /*input*/, void* output, unsigned long frame_count, unsigned long flags, const stream_info&, mixer& mix){
if(flags & paPrimingOutput)
return paContinue;
//mark mixer data as being processed
mix.set_active(true);
float* foutput = reinterpret_cast<float*>(output);
size_t out_channels = mix.output_channels();
size_t out_channels = mix.output_count();
//exit when the exit flag is set
if(mix.should_exit()){
@ -137,20 +137,22 @@ namespace sfx::impl{
ch.set_active(false);
continue;
}
size_t floats_written = 0;
size_t remaining_output = frame_count;
//loop until the output buffer is saturated
while(remaining_output){
mixdata data;
channel::chunk data;
size_t offset = 0;
//if we saved data from last iteration, use it before popping more
if(ch.playing_chunk().data){
if(ch.playing_chunk().frames){
data = ch.playing_chunk();
offset = ch.playing_offset();
ch.playing_chunk().data = nullptr;
ch.playing_chunk().frames = 0;
}else{
if(!ch.try_pop(data))
if(!ch.try_pop(data)){
break;
}
}
size_t real_framecount;
@ -168,9 +170,10 @@ namespace sfx::impl{
size_t offset_float = offset * data.channels;
for(size_t j = 0, k = 0; j < real_floatcount; j += out_channels, k += data.channels){
for(size_t l = 0; l < out_channels; ++l){
foutput[j+l] += ((data.data + offset_float)[k + (l % data.channels)] * data.volume);
foutput[floats_written + j+l] += ((data.data + offset_float)[k + (l % data.channels)] * data.volume);
}
}
floats_written += real_floatcount;
remaining_output -= real_framecount;
}

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -17,19 +17,16 @@
*/
#include "audio/init.hpp"
#include "config.hpp"
#include <portaudio.h>
#if defined(__gnu_linux__)
#ifndef OUR_DICK_ENABLE_DEBUG_VERBOSE_OUTPUT
#define DISABLE_ALSA_DEBUG
#if !defined(OUR_DICK_DEBUG) || OUR_DICK_DEBUG <= 1
#define ENABLE_ALSA_DEBUG
#endif
#else
#define DISABLD_ALSA_DEBUG
#endif
#ifdef DISABLE_ALSA_DEBUG
#ifdef ENABLE_ALSA_DEBUG
#include <cstdarg> //va_arg (missing include in alsa headers)
#include <alsa/error.h> //snd_lib_error_set_handler
namespace sfx::detail{
@ -39,24 +36,18 @@ namespace sfx::detail{
namespace sfx{
pa_system::pa_system(){
#ifdef DISABLE_ALSA_DEBUG
pa_system::pa_system(void){
#ifdef ENABLE_ALSA_DEBUG
//silence excessive stderr warnings from alsa
snd_lib_error_set_handler(detail::linux_alsa_error_handler);
#endif
try_init();
Pa_Initialize();
}
pa_system::~pa_system(){
pa_system::~pa_system(void){
Pa_Terminate();
}
int pa_system::status()const{
return s_status;
}
void pa_system::try_init()const{
s_status = Pa_Initialize();
}
pa_system& pa_system::instance(){
pa_system& pa_system::instance(void){
static pa_system inst;
return inst;
}

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -24,80 +24,58 @@
namespace sfx{
mixchunk::mixchunk(size_t frames, size_t channels, size_t samplerate):
m_data{new float[frames * channels](), frames, channels, samplerate, 1, true}{}
mixchunk::mixchunk(const mixdata& m):
m_data{new float[m.frames * m.channels](), m.frames, m.channels, m.samplerate, m.volume, true}
{
memcpy(m_data.data, m.data, m_data.frames * m_data.channels);
}
mixchunk::mixchunk(mixdata&& m):
m_data{std::exchange(m.data, nullptr), m.frames, m.channels, m.samplerate, m.volume, m.allocated}{}
m_buf(frames * channels),
m_channels(channels),
m_samplerate(samplerate),
m_volume(1){}
mixchunk::mixchunk(const mixchunk& m):
mixchunk(m.m_data){}
m_buf(m.m_buf),
m_channels(m.m_channels),
m_samplerate(m.m_samplerate),
m_volume(m.m_volume){}
mixchunk::mixchunk(mixchunk&& m):
mixchunk(std::move(m.m_data)){}
mixchunk::~mixchunk(){
if(m_data.allocated)
delete[] m_data.data;
}
m_buf(std::move(m.m_buf)),
m_channels(m.m_channels),
m_samplerate(m.m_samplerate),
m_volume(m.m_volume){}
mixchunk& mixchunk::operator=(const mixchunk& m){
size_t this_floats = m_data.frames * m_data.channels;
size_t that_floats = m.m_data.frames * m.m_data.channels;
if(this_floats > that_floats){
memcpy(m_data.data, m.m_data.data, that_floats);
memset(m_data.data + that_floats, 0, this_floats - that_floats);
}else if(this_floats == that_floats){
memcpy(m_data.data, m.m_data.data, that_floats);
}else{
return (*this = mixchunk(m));
}
m_data.frames = m.m_data.frames;
m_data.channels = m.m_data.channels;
m_data.samplerate = m.m_data.samplerate;
m_data.volume = m.m_data.volume;
return *this;
return (*this = mixchunk(m));
}
mixchunk& mixchunk::operator=(mixchunk&& m){
std::swap(m_data.data, m.m_data.data);
std::swap(m_data.allocated, m.m_data.allocated);
m_data.frames = m.m_data.frames;
m_data.channels = m.m_data.channels;
m_data.samplerate = m.m_data.samplerate;
m_data.volume = m.m_data.volume;
m_buf = std::move(m.m_buf);
m_channels = m.m_channels;
m_samplerate = m.m_samplerate;
m_volume = m.m_volume;
return *this;
}
float* mixchunk::data(){
return m_data.data;
float* mixchunk::data(void){
return m_buf.data();
}
const float* mixchunk::data()const{
return m_data.data;
const float* mixchunk::data(void)const{
return m_buf.data();
}
size_t mixchunk::floats()const{
return frames() * channels();
size_t mixchunk::floats(void)const{
return m_buf.size();
}
size_t mixchunk::frames()const{
return m_data.frames;
size_t mixchunk::frames(void)const{
return m_buf.size() / m_channels;
}
size_t mixchunk::channels()const{
return m_data.channels;
size_t mixchunk::channels(void)const{
return m_channels;
}
size_t mixchunk::samplerate()const{
return m_data.samplerate;
size_t mixchunk::samplerate(void)const{
return m_samplerate;
}
float mixchunk::volume()const{
return m_data.volume;
float mixchunk::volume(void)const{
return m_volume;
}
void mixchunk::set_samplerate(size_t s){
m_data.samplerate = s;
m_samplerate = s;
}
void mixchunk::set_volume(float v){
m_data.volume = v;
}
const mixdata& mixchunk::raw()const{
return m_data;
m_volume = v;
}
}

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -23,7 +23,7 @@ namespace sfx{
mixer::mixer(mode m, size_t channel_count, size_t samplerate):
m_mix(new impl::mixer(static_cast<int>(m), channel_count, samplerate)){}
mixer::~mixer(){
mixer::~mixer(void){
delete m_mix;
}
@ -34,7 +34,7 @@ namespace sfx{
return channel(m_mix->channels()[index]);
}
//first free channel
channel mixer::get_channel(){
channel mixer::get_channel(void){
//TODO
for(size_t i = 0; i < m_mix->channels().size(); ++i){
if(m_mix->channels()[i].is_playing())
@ -43,27 +43,30 @@ namespace sfx{
return channel(m_mix->channels()[0]);
}
bool mixer::is_paused()const{
bool mixer::is_paused(void)const{
return m_mix->is_paused();
}
bool mixer::is_terminated()const{
bool mixer::is_terminated(void)const{
return m_mix->should_exit();
}
void mixer::pause(){
void mixer::pause(void){
m_mix->pause();
}
void mixer::resume(){
void mixer::resume(void){
m_mix->resume();
}
void mixer::terminate(){
void mixer::terminate(void){
m_mix->exit();
}
size_t mixer::get_samplerate()const{
return m_mix->get_samplerate();
size_t mixer::samplerate(void)const{
return m_mix->samplerate();
}
size_t mixer::channel_count()const{
size_t mixer::channel_count(void)const{
return m_mix->channel_count();
}
size_t mixer::output_count(void)const{
return m_mix->output_count();
}
}

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -29,7 +29,7 @@ namespace sfx{
sndrd::sndrd(sndrd&& f)noexcept:
m_info(f.m_info),
m_fp(std::exchange(f.m_fp, nullptr)){}
sndrd::~sndrd()noexcept{
sndrd::~sndrd(void)noexcept{
close();
}
sndrd& sndrd::operator=(sndrd&& f)noexcept{
@ -38,16 +38,16 @@ namespace sfx{
return *this;
}
SNDFILE* sndrd::release()noexcept{
SNDFILE* sndrd::release(void)noexcept{
return std::exchange(m_fp, nullptr);
}
size_t sndrd::length()noexcept{
size_t sndrd::length(void)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{
size_t sndrd::position(void)const noexcept{
return sf_seek(m_fp, 0, SEEK_CUR);
}
void sndrd::set_pos(size_t newpos)noexcept{
@ -58,23 +58,23 @@ namespace sfx{
sf_seek(m_fp, pos + offset, SEEK_SET);
}
sndrd::operator SNDFILE*()noexcept{
sndrd::operator SNDFILE*(void)noexcept{
return m_fp;
}
sndrd::operator const SNDFILE*()const noexcept{
sndrd::operator const SNDFILE*(void)const noexcept{
return m_fp;
}
SNDFILE* sndrd::get()noexcept{
SNDFILE* sndrd::get(void)noexcept{
return m_fp;
}
const SNDFILE* sndrd::get()const noexcept{
const SNDFILE* sndrd::get(void)const noexcept{
return m_fp;
}
bool sndrd::valid()const noexcept{
bool sndrd::valid(void)const noexcept{
return m_fp && !last_error();
}
mixchunk sndrd::read_all()noexcept{
mixchunk sndrd::read_all(void)noexcept{
return read_frames(frames());
}
mixchunk sndrd::read(size_t items)noexcept{
@ -99,32 +99,32 @@ namespace sfx{
return sf_writef_float(m_fp, src, frames);
}
void sndrd::close()noexcept{
void sndrd::close(void)noexcept{
if(m_fp)
sf_close(m_fp);
m_fp = nullptr;
}
size_t sndrd::frames()const noexcept{
size_t sndrd::frames(void)const noexcept{
return m_info.frames;
}
int sndrd::samplerate()const noexcept{
int sndrd::samplerate(void)const noexcept{
return m_info.samplerate;
}
int sndrd::channels()const noexcept{
int sndrd::channels(void)const noexcept{
return m_info.channels;
}
int sndrd::file_format()const noexcept{
int sndrd::file_format(void)const noexcept{
return m_info.format;
}
int sndrd::sections()const noexcept{
int sndrd::sections(void)const noexcept{
return m_info.sections;
}
bool sndrd::seekable()const noexcept{
bool sndrd::seekable(void)const noexcept{
return m_info.seekable;
}
error sndrd::last_error()const noexcept{
error sndrd::last_error(void)const noexcept{
return error(sf_error(m_fp));
}
}

View File

@ -1,6 +1,6 @@
/**
This file is a part of our_dick
Copyright (C) 2020 rexy712
Copyright (C) 2021 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
@ -22,24 +22,28 @@
#include <portaudio.h>
#define PORTAUDIO_FIXED_LATENCY 0.030
#define PORTAUDIO_FIXED_LATENCY 0.003
namespace sfx{
stream::~stream(){
static int callback(const void* input, void* output, unsigned long framecount,
const PaStreamCallbackTimeInfo* /*timeinfo*/, PaStreamCallbackFlags statusflags,
void* userdata);
stream::~stream(void){
close();
delete m_cb;
}
error stream::start(){
error stream::start(void){
return error(m_err = Pa_StartStream(m_stream));
}
error stream::stop(){
error stream::stop(void){
return error(m_err = Pa_StopStream(m_stream));
}
error stream::abort(){
error stream::abort(void){
return error(m_err = Pa_AbortStream(m_stream));
}
error stream::close(){
error stream::close(void){
if(m_stream){
m_err = Pa_CloseStream(m_stream);
m_stream = nullptr;
@ -48,28 +52,28 @@ namespace sfx{
return error(paNotInitialized);
}
bool stream::is_stopped()const{
bool stream::is_stopped(void)const{
return (m_err = Pa_IsStreamStopped(m_stream)) == 1;
}
bool stream::is_active()const{
bool stream::is_active(void)const{
return (m_err = Pa_IsStreamActive(m_stream)) == 1;
}
int stream::input_count()const{
return m_in_channels;
int stream::input_count(void)const{
return m_info.in_channels;
}
int stream::output_count()const{
return m_out_channels;
int stream::output_count(void)const{
return m_info.out_channels;
}
double stream::get_samplerate()const{
return m_samplerate;
double stream::samplerate(void)const{
return m_info.samplerate;
}
error stream::last_error()const{
error stream::last_error(void)const{
return error(m_err);
}
PaStream* stream::initialize_global_instance(){
PaStream* stream::initialize_global_instance(void){
pa_system::instance();
return nullptr;
}
@ -83,7 +87,8 @@ namespace sfx{
PaStreamParameters* inp = in_c ? &in : nullptr;
return Pa_OpenStream(stream, inp, outp, samplerate, bufsize, paNoFlag, callback, cb);
}
int stream::callback(const void* input, void* output, unsigned long framecount,
static int callback(const void* input, void* output, unsigned long framecount,
const PaStreamCallbackTimeInfo* /*timeinfo*/, PaStreamCallbackFlags statusflags,
void* userdata)
{

View File

@ -0,0 +1,31 @@
/**
This file is a part of our_dick
Copyright (C) 2021 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 <cstdlib> //getenv
#include <cstring> //strncmp
namespace gfx::backend{
bool is_wayland_display(){
char* wdisplay = getenv("WAYLAND_DISPLAY");
if(!wdisplay || strncmp(wdisplay, "wayland-", 8))
return false;
return true;
}
}

View File

@ -21,6 +21,8 @@
#include "graphics/gl_include.hpp"
#include <mutex> //lock_guard, mutex
namespace gfx{
#ifdef OUR_DICK_ENABLE_DEBUG_OUTPUT
@ -30,26 +32,27 @@ namespace gfx{
#endif
glfw_system::glfw_system(){
try_init();
{
std::lock_guard<std::mutex> lk(s_ref_count_mtx);
++s_ref_count;
}
if(s_status){
debug_print_warn("Call to glfwInit after already initialized\n");
return;
}
#ifdef OUR_DICK_ENABLE_DEBUG_OUTPUT
glfwSetErrorCallback(our_dick_glfw_error_callback);
#endif
s_status = glfwInit();
}
glfw_system::~glfw_system(){
glfwTerminate();
std::lock_guard<std::mutex> lk(s_ref_count_mtx);
--s_ref_count;
if(!s_ref_count)
glfwTerminate();
}
int glfw_system::status()const{
return s_status;
}
void glfw_system::try_init()const{
#ifdef OUR_DICK_ENABLE_DEBUG_OUTPUT
glfwSetErrorCallback(our_dick_glfw_error_callback);
#endif
if(s_status){
debug_print_warn("Call to glfwInit after already initialized\n");
}
s_status = glfwInit();
}
glfw_system& glfw_system::instance(){
static glfw_system inst;
return inst;
}
}

View File

@ -18,6 +18,7 @@
#include "graphics/window.hpp"
#include "graphics/init.hpp"
#include "graphics/backend_check.hpp"
#include <utility> //exchange, swap, pair
#include <cstring> //strlen, strncpy
@ -138,10 +139,6 @@ static void enable_opengl_debug_context(){
}
return false;
}
static int initialize_global_glfw(){
glfw_system& system = glfw_system::instance();
return system.status();
}
static char* copy_title(const char* src, size_t titlelen){
char* dest = new char[titlelen + 1];
strncpy(dest, src, titlelen);
@ -156,7 +153,7 @@ static void enable_opengl_debug_context(){
m_antialias_level(antialias),
m_refresh_rate(refresh)
{
if(initialize_global_glfw() != GLFW_TRUE)
if(m_glfw_handle.status() != GLFW_TRUE)
return; //TODO: user access to errors
GLFWwindow* current_context = glfwGetCurrentContext();
@ -169,7 +166,14 @@ static void enable_opengl_debug_context(){
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
glfwWindowHint(GLFW_DECORATED, GLFW_TRUE);
glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
//GLFW cannot open hidden window on wayland currently (glfw v3.3.4)
if(backend::is_wayland_display()){
glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE);
}else{
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
}
glfwWindowHint(GLFW_REFRESH_RATE, m_refresh_rate);
glfwWindowHint(GLFW_SAMPLES, m_antialias_level);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, cver_maj);
@ -230,7 +234,14 @@ static void enable_opengl_debug_context(){
glfwWindowHint(GLFW_RESIZABLE, w.is_resizable());
glfwWindowHint(GLFW_DECORATED, w.is_decorated());
glfwWindowHint(GLFW_FLOATING, w.is_always_on_top());
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
//GLFW cannot open hidden window on wayland currently (glfw v3.3.4)
if(backend::is_wayland_display()){
glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE);
}else{
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
}
glfwWindowHint(GLFW_REFRESH_RATE, w.m_refresh_rate);
glfwWindowHint(GLFW_SAMPLES, w.m_antialias_level);

View File

@ -37,5 +37,4 @@ int main(){
glfwPollEvents();
}
}