#include "./sdl_audio_frame_stream2.hpp" #include #include SDLAudioInputDeviceDefault::SDLAudioInputDeviceDefault(void) : _stream{nullptr, &SDL_DestroyAudioStream} { constexpr SDL_AudioSpec spec = { SDL_AUDIO_S16, 1, 48000 }; _stream = { SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_CAPTURE, &spec, nullptr, nullptr), &SDL_DestroyAudioStream }; if (!static_cast(_stream)) { std::cerr << "SDL open audio device failed!\n"; } const auto audio_device_id = SDL_GetAudioStreamDevice(_stream.get()); SDL_ResumeAudioDevice(audio_device_id); 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 buffer(buffer_size); static AudioFrame tmp_frame { 0, // TODO: seq spec.freq, spec.channels, std::vector(buffer_size) }; auto& buffer = std::get>(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"; // 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 std::this_thread::sleep_for(std::chrono::milliseconds(int64_t(interval_ms*32))); } } }); } SDLAudioInputDeviceDefault::~SDLAudioInputDeviceDefault(void) { // TODO: pause audio device? _thread_should_quit = true; _thread.join(); // TODO: what to do if readers are still present? } int32_t SDLAudioOutputDeviceDefaultInstance::size(void) { return -1; } std::optional SDLAudioOutputDeviceDefaultInstance::pop(void) { assert(false); // this is an output device, there is no data to pop return std::nullopt; } bool SDLAudioOutputDeviceDefaultInstance::push(const AudioFrame& value) { if (!static_cast(_stream)) { return false; } // verify here the fame has the same sample type, channel count and sample freq // if something changed, we need to either use a temporary stream, just for conversion, or reopen _stream with the new params // because of data temporality, the second options looks like a better candidate if ( value.sample_rate != _last_sample_rate || value.channels != _last_channels || (value.isF32() && _last_format != SDL_AUDIO_F32) || (value.isS16() && _last_format != SDL_AUDIO_S16) ) { const auto device_id = SDL_GetAudioStreamDevice(_stream.get()); SDL_FlushAudioStream(_stream.get()); const SDL_AudioSpec spec = { static_cast((value.isF32() ? SDL_AUDIO_F32 : SDL_AUDIO_S16)), static_cast(value.channels), static_cast(value.sample_rate) }; _stream = { SDL_OpenAudioDeviceStream(device_id, &spec, nullptr, nullptr), &SDL_DestroyAudioStream }; } // HACK assert(value.isS16()); auto data = value.getSpan(); if (data.size == 0) { std::cerr << "empty audio frame??\n"; } if (SDL_PutAudioStreamData(_stream.get(), data.ptr, data.size * sizeof(int16_t)) < 0) { std::cerr << "put data error\n"; return false; // return true? } _last_sample_rate = value.sample_rate; _last_channels = value.channels; _last_format = value.isF32() ? SDL_AUDIO_F32 : SDL_AUDIO_S16; return true; } SDLAudioOutputDeviceDefaultInstance::SDLAudioOutputDeviceDefaultInstance(void) : _stream(nullptr, nullptr) { } SDLAudioOutputDeviceDefaultInstance::SDLAudioOutputDeviceDefaultInstance(SDLAudioOutputDeviceDefaultInstance&& other) : _stream(std::move(other._stream)) { } SDLAudioOutputDeviceDefaultInstance::~SDLAudioOutputDeviceDefaultInstance(void) { } SDLAudioOutputDeviceDefaultInstance SDLAudioOutputDeviceDefaultFactory::create(void) { SDLAudioOutputDeviceDefaultInstance new_instance; constexpr SDL_AudioSpec spec = { SDL_AUDIO_S16, 1, 48000 }; new_instance._stream = { SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec, nullptr, nullptr), &SDL_DestroyAudioStream }; new_instance._last_sample_rate = spec.freq; new_instance._last_channels = spec.channels; new_instance._last_format = spec.format; if (!static_cast(new_instance._stream)) { std::cerr << "SDL open audio device failed!\n"; } const auto audio_device_id = SDL_GetAudioStreamDevice(new_instance._stream.get()); SDL_ResumeAudioDevice(audio_device_id); return new_instance; }