#include #include #include #include #include #include #include #include #include #include #include namespace psemek::audio { struct engine::impl { std::shared_ptr sdl_init; SDL_AudioDeviceID device; std::vector buffer; bool thread_registered = false; channel_ptr output; impl(); ~impl(); static void callback(void * userdata, std::uint8_t * stream, int len); }; engine::impl::impl() : sdl_init(sdl2::init(SDL_INIT_AUDIO)) { SDL_AudioSpec desired, obtained; desired.freq = frequency; desired.channels = 2; desired.format = AUDIO_S16SYS; desired.samples = 256; desired.callback = &callback; desired.userdata = this; if (device = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, 0); device == 0) sdl2::fail("SDL_OpenAudioDevice failed:"); buffer.resize(obtained.samples * obtained.channels); SDL_PauseAudioDevice(device, 0); log::info() << "Initialized audio: " << static_cast(obtained.channels) << " channels, " << obtained.freq << " Hz, " << obtained.samples << " samples"; output = std::make_shared(); } engine::impl::~impl() { SDL_CloseAudioDevice(device); } void engine::impl::callback(void * userdata, std::uint8_t * dst_u8, int len) { static std::string const profiler_str = "audio"; prof::profiler prof(profiler_str); auto self = static_cast(userdata); stream_ptr output = self->output->stream(); std::int16_t * dst = reinterpret_cast(dst_u8); if (!self->thread_registered) { log::register_thread("audio"); self->thread_registered = true; } std::size_t const size = len / 2; std::size_t read = 0; if (output) read = output->read(self->buffer.data(), size); std::fill(self->buffer.data() + read, self->buffer.data() + size, 0.f); for (auto s : self->buffer) *dst++ = static_cast(std::max(std::min((65535.f * s - 1.f) / 2.f, 32767.f), -32768.f)); } engine::engine() : pimpl_(make_impl()) {} engine::~engine() = default; track_ptr engine::load(float const * data, std::size_t sample_count, bool copy) { if ((sample_count % 2) != 0) throw std::runtime_error("bad sample count"); (void)data; (void)sample_count; (void)copy; return nullptr; } track_ptr engine::load(util::span data, bool copy) { return load(data.data(), data.size(), copy); } channel_ptr engine::output() { return impl().output; } }