mirror of
https://github.com/clangen/musikcube.git
synced 2025-03-12 07:13:23 +00:00
- 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:
parent
14aa38518a
commit
05aca20bbd
@ -68,6 +68,7 @@ Mp3Decoder::Mp3Decoder()
|
||||
}
|
||||
|
||||
Mp3Decoder::~Mp3Decoder() {
|
||||
delete decoder;
|
||||
}
|
||||
|
||||
unsigned long Mp3Decoder::GetID3HeaderLength(unsigned char * buffer) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -65,6 +65,7 @@ class WaveOut : public IOutput {
|
||||
void SetFormat(IBuffer *buffer);
|
||||
void StartWaveOutThread();
|
||||
void StopWaveOutThread();
|
||||
void ClearBufferQueue();
|
||||
|
||||
protected:
|
||||
friend class WaveOutBuffer;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,5 @@ class SimpleScrollAdapter : public ScrollAdapterBase {
|
||||
typedef EntryList::iterator Iterator;
|
||||
|
||||
EntryList entries;
|
||||
size_t removedOffset;
|
||||
size_t maxEntries;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user