Skip to content

Commit

Permalink
Merge pull request #6500 from igorkorsukov/mu4/midi/stream
Browse files Browse the repository at this point in the history
[MU4] Rendering midi in parts
  • Loading branch information
igorkorsukov committed Aug 29, 2020
2 parents 28b5492 + 92ce879 commit d0a08e7
Show file tree
Hide file tree
Showing 38 changed files with 660 additions and 346 deletions.
10 changes: 4 additions & 6 deletions framework/audio/audiomodule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,12 @@ void AudioModule::onInit()

s_rpcChannelInvoker = std::make_shared<mu::framework::Invoker>();

s_rpcChannelInvoker->onInvoked([]() {
//! NOTE Called from main thread
s_rpcChannel->process();
});

s_rpcChannel->onWorkerQueueChanged([]() {
//! NOTE Called from worker thread
s_rpcChannelInvoker->invoke();
s_rpcChannelInvoker->invoke([]() {
//! NOTE Called from main thread
s_rpcChannel->process();
});
});

s_worker->run();
Expand Down
59 changes: 38 additions & 21 deletions framework/audio/devtools/audioenginedevtools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,24 +99,41 @@ void AudioEngineDevTools::makeArpeggio()

m_midiStream = std::make_shared<midi::MidiStream>();

auto makeEvents = [](Events& events, uint32_t tick, int pitch) {
/* notes of the arpeggio */
static std::vector<int> notes = { 60, 64, 67, 72, 76, 79, 84, 79, 76, 72, 67, 64 };
static uint32_t duration = 4440;

uint32_t note_duration = duration / notes.size();
uint32_t note_time = tick;

for (int n : notes) {
events.insert({ note_time, Event(0, EventType::ME_NOTEON, n + pitch, 100) });
note_time += note_duration;
events.insert({ note_time, Event(0, EventType::ME_NOTEOFF, n + pitch, 100) });
}
};

makeEvents(m_midiStream->initData.events, 0, 0);

m_midiStream->request.onReceive(this, [this, makeEvents](uint32_t tick) {
Track t;
t.num = 0;
t.channels.push_back(0);
m_midiStream->initData.tracks.push_back(t);

midi::Event e;
e.channel = 0;
e.type = EventType::ME_PROGRAM;
e.a = 0;
m_midiStream->initData.initEvents.push_back(e);

auto makeChunk = [this](Chunk& chunk, uint32_t tick, int pitch) {
/* notes of the arpeggio */
static std::vector<int> notes = { 60, 64, 67, 72, 76, 79, 84, 79, 76, 72, 67, 64 };
static uint32_t duration = 4440;

chunk.beginTick = tick;
chunk.endTick = chunk.beginTick + duration;

uint32_t note_duration = duration / notes.size();
uint32_t note_time = tick + (tick > 0 ? note_duration : 0);

for (int n : notes) {
chunk.events.insert({ note_time, Event(0, EventType::ME_NOTEON, n + pitch, 100) });
note_time += note_duration;
chunk.events.insert({ note_time, Event(0, EventType::ME_NOTEOFF, n + pitch, 100) });
}
};

Chunk chunk;
makeChunk(chunk, 0, 0);
m_midiStream->initData.chunks.insert({ chunk.beginTick, std::move(chunk) });

m_midiStream->isStreamingAllowed = true;
m_midiStream->request.onReceive(this, [this, makeChunk](tick_t tick) {
static int pitch = -11;
++pitch;
if (pitch > 11) {
Expand All @@ -128,9 +145,9 @@ void AudioEngineDevTools::makeArpeggio()
return;
}

MidiData data;
makeEvents(data.events, tick, pitch);
Chunk chunk;
makeChunk(chunk, tick, pitch);

m_midiStream->stream.send(data);
m_midiStream->stream.send(chunk);
});
}
2 changes: 2 additions & 0 deletions framework/audio/internal/platform/lin/linuxaudiodriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <pthread.h>

#include "log.h"
#include "runtime.h"

using namespace mu::audio;

Expand All @@ -30,6 +31,7 @@ static ALSAData* _alsaData{ nullptr };

static void* alsaThread(void* aParam)
{
mu::runtime::setThreadName("audio_driver");
ALSAData* data = static_cast<ALSAData*>(aParam);

int ret = snd_pcm_wait(data->alsaDeviceHandle, 1000);
Expand Down
5 changes: 5 additions & 0 deletions framework/audio/internal/worker/audiothreadstreamworker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include "audiothreadstreamworker.h"

#include "log.h"
#include "runtime.h"
#include "async/processevents.h"

#include "rpcstreamcontroller.h"

Expand Down Expand Up @@ -56,6 +58,8 @@ void AudioThreadStreamWorker::AudioStreamProcess(AudioThreadStreamWorker* self)

void AudioThreadStreamWorker::doAudioStreamProcess()
{
mu::runtime::setThreadName("audio_worker");

IF_ASSERT_FAILED(m_channel) {
return;
}
Expand All @@ -66,6 +70,7 @@ void AudioThreadStreamWorker::doAudioStreamProcess()
m_controller->setup();

while (m_running.load()) {
mu::async::processEvents();
m_channel->process();
std::this_thread::sleep_for(std::chrono::milliseconds(2));
}
Expand Down
1 change: 1 addition & 0 deletions framework/global/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ set(MODULE_SRC
${CMAKE_CURRENT_LIST_DIR}/stringutils.h
${CMAKE_CURRENT_LIST_DIR}/ptrutils.h
${CMAKE_CURRENT_LIST_DIR}/realfn.h
${CMAKE_CURRENT_LIST_DIR}/runtime.cpp
${CMAKE_CURRENT_LIST_DIR}/runtime.h
${CMAKE_CURRENT_LIST_DIR}/translation.cpp
${CMAKE_CURRENT_LIST_DIR}/translation.h
Expand Down
1 change: 1 addition & 0 deletions framework/global/async/async.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ set(ASYNC_SRC
${CMAKE_CURRENT_LIST_DIR}/asyncable.h
${CMAKE_CURRENT_LIST_DIR}/notification.h
${CMAKE_CURRENT_LIST_DIR}/channel.h
${CMAKE_CURRENT_LIST_DIR}/processevents.h
)
37 changes: 37 additions & 0 deletions framework/global/async/processevents.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// 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; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_ASYNC_PROCESSEVENTS_H
#define MU_ASYNC_PROCESSEVENTS_H

#include "thirdparty/deto_async/async/channel.h"
namespace mu {
namespace async {
inline void processEvents()
{
deto::async::processEvents();
}

inline void onMainThreadInvoke(const std::function<void(const std::function<void()>&)>& f)
{
deto::async::onMainThreadInvoke(f);
}
}
}

#endif // MU_ASYNC_PROCESSEVENTS_H
21 changes: 21 additions & 0 deletions framework/global/globalmodule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,21 @@
//=============================================================================
#include "globalmodule.h"

#include <QTimer>

#include "modularity/ioc.h"
#include "internal/globalconfiguration.h"

#include "internal/interactive.h"
#include "invoker.h"

#include "runtime.h"
#include "async/processevents.h"

using namespace mu::framework;

static Invoker s_asyncInvoker;

std::string GlobalModule::moduleName() const
{
return "global";
Expand All @@ -40,4 +47,18 @@ void GlobalModule::registerExports()
void GlobalModule::onInit()
{
Invoker::setup();

mu::async::onMainThreadInvoke([](const std::function<void()>& f) {
s_asyncInvoker.invoke(f);
});

// static QTimer asyncInvoker;
// asyncInvoker.setInterval(1);
// asyncInvoker.setSingleShot(false);

// QObject::connect(&asyncInvoker, &QTimer::timeout, []() {
// mu::async::processEvents();
// });

// asyncInvoker.start();
}
22 changes: 10 additions & 12 deletions framework/global/invoker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,26 +33,24 @@ void Invoker::setup()

void Invoker::invoke(const Call& func)
{
if (func) {
m_call = func;
IF_ASSERT_FAILED(func) {
return;
}

if (std::this_thread::get_id() == m_mainThreadId) {
doInvoke();
func();
} else {
static const char* name = "doInvoke";
QMetaObject::invokeMethod(this, name, Qt::QueuedConnection);
}
}

void Invoker::doInvoke()
{
if (m_call) {
m_call();
Functor* f = new Functor(func);
void* ptr = reinterpret_cast<void*>(f);
QMetaObject::invokeMethod(this, name, Qt::QueuedConnection, Q_ARG(void*, ptr));
}
}

void Invoker::onInvoked(const Call& func)
void Invoker::doInvoke(void* ptr)
{
m_call = func;
Functor* f = reinterpret_cast<Functor*>(ptr);
f->call();
delete f;
}
12 changes: 7 additions & 5 deletions framework/global/invoker.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,18 @@ class Invoker : public QObject

void invoke(const Call& func = nullptr);

void onInvoked(const Call& func);

public slots:
void doInvoke();
void doInvoke(void* ptr);

private:

static std::thread::id m_mainThreadId;
struct Functor {
Call call;
Functor(const Call& c)
: call(c) {}
};

Call m_call;
static std::thread::id m_mainThreadId;
};
}
}
Expand Down
16 changes: 12 additions & 4 deletions framework/global/log.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,25 @@
#define MU_FRAMEWORK_LOG_H

#include <QDebug>
#include <thread>
#include "runtime.h"

inline QDebug operator<<(QDebug debug, const std::string& s)
{
debug << s.c_str();
return debug;
}

#define LOGD() qDebug()
#define LOGI() qInfo()
#define LOGW() qWarning()
#define LOGE() qCritical()
inline QDebug operator<<(QDebug debug, const std::thread::id& id)
{
debug << mu::runtime::toString(id);
return debug;
}

#define LOGD() qDebug() << "[" << mu::runtime::threadName() << "]"
#define LOGI() qInfo() << "[" << mu::runtime::threadName() << "]"
#define LOGW() qWarning() << "[" << mu::runtime::threadName() << "]"
#define LOGE() qCritical() << "[" << mu::runtime::threadName() << "]"

#define IF_ASSERT_FAILED_X(cond, msg) if (!(cond)) { \
LOGE() << "\"ASSERT FAILED!\":" << msg << __FILE__ << __LINE__; \
Expand Down
36 changes: 36 additions & 0 deletions framework/global/runtime.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 MuseScore BVBA and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// 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; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================

#include "runtime.h"

static thread_local std::string s_threadName;

void mu::runtime::setThreadName(const std::string& name)
{
s_threadName = name;
}

const std::string& mu::runtime::threadName()
{
if (s_threadName.empty()) {
static thread_local std::string id = toString(std::this_thread::get_id());
return id;
}
return s_threadName;
}
21 changes: 20 additions & 1 deletion framework/global/runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,35 @@
#ifndef MU_FRAMEWORK_RUNTIME_H
#define MU_FRAMEWORK_RUNTIME_H

#include <thread>
#include <sstream>

namespace mu {
namespace runtime {
inline constexpr bool IsDebug()
inline constexpr bool isDebug()
{
#ifndef NDEBUG
return true;
#else
return false;
#endif
}

inline std::thread::id mainThreadId()
{
static std::thread::id mainId = std::this_thread::get_id();
return mainId;
}

inline std::string toString(const std::thread::id& id)
{
std::ostringstream ss;
ss << id;
return ss.str();
}

void setThreadName(const std::string& name);
const std::string& threadName();
}
}

Expand Down
Loading

0 comments on commit d0a08e7

Please sign in to comment.