From 5e0d257b9423db671bf0bad8aa27c60bda8a712b Mon Sep 17 00:00:00 2001 From: Zachary Hall Date: Sat, 15 Jul 2023 14:52:49 -0700 Subject: [PATCH] Readd speed, add pitch and tempo, and update min size --- main.cpp | 33 ++++++++++++++++++++------- meson.build | 1 + playback.cpp | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++- playback.h | 16 ++++++++++++- 4 files changed, 103 insertions(+), 10 deletions(-) diff --git a/main.cpp b/main.cpp index 7a2840f..9641a33 100644 --- a/main.cpp +++ b/main.cpp @@ -122,7 +122,6 @@ int main(int, char**) if (enable_kms) { SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); } - SDL_SetWindowMinimumSize(window, 475, 54); SDL_Surface* icon = IMG_Load_RW(SDL_RWFromConstMem(icon_data, icon_size), 1); SDL_SetWindowIcon(window, icon); SDL_GLContext gl_context = SDL_GL_CreateContext(window); @@ -238,6 +237,16 @@ int main(int, char**) while (!done) #endif { + { + int min_x; + int min_y; + SDL_GetWindowMinimumSize(window, &min_x, &min_y); + int height = ImGui::GetFrameHeightWithSpacing() + ImGui::GetFrameHeight() + (ImGui::GetStyle().WindowPadding.y * 2) + ((ImGui::GetStyle().FramePadding.y * 2) + ImGui::GetFontSize()); + if (height != min_y) { + min_y = height; + SDL_SetWindowMinimumSize(window, 475, min_y); + } + } position = playback->GetPosition(); // Poll and handle events (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. @@ -291,7 +300,7 @@ int main(int, char**) #endif ImGui::EndMenuBar(); } - ImGui::SetCursorPosY(ImGui::GetWindowHeight() - 27); + ImGui::SetCursorPosY(ImGui::GetWindowHeight() - ImGui::GetFrameHeightWithSpacing() - ImGui::GetFrameHeight() - ImGui::GetStyle().WindowPadding.y); if (ImGui::Button(playback->IsPaused() ? ICON_FK_PLAY "##Pause" : ICON_FK_PAUSE "##Pause")) { playback->Pause(); } @@ -300,7 +309,8 @@ int main(int, char**) playback->Seek(0.0); } ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - (ImGui::GetFontSize() * (22 - 8)) - (ImGui::GetStyle().FramePadding.x * (10 - 2))); + const int NEXT_SLIDER_COUNT = 1; + ImGui::SetNextItemWidth(-(ImGui::GetFontSize() * (1 + (8 * NEXT_SLIDER_COUNT))) - (ImGui::GetStyle().ItemSpacing.x * (NEXT_SLIDER_COUNT + 1)) - (ImGui::GetStyle().WindowPadding.x)); uint8_t components = TimeToComponentCount(playback->GetLength()); string time_str = TimeToString(position, components); if (ImGui::SliderFloat("##Seek", &position, 0.0f, playback->GetLength(), time_str.c_str(), ImGuiSliderFlags_NoRoundToFormat)) @@ -311,15 +321,22 @@ int main(int, char**) } ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); - ImGui::SameLine(); if (ImGui::SliderFloat("##Volume", &playback->volume, 0.0, 100.0, ICON_FK_VOLUME_UP ": %.0f%%")) { playback->Update(); - }/* - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); + } + ImGui::PushItemWidth((ImGui::GetWindowWidth() / 3) - (ImGui::GetStyle().ItemSpacing.x) - (ImGui::GetStyle().WindowPadding.x / 3)); if (ImGui::SliderFloat("##Speed", &playback->speed, 0.25, 4.0, "Speed: %.2fx", ImGuiSliderFlags_Logarithmic)) { playback->Update(); - }*/ + } + ImGui::SameLine(); + if (ImGui::SliderFloat("##Tempo", &playback->tempo, 0.25, 4.0, "Tempo: %.2fx", ImGuiSliderFlags_Logarithmic)) { + playback->Update(); + } + ImGui::SameLine(); + if (ImGui::SliderFloat("##Pitch", &playback->pitch, 0.25, 4.0, "Pitch: %.2fx", ImGuiSliderFlags_Logarithmic)) { + playback->Update(); + } + ImGui::PopItemWidth(); } ImGui::End(); if (prefs_window) { diff --git a/meson.build b/meson.build index aa960c6..5db5c6b 100644 --- a/meson.build +++ b/meson.build @@ -18,6 +18,7 @@ deps = [ dependency('sdl2_image'), dependency('gl'), dependency('jsoncpp'), + dependency('soundtouch'), #raudio_dep, smx_subproj.dependency('SDL2_mixer_ext_Static'), ] diff --git a/playback.cpp b/playback.cpp index c7cae20..3828cf1 100644 --- a/playback.cpp +++ b/playback.cpp @@ -1,9 +1,24 @@ #include "playback.h" #include "SDL_mixer.h" #include +#include #include #include using namespace std::chrono; +size_t CalculateBufSize(SDL_AudioSpec *obtained, double max_seconds, size_t samples_override = 0) { + return ((((samples_override == 0) ? obtained->samples : samples_override) * max_seconds) + 1) * sizeof(SAMPLETYPE) * obtained->channels; +} +void Playback::SDLCallbackInner(Uint8 *stream, int len) { + while (st->numSamples() < len) { + general_mixer(NULL, buf, bufsize); + st->putSamples((SAMPLETYPE*)buf, bufsize / sizeof(SAMPLETYPE) / spec.channels); + } + SDL_memset((void*)stream, 0, len); + st->receiveSamples((SAMPLETYPE*)stream, len / sizeof(SAMPLETYPE) / spec.channels); +} +void Playback::SDLCallback(void *userdata, Uint8 *stream, int len) { + ((Playback*)userdata)->SDLCallbackInner(stream, len); +} Mix_Music *Playback::Load(const char *file) { Mix_Music *output = Mix_LoadMUS(file); if (!output) { @@ -20,8 +35,39 @@ void Playback::Unload(Mix_Music *music) { Mix_FreeMusic(music); } void Playback::ThreadFunc() { + if (!SDL_WasInit(SDL_INIT_AUDIO)) { + if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { + printf("Error initializing SDL: '%s'\n", SDL_GetError()); + throw std::exception(); + } + } + SDL_AudioSpec obtained; + SDL_AudioSpec desired; + desired.format = + #ifdef SOUNDTOUCH_INTEGER_SAMPLES + AUDIO_S16SYS; + #else + AUDIO_F32SYS; + #endif + desired.freq = 48000; + desired.samples = 128; + desired.channels = 2; + desired.callback = Playback::SDLCallback; + desired.userdata = this; + st = new SoundTouch(); + st->setSampleRate(desired.freq); + st->setChannels(desired.channels); Mix_Init(MIX_INIT_FLAC|MIX_INIT_MID|MIX_INIT_MOD|MIX_INIT_MP3|MIX_INIT_OGG|MIX_INIT_OPUS|MIX_INIT_WAVPACK); - Mix_OpenAudioDevice(48000, AUDIO_S16SYS, 2, 4096, NULL, 0); + if ((device = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, 0)) == 0) { + printf("Error opening audio device: '%s'\n", SDL_GetError()); + throw std::exception(); + } + spec = obtained; + bufsize = spec.size; + buf = (Uint8*)malloc(bufsize); + general_mixer = Mix_GetGeneralMixer(); + Mix_InitMixer(&spec, SDL_TRUE); + SDL_PauseAudioDevice(device, 0); Mix_Music *music = Load(filePath.c_str()); while (running) { @@ -40,6 +86,17 @@ void Playback::ThreadFunc() { } if (update.exchange(false)) { Mix_VolumeMusicStream(music, (volume / 100.0 * MIX_MAX_VOLUME)); + SDL_LockAudioDevice(device); + if (speed > 0.0f) { + st->setRate(speed); + } + if (tempo > 0.0f) { + st->setTempo(tempo); + } + if (pitch > 0.0f) { + st->setPitch(pitch); + } + SDL_UnlockAudioDevice(device); } flag_mutex.unlock(); } @@ -50,6 +107,8 @@ void Playback::ThreadFunc() { Unload(music); Mix_CloseAudio(); Mix_Quit(); + SDL_CloseAudioDevice(device); + delete st; free(buf); } @@ -60,6 +119,8 @@ Playback::Playback() { length = 0; volume = 100.0; speed = 1.0; + pitch = 1.0; + tempo = 1.0; } Playback::~Playback() { diff --git a/playback.h b/playback.h index a0320d9..ea5bbfd 100644 --- a/playback.h +++ b/playback.h @@ -1,9 +1,15 @@ #pragma once #include "SDL_mixer.h" #include +#include +#include #include #include #include +#include +#include +using namespace soundtouch; +using std::span; class Playback { private: std::string filePath; @@ -16,8 +22,14 @@ private: double position; double length; bool paused; - Uint8 *buf; + Uint8* buf; size_t bufsize; + Mix_CommonMixer_t general_mixer; + SDL_AudioDeviceID device; + SoundTouch *st; + SDL_AudioSpec spec; + void SDLCallbackInner(Uint8 *stream, int len); + static void SDLCallback(void *userdata, Uint8 *stream, int len); Mix_Music *Load(const char* file); void Unload(Mix_Music* music); void ThreadFunc(); @@ -35,4 +47,6 @@ public: bool IsStopped(); float volume; float speed; + float tempo; + float pitch; }; \ No newline at end of file