rewrite audio input, always open? but thin stream wrapper
This commit is contained in:
parent
5eca1a99e0
commit
5b3e0e2a0b
@ -67,3 +67,6 @@ struct AudioFrame {
|
|||||||
|
|
||||||
using AudioFrameStream2I = FrameStream2I<AudioFrame>;
|
using AudioFrameStream2I = FrameStream2I<AudioFrame>;
|
||||||
|
|
||||||
|
using AudioFrameStream2MultiSource = FrameStream2MultiSource<AudioFrame>;
|
||||||
|
using AudioFrameStream2 = AudioFrameStream2MultiSource::sub_stream_type_t; // just use the default for now
|
||||||
|
|
||||||
|
@ -1,83 +1,171 @@
|
|||||||
#include "./sdl_audio_frame_stream2.hpp"
|
#include "./sdl_audio_frame_stream2.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
SDLAudioInputDeviceDefault::SDLAudioInputDeviceDefault(void) : _stream{nullptr, &SDL_DestroyAudioStream} {
|
// "thin" wrapper around sdl audio streams
|
||||||
constexpr SDL_AudioSpec spec = { SDL_AUDIO_S16, 1, 48000 };
|
// we dont needs to get fance, as they already provide everything we need
|
||||||
|
struct SDLAudioStreamReader : public AudioFrameStream2I {
|
||||||
|
std::unique_ptr<SDL_AudioStream, decltype(&SDL_DestroyAudioStream)> _stream;
|
||||||
|
|
||||||
_stream = {
|
uint32_t _seq_counter {0};
|
||||||
SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_RECORDING, &spec, nullptr, nullptr),
|
|
||||||
&SDL_DestroyAudioStream
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!static_cast<bool>(_stream)) {
|
uint32_t _sample_rate {48'000};
|
||||||
std::cerr << "SDL open audio device failed!\n";
|
size_t _channels {0};
|
||||||
|
SDL_AudioFormat _format {SDL_AUDIO_S16};
|
||||||
|
|
||||||
|
std::vector<int16_t> _buffer;
|
||||||
|
|
||||||
|
SDLAudioStreamReader(void) : _stream(nullptr, nullptr) {}
|
||||||
|
SDLAudioStreamReader(SDLAudioStreamReader&& other) :
|
||||||
|
_stream(std::move(other._stream)),
|
||||||
|
_sample_rate(other._sample_rate),
|
||||||
|
_channels(other._channels),
|
||||||
|
_format(other._format)
|
||||||
|
{
|
||||||
|
static const size_t buffer_size {960*_channels};
|
||||||
|
_buffer.resize(buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto audio_device_id = SDL_GetAudioStreamDevice(_stream.get());
|
~SDLAudioStreamReader(void) {
|
||||||
SDL_ResumeAudioDevice(audio_device_id);
|
if (_stream) {
|
||||||
|
SDL_UnbindAudioStream(_stream.get());
|
||||||
static constexpr size_t buffer_size {512*2}; // in samples
|
|
||||||
const auto interval_ms {(buffer_size * 1000) / spec.freq};
|
|
||||||
|
|
||||||
_thread = std::thread([this, interval_ms, spec](void) {
|
|
||||||
while (!_thread_should_quit) {
|
|
||||||
//static std::vector<int16_t> buffer(buffer_size);
|
|
||||||
static AudioFrame tmp_frame {
|
|
||||||
0, // TODO: seq
|
|
||||||
spec.freq, spec.channels,
|
|
||||||
std::vector<int16_t>(buffer_size)
|
|
||||||
};
|
|
||||||
|
|
||||||
auto& buffer = std::get<std::vector<int16_t>>(tmp_frame.buffer);
|
|
||||||
buffer.resize(buffer_size);
|
|
||||||
|
|
||||||
const auto read_bytes = SDL_GetAudioStreamData(
|
|
||||||
_stream.get(),
|
|
||||||
buffer.data(),
|
|
||||||
buffer.size()*sizeof(int16_t)
|
|
||||||
);
|
|
||||||
//if (read_bytes != 0) {
|
|
||||||
//std::cerr << "read " << read_bytes << "/" << buffer.size()*sizeof(int16_t) << " audio bytes\n";
|
|
||||||
//}
|
|
||||||
|
|
||||||
// no new frame yet, or error
|
|
||||||
if (read_bytes <= 0) {
|
|
||||||
// only sleep 1/5, we expected a frame
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(int64_t(interval_ms/5)));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.resize(read_bytes/sizeof(int16_t)); // this might be costly?
|
|
||||||
|
|
||||||
bool someone_listening {false};
|
|
||||||
someone_listening = push(tmp_frame);
|
|
||||||
|
|
||||||
if (someone_listening) {
|
|
||||||
// double the interval on acquire
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(int64_t(interval_ms/2)));
|
|
||||||
} else {
|
|
||||||
std::cerr << "i guess no one is listening\n";
|
|
||||||
std::cerr << "first value: " << buffer.front() << "\n";
|
|
||||||
// we just sleep 32x as long, bc no one is listening
|
|
||||||
// with the hardcoded settings, this is ~320ms
|
|
||||||
// TODO: just hardcode something like 500ms?
|
|
||||||
// TODO: suspend
|
|
||||||
// TODO: this is not gonna cut it, since playback slows down dramatically and will be very behind
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(int64_t(interval_ms*32)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
int32_t size(void) override {
|
||||||
|
//assert(_stream);
|
||||||
|
// returns bytes
|
||||||
|
//SDL_GetAudioStreamAvailable(_stream.get());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<AudioFrame> pop(void) override {
|
||||||
|
assert(_stream);
|
||||||
|
assert(_format == SDL_AUDIO_S16);
|
||||||
|
|
||||||
|
static const size_t buffer_size {960*_channels};
|
||||||
|
_buffer.resize(buffer_size); // noop?
|
||||||
|
|
||||||
|
const auto read_bytes = SDL_GetAudioStreamData(
|
||||||
|
_stream.get(),
|
||||||
|
_buffer.data(),
|
||||||
|
_buffer.size()*sizeof(int16_t)
|
||||||
|
);
|
||||||
|
|
||||||
|
// no new frame yet, or error
|
||||||
|
if (read_bytes <= 0) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AudioFrame {
|
||||||
|
_seq_counter++,
|
||||||
|
_sample_rate, _channels,
|
||||||
|
Span<int16_t>(_buffer.data(), read_bytes/sizeof(int16_t)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool push(const AudioFrame&) override {
|
||||||
|
// TODO: make universal sdl stream wrapper (combine with SDLAudioOutputDeviceDefaultInstance)
|
||||||
|
assert(false && "read only");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SDLAudioInputDevice::SDLAudioInputDevice(void) : SDLAudioInputDevice(SDL_AUDIO_DEVICE_DEFAULT_RECORDING) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SDLAudioInputDeviceDefault::~SDLAudioInputDeviceDefault(void) {
|
SDLAudioInputDevice::SDLAudioInputDevice(SDL_AudioDeviceID device_id) {
|
||||||
// TODO: pause audio device?
|
// this spec is more like a hint to the hardware
|
||||||
_thread_should_quit = true;
|
SDL_AudioSpec spec {
|
||||||
_thread.join();
|
SDL_AUDIO_S16,
|
||||||
// TODO: what to do if readers are still present?
|
1, // configurable?
|
||||||
|
48'000,
|
||||||
|
};
|
||||||
|
_device_id = SDL_OpenAudioDevice(device_id, &spec);
|
||||||
|
|
||||||
|
if (_device_id == 0) {
|
||||||
|
// TODO: proper error handling
|
||||||
|
throw int(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDLAudioInputDevice::~SDLAudioInputDevice(void) {
|
||||||
|
_streams.clear();
|
||||||
|
|
||||||
|
SDL_CloseAudioDevice(_device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<FrameStream2I<AudioFrame>> SDLAudioInputDevice::subscribe(void) {
|
||||||
|
SDL_AudioSpec spec {
|
||||||
|
SDL_AUDIO_S16,
|
||||||
|
1,
|
||||||
|
48'000,
|
||||||
|
};
|
||||||
|
|
||||||
|
SDL_AudioSpec device_spec {
|
||||||
|
SDL_AUDIO_S16,
|
||||||
|
1,
|
||||||
|
48'000,
|
||||||
|
};
|
||||||
|
// TODO: error check
|
||||||
|
SDL_GetAudioDeviceFormat(_device_id, &device_spec, nullptr);
|
||||||
|
|
||||||
|
// error check
|
||||||
|
auto* sdl_stream = SDL_CreateAudioStream(&device_spec, &spec);
|
||||||
|
|
||||||
|
// error check
|
||||||
|
SDL_BindAudioStream(_device_id, sdl_stream);
|
||||||
|
|
||||||
|
auto new_stream = std::make_shared<SDLAudioStreamReader>();
|
||||||
|
// TODO: move to ctr
|
||||||
|
new_stream->_stream = {sdl_stream, &SDL_DestroyAudioStream};
|
||||||
|
new_stream->_sample_rate = spec.freq;
|
||||||
|
new_stream->_channels = spec.channels;
|
||||||
|
new_stream->_format = spec.format;
|
||||||
|
|
||||||
|
_streams.emplace_back(new_stream);
|
||||||
|
|
||||||
|
return new_stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDLAudioInputDevice::unsubscribe(const std::shared_ptr<FrameStream2I<AudioFrame>>& sub) {
|
||||||
|
for (auto it = _streams.cbegin(); it != _streams.cend(); it++) {
|
||||||
|
if (*it == sub) {
|
||||||
|
_streams.erase(it);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// does not need to be visible in the header
|
||||||
|
struct SDLAudioOutputDeviceDefaultInstance : public AudioFrameStream2I {
|
||||||
|
std::unique_ptr<SDL_AudioStream, decltype(&SDL_DestroyAudioStream)> _stream;
|
||||||
|
|
||||||
|
uint32_t _last_sample_rate {48'000};
|
||||||
|
size_t _last_channels {0};
|
||||||
|
SDL_AudioFormat _last_format {SDL_AUDIO_S16};
|
||||||
|
|
||||||
|
// TODO: audio device
|
||||||
|
SDLAudioOutputDeviceDefaultInstance(void);
|
||||||
|
SDLAudioOutputDeviceDefaultInstance(SDLAudioOutputDeviceDefaultInstance&& other);
|
||||||
|
|
||||||
|
~SDLAudioOutputDeviceDefaultInstance(void);
|
||||||
|
|
||||||
|
int32_t size(void) override;
|
||||||
|
std::optional<AudioFrame> pop(void) override;
|
||||||
|
bool push(const AudioFrame& value) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
SDLAudioOutputDeviceDefaultInstance::SDLAudioOutputDeviceDefaultInstance(void) : _stream(nullptr, nullptr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
SDLAudioOutputDeviceDefaultInstance::SDLAudioOutputDeviceDefaultInstance(SDLAudioOutputDeviceDefaultInstance&& other) : _stream(std::move(other._stream)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
SDLAudioOutputDeviceDefaultInstance::~SDLAudioOutputDeviceDefaultInstance(void) {
|
||||||
|
}
|
||||||
int32_t SDLAudioOutputDeviceDefaultInstance::size(void) {
|
int32_t SDLAudioOutputDeviceDefaultInstance::size(void) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -144,15 +232,6 @@ bool SDLAudioOutputDeviceDefaultInstance::push(const AudioFrame& value) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDLAudioOutputDeviceDefaultInstance::SDLAudioOutputDeviceDefaultInstance(void) : _stream(nullptr, nullptr) {
|
|
||||||
}
|
|
||||||
|
|
||||||
SDLAudioOutputDeviceDefaultInstance::SDLAudioOutputDeviceDefaultInstance(SDLAudioOutputDeviceDefaultInstance&& other) : _stream(std::move(other._stream)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
SDLAudioOutputDeviceDefaultInstance::~SDLAudioOutputDeviceDefaultInstance(void) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SDLAudioOutputDeviceDefaultSink::~SDLAudioOutputDeviceDefaultSink(void) {
|
SDLAudioOutputDeviceDefaultSink::~SDLAudioOutputDeviceDefaultSink(void) {
|
||||||
// TODO: pause and close device?
|
// TODO: pause and close device?
|
||||||
|
@ -6,52 +6,30 @@
|
|||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <variant>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
// we dont have to multicast ourself, because sdl streams and virtual devices already do this, but we do it anyway
|
// we dont have to multicast ourself, because sdl streams and virtual devices already do this
|
||||||
using AudioFrameStream2MultiSource = FrameStream2MultiSource<AudioFrame>;
|
|
||||||
using AudioFrameStream2 = AudioFrameStream2MultiSource::sub_stream_type_t; // just use the default for now
|
|
||||||
|
|
||||||
// object components?
|
|
||||||
|
|
||||||
// source
|
// source
|
||||||
struct SDLAudioInputDeviceDefault : public AudioFrameStream2MultiSource {
|
// opens device
|
||||||
std::unique_ptr<SDL_AudioStream, decltype(&SDL_DestroyAudioStream)> _stream;
|
// creates a sdl audio stream for each subscribed reader stream
|
||||||
|
struct SDLAudioInputDevice : public FrameStream2SourceI<AudioFrame> {
|
||||||
|
// held by instances
|
||||||
|
using sdl_stream_type = std::unique_ptr<SDL_AudioStream, decltype(&SDL_DestroyAudioStream)>;
|
||||||
|
|
||||||
std::atomic<bool> _thread_should_quit {false};
|
SDL_AudioDeviceID _device_id {0};
|
||||||
std::thread _thread;
|
|
||||||
|
|
||||||
// construct source and start thread
|
std::vector<std::shared_ptr<FrameStream2I<AudioFrame>>> _streams;
|
||||||
// TODO: optimize so the thread is not always running
|
|
||||||
SDLAudioInputDeviceDefault(void);
|
|
||||||
|
|
||||||
// stops the thread and closes the device?
|
SDLAudioInputDevice(void);
|
||||||
~SDLAudioInputDeviceDefault(void);
|
SDLAudioInputDevice(SDL_AudioDeviceID device_id);
|
||||||
|
~SDLAudioInputDevice(void);
|
||||||
|
|
||||||
using AudioFrameStream2MultiSource::subscribe;
|
std::shared_ptr<FrameStream2I<AudioFrame>> subscribe(void) override;
|
||||||
using AudioFrameStream2MultiSource::unsubscribe;
|
bool unsubscribe(const std::shared_ptr<FrameStream2I<AudioFrame>>& sub) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
// sink
|
// sink
|
||||||
struct SDLAudioOutputDeviceDefaultInstance : public AudioFrameStream2I {
|
|
||||||
std::unique_ptr<SDL_AudioStream, decltype(&SDL_DestroyAudioStream)> _stream;
|
|
||||||
|
|
||||||
uint32_t _last_sample_rate {48'000};
|
|
||||||
size_t _last_channels {0};
|
|
||||||
SDL_AudioFormat _last_format {SDL_AUDIO_S16};
|
|
||||||
|
|
||||||
SDLAudioOutputDeviceDefaultInstance(void);
|
|
||||||
SDLAudioOutputDeviceDefaultInstance(SDLAudioOutputDeviceDefaultInstance&& other);
|
|
||||||
|
|
||||||
~SDLAudioOutputDeviceDefaultInstance(void);
|
|
||||||
|
|
||||||
int32_t size(void) override;
|
|
||||||
std::optional<AudioFrame> pop(void) override;
|
|
||||||
bool push(const AudioFrame& value) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
// constructs entirely new streams, since sdl handles sync and mixing for us (or should)
|
// constructs entirely new streams, since sdl handles sync and mixing for us (or should)
|
||||||
struct SDLAudioOutputDeviceDefaultSink : public FrameStream2SinkI<AudioFrame> {
|
struct SDLAudioOutputDeviceDefaultSink : public FrameStream2SinkI<AudioFrame> {
|
||||||
// TODO: pause device?
|
// TODO: pause device?
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#include "./debug_tox_call.hpp"
|
#include "./debug_tox_call.hpp"
|
||||||
|
|
||||||
#include "./stream_manager.hpp"
|
#include "./stream_manager.hpp"
|
||||||
|
#include "./content/audio_stream.hpp"
|
||||||
#include "./content/sdl_video_frame_stream2.hpp"
|
#include "./content/sdl_video_frame_stream2.hpp"
|
||||||
#include "./content/sdl_audio_frame_stream2.hpp"
|
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
@ -341,14 +341,25 @@ void DebugToxCall::tick(float) {
|
|||||||
const auto& new_frame = new_frame_opt.value();
|
const auto& new_frame = new_frame_opt.value();
|
||||||
assert(new_frame.isS16());
|
assert(new_frame.isS16());
|
||||||
|
|
||||||
// TODO: error code
|
//* @param sample_count Number of samples in this frame. Valid numbers here are
|
||||||
_toxav.toxavAudioSendFrame(
|
//* `((sample rate) * (audio length) / 1000)`, where audio length can be
|
||||||
|
//* 2.5, 5, 10, 20, 40 or 60 milliseconds.
|
||||||
|
|
||||||
|
// we likely needs to subdivide/repackage
|
||||||
|
// frame size should be an option exposed to the user
|
||||||
|
// with 10ms as a default ?
|
||||||
|
// the larger the frame size, the less overhead but the more delay
|
||||||
|
|
||||||
|
auto err = _toxav.toxavAudioSendFrame(
|
||||||
asink->_fid,
|
asink->_fid,
|
||||||
new_frame.getSpan<int16_t>().ptr,
|
new_frame.getSpan<int16_t>().ptr,
|
||||||
new_frame.getSpan<int16_t>().size / new_frame.channels,
|
new_frame.getSpan<int16_t>().size / new_frame.channels,
|
||||||
new_frame.channels,
|
new_frame.channels,
|
||||||
new_frame.sample_rate
|
new_frame.sample_rate
|
||||||
);
|
);
|
||||||
|
if (err != TOXAV_ERR_SEND_FRAME_OK) {
|
||||||
|
std::cerr << "DTC: failed to send audio frame " << err << "\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,8 +79,8 @@ int main(int argc, char** argv) {
|
|||||||
if (!SDL_Init(SDL_INIT_AUDIO)) {
|
if (!SDL_Init(SDL_INIT_AUDIO)) {
|
||||||
std::cerr << "SDL_Init AUDIO failed (" << SDL_GetError() << ")\n";
|
std::cerr << "SDL_Init AUDIO failed (" << SDL_GetError() << ")\n";
|
||||||
} else if (false) {
|
} else if (false) {
|
||||||
SDLAudioInputDeviceDefault aidd;
|
SDLAudioInputDevice aid;
|
||||||
auto reader = aidd.subscribe();
|
auto reader = aid.subscribe();
|
||||||
|
|
||||||
auto writer = SDLAudioOutputDeviceDefaultSink{}.subscribe();
|
auto writer = SDLAudioOutputDeviceDefaultSink{}.subscribe();
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aidd.unsubscribe(reader);
|
aid.unsubscribe(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SDL_Init(SDL_INIT_CAMERA)) {
|
if (!SDL_Init(SDL_INIT_CAMERA)) {
|
||||||
|
@ -162,11 +162,11 @@ MainScreen::MainScreen(SimpleConfigModel&& conf_, SDL_Renderer* renderer_, Theme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (false) { // audio in
|
if (true) { // audio in
|
||||||
ObjectHandle asrc {os.registry(), os.registry().create()};
|
ObjectHandle asrc {os.registry(), os.registry().create()};
|
||||||
try {
|
try {
|
||||||
asrc.emplace<Components::FrameStream2Source<AudioFrame>>(
|
asrc.emplace<Components::FrameStream2Source<AudioFrame>>(
|
||||||
std::make_unique<SDLAudioInputDeviceDefault>()
|
std::make_unique<SDLAudioInputDevice>()
|
||||||
);
|
);
|
||||||
|
|
||||||
asrc.emplace<Components::StreamSource>(Components::StreamSource::create<AudioFrame>("SDL Audio Default Recording Device"));
|
asrc.emplace<Components::StreamSource>(Components::StreamSource::create<AudioFrame>("SDL Audio Default Recording Device"));
|
||||||
|
Loading…
Reference in New Issue
Block a user