2023-04-24 13:45:06 -07:00
|
|
|
#pragma once
|
2024-03-23 18:41:26 -07:00
|
|
|
#include <SDL_mixer.h>
|
2023-04-24 13:45:06 -07:00
|
|
|
#include <thread>
|
2023-07-15 14:52:49 -07:00
|
|
|
#include <SDL.h>
|
|
|
|
#include <SDL_audio.h>
|
2023-04-24 13:45:06 -07:00
|
|
|
#include <string>
|
|
|
|
#include <atomic>
|
|
|
|
#include <mutex>
|
2023-07-15 14:52:49 -07:00
|
|
|
#include <SoundTouch.h>
|
|
|
|
#include <span>
|
2023-09-03 11:54:07 -07:00
|
|
|
#include <optional>
|
2023-12-22 14:13:48 -08:00
|
|
|
#include <vector>
|
|
|
|
#include <queue>
|
2023-07-15 14:52:49 -07:00
|
|
|
using namespace soundtouch;
|
|
|
|
using std::span;
|
2023-09-03 11:54:07 -07:00
|
|
|
using std::optional;
|
2023-12-22 14:13:48 -08:00
|
|
|
using std::vector;
|
|
|
|
using std::queue;
|
2024-03-23 18:41:26 -07:00
|
|
|
enum {
|
|
|
|
PlaybackSignalNone = 0,
|
|
|
|
PlaybackSignalFileChanged = 1 << 0,
|
|
|
|
PlaybackSignalSpeedChanged = 1 << 1,
|
|
|
|
PlaybackSignalTempoChanged = 1 << 2,
|
|
|
|
PlaybackSignalPitchChanged = 1 << 3,
|
|
|
|
PlaybackSignalPaused = 1 << 4,
|
|
|
|
PlaybackSignalResumed = 1 << 5,
|
|
|
|
PlaybackSignalStopped = 1 << 6,
|
|
|
|
PlaybackSignalErrorOccurred = 1 << 7,
|
|
|
|
PlaybackSignalSeeked = 1 << 8,
|
|
|
|
PlaybackSignalStarted = 1 << 9
|
|
|
|
};
|
2023-04-24 13:45:06 -07:00
|
|
|
class Playback {
|
2024-03-26 18:39:02 -07:00
|
|
|
public:
|
|
|
|
inline Playback() {};
|
|
|
|
inline virtual ~Playback() {}
|
|
|
|
inline virtual std::optional<std::string> get_current_file() {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
inline virtual std::optional<std::string> get_current_title() {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
inline virtual double GetPosition() {
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
inline virtual double GetLength() {
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
inline virtual void Seek(double position) {}
|
|
|
|
inline virtual void Start(std::string filePath) {}
|
|
|
|
inline virtual bool IsPaused() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
inline virtual void Pause() {}
|
|
|
|
inline virtual void Stop() {}
|
|
|
|
inline virtual bool IsStopped() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
inline virtual void SetTempo(float tempo) {}
|
|
|
|
inline virtual void SetSpeed(float speed) {}
|
|
|
|
inline virtual void SetPitch(float pitch) {}
|
|
|
|
inline virtual void SetVolume(float volume) {}
|
|
|
|
inline virtual float GetTempo() {
|
|
|
|
return 1.0;
|
|
|
|
}
|
|
|
|
inline virtual float GetSpeed() {
|
|
|
|
return 1.0;
|
|
|
|
}
|
|
|
|
inline virtual float GetPitch() {
|
|
|
|
return 1.0;
|
|
|
|
}
|
|
|
|
inline virtual float GetVolume() {
|
|
|
|
return 1.0;
|
|
|
|
}
|
|
|
|
inline virtual uint16_t handle_signals(uint16_t signal) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
inline virtual optional<std::string> GetError() {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
inline virtual bool ErrorExists() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
inline virtual void SetPaused(bool paused) {
|
|
|
|
if (IsPaused() != paused) {
|
|
|
|
Pause();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
class DBusAPISender;
|
|
|
|
class PlaybackRemote : public Playback {
|
|
|
|
private:
|
|
|
|
DBusAPISender *sender;
|
|
|
|
public:
|
|
|
|
|
|
|
|
double GetPosition() override;
|
|
|
|
double GetLength() override;
|
|
|
|
void Seek(double position) override;
|
|
|
|
void Start(std::string filePath) override;
|
|
|
|
bool IsPaused() override;
|
|
|
|
void Pause() override;
|
|
|
|
void Stop() override;
|
|
|
|
void Update();
|
|
|
|
bool IsStopped() override;
|
|
|
|
void SetTempo(float tempo) override;
|
|
|
|
void SetPitch(float pitch) override;
|
|
|
|
void SetSpeed(float speed) override;
|
|
|
|
void SetVolume(float volume) override;
|
|
|
|
float GetTempo() override;
|
|
|
|
float GetPitch() override;
|
|
|
|
float GetSpeed() override;
|
|
|
|
float GetVolume() override;
|
|
|
|
uint16_t handle_signals(uint16_t signal) override;
|
|
|
|
optional<std::string> GetError() override;
|
|
|
|
bool ErrorExists() override;
|
|
|
|
PlaybackRemote();
|
|
|
|
};
|
|
|
|
class PlaybackInstance : public Playback {
|
2023-04-24 13:45:06 -07:00
|
|
|
private:
|
|
|
|
std::string filePath;
|
|
|
|
std::atomic_bool running;
|
|
|
|
std::atomic_bool file_changed;
|
|
|
|
std::atomic_bool seeking;
|
|
|
|
std::atomic_bool update;
|
2023-09-03 11:54:07 -07:00
|
|
|
std::atomic_bool restart;
|
|
|
|
std::atomic_bool playback_ready;
|
2024-03-23 18:41:26 -07:00
|
|
|
std::atomic_bool speed_changed;
|
|
|
|
std::atomic_bool tempo_changed;
|
|
|
|
std::atomic_bool pitch_changed;
|
|
|
|
std::atomic_bool pause_changed;
|
2023-04-24 13:45:06 -07:00
|
|
|
std::mutex flag_mutex;
|
2023-12-22 14:13:48 -08:00
|
|
|
std::mutex error_mutex;
|
2023-04-24 13:45:06 -07:00
|
|
|
std::thread thread;
|
|
|
|
double position;
|
|
|
|
double length;
|
|
|
|
bool paused;
|
2023-07-15 14:52:49 -07:00
|
|
|
Uint8* buf;
|
2023-07-10 12:45:24 -07:00
|
|
|
size_t bufsize;
|
2023-07-15 14:52:49 -07:00
|
|
|
Mix_CommonMixer_t general_mixer;
|
|
|
|
SDL_AudioDeviceID device;
|
|
|
|
SoundTouch *st;
|
|
|
|
SDL_AudioSpec spec;
|
2023-10-16 10:44:25 -07:00
|
|
|
/// @brief A fake SDL_AudioSpec used to trick SDL Mixer X into allocating a bigger buffer.
|
|
|
|
SDL_AudioSpec fakespec;
|
2023-07-15 14:52:49 -07:00
|
|
|
void SDLCallbackInner(Uint8 *stream, int len);
|
|
|
|
static void SDLCallback(void *userdata, Uint8 *stream, int len);
|
2023-07-10 12:45:24 -07:00
|
|
|
Mix_Music *Load(const char* file);
|
|
|
|
void Unload(Mix_Music* music);
|
2023-04-24 13:45:06 -07:00
|
|
|
void ThreadFunc();
|
2023-09-03 11:54:07 -07:00
|
|
|
void UpdateST();
|
|
|
|
double GetMaxSeconds();
|
2023-12-22 14:13:48 -08:00
|
|
|
queue<std::string> errors;
|
2024-03-23 18:41:26 -07:00
|
|
|
std::mutex current_file_mutex;
|
|
|
|
std::optional<std::string> current_file;
|
2024-03-26 18:39:02 -07:00
|
|
|
std::optional<std::string> current_title;
|
2024-03-23 18:41:26 -07:00
|
|
|
uint16_t signals_occurred = PlaybackSignalNone;
|
|
|
|
std::mutex signal_mutex;
|
|
|
|
void set_signal(uint16_t signal);
|
|
|
|
float prev_pitch, prev_speed, prev_tempo;
|
2024-03-26 18:39:02 -07:00
|
|
|
|
2023-04-24 13:45:06 -07:00
|
|
|
public:
|
2024-03-26 18:39:02 -07:00
|
|
|
PlaybackInstance();
|
|
|
|
~PlaybackInstance() override;
|
|
|
|
std::optional<std::string> get_current_file() override;
|
|
|
|
std::optional<std::string> get_current_title() override;
|
|
|
|
|
|
|
|
double GetPosition() override;
|
|
|
|
double GetLength() override;
|
|
|
|
void Seek(double position) override;
|
|
|
|
void Start(std::string filePath) override;
|
|
|
|
bool IsPaused() override;
|
|
|
|
void Pause() override;
|
|
|
|
void Stop() override;
|
2023-04-24 13:45:06 -07:00
|
|
|
void Update();
|
2024-03-26 18:39:02 -07:00
|
|
|
bool IsStopped() override;
|
|
|
|
void SetTempo(float tempo) override;
|
|
|
|
void SetPitch(float pitch) override;
|
|
|
|
void SetSpeed(float speed) override;
|
|
|
|
void SetVolume(float volume) override;
|
|
|
|
float GetTempo() override;
|
|
|
|
float GetPitch() override;
|
|
|
|
float GetSpeed() override;
|
|
|
|
float GetVolume() override;
|
2023-04-24 13:45:06 -07:00
|
|
|
float volume;
|
|
|
|
float speed;
|
2023-07-15 14:52:49 -07:00
|
|
|
float tempo;
|
|
|
|
float pitch;
|
2023-12-22 14:13:48 -08:00
|
|
|
float MaxSeconds = 100.0;
|
|
|
|
float MaxSpeed = 4.0;
|
|
|
|
float MaxPitch = 4.0;
|
|
|
|
float MaxTempo = 4.0;
|
|
|
|
float MinSpeed = 0.25;
|
|
|
|
float MinPitch = 0.25;
|
|
|
|
float MinTempo = 0.25;
|
2024-03-26 18:39:02 -07:00
|
|
|
uint16_t handle_signals(uint16_t signal) override;
|
|
|
|
optional<std::string> GetError() override;
|
|
|
|
bool ErrorExists() override;
|
2023-04-24 13:45:06 -07:00
|
|
|
};
|