Fix wayland build and merge fixes from other project using audio
This commit is contained in:
parent
835f190aa9
commit
59b6cd65fd
BIN
class diagram.dia
Normal file
BIN
class diagram.dia
Normal file
Binary file not shown.
@ -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
|
||||
|
||||
@ -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{});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
32
include/audio/stream_info.hpp
Normal file
32
include/audio/stream_info.hpp
Normal 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
|
||||
23
include/graphics/backend_check.hpp
Normal file
23
include/graphics/backend_check.hpp
Normal 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();
|
||||
|
||||
}
|
||||
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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(){
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
31
src/graphics/backend_check.cpp
Normal file
31
src/graphics/backend_check.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -37,5 +37,4 @@ int main(){
|
||||
|
||||
glfwPollEvents();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user