I don't understand a single *THING* that's happenning but the reported times are wrong and always have something to do with the pitch being multiplied one extra time

This commit is contained in:
Stepland 2022-10-05 01:42:50 +02:00
parent 54aef06e04
commit 6555cb168e
6 changed files with 146 additions and 22 deletions

View File

@ -10,18 +10,18 @@
BeatTicks::BeatTicks(
const better::Timing* timing_,
const std::filesystem::path& assets,
float pitch
float pitch_
) :
FakePitchedSoundStream(assets / "sounds" / "beat.wav", pitch),
FakePitchedSoundStream(assets / "sounds" / "beat.wav", pitch_),
timing(timing_)
{}
BeatTicks::BeatTicks(
const better::Timing* timing_,
std::shared_ptr<sf::SoundBuffer> beat_tick,
float pitch
float pitch_
) :
FakePitchedSoundStream(beat_tick, pitch),
FakePitchedSoundStream(beat_tick, pitch_),
timing(timing_)
{}
@ -29,11 +29,11 @@ void BeatTicks::set_timing(const better::Timing* timing_) {
timing = timing_;
}
std::shared_ptr<BeatTicks> BeatTicks::with_pitch(float pitch) {
std::shared_ptr<BeatTicks> BeatTicks::with_pitch(float new_pitch) {
return std::make_shared<BeatTicks>(
timing,
sample,
pitch
new_pitch
);
}

View File

@ -1,5 +1,6 @@
#pragma once
#include <SFML/System/Time.hpp>
#include <array>
#include <filesystem>
@ -19,6 +20,16 @@ struct PreciseSoundStream : public OpenSoundStream {
};
template<class T>
sf::Uint64 timeToSamples(sf::Time position, T sample_rate, T channel_count) {
sf::Uint64 time_to_samples(sf::Time position, T sample_rate, T channel_count) {
return ((static_cast<sf::Uint64>(position.asMicroseconds()) * sample_rate * channel_count) + 500000) / 1000000;
};
template<class T>
sf::Time samples_to_time(std::int64_t samples, T sample_rate, T channel_count) {
// Make sure we don't divide by 0
if (sample_rate != 0 && channel_count != 0) {
return sf::microseconds((samples * 1000000) / (channel_count * sample_rate));
} else {
return sf::Time::Zero;
}
};

View File

@ -14,8 +14,10 @@
#include <variant>
#include "../variant_visitor.hpp"
#include "../toolbox.hpp"
#include "al_check.hpp"
#include "audio_device.hpp"
#include "imgui.h"
#include "precise_sound_stream.hpp"
#ifdef _MSC_VER
@ -155,6 +157,11 @@ void SyncedSoundStreams::play() {
// Start updating the stream in a separate thread to avoid blocking the application
launchStreamingThread(sf::SoundSource::Playing);
// Set lag values
for (const auto& [_, s]: streams) {
s.stream->lag = s.stream->alSecOffsetLatencySoft()[1];
}
}
@ -219,7 +226,11 @@ void SyncedSoundStreams::setPlayingOffset(sf::Time timeOffset) {
for (auto& [_, s]: streams) {
s.stream->public_seek_callback(timeOffset);
// Restart streaming
s.buffers.m_samplesProcessed = timeToSamples(timeOffset, s.buffers.m_sampleRate, s.buffers.m_channelCount);
if (s.reconstruct_on_pitch_change) {
timeOffset /= pitch;
}
s.buffers.m_samplesProcessed = time_to_samples(timeOffset, s.buffers.m_sampleRate, s.buffers.m_channelCount);
}
if (oldStatus == sf::SoundSource::Stopped) {
@ -234,18 +245,19 @@ sf::Time SyncedSoundStreams::getPlayingOffset() const {
if (streams.empty()) {
return sf::Time::Zero;
}
const auto& s = streams.begin()->second;
return getPlayingOffset(streams.begin()->second);
}
sf::Time SyncedSoundStreams::getPlayingOffset(const InternalStream& s) const {
if (not (s.buffers.m_sampleRate && s.buffers.m_channelCount)) {
return sf::Time::Zero;
}
ALfloat secs = 0.f;
alCheck(alGetSourcef(s.stream->get_source(), AL_SEC_OFFSET, &secs));
const auto openal_seconds = sf::seconds(
secs
+ static_cast<float>(s.buffers.m_samplesProcessed)
/ static_cast<float>(s.buffers.m_sampleRate)
/ static_cast<float>(s.buffers.m_channelCount)
const auto openal_seconds = sf::seconds(secs) + samples_to_time(
s.buffers.m_samplesProcessed,
s.buffers.m_sampleRate,
s.buffers.m_channelCount
);
if (s.reconstruct_on_pitch_change) {
return openal_seconds * pitch;
@ -255,14 +267,14 @@ sf::Time SyncedSoundStreams::getPlayingOffset() const {
}
sf::Time SyncedSoundStreams::getPrecisePlayingOffset() const {
const auto base = getPlayingOffset();
if (streams.empty()) {
return base;
}
const auto& s = streams.begin()->second;
if (not (s.buffers.m_sampleRate && s.buffers.m_channelCount)) {
return base;
return sf::Time::Zero;
}
return getPrecisePlayingOffset(streams.begin()->second);
}
sf::Time SyncedSoundStreams::getPrecisePlayingOffset(const InternalStream& s) const {
const auto base = getPlayingOffset(s);
const auto correction = (
s.stream->alSecOffsetLatencySoft()[1] - s.stream->lag
);
@ -288,6 +300,94 @@ bool SyncedSoundStreams::getLoop() const {
return m_loop;
}
void SyncedSoundStreams::display_debug() const {
if (ImGui::BeginTable("SSS debug props", streams.size() + 1, ImGuiTableFlags_Borders)) {
ImGui::TableSetupColumn("");
for (const auto& [name, _] : streams) {
ImGui::TableSetupColumn(name.c_str());
}
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted("reconstruct on pitch change");
for (const auto& [_, s] : streams) {
ImGui::TableNextColumn();
ImGui::TextUnformatted(fmt::format("{}", s.reconstruct_on_pitch_change).c_str());
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted("SyncedSoundStreams offset");
for (const auto& [_, s] : streams) {
ImGui::TableNextColumn();
ImGui::TextUnformatted(Toolbox::to_string(getPlayingOffset(s)).c_str());
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted("internal stream offset");
for (const auto& [_, s] : streams) {
ImGui::TableNextColumn();
ImGui::TextUnformatted(Toolbox::to_string(s.stream->getPlayingOffset()).c_str());
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted("SyncedSoundStreams offset (precise)");
for (const auto& [_, s] : streams) {
ImGui::TableNextColumn();
ImGui::TextUnformatted(Toolbox::to_string(getPrecisePlayingOffset(s)).c_str());
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted("internal stream offset (precise)");
for (const auto& [_, s] : streams) {
ImGui::TableNextColumn();
ImGui::TextUnformatted(Toolbox::to_string(s.stream->getPrecisePlayingOffset()).c_str());
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted("stream info in .buffers");
for (const auto& [_, s] : streams) {
ImGui::TableNextColumn();
ImGui::TextUnformatted(fmt::format("{}Hz * {}ch", s.buffers.m_sampleRate, s.buffers.m_channelCount).c_str());
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted("stream info in .stream");
for (const auto& [_, s] : streams) {
ImGui::TableNextColumn();
ImGui::TextUnformatted(fmt::format("{}Hz * {}ch", s.stream->getSampleRate(), s.stream->getChannelCount()).c_str());
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted("internal stream pitch");
for (const auto& [_, s] : streams) {
ImGui::TableNextColumn();
ImGui::TextUnformatted(fmt::format("x{}", s.stream->getPitch()).c_str());
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted("AL_SEC_OFFSET");
for (const auto& [_, s] : streams) {
ImGui::TableNextColumn();
ALfloat secs = 0.f;
alCheck(alGetSourcef(s.stream->get_source(), AL_SEC_OFFSET, &secs));
ImGui::TextUnformatted(Toolbox::to_string(sf::seconds(secs)).c_str());
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted("samples_to_time()");
for (const auto& [_, s] : streams) {
ImGui::TableNextColumn();
const auto time = samples_to_time(
s.buffers.m_samplesProcessed,
s.buffers.m_sampleRate,
s.buffers.m_channelCount
);
ImGui::TextUnformatted(Toolbox::to_string(time).c_str());
}
ImGui::EndTable();
}
}
void SyncedSoundStreams::setProcessingInterval(sf::Time interval) {
m_processingInterval = interval;

View File

@ -77,6 +77,8 @@ public:
void setLoop(bool loop);
bool getLoop() const;
void display_debug() const;
protected:
void setProcessingInterval(sf::Time interval);
@ -94,6 +96,9 @@ private:
void unsafe_update_streams();
void reload_sources();
sf::Time getPlayingOffset(const InternalStream& s) const;
sf::Time getPrecisePlayingOffset(const InternalStream& s) const;
float pitch = 1.f;
std::thread m_thread; // Thread running the background tasks
mutable std::recursive_mutex m_threadMutex; // Thread mutex

View File

@ -124,6 +124,7 @@ void EditorState::toggle_beat_ticks() {
if (audio.contains_stream(beat_tick_stream)) {
audio.remove_stream(beat_tick_stream);
} else {
beat_ticks = beat_ticks->with_pitch(get_pitch());
audio.add_stream(beat_tick_stream, {beat_ticks, true});
}
}
@ -158,6 +159,10 @@ void EditorState::set_pitch(float pitch) {
audio.setPitch(pitch);
}
float EditorState::get_pitch() const {
return speed / 10.f;
}
void EditorState::set_playback_position(std::variant<sf::Time, Fraction> newPosition) {
const auto clamp_ = VariantVisitor {
[this](const sf::Time& seconds) {
@ -515,6 +520,8 @@ void EditorState::display_status() {
"No jacket loaded");
}
}
audio.display_debug();
}
ImGui::End();
};

View File

@ -25,7 +25,7 @@
const std::string music_stream = "music";
const std::string note_clap_stream = "note_clap";
const std::string note_clap_stream = "aaa_note_clap";
const std::string beat_tick_stream = "beat_tick";
/*
@ -86,6 +86,7 @@ public:
void stop();
sf::SoundSource::Status get_status();
void set_pitch(float pitch);
float get_pitch() const;
void set_playback_position(std::variant<sf::Time, Fraction> newPosition);
sf::Time get_precise_playback_position();