- Fixed a memory leak in Mp3Decoder

- Fixed WaveOut::Stop() to only reset the output device, not close it,
  to prevent cracking while seeking and also get us a step closer to
  gapless playback.
- Fixed GlobalHotkey handling so key presses aren't propagated to the
  focused control after they're handled
- Removed old, unused state from SimpleScrollAdapter
- Fixed crash on exit when using the "X" button by registering a custom
  close handler with PDCurses.
This commit is contained in:
casey 2016-05-24 20:31:43 -07:00
parent 14aa38518a
commit 05aca20bbd
13 changed files with 61 additions and 85 deletions

View File

@ -68,6 +68,7 @@ Mp3Decoder::Mp3Decoder()
}
Mp3Decoder::~Mp3Decoder() {
delete decoder;
}
unsigned long Mp3Decoder::GetID3HeaderLength(unsigned char * buffer) {

View File

@ -53,10 +53,30 @@ WaveOut::WaveOut()
}
WaveOut::~WaveOut() {
this->Stop();
}
void WaveOut::Destroy() {
{
boost::recursive_mutex::scoped_lock lock(this->outputDeviceMutex);
/* reset playback immediately. this will invalidate all pending
buffers */
if (this->waveHandle != NULL) {
waveOutReset(this->waveHandle);
}
/* stop the thread so nothing else is processed */
this->StopWaveOutThread();
/* close it down after the threadproc has finished */
if (this->waveHandle != NULL) {
waveOutClose(this->waveHandle);
this->waveHandle = NULL;
}
}
this->ClearBufferQueue();
delete this;
}
@ -79,28 +99,25 @@ void WaveOut::SetVolume(double volume) {
}
void WaveOut::Stop() {
boost::recursive_mutex::scoped_lock lock(this->outputDeviceMutex);
{
boost::recursive_mutex::scoped_lock lock(this->outputDeviceMutex);
/* reset waveout first. if we don't do this, it seems like it'll still
try to send events to the thread, and fail with a runtime exception. */
if (this->waveHandle != NULL) {
waveOutReset(this->waveHandle);
if (this->waveHandle != NULL) {
waveOutReset(this->waveHandle);
}
}
/* stop the thread so nothing else is processed */
this->StopWaveOutThread();
this->ClearBufferQueue();
}
/* dealloc the handle, we'll create a new one later if we need to... */
if (this->waveHandle != NULL) {
waveOutClose(this->waveHandle);
this->waveHandle = NULL;
}
void WaveOut::ClearBufferQueue() {
boost::recursive_mutex::scoped_lock lock(this->bufferQueueMutex);
/* notify and free any pending buffers, the Player in the core
will be waiting for all pending buffers to be processed. */
if (this->queuedBuffers.size() > 0) {
BufferList::iterator it = this->queuedBuffers.begin();
for (; it != this->queuedBuffers.end(); ++it) {
for (; it != this->queuedBuffers.end(); it++) {
notifyBufferProcessed((*it).get());
}
@ -110,14 +127,13 @@ void WaveOut::Stop() {
void WaveOut::OnBufferWrittenToOutput(WaveOutBuffer *buffer) {
boost::recursive_mutex::scoped_lock lock(this->bufferQueueMutex);
notifyBufferProcessed(buffer);
/* removed the buffer. it should be at the front of the queue. */
BufferList::iterator it = this->queuedBuffers.begin();
for( ; it != this->queuedBuffers.end(); ++it) {
for( ; it != this->queuedBuffers.end(); it++) {
if (it->get() == buffer) {
it = this->queuedBuffers.erase(it);
notifyBufferProcessed(buffer);
this->queuedBuffers.erase(it);
return;
}
}

View File

@ -65,6 +65,7 @@ class WaveOut : public IOutput {
void SetFormat(IBuffer *buffer);
void StartWaveOutThread();
void StopWaveOutThread();
void ClearBufferQueue();
protected:
friend class WaveOutBuffer;

View File

@ -82,7 +82,6 @@ void Transport::Start(const std::string& url) {
musik::debug::info(TAG, "we were asked to start the track at " + url);
Player* newPlayer = new Player(url);
newPlayer->SetVolume(this->volume);
musik::debug::info(TAG, "Player created successfully");
this->StartWithPlayer(newPlayer);
@ -108,6 +107,7 @@ void Transport::StartWithPlayer(Player* newPlayer) {
musik::debug::info(TAG, "play()");
this->active.push_front(newPlayer);
newPlayer->SetVolume(this->volume);
newPlayer->Play();
}
@ -193,7 +193,8 @@ void Transport::SetPosition(double seconds) {
boost::recursive_mutex::scoped_lock lock(this->stateMutex);
if (!this->active.empty()) {
return this->active.front()->SetPosition(seconds);
this->active.front()->SetPosition(seconds);
this->TimeChanged(seconds);
}
}

View File

@ -47,6 +47,7 @@ namespace musik { namespace core { namespace audio {
sigslot::signal2<int, std::string> StreamEvent;
sigslot::signal1<int> PlaybackEvent;
sigslot::signal0<> VolumeChanged;
sigslot::signal1<double> TimeChanged;
typedef enum {
PlaybackStopped,

View File

@ -39,58 +39,13 @@ namespace musik { namespace core { namespace audio {
class IBuffer {
public:
//////////////////////////////////////////
///\brief
///Get the samplerate of the buffer
//////////////////////////////////////////
virtual long SampleRate() const = 0;
//////////////////////////////////////////
///\brief
///Set the buffers samplerate
//////////////////////////////////////////
virtual void SetSampleRate(long sampleRate) = 0;
//////////////////////////////////////////
///\brief
///Get the number of channels of the buffer
//////////////////////////////////////////
virtual int Channels() const = 0;
//////////////////////////////////////////
///\brief
///Set the number of channels of the buffer
//////////////////////////////////////////
virtual void SetChannels(int channels) = 0;
//////////////////////////////////////////
///\brief
///Get the pointer to the real buffer.
///
///The pointer may change when you set any of the buffers
///properties like samplerate, samples and channels
//////////////////////////////////////////
virtual float* BufferPointer() const = 0;
//////////////////////////////////////////
///\brief
///Get the number of samples in the buffer
///
///To clairify, one sample = one sample for each channel
///and that means that one sample = sizeof(float)*channels bytes big
//////////////////////////////////////////
virtual long Samples() const = 0;
//////////////////////////////////////////
///\brief
///Set the number of samples in the buffer
//////////////////////////////////////////
virtual void SetSamples(long samples) = 0;
//////////////////////////////////////////
///\brief
///How many bytes does this object take
//////////////////////////////////////////
virtual long Bytes() const = 0;
};

View File

@ -38,19 +38,12 @@
namespace musik { namespace core { namespace audio {
//////////////////////////////////////////
///\brief
///Interface for the audio::Player to make IOuput plugins be able to make callbacks
//////////////////////////////////////////
class IBufferProvider {
public:
virtual ~IBufferProvider() = 0 { }
//////////////////////////////////////////
///\brief
///Release used by the output to notify the player a buffer has finished
///processing.
//////////////////////////////////////////
/* the output calls this interface to let the provider know
it's done with the Buffer, so it can be recycled or released */
virtual void OnBufferProcessed(IBuffer *buffer) = 0;
};

View File

@ -163,6 +163,7 @@ int main(int argc, char* argv[])
#ifdef __PDCURSES__
PDC_set_title("musikbox ♫");
PDC_set_function_key(FUNCTION_KEY_SHUT_DOWN, 4);
#endif
{

View File

@ -24,6 +24,7 @@ bool GlobalHotkeys::Handle(int64 ch) {
else if (state == Transport::PlaybackPlaying) {
this->transport.Pause();
}
return true;
}
if (kn == "ALT_I") {
this->transport.SetVolume(this->transport.Volume() + 0.05); /* 5% */
@ -35,20 +36,25 @@ bool GlobalHotkeys::Handle(int64 ch) {
}
else if (kn == "ALT_J") {
this->playback.Previous();
return true;
}
else if (kn == "ALT_L") {
this->playback.Next();
return true;
}
else if (kn == "ALT_U") {
double time = this->transport.Position();
this->transport.SetPosition(time - 10.0f);
return true;
}
else if (kn == "ALT_O") {
double time = this->transport.Position();
this->transport.SetPosition(time + 10.0f);
return true;
}
else if (kn == "^R") {
library->Indexer()->Synchronize(true);
return true;
}
return false;

View File

@ -32,7 +32,7 @@ using namespace boost::chrono;
#define REFRESH_TRANSPORT_READOUT 1001
#define REFRESH_INTERVAL_MS 1000
#define SCHEDULE_REFRESH(x) \
#define DEBOUNCE_REFRESH(x) \
this->RemoveMessage(REFRESH_TRANSPORT_READOUT); \
this->PostMessage(REFRESH_TRANSPORT_READOUT, 0, 0, x);
@ -45,6 +45,7 @@ TransportWindow::TransportWindow(LibraryPtr library, Transport& transport)
this->transport = &transport;
this->transport->StreamEvent.connect(this, &TransportWindow::OnTransportStreamEvent);
this->transport->VolumeChanged.connect(this, &TransportWindow::OnTransportVolumeChanged);
this->transport->TimeChanged.connect(this, &TransportWindow::OnTransportTimeChanged);
this->paused = false;
}
@ -61,7 +62,7 @@ void TransportWindow::ProcessMessage(IMessage &message) {
if (type == REFRESH_TRANSPORT_READOUT) {
this->Update();
SCHEDULE_REFRESH(REFRESH_INTERVAL_MS)
DEBOUNCE_REFRESH(REFRESH_INTERVAL_MS)
}
}
@ -69,18 +70,22 @@ void TransportWindow::OnTransportStreamEvent(int eventType, std::string url) {
if (eventType == Transport::StreamPlaying) {
this->trackQuery.reset(new SingleTrackQuery(url));
this->library->Enqueue(this->trackQuery);
SCHEDULE_REFRESH(0)
DEBOUNCE_REFRESH(0);
}
}
void TransportWindow::OnTransportVolumeChanged() {
SCHEDULE_REFRESH(0)
DEBOUNCE_REFRESH(0)
}
void TransportWindow::OnTransportTimeChanged(double time) {
DEBOUNCE_REFRESH(0)
}
void TransportWindow::OnQueryCompleted(QueryPtr query) {
if (query == this->trackQuery && query->GetStatus() == QueryBase::Finished) {
this->currentTrack = this->trackQuery->GetResult();
SCHEDULE_REFRESH(0)
DEBOUNCE_REFRESH(0)
}
}

View File

@ -25,6 +25,7 @@ class TransportWindow : public Window, public sigslot::has_slots<> {
private:
void OnTransportStreamEvent(int eventType, std::string url);
void OnTransportVolumeChanged();
void OnTransportTimeChanged(double time);
void OnQueryCompleted(QueryPtr query);
bool paused;

View File

@ -11,10 +11,6 @@
typedef IScrollAdapter::EntryPtr EntryPtr;
SimpleScrollAdapter::SimpleScrollAdapter() {
/* the adapters can have a maximum size. as we remove elements from
the back, we don't want to re-index everything. instead, we'll use
this offset for future calculations when searching for items. */
this->removedOffset = 0;
this->maxEntries = MAX_ENTRY_COUNT;
}

View File

@ -20,6 +20,5 @@ class SimpleScrollAdapter : public ScrollAdapterBase {
typedef EntryList::iterator Iterator;
EntryList entries;
size_t removedOffset;
size_t maxEntries;
};