Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alternative splash screen status message implementation #1982

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/GuiApplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@

#include "export.h"

class QLabel;

class AutomationEditorWindow;
class BBEditor;
class ControllerRackView;
class FxMixerView;
class MainWindow;
class Message;
class PianoRollWindow;
class ProjectNotes;
class SongEditorWindow;
Expand All @@ -54,6 +57,8 @@ class EXPORT GuiApplication
ControllerRackView* getControllerRackView() { return m_controllerRackView; }

private:
void onInitProgress(const Message &msg);

static GuiApplication* s_instance;

MainWindow* m_mainWindow;
Expand All @@ -64,6 +69,7 @@ class EXPORT GuiApplication
PianoRollWindow* m_pianoRoll;
ProjectNotes* m_projectNotes;
ControllerRackView* m_controllerRackView;
QLabel* m_loadingProgressLabel;
};

#define gui GuiApplication::instance()
Expand Down
142 changes: 142 additions & 0 deletions include/Messenger.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* Messenger.h - class Messenger, a singleton that helps route string-based messages through core <-> gui
*
* Copyright (c) 2015 Colin Wallace <wallacoloo/at/gmail.com>
*
* This file is part of LMMS - http://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/

#ifndef MESSENGER_H
#define MESSENGER_H

#include <QVector>
#include <QString>
#include <QSemaphore>

class Message
{
// This object is passed to MessageReceivers that are set to listen to the relevant message type.
public:
enum MessageType
{
INIT_STATUS,
NUM_MESSAGE_TYPES // KEEP THIS AT THE END OF THE LIST. It's used to determine the number of possible message types.
};

Message(const QString &msg, MessageType type);

const QString& getMessage() const;
MessageType getType() const;
private:

QString m_msg;
MessageType m_type;
};


class MessageReceiver
{
// This represents a Messenger callback in a way
// such that static functions, member functions, and non-member functions can all receive messages
friend class Messenger;
virtual void messageReceived(const Message &msg) = 0;
protected:
virtual ~MessageReceiver() {};
};


class MessageReceiverFuncPtr : public MessageReceiver
{
// MessageReceiver implementation for C function pointer callback functions
public:
MessageReceiverFuncPtr(void (*receiverFunc)(const Message &msg));
private:
void (*m_receiverFunc)(const Message &msg);
void messageReceived(const Message &msg);
};


template <typename T> class MessageReceiverMemberFunc : public MessageReceiver
{
// MessageReceiver implementation for member callback functions
public:
MessageReceiverMemberFunc(void (T::*memberFunction)(const Message &msg), T *this_)
: m_this(this_),
m_memberFunction(memberFunction)
{
}
private:
T *m_this;
void (T::*m_memberFunction)(const Message &msg);
void messageReceived(const Message &msg)
{
(m_this->*m_memberFunction)(msg);
}
};


class MessageReceiverHandle
{
// This is used for automatically removing a message handler from the callback list
// when it's owner goes out-of-scope.
// e.g. store the MessageReceiverHandle returned from Messenger::registerHandler as a member variable
// to have the handler removed when the instance is destroyed.
MessageReceiver *m_messageReceiver;
Message::MessageType m_subscriptionType;
public:
MessageReceiverHandle(MessageReceiver *messageReceiver, Message::MessageType subscriptionType);
~MessageReceiverHandle();
};


class Messenger
{
friend class MessageReceiverHandle;
public:
static void broadcast(Message msg);
static void broadcast(const QString &msg, Message::MessageType type);

static MessageReceiverHandle subscribe(MessageReceiver *receiver, Message::MessageType subscriptionType);

// subscribe overload for C function pointers
inline static MessageReceiverHandle subscribe(void (*receiverFunc)(const Message &msg), Message::MessageType subscriptionType)
{
return subscribe(new MessageReceiverFuncPtr(receiverFunc), subscriptionType);
}
// subscribe overload for member function pointers
template <typename T> static MessageReceiverHandle subscribe(void (T::*memberFunction)(const Message &msg), T *this_,
Message::MessageType subscriptionType)
{
return subscribe(new MessageReceiverMemberFunc<T>(memberFunction, this_), subscriptionType);
}

private:
static void removeReceiver(MessageReceiver *receiver, Message::MessageType subscriptionType);

// use a semaphore in order to allow many threads to iterate through the message handlers,
// but a call to subscribe must block ALL threads
static QSemaphore accessSemaphore;
static const int maxReaders = 32; // 32 is a mostly arbitrary choice. Any large number should do.

// store callback pointers in an array of vectors.
// All callbacks listening for a Message::MessageType `type' are contained in receivers[type]
static QVector<MessageReceiver*> receivers[Message::NUM_MESSAGE_TYPES];
};

#endif
1 change: 1 addition & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ set(LMMS_SRCS
core/MemoryHelper.cpp
core/MemoryManager.cpp
core/MeterModel.cpp
core/Messenger.cpp
core/Mixer.cpp
core/MixerProfiler.cpp
core/MixerWorkerThread.cpp
Expand Down
7 changes: 7 additions & 0 deletions src/core/Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@
*
*/

#include <QObject>

#include "Engine.h"
#include "BBTrackContainer.h"
#include "ConfigManager.h"
#include "FxMixer.h"
#include "Ladspa2LMMS.h"
#include "Messenger.h"
#include "Mixer.h"
#include "PresetPreviewPlayHandle.h"
#include "ProjectJournal.h"
Expand All @@ -53,10 +55,13 @@ QMap<QString, QString> Engine::s_pluginFileHandling;
void Engine::init()
{
// generate (load from file) bandlimited wavetables
Messenger::broadcast(QObject::tr("Generating wavetables"), Message::INIT_STATUS);
BandLimitedWave::generateWaves();

Messenger::broadcast(QObject::tr("Locating plugins"), Message::INIT_STATUS);
initPluginFileHandling();

Messenger::broadcast(QObject::tr("Initializing data structures"), Message::INIT_STATUS);
s_projectJournal = new ProjectJournal;
s_mixer = new Mixer;
s_song = new Song;
Expand All @@ -67,11 +72,13 @@ void Engine::init()

s_projectJournal->setJournalling( true );

Messenger::broadcast(QObject::tr("Opening audio and midi devices"), Message::INIT_STATUS);
s_mixer->initDevices();

PresetPreviewPlayHandle::init();
s_dummyTC = new DummyTrackContainer;

Messenger::broadcast(QObject::tr("Launching mixer threads"), Message::INIT_STATUS);
s_mixer->startProcessing();
}

Expand Down
118 changes: 118 additions & 0 deletions src/core/Messenger.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Messenger.cpp - class Messenger, a singleton that helps route string-based messages through core <-> gui
*
* Copyright (c) 2015 Colin Wallace <wallacoloo/at/gmail.com>
*
* This file is part of LMMS - http://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/

#include "Messenger.h"

#include <cstdlib>

QSemaphore Messenger::accessSemaphore(Messenger::maxReaders);
QVector<MessageReceiver*> Messenger::receivers[Message::NUM_MESSAGE_TYPES];


Message::Message(const QString &msg, MessageType type)
: m_msg(msg),
m_type(type)
{
}

const QString& Message::getMessage() const
{
return m_msg;
}

Message::MessageType Message::getType() const
{
return m_type;
}


MessageReceiverFuncPtr::MessageReceiverFuncPtr(void (*receiverFunc)(const Message &msg))
: m_receiverFunc(receiverFunc)
{
}

void MessageReceiverFuncPtr::messageReceived(const Message &msg)
{
m_receiverFunc(msg);
}


MessageReceiverHandle::MessageReceiverHandle(MessageReceiver *messageReceiver, Message::MessageType subscriptionType)
: m_messageReceiver(messageReceiver),
m_subscriptionType(subscriptionType)
{
}

MessageReceiverHandle::~MessageReceiverHandle()
{
// remove the corresponding receiver from the Messenger's callback list
if (m_messageReceiver)
{
Messenger::removeReceiver(m_messageReceiver, m_subscriptionType);
}
}

MessageReceiverHandle Messenger::subscribe(MessageReceiver *receiver, Message::MessageType subscriptionType)
{
accessSemaphore.acquire(maxReaders);
receivers[subscriptionType].append(receiver);
accessSemaphore.release(maxReaders);
return MessageReceiverHandle(receiver, subscriptionType);
}

void Messenger::broadcast(Message msg)
{
// send the message to all receivers subscribed to the message's type
accessSemaphore.acquire();
QVector<MessageReceiver*>::iterator begin = receivers[msg.getType()].begin();
QVector<MessageReceiver*>::iterator end = receivers[msg.getType()].end();
for (QVector<MessageReceiver*>::iterator i=begin; i != end; ++i)
{
(*i)->messageReceived(msg);
}
accessSemaphore.release();
}

void Messenger::broadcast(const QString &msg, Message::MessageType type)
{
broadcast(Message(msg, type));
}

void Messenger::removeReceiver(MessageReceiver *receiver, Message::MessageType subscriptionType)
{
accessSemaphore.acquire(maxReaders);
int i = receivers[subscriptionType].indexOf(receiver);
if (i == -1)
{
// receiver function is not registered as a callback
qWarning("Messenger::removeReceiver: attempt to remove a non-existent message receiver");
}
else
{
// remove the receiver from the associated callback vector
delete receivers[subscriptionType][i];
receivers[subscriptionType].remove(i);
}
accessSemaphore.release(maxReaders);
}
Loading