Added pause/resume.

Fixed updates of slider and time labels (now happen when audio events are received).
Integrated slider manipulation and UI updates with play/pause/resume/stop.
Renamed audio events to include "Event" prefix.  Updated all references.
Fixed division by 0 error when calculating slider position.  Slider will not work if length is unknown.
This commit is contained in:
bjorn.olievier 2008-05-15 10:52:04 +00:00
parent 9cb22f99ea
commit 7ef78901ac
10 changed files with 242 additions and 51 deletions

View File

@ -53,10 +53,10 @@ PlaybackQueue PlaybackQueue::sInstance;
PlaybackQueue::PlaybackQueue(void) :
nowPlaying( new musik::core::tracklist::Standard() ),
signalDisabled(false),
playing(false)
playing(false),
paused(false)
{
this->transport.MixpointReached.connect(this,&PlaybackQueue::OnPlaybackEndOrFail);
// this->transport.PlaybackStoppedFail.connect(this,&PlaybackQueue::OnPlaybackEndOrFail);
this->transport.EventMixpointReached.connect(this,&PlaybackQueue::OnPlaybackEndOrFail);
}
//////////////////////////////////////////
@ -74,6 +74,8 @@ PlaybackQueue::~PlaybackQueue(void)
//////////////////////////////////////////
void PlaybackQueue::OnPlaybackEndOrFail(){
this->playing = false;
this->paused = false;
if(!this->signalDisabled){
this->Next();
}
@ -103,6 +105,34 @@ void PlaybackQueue::Play(){
this->playing = true;
this->transport.Start(path);
this->paused = false;
}
}
//////////////////////////////////////////
///\brief
///Pause the currently playing track.
//////////////////////////////////////////
void PlaybackQueue::Pause()
{
if(this->playing && !this->paused)
{
if (this->transport.Pause())
this->paused = true;
}
}
//////////////////////////////////////////
///\brief
///Resume the track.
//////////////////////////////////////////
void PlaybackQueue::Resume()
{
if(this->playing && this->paused)
{
if (this->transport.Resume())
this->paused = false;
}
}

View File

@ -76,6 +76,7 @@ class PlaybackQueue : public sigslot::has_slots<>{
tracklist::Standard::Ptr nowPlaying;
bool playing;
bool paused;
public:
~PlaybackQueue(void);
@ -102,6 +103,8 @@ class PlaybackQueue : public sigslot::has_slots<>{
void Next();
void Previous();
void Stop();
void Pause();
void Resume();
musik::core::TrackPtr CurrentTrack();

View File

@ -48,6 +48,11 @@ bool AudioStream::GetBuffer(float * pAudioBuffer, unsigned long NumSamples)
{
boost::mutex::scoped_lock lock(this->mutex);
while (this->playState == PlayStatePaused)
{
this->pauseCondition.wait(lock);
}
if (this->isFinished)
{
return false;
@ -57,7 +62,7 @@ bool AudioStream::GetBuffer(float * pAudioBuffer, unsigned long NumSamples)
{
if(!this->mixNotify)
{
transport->MixpointReached();
transport->EventMixpointReached();
this->mixNotify = true;
}
this->isFinished = true;
@ -121,7 +126,7 @@ bool AudioStream::GetBuffer(float * pAudioBuffer, unsigned long NumSamples)
if (len <= cft || pos >= (len - cft))
{
this->isLast = !GetActivePlaylistCheckNext();
transport->MixpointReached();
transport->EventMixpointReached();
this->mixNotify = true;
}
}
@ -131,7 +136,7 @@ bool AudioStream::GetBuffer(float * pAudioBuffer, unsigned long NumSamples)
//used for repeatnone where this is the end of line.
if(pos >= len && this->isLast)
{
transport->PlaybackStoppedOk();
transport->EventPlaybackStoppedOk();
this->playState = PlayStateStopped;
}
@ -161,6 +166,9 @@ bool AudioStream::Stop()
if ((this->output != 0) || this->output->Stop())
{
this->playState = PlayStateStopped;
this->pauseCondition.notify_one();
return true;
}
else
@ -169,6 +177,26 @@ bool AudioStream::Stop()
}
}
bool AudioStream::Pause()
{
boost::mutex::scoped_lock lock(this->mutex);
this->playState = PlayStatePaused;
return true;
}
bool AudioStream::Resume()
{
boost::mutex::scoped_lock lock(this->mutex);
this->playState = PlayStatePlaying;
this->pauseCondition.notify_one();
return true;
}
unsigned long AudioStream::GetLength() const
{
unsigned long Length;

View File

@ -1,7 +1,8 @@
#pragma once
#include <boost/thread/thread.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include <core/audio/AudioPacketizer.h>
#include <core/audio/IAudioCallBack.h>
@ -15,7 +16,7 @@ class Transport;
class AudioStream : public IAudioCallback
{
// Cleaned up
public: enum PlayState { PlayStateUnknown = -1, PlayStateStopped, PlayStatePlaying };
public: enum PlayState { PlayStateUnknown = -1, PlayStateStopped, PlayStatePlaying, PlayStatePaused };
public: enum FadeState { FadeStateNone, FadeStateIn, FadeStateOut };
public: enum AudioStreamEvent { EventPlaybackStarted, EventPlaybackFinished, EventMixPointReached };
@ -26,6 +27,9 @@ public: ~AudioStream();
public: bool Start();
public: bool Stop();
public: bool Pause();
public: bool Resume();
public: bool SetVolumeScale(float scale);
public: bool GetBuffer(float * pAudioBuffer, unsigned long NumSamples); // IAudioCallback
@ -51,7 +55,8 @@ private: bool isLast; // This can probably be removed once we have
private: unsigned long channels;
private: boost::mutex mutex;
private: boost::mutex mutex;
private: boost::condition pauseCondition;
private: static unsigned long streamsCreated;
private: unsigned long streamId;

View File

@ -87,11 +87,11 @@ void Transport::Start(const utfstring path)
if (success)
{
this->PlaybackStartedOk();
this->EventPlaybackStartedOk();
}
else
{
this->PlaybackStartedFail();
this->EventPlaybackStartedFail();
}
}
@ -101,7 +101,7 @@ void Transport::Stop(size_t idx)
if (this->openStreams.empty() || (idx < 0 || idx > this->openStreams.size()-1))
{
this->PlaybackStoppedFail();
this->EventPlaybackStoppedFail();
return;
}
@ -113,14 +113,59 @@ void Transport::Stop(size_t idx)
this->openStreams.erase(this->openStreams.begin() + idx);
this->PlaybackStoppedOk();
this->EventPlaybackStoppedOk();
}
else
{
this->PlaybackStoppedFail();
this->EventPlaybackStoppedFail();
}
}
bool Transport::Pause()
{
if (this->openStreams.empty())
{
this->EventPlaybackPausedFail();
return false;
}
std::vector<AudioStream*>::iterator it;
bool ret = true;
for(it = this->openStreams.begin(); it != this->openStreams.end(); it++)
{
AudioStream* stream = *(it);
ret &= stream->Pause();
}
if (ret) this->EventPlaybackPausedOk();
else this->EventPlaybackPausedFail();
return ret;
}
bool Transport::Resume()
{
if (this->openStreams.empty())
{
this->EventPlaybackResumedFail();
return false;
}
std::vector<AudioStream*>::iterator it;
bool ret = true;
for(it = this->openStreams.begin(); it != this->openStreams.end(); it++)
{
AudioStream* stream = *(it);
ret &= stream->Resume();
}
if (ret) this->EventPlaybackResumedOk();
else this->EventPlaybackResumedFail();
return ret;
}
void Transport::JumpToPosition(unsigned long position)
{
AudioStream* stream = this->openStreams[0];
@ -144,7 +189,7 @@ void Transport::SetVolume(short volume)
{
if (volume < 0 || volume > 100)
{
this->VolumeChangedFail();
this->EventVolumeChangedFail();
return;
}
@ -158,7 +203,7 @@ void Transport::SetVolume(short volume)
this->currVolume = volume;
this->VolumeChangedOk();
this->EventVolumeChangedOk();
}
size_t Transport::NumOfStreams() const

View File

@ -58,6 +58,8 @@ public: ~Transport();
public: void Start(const utfstring path);
public: void Stop(size_t idx);
public: bool Pause();
public: bool Resume();
public: void JumpToPosition(unsigned long position);
public: unsigned long FirstTrackPosition() const;
@ -82,13 +84,18 @@ private: std::vector<AudioStream*> openStreams;
private: short currVolume;
// Signals
public: sigslot::signal0<> PlaybackStartedOk;
public: sigslot::signal0<> PlaybackStartedFail;
public: sigslot::signal0<> PlaybackStoppedOk;
public: sigslot::signal0<> PlaybackStoppedFail;
public: sigslot::signal0<> VolumeChangedOk;
public: sigslot::signal0<> VolumeChangedFail;
public: sigslot::signal0<> MixpointReached; // TODO: For crossfading. Consider renaming.
public: sigslot::signal0<> EventPlaybackPausedOk;
public: sigslot::signal0<> EventPlaybackPausedFail;
public: sigslot::signal0<> EventPlaybackResumedOk;
public: sigslot::signal0<> EventPlaybackResumedFail;
public: sigslot::signal0<> EventPlaybackStartedOk;
public: sigslot::signal0<> EventPlaybackStartedFail;
public: sigslot::signal0<> EventPlaybackStoppedOk;
public: sigslot::signal0<> EventPlaybackStoppedFail;
public: sigslot::signal0<> EventVolumeChangedOk;
public: sigslot::signal0<> EventVolumeChangedFail;
public: sigslot::signal0<> EventMixpointReached; // TODO: For crossfading. Consider renaming.
/* Possible other signals
public: sigslot::signal0<> PlaybackInterrupted;
public: sigslot::signal0<> StreamOpenOk;

View File

@ -53,6 +53,8 @@ using namespace musik::cube;
: transportView(transportView)
, playbackSliderTimer(500)
, playbackSliderMouseDown(false)
, paused(false)
, playing(false)
{
this->transportView.Created.connect(
this, &TransportController::OnViewCreated);
@ -92,6 +94,12 @@ void TransportController::OnViewCreated(Window* window)
this->transportView.playbackSlider->MouseButtonUp.connect(
this, &TransportController::OnPlaybackSliderMouseUp);
musik::core::PlaybackQueue::Instance().Transport().EventPlaybackStartedOk.connect(this, &TransportController::OnPlaybackStarted);
musik::core::PlaybackQueue::Instance().Transport().EventPlaybackStoppedOk.connect(this, &TransportController::OnPlaybackStopped);
musik::core::PlaybackQueue::Instance().Transport().EventPlaybackPausedOk.connect(this, &TransportController::OnPlaybackPaused);
musik::core::PlaybackQueue::Instance().Transport().EventPlaybackResumedOk.connect(this, &TransportController::OnPlaybackResumed);
this->playbackSliderTimer.ConnectToWindow(this->transportView.playbackSlider);
this->playbackSliderTimer.OnTimout.connect(this, &TransportController::OnPlaybackSliderTimerTimedOut);
@ -103,7 +111,9 @@ void TransportController::OnViewResized(Window* window, Size size)
void TransportController::OnPlayPressed(Button* button)
{
musik::core::PlaybackQueue::Instance().Play();
if (!this->playing) musik::core::PlaybackQueue::Instance().Play();
else if (this->paused) musik::core::PlaybackQueue::Instance().Resume();
else musik::core::PlaybackQueue::Instance().Pause();
}
void TransportController::OnStopPressed(Button* button)
@ -149,11 +159,6 @@ void TransportController::OnTrackChange(musik::core::TrackPtr track){
this->transportView.titleLabel->SetCaption(title);
this->transportView.artistLabel->SetCaption(artist);
this->transportView.timeDurationLabel->SetCaption(this->FormatTime(musik::core::PlaybackQueue::Instance().Transport().FirstTrackLength()));
this->transportView.playbackSlider->SetPosition(0);
this->playbackSliderTimer.Start();
}
void TransportController::OnPlaybackSliderChange(Trackbar *trackBar)
@ -161,9 +166,7 @@ void TransportController::OnPlaybackSliderChange(Trackbar *trackBar)
unsigned long lengthMs = musik::core::PlaybackQueue::Instance().Transport().FirstTrackLength();
unsigned long newPosMs = lengthMs * trackBar->Position() / trackBar->Range();
//this->playbackSliderTimer.Stop();
musik::core::PlaybackQueue::Instance().Transport().JumpToPosition(newPosMs);
//this->playbackSliderTimer.Start();
}
void TransportController::OnPlaybackSliderTimerTimedOut()
@ -174,7 +177,7 @@ void TransportController::OnPlaybackSliderTimerTimedOut()
this->transportView.timeElapsedLabel->SetCaption(this->FormatTime(currPosMs));
if (!this->playbackSliderMouseDown)
if (!this->playbackSliderMouseDown && lengthMs != 0)
{
this->transportView.playbackSlider->SetPosition(sliderRange * currPosMs / lengthMs);
}
@ -190,6 +193,66 @@ void TransportController::OnPlaybackSliderMouseUp(Window* windows, MouseEventFla
this->playbackSliderMouseDown = false;
}
void TransportController::OnPlaybackStarted()
{
if(!win32cpp::ApplicationThread::InMainThread())
{
win32cpp::ApplicationThread::Call0(this, &TransportController::OnPlaybackStarted);
return;
}
this->playing = true;
this->transportView.playButton->SetCaption(_T("Pause"));
this->transportView.timeDurationLabel->SetCaption(this->FormatTime(musik::core::PlaybackQueue::Instance().Transport().FirstTrackLength()));
this->transportView.playbackSlider->SetPosition(0);
this->playbackSliderTimer.Start();
}
void TransportController::OnPlaybackStopped()
{
if(!win32cpp::ApplicationThread::InMainThread())
{
win32cpp::ApplicationThread::Call0(this, &TransportController::OnPlaybackStopped);
return;
}
this->playing = false;
this->paused = false;
this->transportView.playButton->SetCaption(_T("Play"));
this->transportView.playbackSlider->SetPosition(0);
this->playbackSliderTimer.Stop();
this->transportView.timeElapsedLabel->SetCaption(_T("0:00"));
this->transportView.timeDurationLabel->SetCaption(_T("0:00"));
}
void TransportController::OnPlaybackPaused()
{
if(!win32cpp::ApplicationThread::InMainThread())
{
win32cpp::ApplicationThread::Call0(this, &TransportController::OnPlaybackPaused);
return;
}
this->paused = true;
this->transportView.playButton->SetCaption(_T("Resume"));
}
void TransportController::OnPlaybackResumed()
{
if(!win32cpp::ApplicationThread::InMainThread())
{
win32cpp::ApplicationThread::Call0(this, &TransportController::OnPlaybackResumed);
return;
}
this->paused = false;
this->transportView.playButton->SetCaption(_T("Pause"));
}
win32cpp::uistring TransportController::FormatTime(unsigned long ms)
{
unsigned long seconds = ms / 1000 % 60;

View File

@ -54,23 +54,33 @@ namespace musik { namespace cube {
class TransportController : public EventHandler
{
public: /*ctor*/ TransportController(TransportView& transportView);
public:
/*ctor*/ TransportController(TransportView& transportView);
protected: void OnViewCreated(Window* window);
protected: void OnViewResized(Window* window, Size size);
protected:
void OnViewCreated(Window* window);
void OnViewResized(Window* window, Size size);
protected: void OnPlayPressed(Button* button);
protected: void OnStopPressed(Button* button);
protected: void OnNextPressed(Button* button);
protected: void OnPreviousPressed(Button* button);
protected: void OnVolumeSliderChange(Trackbar* trackbar);
protected: void OnTrackChange(musik::core::TrackPtr track);
protected: void OnPlaybackSliderChange(Trackbar* trackBar);
void OnPlayPressed(Button* button);
void OnStopPressed(Button* button);
void OnNextPressed(Button* button);
void OnPreviousPressed(Button* button);
void OnVolumeSliderChange(Trackbar* trackbar);
void OnTrackChange(musik::core::TrackPtr track);
void OnPlaybackSliderChange(Trackbar* trackBar);
protected: void OnPlaybackSliderTimerTimedOut();
protected: void OnPlaybackSliderMouseDown(Window* windows, MouseEventFlags flags, Point point);
protected: void OnPlaybackSliderMouseUp(Window* windows, MouseEventFlags flags, Point point);
protected: TransportView& transportView;
void OnPlaybackStarted();
void OnPlaybackStopped();
void OnPlaybackPaused();
void OnPlaybackResumed();
TransportView& transportView;
bool paused;
bool playing;
protected: Timer playbackSliderTimer;
protected: bool playbackSliderMouseDown;

View File

@ -96,7 +96,7 @@ void TransportView::OnCreated()
// bottom row layout
this->timeElapsedLabel = bottomRowLayout->AddChild(new Label(_T("0:00")));
this->playbackSlider = bottomRowLayout->AddChild(new Trackbar(0, 1000));
this->playbackSlider = bottomRowLayout->AddChild(new Trackbar(0, 10000));
this->timeDurationLabel = bottomRowLayout->AddChild(new Label(_T("0:00")));
//
this->playbackSlider->Resize(100, 20);

View File

@ -49,16 +49,16 @@ ConsoleUI::ConsoleUI()
: shouldQuit(false)
, audioEventHandler(this)
{
this->transport.PlaybackStartedOk.connect(&audioEventHandler, &DummyAudioEventHandler::OnPlaybackStartedOk);
this->transport.PlaybackStartedFail.connect(&audioEventHandler, &DummyAudioEventHandler::OnPlaybackStartedFail);
this->transport.EventPlaybackStartedOk.connect(&audioEventHandler, &DummyAudioEventHandler::OnPlaybackStartedOk);
this->transport.EventPlaybackStartedFail.connect(&audioEventHandler, &DummyAudioEventHandler::OnPlaybackStartedFail);
this->transport.PlaybackStoppedOk.connect(&audioEventHandler, &DummyAudioEventHandler::OnPlaybackStoppedOk);
this->transport.PlaybackStoppedFail.connect(&audioEventHandler, &DummyAudioEventHandler::OnPlaybackStoppedFail);
this->transport.EventPlaybackStoppedOk.connect(&audioEventHandler, &DummyAudioEventHandler::OnPlaybackStoppedOk);
this->transport.EventPlaybackStoppedFail.connect(&audioEventHandler, &DummyAudioEventHandler::OnPlaybackStoppedFail);
this->transport.VolumeChangedOk.connect(&audioEventHandler, &DummyAudioEventHandler::OnVolumeChangedOk);
this->transport.VolumeChangedFail.connect(&audioEventHandler, &DummyAudioEventHandler::OnVolumeChangedFail);
this->transport.EventVolumeChangedOk.connect(&audioEventHandler, &DummyAudioEventHandler::OnVolumeChangedOk);
this->transport.EventVolumeChangedFail.connect(&audioEventHandler, &DummyAudioEventHandler::OnVolumeChangedFail);
this->transport.MixpointReached.connect(&audioEventHandler, &DummyAudioEventHandler::OnMixpointReached);
this->transport.EventMixpointReached.connect(&audioEventHandler, &DummyAudioEventHandler::OnMixpointReached);
}
ConsoleUI::~ConsoleUI()