mirror of
https://github.com/clangen/musikcube.git
synced 2024-10-02 13:02:35 +00:00
More incremental work to support a crossfading transport.
This commit is contained in:
parent
06a83344b7
commit
1f0b306987
@ -40,6 +40,7 @@
|
||||
#include <core/audio/Outputs.h>
|
||||
#include <algorithm>
|
||||
|
||||
#define CROSS_FADE_DURATION_MS 2000
|
||||
#define END_OF_TRACK_MIXPOINT 1001
|
||||
|
||||
using namespace musik::core::audio;
|
||||
@ -52,17 +53,15 @@ CrossfadeTransport::CrossfadeTransport()
|
||||
, state(PlaybackStopped)
|
||||
, nextCanStart(false)
|
||||
, muted(false)
|
||||
, crossfader(*this) {
|
||||
, crossfader(*this)
|
||||
, active(crossfader)
|
||||
, next(crossfader) {
|
||||
|
||||
}
|
||||
|
||||
CrossfadeTransport::~CrossfadeTransport() {
|
||||
this->disconnect_all();
|
||||
|
||||
//LockT lock(this->stateMutex);
|
||||
|
||||
//RESET_NEXT_PLAYER(this);
|
||||
//RESET_ACTIVE_PLAYER(this);
|
||||
this->Stop();
|
||||
}
|
||||
|
||||
PlaybackState CrossfadeTransport::GetPlaybackState() {
|
||||
@ -71,67 +70,22 @@ PlaybackState CrossfadeTransport::GetPlaybackState() {
|
||||
}
|
||||
|
||||
void CrossfadeTransport::PrepareNextTrack(const std::string& trackUrl) {
|
||||
//bool startNext = false;
|
||||
//{
|
||||
// LockT lock(this->stateMutex);
|
||||
// RESET_NEXT_PLAYER(this);
|
||||
// this->nextPlayer = Player::Create(trackUrl, this->output, this);
|
||||
// startNext = this->nextCanStart;
|
||||
//}
|
||||
|
||||
//if (startNext) {
|
||||
// this->StartWithPlayer(this->nextPlayer);
|
||||
//}
|
||||
//Lock lock(this->stateMutex);
|
||||
//this->next.Reset(trackUrl, this);
|
||||
}
|
||||
|
||||
void CrossfadeTransport::Start(const std::string& url) {
|
||||
|
||||
{
|
||||
Lock lock(this->stateMutex);
|
||||
|
||||
musik::debug::info(TAG, "we were asked to start the track at " + url);
|
||||
this->nextPlayer.Reset(this->crossfader, Crossfader::SoftCut);
|
||||
this->activePlayer.Reset(this->crossfader, Crossfader::SoftCut, url, this);
|
||||
musik::debug::info(TAG, "Player created successfully");
|
||||
musik::debug::info(TAG, "trying to play " + url);
|
||||
|
||||
this->activePlayer.Play(this->crossfader);
|
||||
this->next.Stop();
|
||||
this->active.Reset(url, this);
|
||||
this->active.Play();
|
||||
}
|
||||
|
||||
this->RaiseStreamEvent(StreamScheduled, this->activePlayer.player);
|
||||
}
|
||||
|
||||
void CrossfadeTransport::Restart() {
|
||||
//if (newPlayer) {
|
||||
// bool playingNext = false;
|
||||
|
||||
// {
|
||||
// LockT lock(this->stateMutex);
|
||||
|
||||
// playingNext = (newPlayer == nextPlayer);
|
||||
// if (nextPlayer != nullptr && newPlayer != nextPlayer) {
|
||||
// this->nextPlayer->Destroy();
|
||||
// }
|
||||
|
||||
// RESET_ACTIVE_PLAYER(this);
|
||||
|
||||
// this->nextPlayer = nullptr;
|
||||
// this->activePlayer = newPlayer;
|
||||
// }
|
||||
|
||||
// /* first argument suppresses the "Stop" event from getting triggered,
|
||||
// the second param is used for gapless playback -- we won't stop the output
|
||||
// and will allow pending buffers to finish if we're not automatically
|
||||
// playing the next track. note we do this outside of critical section so
|
||||
// outputs *can* stop buffers immediately, and not to worry about causing a
|
||||
// deadlock. */
|
||||
// this->StopInternal(true, !playingNext, newPlayer);
|
||||
// this->SetNextCanStart(false);
|
||||
// this->output->Resume();
|
||||
// newPlayer->Play();
|
||||
// musik::debug::info(TAG, "play()");
|
||||
|
||||
// this->RaiseStreamEvent(StreamScheduled, newPlayer);
|
||||
//}
|
||||
this->RaiseStreamEvent(StreamScheduled, this->active.player);
|
||||
}
|
||||
|
||||
void CrossfadeTransport::ReloadOutput() {
|
||||
@ -139,74 +93,42 @@ void CrossfadeTransport::ReloadOutput() {
|
||||
}
|
||||
|
||||
void CrossfadeTransport::Stop() {
|
||||
//this->StopInternal(false, true);
|
||||
}
|
||||
{
|
||||
Lock lock(this->stateMutex);
|
||||
this->crossfader.Stop();
|
||||
this->active.Stop();
|
||||
this->next.Stop();
|
||||
}
|
||||
|
||||
void CrossfadeTransport::StopInternal(
|
||||
bool suppressStopEvent,
|
||||
bool stopOutput,
|
||||
Player* exclude)
|
||||
{
|
||||
//musik::debug::info(TAG, "stop");
|
||||
|
||||
///* if we stop the output, we kill all of the Players immediately.
|
||||
//otherwise, we let them finish naturally; RemoveActive() will take
|
||||
//care of disposing of them */
|
||||
//if (stopOutput) {
|
||||
// {
|
||||
// LockT lock(this->stateMutex);
|
||||
|
||||
// RESET_NEXT_PLAYER(this);
|
||||
|
||||
// if (this->activePlayer && this->activePlayer != exclude) {
|
||||
// this->activePlayer->Destroy();
|
||||
// this->activePlayer = nullptr;
|
||||
// }
|
||||
// }
|
||||
|
||||
// /* stopping the transport will stop any buffers that are currently in
|
||||
// flight. this makes the sound end immediately. */
|
||||
// this->output->Stop();
|
||||
//}
|
||||
|
||||
//if (!suppressStopEvent) {
|
||||
// /* if we know we're starting another track immediately, suppress
|
||||
// the stop event. this functionality is not available to the public
|
||||
// interface, it's an internal optimization */
|
||||
// this->SetPlaybackState(PlaybackStopped);
|
||||
//}
|
||||
this->SetPlaybackState(PlaybackStopped);
|
||||
}
|
||||
|
||||
bool CrossfadeTransport::Pause() {
|
||||
//musik::debug::info(TAG, "pause");
|
||||
{
|
||||
Lock lock(this->stateMutex);
|
||||
this->crossfader.Pause();
|
||||
this->active.Pause();
|
||||
}
|
||||
|
||||
//this->output->Pause();
|
||||
|
||||
//if (this->activePlayer) {
|
||||
// this->SetPlaybackState(PlaybackPaused);
|
||||
// return true;
|
||||
//}
|
||||
if (this->active.player) {
|
||||
this->SetPlaybackState(PlaybackPaused);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CrossfadeTransport::Resume() {
|
||||
//musik::debug::info(TAG, "resume");
|
||||
{
|
||||
Lock lock(this->stateMutex);
|
||||
this->crossfader.Resume();
|
||||
this->active.Resume();
|
||||
}
|
||||
|
||||
//this->output->Resume();
|
||||
|
||||
//{
|
||||
// LockT lock(this->stateMutex);
|
||||
|
||||
// if (this->activePlayer) {
|
||||
// this->activePlayer->Play();
|
||||
// }
|
||||
//}
|
||||
|
||||
//if (this->activePlayer) {
|
||||
// this->SetPlaybackState(PlaybackPlaying);
|
||||
// return true;
|
||||
//}
|
||||
if (this->active.player) {
|
||||
this->SetPlaybackState(PlaybackPlaying);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -214,30 +136,30 @@ bool CrossfadeTransport::Resume() {
|
||||
double CrossfadeTransport::Position() {
|
||||
Lock lock(this->stateMutex);
|
||||
|
||||
if (this->activePlayer.player) {
|
||||
return this->activePlayer.player->GetPosition();
|
||||
if (this->active.player) {
|
||||
return this->active.player->GetPosition();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CrossfadeTransport::SetPosition(double seconds) {
|
||||
//{
|
||||
// LockT lock(this->stateMutex);
|
||||
{
|
||||
Lock lock(this->stateMutex);
|
||||
|
||||
// if (this->activePlayer) {
|
||||
// this->activePlayer->SetPosition(seconds);
|
||||
// }
|
||||
//}
|
||||
if (this->active.player) {
|
||||
this->active.player->SetPosition(seconds);
|
||||
}
|
||||
}
|
||||
|
||||
//if (this->activePlayer) {
|
||||
// this->TimeChanged(seconds);
|
||||
//}
|
||||
if (this->active.player) {
|
||||
this->TimeChanged(seconds);
|
||||
}
|
||||
}
|
||||
|
||||
double CrossfadeTransport::GetDuration() {
|
||||
Lock lock(this->stateMutex);
|
||||
return this->activePlayer.player ? this->activePlayer.player->GetDuration() : -1.0f;
|
||||
return this->active.player ? this->active.player->GetDuration() : -1.0f;
|
||||
}
|
||||
|
||||
bool CrossfadeTransport::IsMuted() {
|
||||
@ -245,11 +167,17 @@ bool CrossfadeTransport::IsMuted() {
|
||||
}
|
||||
|
||||
void CrossfadeTransport::SetMuted(bool muted) {
|
||||
//if (this->muted != muted) {
|
||||
// this->muted = muted;
|
||||
// this->output->SetVolume(muted ? 0.0f : this->volume);
|
||||
// this->VolumeChanged();
|
||||
//}
|
||||
if (this->muted != muted) {
|
||||
this->muted = muted;
|
||||
this->active.SetVolume(muted ? 0.0f : this->volume);
|
||||
this->next.SetVolume(muted ? 0.0f : this->volume);
|
||||
|
||||
if (muted) {
|
||||
this->crossfader.Reset();
|
||||
}
|
||||
|
||||
this->VolumeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
double CrossfadeTransport::Volume() {
|
||||
@ -257,22 +185,20 @@ double CrossfadeTransport::Volume() {
|
||||
}
|
||||
|
||||
void CrossfadeTransport::SetVolume(double volume) {
|
||||
//double oldVolume = this->volume;
|
||||
double oldVolume = this->volume;
|
||||
|
||||
//volume = std::max(0.0, std::min(1.0, volume));
|
||||
volume = std::max(0.0, std::min(1.0, volume));
|
||||
|
||||
//this->volume = volume;
|
||||
this->volume = volume;
|
||||
if (oldVolume != this->volume) {
|
||||
this->VolumeChanged();
|
||||
}
|
||||
|
||||
//if (oldVolume != this->volume) {
|
||||
// this->VolumeChanged();
|
||||
//}
|
||||
|
||||
//std::string output = boost::str(
|
||||
// boost::format("set volume %d%%") % round(volume * 100));
|
||||
|
||||
//musik::debug::info(TAG, output);
|
||||
|
||||
//this->output->SetVolume(this->volume);
|
||||
{
|
||||
Lock lock(this->stateMutex);
|
||||
active.SetVolume(volume);
|
||||
next.SetVolume(volume);
|
||||
}
|
||||
}
|
||||
|
||||
void CrossfadeTransport::SetNextCanStart(bool nextCanStart) {
|
||||
@ -289,9 +215,12 @@ void CrossfadeTransport::OnPlayerStarted(Player* player) {
|
||||
{
|
||||
Lock lock(this->stateMutex);
|
||||
|
||||
double offset = this->activePlayer.player->GetDuration() - 2.0f;
|
||||
double offset =
|
||||
player->GetDuration() -
|
||||
((float) CROSS_FADE_DURATION_MS / 1000.0f);
|
||||
|
||||
if (offset > 0.0) {
|
||||
this->activePlayer.player->AddMixPoint(END_OF_TRACK_MIXPOINT, offset);
|
||||
player->AddMixPoint(END_OF_TRACK_MIXPOINT, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -313,50 +242,41 @@ void CrossfadeTransport::OnPlayerAlmostEnded(Player* player) {
|
||||
}
|
||||
|
||||
void CrossfadeTransport::OnPlayerFinished(Player* player) {
|
||||
//this->RaiseStreamEvent(StreamFinished, player);
|
||||
this->RaiseStreamEvent(StreamFinished, player);
|
||||
|
||||
//bool stopped = false;
|
||||
|
||||
//{
|
||||
// LockT lock(this->stateMutex);
|
||||
|
||||
// bool startedNext = false;
|
||||
// bool playerIsActive = (player == this->activePlayer);
|
||||
|
||||
// /* only start the next player if the currently active player is the
|
||||
// one that just finished. */
|
||||
// if (playerIsActive && this->nextPlayer) {
|
||||
// this->StartWithPlayer(this->nextPlayer);
|
||||
// startedNext = true;
|
||||
// }
|
||||
|
||||
// if (!startedNext) {
|
||||
// stopped = playerIsActive;
|
||||
// }
|
||||
//}
|
||||
|
||||
//if (stopped) {
|
||||
// this->Stop();
|
||||
//}
|
||||
/* the next player will have already started due to
|
||||
hitting the mix point... TODO: case where we couldn't
|
||||
do a crossfade because the track is too short. */
|
||||
if (active.player != nullptr) {
|
||||
this->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
void CrossfadeTransport::OnPlayerError(Player* player) {
|
||||
this->RaiseStreamEvent(StreamError, player);
|
||||
this->SetPlaybackState(PlaybackStopped);
|
||||
this->Stop();
|
||||
}
|
||||
|
||||
void CrossfadeTransport::OnPlayerDestroying(Player *player) {
|
||||
//LockT lock(this->stateMutex);
|
||||
Lock lock(this->stateMutex);
|
||||
|
||||
//if (player == this->activePlayer) {
|
||||
// RESET_ACTIVE_PLAYER(this);
|
||||
// return;
|
||||
//}
|
||||
if (active.player == player) {
|
||||
active.Reset();
|
||||
}
|
||||
|
||||
if (next.player == player) {
|
||||
next.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void CrossfadeTransport::OnPlayerMixPoint(Player* player, int id, double time) {
|
||||
if (id == END_OF_TRACK_MIXPOINT) {
|
||||
/* fade out */
|
||||
if (player == active.player) {
|
||||
active.Reset();
|
||||
next.TransferTo(active);
|
||||
//active.Play(); /* fade the new one in, if it exists */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -378,15 +298,19 @@ void CrossfadeTransport::RaiseStreamEvent(int type, Player* player) {
|
||||
this->StreamEvent(type, player->GetUrl());
|
||||
}
|
||||
|
||||
CrossfadeTransport::PlayerContext::PlayerContext() {
|
||||
this->player = nullptr;
|
||||
CrossfadeTransport::PlayerContext::PlayerContext(Crossfader& crossfader)
|
||||
: crossfader(crossfader)
|
||||
, player(nullptr) {
|
||||
|
||||
}
|
||||
|
||||
void CrossfadeTransport::PlayerContext::Reset(
|
||||
Crossfader& crossfader, Crossfader::Cut cut)
|
||||
{
|
||||
//if (this->player) {
|
||||
// crossfader.Remove(this->player, cut);
|
||||
void CrossfadeTransport::PlayerContext::Reset() {
|
||||
//if (this->player && this->output) {
|
||||
// crossfader.Fade(
|
||||
// this->player,
|
||||
// this->output,
|
||||
// Crossfader::FadeOut,
|
||||
// CROSS_FADE_DURATION_MS);
|
||||
//}
|
||||
|
||||
this->player = nullptr;
|
||||
@ -394,32 +318,66 @@ void CrossfadeTransport::PlayerContext::Reset(
|
||||
}
|
||||
|
||||
void CrossfadeTransport::PlayerContext::Reset(
|
||||
Crossfader& crossfader,
|
||||
Crossfader::Cut cut,
|
||||
const std::string& url,
|
||||
Player::PlayerEventListener* listener)
|
||||
{
|
||||
//if (this->player) {
|
||||
// if (this->output) {
|
||||
// crossfader.Remove(this->player, cut);
|
||||
// }
|
||||
//}
|
||||
if (this->player && this->output) {
|
||||
crossfader.Fade(
|
||||
this->player,
|
||||
this->output,
|
||||
Crossfader::FadeOut,
|
||||
CROSS_FADE_DURATION_MS);
|
||||
}
|
||||
|
||||
this->output = outputs::SelectedOutput();
|
||||
this->player = Player::Create(url, this->output, listener);
|
||||
}
|
||||
|
||||
void CrossfadeTransport::PlayerContext::Play(Crossfader& crossfader) {
|
||||
void CrossfadeTransport::PlayerContext::TransferTo(PlayerContext& to) {
|
||||
to.player = player;
|
||||
to.output = output;
|
||||
this->player = player;
|
||||
this->output.reset();
|
||||
}
|
||||
|
||||
void CrossfadeTransport::PlayerContext::Play() {
|
||||
if (this->output && this->player) {
|
||||
this->output->SetVolume(0.0f);
|
||||
this->output->Resume();
|
||||
this->player->Play();
|
||||
|
||||
//crossfader.Fade(
|
||||
// this->player,
|
||||
// this->output,
|
||||
// Crossfader::FadeIn,
|
||||
// 1.0f,
|
||||
// 1000);
|
||||
crossfader.Fade(
|
||||
this->player,
|
||||
this->output,
|
||||
Crossfader::FadeIn,
|
||||
CROSS_FADE_DURATION_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void CrossfadeTransport::PlayerContext::Stop() {
|
||||
if (this->output && this->player) {
|
||||
this->output->Stop();
|
||||
this->player->Destroy();
|
||||
}
|
||||
|
||||
this->player = nullptr;
|
||||
this->output.reset();
|
||||
}
|
||||
|
||||
void CrossfadeTransport::PlayerContext::Pause() {
|
||||
if (this->player && this->output) {
|
||||
this->Pause();
|
||||
}
|
||||
}
|
||||
|
||||
void CrossfadeTransport::PlayerContext::Resume() {
|
||||
if (this->player && this->output) {
|
||||
this->Resume();
|
||||
}
|
||||
}
|
||||
|
||||
void CrossfadeTransport::PlayerContext::SetVolume(double volume) {
|
||||
if (this->output) {
|
||||
this->output->SetVolume(volume);
|
||||
}
|
||||
}
|
@ -89,31 +89,27 @@ namespace musik { namespace core { namespace audio {
|
||||
using MessageQueue = musik::core::runtime::MessageQueue;
|
||||
|
||||
struct PlayerContext {
|
||||
PlayerContext();
|
||||
PlayerContext(Crossfader& crossfader);
|
||||
|
||||
void Reset();
|
||||
|
||||
void Reset(
|
||||
Crossfader& crossfader,
|
||||
Crossfader::Cut cut);
|
||||
|
||||
void Reset(
|
||||
Crossfader& crossfader,
|
||||
Crossfader::Cut cut,
|
||||
const std::string& url,
|
||||
Player::PlayerEventListener* listener);
|
||||
|
||||
void Play(Crossfader& crossfader);
|
||||
void TransferTo(PlayerContext& context);
|
||||
|
||||
void Stop();
|
||||
void Play();
|
||||
void Pause();
|
||||
void Resume();
|
||||
void SetVolume(double volume);
|
||||
|
||||
Output output;
|
||||
Player *player;
|
||||
Crossfader& crossfader;
|
||||
};
|
||||
|
||||
void Restart();
|
||||
|
||||
void StopInternal(
|
||||
bool suppressStopEvent,
|
||||
bool stopOutput,
|
||||
Player* exclude = nullptr);
|
||||
|
||||
void SetNextCanStart(bool nextCanStart);
|
||||
|
||||
void RaiseStreamEvent(int type, Player* player);
|
||||
@ -128,9 +124,9 @@ namespace musik { namespace core { namespace audio {
|
||||
|
||||
musik::core::sdk::PlaybackState state;
|
||||
std::recursive_mutex stateMutex;
|
||||
PlayerContext activePlayer;
|
||||
PlayerContext nextPlayer;
|
||||
Crossfader crossfader;
|
||||
PlayerContext active;
|
||||
PlayerContext next;
|
||||
double volume;
|
||||
bool nextCanStart;
|
||||
bool muted;
|
||||
|
@ -42,7 +42,8 @@ using namespace musik::core::audio;
|
||||
using namespace musik::core::sdk;
|
||||
using namespace musik::core::runtime;
|
||||
|
||||
#define TICK_TIME_MILLIS 100
|
||||
#define TICKS_PER_SECOND 10
|
||||
#define TICK_TIME_MILLIS (1000 / TICKS_PER_SECOND)
|
||||
|
||||
#define ENQUEUE_TICK() \
|
||||
this->messageQueue.Post(Message::Create( \
|
||||
@ -57,6 +58,7 @@ using namespace musik::core::runtime;
|
||||
Crossfader::Crossfader(ITransport& transport)
|
||||
: transport(transport) {
|
||||
this->quit = false;
|
||||
this->paused = false;
|
||||
|
||||
this->thread = std::make_unique<std::thread>(
|
||||
std::bind(&Crossfader::ThreadLoop, this));
|
||||
@ -72,19 +74,16 @@ void Crossfader::Fade(
|
||||
Player* player,
|
||||
std::shared_ptr<IOutput> output,
|
||||
Direction direction,
|
||||
float targetPercent,
|
||||
long durationMs)
|
||||
{
|
||||
LOCK(this->contextListLock);
|
||||
|
||||
std::shared_ptr<FadeContext> context = std::make_shared<FadeContext>();
|
||||
context->targetPercent = targetPercent;
|
||||
context->output = output;
|
||||
context->player = player;
|
||||
context->durationMs = durationMs;
|
||||
context->direction = direction;
|
||||
context->ticksCounted = 0;
|
||||
context->ticksTotal = context->durationMs / TICK_TIME_MILLIS;
|
||||
context->ticksTotal = (durationMs / TICK_TIME_MILLIS);
|
||||
contextList.push_back(context);
|
||||
|
||||
if (contextList.size() == 1) {
|
||||
@ -92,50 +91,59 @@ void Crossfader::Fade(
|
||||
}
|
||||
}
|
||||
|
||||
void Crossfader::Stop() {
|
||||
LOCK(this->contextListLock);
|
||||
|
||||
auto it = this->contextList.begin();
|
||||
while (it != this->contextList.end()) {
|
||||
if ((*it)->player) {
|
||||
(*it)->player->Destroy();
|
||||
}
|
||||
|
||||
(*it)->output->Stop();
|
||||
++it;
|
||||
}
|
||||
|
||||
this->contextList.clear();
|
||||
}
|
||||
|
||||
void Crossfader::Pause() {
|
||||
LOCK(this->contextListLock);
|
||||
|
||||
this->paused = true;
|
||||
|
||||
std::for_each(
|
||||
this->contextList.begin(),
|
||||
this->contextList.end(),
|
||||
[](auto it) {
|
||||
it->output->Pause();
|
||||
});
|
||||
|
||||
this->messageQueue.Remove(this, MESSAGE_TICK);
|
||||
}
|
||||
|
||||
void Crossfader::Resume() {
|
||||
LOCK(this->contextListLock);
|
||||
|
||||
this->paused = false;
|
||||
|
||||
std::for_each(
|
||||
this->contextList.begin(),
|
||||
this->contextList.end(),
|
||||
[](auto it) {
|
||||
it->output->Resume();
|
||||
});
|
||||
|
||||
this->messageQueue.Post(
|
||||
Message::Create(this, MESSAGE_TICK, 0, 0), 0);
|
||||
}
|
||||
|
||||
void Crossfader::Reset() {
|
||||
LOCK(this->contextListLock);
|
||||
this->contextList.clear();
|
||||
}
|
||||
|
||||
bool Crossfader::Remove(Player* player, Cut cut) {
|
||||
LOCK(this->contextListLock);
|
||||
|
||||
bool found = false;
|
||||
|
||||
if (cut == SoftCut) {
|
||||
std::for_each(
|
||||
this->contextList.begin(),
|
||||
this->contextList.end(),
|
||||
[player, &found](auto item) {
|
||||
/* don't worry about the player! it'll get cleaned up
|
||||
automatically. just worry about fading the output's volume
|
||||
to nothing. */
|
||||
if (item->player == player) {
|
||||
item->direction = FadeOut;
|
||||
item->player = nullptr;
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
std::remove_if(
|
||||
this->contextList.begin(),
|
||||
this->contextList.end(),
|
||||
[player, &found](auto item) {
|
||||
/* hard cut stops the output and destroys the player
|
||||
immediately! */
|
||||
if (item->player == player) {
|
||||
item->player->Destroy();
|
||||
item->output->Stop();
|
||||
found = true;
|
||||
}
|
||||
|
||||
return item->player == player;
|
||||
});
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
#include <boost/format.hpp>
|
||||
|
||||
void Crossfader::ProcessMessage(IMessage &message) {
|
||||
switch (message.Type()) {
|
||||
@ -147,28 +155,38 @@ void Crossfader::ProcessMessage(IMessage &message) {
|
||||
|
||||
while (it != this->contextList.end()) {
|
||||
auto fade = *it;
|
||||
++fade->ticksCounted;
|
||||
|
||||
float percent =
|
||||
double percent =
|
||||
(float) fade->ticksCounted /
|
||||
(float) fade->ticksTotal;
|
||||
|
||||
fade->output->SetVolume(globalVolume * percent);
|
||||
++fade->ticksCounted;
|
||||
if (fade->direction == FadeOut) {
|
||||
percent = (1.0f - percent);
|
||||
}
|
||||
|
||||
float outputVolume = globalVolume * percent;
|
||||
|
||||
#if 1
|
||||
std::string dir = (fade->direction == FadeIn) ? "in" : "out";
|
||||
std::string dbg = boost::str(boost::format("%s %f\n") % dir % outputVolume);
|
||||
OutputDebugStringA(dbg.c_str());
|
||||
#endif
|
||||
|
||||
fade->output->SetVolume(outputVolume);
|
||||
|
||||
if (fade->ticksCounted >= fade->ticksTotal) {
|
||||
/* contract: if we're fading the player out, we are responsible for
|
||||
destroying it. if it gets destroyed before we get here, the reference
|
||||
here will be null. */
|
||||
if (fade->direction == FadeOut && fade->player) {
|
||||
fade->player->Destroy();
|
||||
if (fade->direction == FadeOut) {
|
||||
(*it)->player->Destroy();
|
||||
(*it)->output->Stop();
|
||||
}
|
||||
|
||||
it = this->contextList.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->contextList.size()) {
|
||||
ENQUEUE_TICK();
|
||||
|
@ -54,7 +54,6 @@ namespace musik { namespace core { namespace audio {
|
||||
{
|
||||
public:
|
||||
enum Direction { FadeIn, FadeOut };
|
||||
enum Cut { HardCut, SoftCut };
|
||||
|
||||
Crossfader(ITransport& transport);
|
||||
virtual ~Crossfader();
|
||||
@ -66,12 +65,12 @@ namespace musik { namespace core { namespace audio {
|
||||
Player* player,
|
||||
std::shared_ptr<musik::core::sdk::IOutput> output,
|
||||
Direction direction,
|
||||
float targetPercent,
|
||||
long durationMs);
|
||||
|
||||
void Reset();
|
||||
|
||||
bool Remove(Player* player, Cut cut = SoftCut);
|
||||
void Pause();
|
||||
void Resume();
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
void ThreadLoop();
|
||||
@ -80,17 +79,15 @@ namespace musik { namespace core { namespace audio {
|
||||
std::shared_ptr<musik::core::sdk::IOutput> output;
|
||||
Player* player;
|
||||
Direction direction;
|
||||
long durationMs;
|
||||
long ticksCounted;
|
||||
long ticksTotal;
|
||||
float targetPercent;
|
||||
};
|
||||
|
||||
std::mutex contextListLock;
|
||||
std::unique_ptr<std::thread> thread;
|
||||
musik::core::runtime::MessageQueue messageQueue;
|
||||
std::list<std::shared_ptr<FadeContext>> contextList;
|
||||
std::atomic<bool> quit;
|
||||
std::atomic<bool> quit, paused;
|
||||
ITransport& transport;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user