Encapsulate music and timer interpolation, test with pause

This commit is contained in:
NaiJi ✨ 2021-08-05 21:59:48 +03:00
parent c2677bdd2b
commit cf1119c742
15 changed files with 159 additions and 58 deletions

View File

@ -2,7 +2,7 @@
#define INPUTTYPE_H
#include <SFML/Window/Event.hpp>
#include "game/mathutils.h"
#include "tools/mathutils.h"
struct PlayerInput
{

View File

@ -1,7 +1,7 @@
#ifndef PRECISIONEVALUATOR_H
#define PRECISIONEVALUATOR_H
#include "game/mathutils.h"
#include "tools/mathutils.h"
#include <numeric>
#include <type_traits>
#include <vector>

View File

@ -1,7 +1,7 @@
#ifndef TIMELINE_H
#define TIMELINE_H
#include "game/mathutils.h"
#include "tools/mathutils.h"
#include <memory>
#include <vector>
@ -13,10 +13,9 @@ class Timeline
public:
virtual ~Timeline() = default;
virtual void update() = 0;
virtual void update(const microsec& offset) = 0;
virtual void clear() = 0;
virtual microsec currentMusicOffset() const = 0;
virtual void drawVisibleNotes() const = 0;
};

21
include/tools/music.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include "tools/mathutils.h"
#include <string>
class Music
{
public:
virtual ~Music() = default;
virtual bool openFromFile(const std::string& filepath) = 0;
virtual void play() = 0;
virtual void pause() = 0;
virtual void stop() = 0;
virtual void setVolume(int volume) = 0;
virtual void setOffset(const microsec& offset) = 0;
virtual microsec fetchOffset() = 0;
};

View File

@ -7,13 +7,15 @@
#include "gui/mainmenu.h"
#include "gui/gamestate.h"
#include "tools/musicsfml.h"
#include <iostream>
const sf::Time TIME_PER_FRAME = sf::seconds(1.f / 90.f);
Application::Application() :
_game_window({1280, 720}, "Test", sf::Style::Default),
_game(std::make_unique<ClassicGame>(std::make_unique<ClassicGraphicsManager>(_game_window)))
_game(std::make_unique<ClassicGame>(std::make_unique<ClassicGraphicsManager>(_game_window), std::make_unique<MusicSFML>()))
{
_game_window.setFramerateLimit(60);
_game_window.setKeyRepeatEnabled(false);

View File

@ -1,6 +1,6 @@
#pragma once
#include "game/mathutils.h"
#include "tools/mathutils.h"
#include <memory>
class ClassicSprite;

View File

@ -2,11 +2,14 @@
#include "classictimeline.h"
#include "classicnote.h"
#include "classicmapcreator.h"
#include "tools/music.h"
#include <iostream>
ClassicGame::ClassicGame(std::unique_ptr<ClassicGraphicsManager>&& manager) :
ClassicGame::ClassicGame(std::unique_ptr<ClassicGraphicsManager>&& manager, std::unique_ptr<Music>&& music) :
_timeline(std::make_unique<ClassicTimeline>()),
_graphics_manager(std::move(manager))
_graphics_manager(std::move(manager)),
_music(std::move(music)),
_is_paused(false)
{
_slap_buffer.loadFromFile("very-final-slap.wav");
_slap.setBuffer(_slap_buffer);
@ -58,12 +61,15 @@ void ClassicGame::run()
{
ClassicMapCreator creator(_graphics_manager);
auto beatmap = creator.createBeatmap("aa");
_music->openFromFile("METEOR.flac");
_music->setVolume(10);
_music->play();
_timeline->run(std::move(beatmap.notes), beatmap.visibility_offset);
}
void ClassicGame::input(PlayerInput&& inputdata)
{
inputdata.timestamp = _timeline->currentMusicOffset();
inputdata.timestamp = _music->fetchOffset();
switch (inputdata.event.type)
{
@ -73,6 +79,22 @@ void ClassicGame::input(PlayerInput&& inputdata)
case sf::Event::KeyPressed:
{
if (inputdata.event.key.code == sf::Keyboard::Space)
{
if (!_is_paused)
{
_is_paused = true;
_music->pause();
return;
}
else
{
_is_paused = false;
_music->play();
return;
}
}
auto note_it = _timeline->getActiveNote();
if (!_timeline->isExpired(note_it))
@ -111,7 +133,7 @@ void ClassicGame::input(PlayerInput&& inputdata)
void ClassicGame::update()
{
_timeline->update();
_timeline->update(_music->fetchOffset());
_timeline->fetchVisibleNotes();
}

View File

@ -7,6 +7,7 @@
#include <SFML/Audio/SoundBuffer.hpp>
#include <SFML/Audio/Sound.hpp>
class Music;
class ClassicNote;
class ClassicTimeline;
class ClassicGraphicsManager;
@ -14,7 +15,7 @@ class ClassicGraphicsManager;
class ClassicGame final : public Game
{
public:
explicit ClassicGame(std::unique_ptr<ClassicGraphicsManager>&& manager);
explicit ClassicGame(std::unique_ptr<ClassicGraphicsManager>&& manager, std::unique_ptr<Music>&& music);
virtual ~ClassicGame() override;
virtual void run() override;
@ -34,6 +35,10 @@ private:
std::unique_ptr<ClassicGraphicsManager> _graphics_manager;
sf::SoundBuffer _slap_buffer;
sf::Sound _slap;
std::unique_ptr<Music> _music;
bool _is_paused;
};
#endif // CLASSICGAME_H

View File

@ -3,7 +3,7 @@
#include <vector>
#include "game/mathutils.h"
#include "tools/mathutils.h"
#include "classicgraphicsmanager.h"
struct Beatmap

View File

@ -1,6 +1,6 @@
#pragma once
#include "game/mathutils.h"
#include "tools/mathutils.h"
#include "game/sprite.h"
#include "SFML/Graphics/RectangleShape.hpp"
#include "SFML/Graphics/Text.hpp"

View File

@ -3,19 +3,8 @@
#include <iostream>
ClassicTimeline::ClassicTimeline() :
_sfml_music_offset(0),
_previous_frame_offset(0),
_absolute_offset(0)
{
// BPM of METEOR is 170.
// Length is 1:14
// I calculated that the time between beats is about 1412162 microseconds
std::string song_filename = "METEOR.flac";
_music.openFromFile(song_filename);
_music.setVolume(10);
}
_current_offset(0)
{}
void ClassicTimeline::run(std::vector<ClassicNote*>&& notes, const microsec& visibility)
{
@ -28,9 +17,6 @@ void ClassicTimeline::run(std::vector<ClassicNote*>&& notes, const microsec& vis
expire(_active_note);
fetchVisibleNotes();
_music.play();
_previous_frame_offset = _offset_interpolator.restart().asMicroseconds();
}
ClassicTimeline::~ClassicTimeline()
@ -46,22 +32,13 @@ void ClassicTimeline::clear()
_timeline.clear();
}
void ClassicTimeline::update()
void ClassicTimeline::update(const microsec& offset)
{
const auto interpolator_timestamp = _offset_interpolator.getElapsedTime().asMicroseconds();
const auto sfml_new_offset = currentMusicOffset();
_absolute_offset += (interpolator_timestamp - _previous_frame_offset);
_previous_frame_offset = interpolator_timestamp;
if (sfml_new_offset != _sfml_music_offset)
{
_absolute_offset = ((_absolute_offset + sfml_new_offset) / 2);
_sfml_music_offset = sfml_new_offset;
}
_current_offset = offset;
checkCurrentActiveNote();
checkForNextActiveNote();
updateVisibleSprites(_absolute_offset);
updateVisibleSprites(_current_offset);
}
void ClassicTimeline::checkCurrentActiveNote()
@ -115,11 +92,6 @@ void ClassicTimeline::expire(Iterator& iterator)
iterator = _timeline.end();
}
microsec ClassicTimeline::currentMusicOffset() const
{
return _music.getPlayingOffset().asMicroseconds();
}
bool ClassicTimeline::isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const
{
return ((*iterator)->offset() - _visibility_offset) <= music_offset;
@ -127,7 +99,7 @@ bool ClassicTimeline::isVisiblyClose(const Iterator& iterator, const microsec& m
void ClassicTimeline::fetchVisibleNotes()
{
findLastVisibleNote(_absolute_offset);
findLastVisibleNote(_current_offset);
findFirstVisibleNote();
}

View File

@ -2,8 +2,6 @@
#include "game/timeline.h"
#include <SFML/Audio/Music.hpp>
#include <SFML/System/Clock.hpp>
#include <vector>
#include <memory>
@ -14,10 +12,9 @@ class ClassicTimeline : public Timeline
public:
explicit ClassicTimeline();
virtual ~ClassicTimeline();
virtual void update() override;
virtual void update(const microsec& offset) override;
virtual void clear() override;
virtual microsec currentMusicOffset() const override;
virtual void drawVisibleNotes() const override;
void run(std::vector<ClassicNote*>&& notes, const microsec& visibility);
@ -37,12 +34,7 @@ private:
std::vector<microsec> _input_intervals;
std::vector<ClassicNote*> _timeline;
microsec _visibility_offset;
sf::Music _music;
sf::Clock _offset_interpolator;
microsec _sfml_music_offset;
microsec _previous_frame_offset;
microsec _absolute_offset;
microsec _current_offset;
void updateVisibleSprites(const microsec& music_offset);
void checkCurrentActiveNote();

58
src/tools/musicsfml.cpp Normal file
View File

@ -0,0 +1,58 @@
#include "musicsfml.h"
MusicSFML::MusicSFML() :
_sfml_music_offset(0),
_previous_frame_offset(0),
_absolute_offset(0)
{}
bool MusicSFML::openFromFile(const std::string& filepath)
{
return _music.openFromFile(filepath);
}
void MusicSFML::play()
{
_music.play();
_sfml_music_offset = _offset_interpolator.restart().asMicroseconds();
}
void MusicSFML::pause()
{
_music.pause();
}
void MusicSFML::stop()
{
_music.stop();
}
void MusicSFML::setVolume(int volume)
{
_music.setVolume(volume);
}
void MusicSFML::setOffset(const microsec& offset)
{
_previous_frame_offset += (offset - _absolute_offset);
_music.setPlayingOffset(sf::microseconds(offset));
}
microsec MusicSFML::fetchOffset()
{
if (_music.getStatus() != sf::Music::Status::Playing)
return _absolute_offset;
const auto interpolator_timestamp = _offset_interpolator.getElapsedTime().asMicroseconds();
const auto sfml_new_offset = _music.getPlayingOffset().asMicroseconds();
_absolute_offset += (interpolator_timestamp - _previous_frame_offset);
_previous_frame_offset = interpolator_timestamp;
if (sfml_new_offset != _sfml_music_offset)
{
_absolute_offset = ((_absolute_offset + sfml_new_offset) / 2);
_sfml_music_offset = sfml_new_offset;
}
return _absolute_offset;
}

30
src/tools/musicsfml.h Normal file
View File

@ -0,0 +1,30 @@
#pragma once
#include "tools/music.h"
#include <SFML/Audio/Music.hpp>
class MusicSFML : public Music
{
public:
explicit MusicSFML();
virtual bool openFromFile(const std::string& filepath) override;
virtual void play() override;
virtual void pause() override;
virtual void stop() override;
virtual void setVolume(int volume) override;
virtual void setOffset(const microsec& offset) override;
virtual microsec fetchOffset() override;
private:
sf::Music _music;
sf::Clock _offset_interpolator;
microsec _sfml_music_offset;
microsec _previous_frame_offset;
microsec _absolute_offset;
};