Skip to content

Commit

Permalink
Use SPSCQueue to record Macros
Browse files Browse the repository at this point in the history
  • Loading branch information
xeruf committed Aug 1, 2020
1 parent 4c8f584 commit 27b1d08
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 131 deletions.
15 changes: 12 additions & 3 deletions src/macros/macromanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,19 @@ MacroManager::MacroManager(mixxx::DbConnectionPoolPtr pDbConnectionPool)
&MacroManager::saveMacro);
}

void MacroManager::saveMacro(ChannelHandle channel, Macro macro) {
QByteArray serialize(QVector<MacroAction> actions) {
proto::Macro macroProto;
auto actionsProto = macroProto.mutable_actions();
for (auto action : actions) {
actionsProto->AddAllocated(action.serialize());
}
auto string = macroProto.SerializeAsString();
return QByteArray(string.data(), string.length());
}

void MacroManager::saveMacro(ChannelHandle channel, QVector<MacroAction> actions) {
qCDebug(macroLoggingCategory) << "Saving Macro for channel" << channel.handle();
// TODO(xerus) add test
macro.dump();
QSqlQuery query(m_database);
query.prepare(QStringLiteral(
"INSERT INTO macros "
Expand All @@ -30,7 +39,7 @@ void MacroManager::saveMacro(ChannelHandle channel, Macro macro) {
query.bindValue(":state", 0);
// TODO(xerus) proper labels
query.bindValue(":label", QString("testch%1").arg(QString::number(channel.handle())));
query.bindValue(":content", macro.serialize());
query.bindValue(":content", serialize(actions));
if (!query.exec()) {
LOG_FAILED_QUERY(query);
return;
Expand Down
2 changes: 1 addition & 1 deletion src/macros/macromanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class MacroManager : public QObject {
MacroRecorder* getRecorder();

public slots:
void saveMacro(ChannelHandle channel, Macro macro);
void saveMacro(ChannelHandle channel, QVector<MacroAction> actions);

private:
std::unique_ptr<MacroRecorder> m_pMacroRecorder;
Expand Down
76 changes: 30 additions & 46 deletions src/macros/macrorecorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
// TODO(xerus) handle track eject while recording

namespace {
constexpr uint kMaxMacroSize = 1000;
const QString kConfigGroup = QStringLiteral("[MacroRecording]");
}

MacroRecorder::MacroRecorder()
: m_COToggleRecording(ConfigKey(kConfigGroup, "recording_toggle")),
m_CORecStatus(ConfigKey(kConfigGroup, "recording_status")),
m_activeChannel(nullptr),
m_macroRecordingState(State::Disabled),
m_pStartRecordingTimer(this),
m_recordedMacro() {
m_pRecordedActions(kMaxMacroSize) {
qCDebug(macroLoggingCategory) << "MacroRecorder construct";

connect(&m_COToggleRecording,
Expand All @@ -33,83 +33,67 @@ MacroRecorder::MacroRecorder()
void MacroRecorder::notifyCueJump(
ChannelHandle* channel, double sourceFramePos, double destFramePos) {
qCDebug(macroLoggingCategory) << "Jump in channel" << channel->handle();
if (checkOrClaimRecording(channel)) {
m_recordedMacro.appendJump(sourceFramePos, destFramePos);
if (isRecordingActive() && checkOrClaimRecording(channel)) {
m_pRecordedActions.emplace(sourceFramePos, destFramePos);
qCDebug(macroLoggingCategory) << "Recorded jump in channel" << channel->handle();
setState(State::Armed);
}
}

bool MacroRecorder::checkOrClaimRecording(ChannelHandle* channel) {
if (m_activeChannel != nullptr) {
return m_activeChannel->handle() == channel->handle() && claimRecording();
} else if (claimRecording()) {
m_activeChannel = channel;
qCDebug(macroLoggingCategory) << "Claimed recording for channel" << channel->handle();
return true;
}
return false;
}

bool MacroRecorder::claimRecording() {
auto armed = State::Armed;
return m_macroRecordingState.compare_exchange_weak(armed, State::Recording);
ChannelHandle* value = nullptr;
return m_activeChannel.compare_exchange_strong(value, channel) ||
value->handle() == channel->handle();
}

void MacroRecorder::pollRecordingStart() {
qCDebug(macroLoggingCategory) << "Polling for recording start";
if (getActiveChannel() == nullptr) {
return;
}
if (getState() != State::Disabled) {
m_CORecStatus.set(Status::Recording);
}
m_pStartRecordingTimer.stop();
m_CORecStatus.set(Status::Recording);
}

void MacroRecorder::startRecording() {
qCDebug(macroLoggingCategory) << "MacroRecorder recording armed";
m_CORecStatus.set(Status::Armed);
m_recordedMacro.clear();
setState(State::Armed);
m_pStartRecordingTimer.start(300);
}

void MacroRecorder::stopRecording() {
qCDebug(macroLoggingCategory) << "MacroRecorder recording stop";
auto armed = State::Armed;
while (!m_macroRecordingState.compare_exchange_strong(armed, State::Disabled)) {
QThread::yieldCurrentThread();
if (getState() == State::Disabled) {
return;
}
armed = State::Armed;
}
m_pStartRecordingTimer.stop();
m_CORecStatus.set(Status::Disabled);
if (m_activeChannel == nullptr) {

ChannelHandle* channel = m_activeChannel.exchange(nullptr);
if (channel == nullptr) {
return;
}
auto channel = m_activeChannel;
m_activeChannel = nullptr;
emit saveMacro(*channel, m_recordedMacro);
}

Macro MacroRecorder::getMacro() const {
return m_recordedMacro;
QVector<MacroAction> actions;
while (MacroAction* action = m_pRecordedActions.front()) {
m_pRecordedActions.pop();
actions.append(*action);
}
emit saveMacro(*channel, actions);
}

ChannelHandle* MacroRecorder::getActiveChannel() const {
return m_activeChannel;
const MacroAction MacroRecorder::getRecordedAction() {
return *m_pRecordedActions.front();
}

bool MacroRecorder::isRecordingActive() const {
return getState() != State::Disabled;
size_t MacroRecorder::getRecordingSize() const {
return m_pRecordedActions.size();
}

MacroRecorder::State MacroRecorder::getState() const {
return m_macroRecordingState.load();
const ChannelHandle* MacroRecorder::getActiveChannel() const {
return m_activeChannel.load();
}

void MacroRecorder::setState(State state) {
m_macroRecordingState.store(state);
MacroRecorder::Status MacroRecorder::getStatus() const {
return Status(m_CORecStatus.get());
}

bool MacroRecorder::isRecordingActive() const {
return getStatus() > 0;
}
40 changes: 11 additions & 29 deletions src/macros/macrorecorder.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <gtest/gtest_prod.h>
#include <rigtorp/SPSCQueue.h>

#include <QDebug>
#include <QObject>
Expand All @@ -11,8 +12,6 @@
#include "macros/macro.h"
#include "recording/recordingmanagerbase.h"

// TODO(xerus) add enum for recording_status

/// The MacroRecorder handles the recording of Macros and the [MacroRecording] controls.
class MacroRecorder : public RecordingManagerBase {
Q_OBJECT
Expand All @@ -21,7 +20,6 @@ class MacroRecorder : public RecordingManagerBase {

void startRecording() override;
void stopRecording() override;
bool isRecordingActive() const override;

enum Status : uint8_t {
/// Nothing is going on
Expand All @@ -32,54 +30,38 @@ class MacroRecorder : public RecordingManagerBase {
Recording = 2,
};

bool isRecordingActive() const override;
Status getStatus() const;

/// Tries to append this cue jump to the currently recorded Macro.
/// Returns true if the currently recorded Macro was changed - so only if
/// recording is active and the channel handle matches.
/// Only called in realtime code.
void notifyCueJump(ChannelHandle* channel, double sourceFramePos, double destFramePos);

Macro getMacro() const;
ChannelHandle* getActiveChannel() const;
size_t getRecordingSize() const;
const MacroAction getRecordedAction();
const ChannelHandle* getActiveChannel() const;

signals:
void saveMacro(ChannelHandle channel, Macro macro);
void saveMacro(ChannelHandle channel, QVector<MacroAction> actions);

private slots:
void pollRecordingStart();

private:
FRIEND_TEST(MacroRecordingTest, ClaimRecording);
FRIEND_TEST(MacroRecordingTest, RecordCueJump);
FRIEND_TEST(MacroRecordingTest, StopRecordingAsync);

enum class State : uint8_t {
/// Nothing is going on
Disabled,
/// Recording is active, but nothing currently going on
Armed,
/// An Action is currently being recorded
Recording,
};

State getState() const;

/// Checks if ths channel is recording, otherwise tries to claim it.
/// Checks if this channel is recording, otherwise tries to claim it.
/// Returns true if this channel is recording.
/// Called in realtime code.
bool checkOrClaimRecording(ChannelHandle* channel);

/// Claims the recording if it is Armed.
/// Called in realtime code.
bool claimRecording();

void setState(State state);

ControlPushButton m_COToggleRecording;
ControlObject m_CORecStatus;

ChannelHandle* m_activeChannel;
std::atomic<State> m_macroRecordingState;
std::atomic<ChannelHandle*> m_activeChannel;
QTimer m_pStartRecordingTimer;

Macro m_recordedMacro;
rigtorp::SPSCQueue<MacroAction> m_pRecordedActions;
};

0 comments on commit 27b1d08

Please sign in to comment.