Repository navigation
Expand file tree
/
Copy pathMtcTickSource.cpp
More file actions
101 lines (85 loc) · 3.01 KB
/
Copy pathMtcTickSource.cpp
File metadata and controls
101 lines (85 loc) · 3.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/*
* ***
* SPDX-FileCopyrightText: 2026 Stagelab Coop SCCL
* SPDX-License-Identifier: GPL-3.0-or-later
* ***
*/
/**
* @file MtcTickSource.cpp
* @brief Implementation of gme::time::MtcTickSource.
*/
#include "time/MtcTickSource.h"
#include <climits>
#ifdef __linux__
#include <unistd.h>
#endif
namespace gme {
namespace time {
void MtcTickSource::setTickCallback(std::function<void(long)> cb) {
if (cb) {
MtcReceiver::setTickCallback(
[cb = std::move(cb)](long ms, bool /*isCompleteFrame*/) {
cb(ms);
});
} else {
MtcReceiver::setTickCallback({});
}
}
MtcTickSource::~MtcTickSource() {
MtcReceiver::setTickCallback({});
}
MtcStartError MtcTickSource::start(const std::string& midiPort) {
// Enable network mode before constructing MtcReceiver so that network
// MTC sources (e.g., rtpmidid) work without additional configuration (FR-002).
MtcReceiver::setNetworkMode(true);
// Bail out early on Linux if the ALSA sequencer device is not present.
// This avoids an RtMidiError throw whose catch can silently fail when
// librtmidi's typeinfo address differs from the weak copy baked into
// static archives (RTTI mismatch across shared-library boundaries).
#ifdef __linux__
if (access("/dev/snd/seq", F_OK) != 0) {
return MtcStartError::kNoPortsAvailable;
}
#endif
// Scan available ports for a name containing midiPort.
unsigned int portIndex = UINT_MAX;
try {
RtMidiIn probe;
unsigned int nPorts = probe.getPortCount();
if (nPorts == 0) {
return MtcStartError::kNoPortsAvailable;
}
for (unsigned int i = 0; i < nPorts; ++i) {
if (probe.getPortName(i).find(midiPort) != std::string::npos) {
portIndex = i;
break;
}
}
} catch (const RtMidiError&) {
return MtcStartError::kNoPortsAvailable;
} catch (...) {
return MtcStartError::kNoPortsAvailable;
}
if (portIndex == UINT_MAX) {
return MtcStartError::kPortNotFound;
}
// Clear the >24h wrap accumulator before (re)starting. MtcReceiver keeps it
// in a process-global static, so a tick source restarted in-process after a
// >24h run would otherwise inherit a stale +86_400_000 ms offset and put the
// new session 24h ahead. The wire-driven reset can't catch a graceful
// restart (small backward delta), so clear it explicitly here. (Plan 3d)
MtcReceiver::resetWrapOffset();
// Construct MtcReceiver — opens the port and starts the checker thread.
// portIndex is the last constructor parameter (preserves positional compat).
receiver_ = std::make_unique<MtcReceiver>(
MTCRECV_DEFAULT_API, "Cuems Mtc Receiver", 100, portIndex);
return MtcStartError::kOk;
}
long MtcTickSource::getMtcMs() const {
return MtcReceiver::mtcHead.load();
}
bool MtcTickSource::isRunning() const {
return MtcReceiver::isTimecodeRunning.load();
}
} // namespace time
} // namespace gme