ALSA Sequencer Interim checkin.
This commit is contained in:
+45
-294
@@ -144,8 +144,6 @@ namespace pipedal
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct AudioFormat
|
||||
{
|
||||
char name[40];
|
||||
@@ -304,6 +302,7 @@ namespace pipedal
|
||||
AlsaDriverImpl(AudioDriverHost *driverHost)
|
||||
: driverHost(driverHost)
|
||||
{
|
||||
midiEventMemoryIndex = 0;
|
||||
midiEventMemory.resize(MAX_MIDI_EVENT * MAX_MIDI_EVENT_SIZE);
|
||||
midiEvents.resize(MAX_MIDI_EVENT);
|
||||
for (size_t i = 0; i < midiEvents.size(); ++i)
|
||||
@@ -415,14 +414,7 @@ namespace pipedal
|
||||
snd_pcm_sw_params_free(playbackSwParams);
|
||||
playbackSwParams = nullptr;
|
||||
}
|
||||
for (auto &midiState : this->midiDevices)
|
||||
{
|
||||
if (midiState)
|
||||
{
|
||||
midiState->Close();
|
||||
}
|
||||
}
|
||||
midiDevices.resize(0);
|
||||
this->alsaSequencer = nullptr;
|
||||
}
|
||||
|
||||
std::string discover_alsa_using_apps()
|
||||
@@ -913,7 +905,7 @@ namespace pipedal
|
||||
|
||||
std::vector<float *> &buffers = this->playbackBuffers;
|
||||
int channels = this->playbackChannels;
|
||||
constexpr double scale = 0x00FFFFFF;
|
||||
constexpr double scale = 0x00FFFFFF;
|
||||
for (size_t frame = 0; frame < frames; ++frame)
|
||||
{
|
||||
for (int channel = 0; channel < channels; ++channel)
|
||||
@@ -1084,7 +1076,6 @@ namespace pipedal
|
||||
open = true;
|
||||
try
|
||||
{
|
||||
OpenMidi(jackServerSettings, channelSelection);
|
||||
OpenAudio(jackServerSettings, channelSelection);
|
||||
std::atomic_thread_fence(std::memory_order::release);
|
||||
}
|
||||
@@ -1534,13 +1525,37 @@ namespace pipedal
|
||||
|
||||
void ReadMidiData(uint32_t audioFrame)
|
||||
{
|
||||
for (size_t i = 0; i < midiDevices.size(); ++i)
|
||||
AlsaMidiMessage message;
|
||||
|
||||
midiEventCount = 0;
|
||||
while(alsaSequencer->ReadMessage(message,0))
|
||||
{
|
||||
size_t nRead = midiDevices[i]->ReadMidiEvents(
|
||||
this->midiEvents,
|
||||
midiEventCount,
|
||||
audioFrame);
|
||||
midiEventCount += nRead;
|
||||
size_t messageSize = message.size;
|
||||
if (messageSize == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (midiEventMemoryIndex + messageSize >= this->midiEventMemory.size()) {
|
||||
continue;
|
||||
}
|
||||
if (midiEventCount >= this->midiEvents.size()) {
|
||||
midiEvents.resize(midiEventCount*2);
|
||||
}
|
||||
// for now, prevent META event messages from propagating.
|
||||
if (message.data[0] == 0xFF && message.size > 1) {
|
||||
continue;
|
||||
}
|
||||
MidiEvent *pEvent = midiEvents.data() + midiEventCount++;
|
||||
pEvent->time = audioFrame;
|
||||
pEvent->size = messageSize;
|
||||
pEvent->buffer = midiEventMemory.data() + midiEventMemoryIndex;
|
||||
|
||||
memcpy(
|
||||
midiEventMemory.data() + midiEventMemoryIndex,
|
||||
message.data,
|
||||
message.size);
|
||||
midiEventMemoryIndex += messageSize;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1671,16 +1686,17 @@ namespace pipedal
|
||||
pBuffer[j] = 0;
|
||||
}
|
||||
}
|
||||
try {
|
||||
try
|
||||
{
|
||||
while (!terminateAudio())
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
// zero out input buffers.
|
||||
this->driverHost->OnProcess(this->bufferSize);
|
||||
}
|
||||
} catch (const std::exception &e)
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
this->driverHost->OnAudioTerminated();
|
||||
@@ -1772,282 +1788,17 @@ namespace pipedal
|
||||
|
||||
size_t midiEventCount = 0;
|
||||
std::vector<MidiEvent> midiEvents;
|
||||
size_t midiEventMemoryIndex = 0;
|
||||
std::vector<uint8_t> midiEventMemory;
|
||||
AlsaSequencer::ptr alsaSequencer;
|
||||
|
||||
public:
|
||||
class AlsaMidiDeviceImpl
|
||||
|
||||
|
||||
|
||||
virtual void SetAlsaSequencer(AlsaSequencer::ptr alsaSequencer) override
|
||||
{
|
||||
private:
|
||||
snd_rawmidi_t *hIn = nullptr;
|
||||
snd_rawmidi_params_t *hInParams = nullptr;
|
||||
std::string deviceName;
|
||||
|
||||
// running status state.
|
||||
uint8_t runningStatus = 0;
|
||||
int dataLength = 0;
|
||||
int dataIndex = 0;
|
||||
size_t statusBytesRemaining = 0;
|
||||
size_t data0 = 0;
|
||||
size_t data1 = 0;
|
||||
|
||||
bool inputProcessingSysex = false;
|
||||
size_t inputSysexBufferCount = 0;
|
||||
std::vector<uint8_t> inputSysexBuffer;
|
||||
|
||||
uint8_t readBuffer[1024];
|
||||
|
||||
void checkError(int result, const char *message)
|
||||
{
|
||||
if (result < 0)
|
||||
{
|
||||
throw PiPedalStateException(SS("Unexpected error: " << message << " (" << this->deviceName));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
AlsaMidiDeviceImpl()
|
||||
{
|
||||
inputSysexBuffer.resize(1024);
|
||||
}
|
||||
~AlsaMidiDeviceImpl() {
|
||||
Close();
|
||||
}
|
||||
void Open(const AlsaMidiDeviceInfo &device)
|
||||
{
|
||||
runningStatus = 0;
|
||||
inputProcessingSysex = false;
|
||||
inputSysexBufferCount = 0;
|
||||
|
||||
dataIndex = 0;
|
||||
dataLength = 0;
|
||||
|
||||
this->deviceName = device.description_;
|
||||
|
||||
int err = snd_rawmidi_open(&hIn, nullptr, device.name_.c_str(), SND_RAWMIDI_NONBLOCK);
|
||||
if (err < 0)
|
||||
{
|
||||
throw PiPedalStateException(SS("Can't open midi device " << deviceName << ". (" << snd_strerror(err)));
|
||||
}
|
||||
|
||||
err = snd_rawmidi_params_malloc(&hInParams);
|
||||
checkError(err, "snd_rawmidi_params_malloc failed.");
|
||||
|
||||
err = snd_rawmidi_params_set_buffer_size(hIn, hInParams, 2048);
|
||||
checkError(err, "snd_rawmidi_params_set_buffer_size failed.");
|
||||
|
||||
err = snd_rawmidi_params_set_no_active_sensing(hIn, hInParams, 1);
|
||||
checkError(err, "snd_rawmidi_params_set_no_active_sensing failed.");
|
||||
}
|
||||
void Close()
|
||||
{
|
||||
if (hIn)
|
||||
{
|
||||
snd_rawmidi_close(hIn);
|
||||
hIn = nullptr;
|
||||
}
|
||||
if (hInParams)
|
||||
{
|
||||
snd_rawmidi_params_free(hInParams);
|
||||
hInParams = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int GetDataLength(uint8_t cc)
|
||||
{
|
||||
static int sDataLength[] = {0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 1, -1};
|
||||
return sDataLength[cc >> 4];
|
||||
}
|
||||
|
||||
void MidiPut(uint8_t cc, uint8_t d0, uint8_t d1)
|
||||
{
|
||||
if (cc == 0)
|
||||
return;
|
||||
|
||||
// check for overrun.
|
||||
if (inputEventBufferIndex >= pInputEventBuffer->size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto &event = (*pInputEventBuffer)[inputEventBufferIndex];
|
||||
|
||||
event.time = inputSampleFrame;
|
||||
event.size = dataLength + 1;
|
||||
assert(dataLength + 1 <= MAX_MIDI_EVENT_SIZE);
|
||||
event.buffer[0] = cc;
|
||||
event.buffer[1] = d0;
|
||||
event.buffer[2] = d1;
|
||||
++inputEventBufferIndex;
|
||||
}
|
||||
|
||||
void FillInputBuffer()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
ssize_t nRead = snd_rawmidi_read(hIn, readBuffer, sizeof(readBuffer));
|
||||
if (nRead == -EAGAIN)
|
||||
return;
|
||||
if (nRead < 0)
|
||||
{
|
||||
checkError(nRead, SS(this->deviceName << "MIDI event read failed. (" << snd_strerror(nRead)).c_str());
|
||||
}
|
||||
ProcessInputBuffer(readBuffer, nRead); // expose write to test code.
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t inputSampleFrame = -1;
|
||||
size_t inputEventBufferIndex;
|
||||
std::vector<MidiEvent> *pInputEventBuffer = nullptr;
|
||||
|
||||
size_t ReadMidiEvents(
|
||||
std::vector<MidiEvent> &outputBuffer,
|
||||
size_t startIndex,
|
||||
uint32_t sampleFrame)
|
||||
{
|
||||
inputSampleFrame = sampleFrame;
|
||||
inputEventBufferIndex = startIndex;
|
||||
pInputEventBuffer = &outputBuffer;
|
||||
FillInputBuffer();
|
||||
pInputEventBuffer = nullptr;
|
||||
return inputEventBufferIndex - startIndex;
|
||||
}
|
||||
|
||||
void FlushSysex()
|
||||
{
|
||||
if (inputProcessingSysex)
|
||||
{
|
||||
// just discard it. :-/
|
||||
// if (this->eventCount != MAX_MIDI_EVENT)
|
||||
// {
|
||||
// auto *event = &(events[eventCount++]);
|
||||
// event->size = this->bufferCount - sysexStartIndex;
|
||||
// event->buffer = &(this->buffer[this->sysexStartIndex]);
|
||||
// event->time = 0;
|
||||
// }
|
||||
// sysexStartIndex = -1;
|
||||
}
|
||||
inputProcessingSysex = false;
|
||||
}
|
||||
|
||||
int GetSystemCommonLength(uint8_t cc)
|
||||
{
|
||||
static int sizes[] = {-1, 1, 2, 1, -1, -1, 0, 0};
|
||||
return sizes[(cc >> 4) & 0x07];
|
||||
}
|
||||
void ProcessInputBuffer(uint8_t *readBuffer, size_t nRead)
|
||||
{
|
||||
for (ssize_t i = 0; i < nRead; ++i)
|
||||
{
|
||||
uint8_t v = readBuffer[i];
|
||||
|
||||
if (v >= 0x80)
|
||||
{
|
||||
if (v >= 0xF0)
|
||||
{
|
||||
if (v == 0xF0)
|
||||
{
|
||||
inputProcessingSysex = true;
|
||||
inputSysexBufferCount = 0;
|
||||
|
||||
inputSysexBuffer[inputSysexBufferCount++] = 0xF0;
|
||||
|
||||
runningStatus = 0; // discard subsequent data.
|
||||
dataLength = -2; // indefinitely.
|
||||
dataIndex = -1;
|
||||
}
|
||||
else if (v >= 0xF8)
|
||||
{
|
||||
// don't overwrite running status.
|
||||
// don't break sysexes on a running status message.
|
||||
// LV2 standard is ambiguous how realtime messages are handled, so just discard them.
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
FlushSysex();
|
||||
int length = GetSystemCommonLength(v);
|
||||
if (length == -1)
|
||||
break; // ignore illegal messages.
|
||||
runningStatus = v;
|
||||
dataLength = length;
|
||||
dataIndex = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FlushSysex();
|
||||
int dataLength = GetDataLength(v);
|
||||
runningStatus = v;
|
||||
if (dataLength == -1)
|
||||
{
|
||||
this->dataLength = dataLength;
|
||||
dataIndex = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->dataLength = dataLength;
|
||||
dataIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (inputProcessingSysex)
|
||||
{
|
||||
if (inputSysexBufferCount != inputSysexBuffer.size())
|
||||
{
|
||||
inputSysexBuffer[inputSysexBufferCount++] = v;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (dataIndex)
|
||||
{
|
||||
default:
|
||||
// discard.
|
||||
break;
|
||||
case 0:
|
||||
data0 = v;
|
||||
dataIndex = 1;
|
||||
break;
|
||||
case 1:
|
||||
data1 = v;
|
||||
dataIndex = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dataIndex == dataLength && dataLength >= 0 && runningStatus != 0)
|
||||
{
|
||||
MidiPut(runningStatus, data0, data1);
|
||||
dataIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::unique_ptr<AlsaMidiDeviceImpl>> midiDevices;
|
||||
|
||||
void OpenMidi(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection)
|
||||
{
|
||||
const auto &devices = channelSelection.GetInputMidiDevices();
|
||||
|
||||
midiDevices.reserve(devices.size());
|
||||
|
||||
for (size_t i = 0; i < devices.size(); ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto &device = devices[i];
|
||||
auto midiDevice = std::make_unique<AlsaMidiDeviceImpl>();
|
||||
midiDevice->Open(device);
|
||||
midiDevices.push_back(std::move(midiDevice));
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Lv2Log::error(e.what());
|
||||
}
|
||||
}
|
||||
this->alsaSequencer = alsaSequencer;
|
||||
}
|
||||
|
||||
virtual size_t InputBufferCount() const { return activeCaptureBuffers.size(); }
|
||||
@@ -2286,7 +2037,7 @@ namespace pipedal
|
||||
}
|
||||
|
||||
#ifdef JUNK
|
||||
static void ExpectEvent(AlsaDriverImpl::AlsaMidiDeviceImpl &m, int event, const std::vector<uint8_t> message)
|
||||
static void ExpectEvent(AlsaDriverImpl::AlsaMidiDeviceImpl &m, int event, const std::vector<uint8_t> message)
|
||||
{
|
||||
MidiEvent e;
|
||||
m.GetMidiInputEvent(&e, event);
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@
|
||||
|
||||
#include "JackConfiguration.hpp"
|
||||
#include <functional>
|
||||
|
||||
#include "AlsaSequencer.hpp"
|
||||
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace pipedal {
|
||||
virtual float*GetOutputBuffer(size_t channe) = 0;
|
||||
|
||||
virtual void Open(const JackServerSettings & jackServerSettings,const JackChannelSelection &channelSelection) = 0;
|
||||
|
||||
virtual void SetAlsaSequencer(AlsaSequencer::ptr alsaSequencer) = 0;
|
||||
virtual void Activate() = 0;
|
||||
virtual void Deactivate() = 0;
|
||||
virtual void Close() = 0;
|
||||
|
||||
+7
-1
@@ -21,6 +21,7 @@
|
||||
#include "util.hpp"
|
||||
#include <lv2/atom/atom.h>
|
||||
#include "SchedulerPriority.hpp"
|
||||
#include "AlsaSequencer.hpp"
|
||||
|
||||
#include "Lv2Log.hpp"
|
||||
|
||||
@@ -388,6 +389,8 @@ bool SystemMidiBinding::IsMatch(const MidiEvent &event)
|
||||
class AudioHostImpl : public AudioHost, private AudioDriverHost, private IPatchWriterCallback
|
||||
{
|
||||
private:
|
||||
AlsaSequencer::ptr alsaSequencer;
|
||||
|
||||
void OnWritePatchPropertyBuffer(
|
||||
PatchPropertyWriter::Buffer *);
|
||||
|
||||
@@ -599,6 +602,7 @@ private:
|
||||
this->outputRingBuffer.reset();
|
||||
|
||||
audioDriver = nullptr;
|
||||
alsaSequencer = nullptr;
|
||||
}
|
||||
|
||||
void ZeroBuffer(float *buffer, size_t nframes)
|
||||
@@ -1248,6 +1252,7 @@ public:
|
||||
lv2_atom_forge_init(&inputWriterForge, pHost->GetMapFeature().GetMap());
|
||||
|
||||
cpuTemperatureMonitor = CpuTemperatureMonitor::Get();
|
||||
this->alsaSequencer = AlsaSequencer::Create();
|
||||
}
|
||||
virtual ~AudioHostImpl()
|
||||
{
|
||||
@@ -1637,6 +1642,7 @@ public:
|
||||
{
|
||||
this->isDummyAudioDriver = true;
|
||||
this->audioDriver = std::unique_ptr<AudioDriver>(CreateDummyAudioDriver(this, jackServerSettings.GetAlsaInputDevice()));
|
||||
this->audioDriver->SetAlsaSequencer(this->alsaSequencer);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1666,7 +1672,7 @@ public:
|
||||
try
|
||||
{
|
||||
audioDriver->Open(jackServerSettings, this->channelSelection);
|
||||
|
||||
audioDriver->SetAlsaSequencer(this->alsaSequencer);
|
||||
this->sampleRate = audioDriver->GetSampleRate();
|
||||
|
||||
this->overrunGracePeriodSamples = (uint64_t)(((uint64_t)this->sampleRate) * OVERRUN_GRACE_PERIOD_S);
|
||||
|
||||
@@ -217,7 +217,6 @@ set (PIPEDAL_SOURCES
|
||||
Lv2PluginChangeMonitor.cpp Lv2PluginChangeMonitor.hpp
|
||||
WebServerConfig.cpp WebServerConfig.hpp
|
||||
Locale.hpp Locale.cpp
|
||||
Finally.hpp
|
||||
ZipFile.cpp ZipFile.hpp
|
||||
TemporaryFile.cpp TemporaryFile.hpp
|
||||
FilePropertyDirectoryTree.cpp FilePropertyDirectoryTree.hpp
|
||||
|
||||
@@ -122,6 +122,7 @@ namespace pipedal
|
||||
}
|
||||
|
||||
JackServerSettings jackServerSettings;
|
||||
AlsaSequencer::ptr alsaSequencer;
|
||||
|
||||
unsigned int periods = 0;
|
||||
|
||||
@@ -190,7 +191,11 @@ namespace pipedal
|
||||
throw;
|
||||
}
|
||||
}
|
||||
virtual std::string GetConfigurationDescription()
|
||||
virtual void SetAlsaSequencer(AlsaSequencer::ptr alsaSequencer) override
|
||||
{
|
||||
this->alsaSequencer = alsaSequencer;
|
||||
}
|
||||
virtual std::string GetConfigurationDescription()
|
||||
{
|
||||
std::string result = SS(
|
||||
"DUMMY, "
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "AlsaDriver.hpp"
|
||||
#include "Lv2Log.hpp"
|
||||
#include "PiPedalException.hpp"
|
||||
#include "AlsaSequencer.hpp"
|
||||
|
||||
|
||||
#if JACK_HOST
|
||||
@@ -106,6 +107,23 @@ namespace pipedal {
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::vector<AlsaMidiDeviceInfo> GetAlsaSequencers() {
|
||||
std::vector<AlsaMidiDeviceInfo> result;
|
||||
try {
|
||||
auto sequencer = AlsaSequencer::Create();
|
||||
if (sequencer)
|
||||
{
|
||||
std::vector<AlsaSequencerPort> ports = sequencer->EnumeratePorts();
|
||||
for (const auto &port : ports)
|
||||
{
|
||||
result.push_back(AlsaMidiDeviceInfo(port.id.c_str(), port.name.c_str()));
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
Lv2Log::error("Failed to enumerate ALSA sequencers: %s", e.what());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
void JackConfiguration::AlsaInitialize(
|
||||
const JackServerSettings &jackServerSettings)
|
||||
{
|
||||
@@ -116,7 +134,7 @@ void JackConfiguration::AlsaInitialize(
|
||||
{
|
||||
this->inputMidiDevices_.clear();
|
||||
} else {
|
||||
this->inputMidiDevices_ = GetAlsaMidiInputDevices();
|
||||
this->inputMidiDevices_ = GetAlsaSequencers(); // NB: Sequencers, not rawmidi devices, anymore.
|
||||
}
|
||||
if (jackServerSettings.IsValid())
|
||||
{
|
||||
|
||||
@@ -111,6 +111,10 @@ namespace pipedal
|
||||
{
|
||||
return inputMidiDevices_;
|
||||
}
|
||||
std::vector<AlsaMidiDeviceInfo>& GetInputMidiDevices()
|
||||
{
|
||||
return inputMidiDevices_;
|
||||
}
|
||||
JackChannelSelection RemoveInvalidChannels(const JackConfiguration&config) const;
|
||||
|
||||
static JackChannelSelection MakeDefault(const JackConfiguration&config);
|
||||
|
||||
+2
-2
@@ -433,11 +433,11 @@ static std::vector<AlsaMidiDeviceInfo> OldGetAlsaDevices(const char *devname, co
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<AlsaMidiDeviceInfo> pipedal::GetAlsaMidiInputDevices()
|
||||
std::vector<AlsaMidiDeviceInfo> pipedal::LegacyGetAlsaMidiInputDevices()
|
||||
{
|
||||
return GetAlsaDevices("rawmidi", "Input");
|
||||
}
|
||||
std::vector<AlsaMidiDeviceInfo> pipedal::GetAlsaMidiOutputDevices()
|
||||
std::vector<AlsaMidiDeviceInfo> pipedal::LegacyGetAlsaMidiOutputDevices()
|
||||
{
|
||||
return GetAlsaDevices("rawmidi", "Output");
|
||||
}
|
||||
|
||||
+5
-2
@@ -53,6 +53,7 @@ namespace pipedal {
|
||||
std::string name_;
|
||||
std::string description_;
|
||||
|
||||
// non-serialized.
|
||||
int card_ = -1;
|
||||
int device_ = -1;
|
||||
int subdevice_ = -1;
|
||||
@@ -74,7 +75,9 @@ namespace pipedal {
|
||||
|
||||
std::vector<AlsaDeviceInfo> GetAlsaDevices();
|
||||
};
|
||||
std::vector<AlsaMidiDeviceInfo> GetAlsaMidiInputDevices();
|
||||
std::vector<AlsaMidiDeviceInfo> GetAlsaMidiOutputDevices();
|
||||
// we use ALSA sequencers now instead of ALSA rawmidi devices.
|
||||
// Used by test suite to verify migration behaviour.
|
||||
std::vector<AlsaMidiDeviceInfo> LegacyGetAlsaMidiInputDevices();
|
||||
std::vector<AlsaMidiDeviceInfo> LegacyGetAlsaMidiOutputDevices();
|
||||
|
||||
}
|
||||
+10
-11
@@ -38,10 +38,8 @@ static void DiscoveryTest()
|
||||
auto result = devices.GetAlsaDevices();
|
||||
std::cout << result.size() << " ALSA devices found." << std::endl;
|
||||
|
||||
auto midiInputDevices = GetAlsaMidiInputDevices();
|
||||
std::cout << midiInputDevices.size() << " ALSA MIDI input devices found." << std::endl;
|
||||
auto midiOutputDevices = GetAlsaMidiOutputDevices();
|
||||
std::cout << midiOutputDevices.size() << " ALSA MIDI output devices found." << std::endl;
|
||||
auto midiInputDevices = AlsaSequencer::EnumeratePorts();
|
||||
std::cout << midiInputDevices.size() << " ALSA MIDI input sequencers found." << std::endl;
|
||||
}
|
||||
|
||||
void EnumerateSequencers()
|
||||
@@ -86,18 +84,19 @@ void EnumerateSequencers()
|
||||
void ReadFromsequencerTest()
|
||||
{
|
||||
cout << "--- Reading from ALSA Sequencer" << endl;
|
||||
AlsaSequencer sequencer;
|
||||
sequencer.ConnectPort("V25 V25 In");
|
||||
AlsaSequencer::ptr sequencer = AlsaSequencer::Create();
|
||||
|
||||
sequencer->ConnectPort("V25 V25 In");
|
||||
|
||||
AlsaMidiMessage message;
|
||||
while (true)
|
||||
{
|
||||
if (sequencer.ReadMessage(message,true))
|
||||
if (sequencer->ReadMessage(message,true))
|
||||
{
|
||||
cout << "Received MIDI message: "
|
||||
<< " " << hex << setfill('0') << setw(2) << static_cast<int>(message.cc0)
|
||||
<< " " << hex << setfill('0') << setw(2) << static_cast<int>(message.cc1)
|
||||
<< " " << hex << setfill('0') << setw(2) << static_cast<int>(message.cc2)
|
||||
<< " " << hex << setfill('0') << setw(2) << static_cast<int>(message.cc0())
|
||||
<< " " << hex << setfill('0') << setw(2) << static_cast<int>(message.cc1())
|
||||
<< " " << hex << setfill('0') << setw(2) << static_cast<int>(message.cc2())
|
||||
<< " Timestamp: " << setw(8) << dec << message.timestamp
|
||||
<< std::endl;
|
||||
}
|
||||
@@ -109,7 +108,7 @@ void TestConfigMigration()
|
||||
cout << "--- Testing ALSA Config Migration" << endl;
|
||||
// older versions of PiPedal uses ALSA rawmidi.
|
||||
// this code test coversion of rawmidi device IDs to ALSA Sequencer IDs.
|
||||
auto rawDevices = GetAlsaMidiInputDevices();
|
||||
auto rawDevices = LegacyGetAlsaMidiInputDevices();
|
||||
auto seqDevices = AlsaSequencer::EnumeratePorts();
|
||||
|
||||
for (const auto&seqDevice: seqDevices) {
|
||||
|
||||
+44
-1
@@ -22,6 +22,7 @@
|
||||
#include "Storage.hpp"
|
||||
#include "AudioConfig.hpp"
|
||||
#include "PiPedalException.hpp"
|
||||
#include "AlsaSequencer.hpp"
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include "json.hpp"
|
||||
@@ -734,6 +735,48 @@ JackChannelSelection Storage::GetJackChannelSelection(const JackConfiguration &j
|
||||
return jackChannelSelection.RemoveInvalidChannels(jackConfiguration);
|
||||
}
|
||||
|
||||
|
||||
static void MigrateRawMidiToAlsaSequencer(JackChannelSelection &channelSelection)
|
||||
{
|
||||
try {
|
||||
auto sequencerPorts = AlsaSequencer::EnumeratePorts();
|
||||
|
||||
// Migrate raw MIDI devices to ALSA sequencer ports.
|
||||
std::vector<AlsaMidiDeviceInfo>& selectedDevices = channelSelection.GetInputMidiDevices();
|
||||
|
||||
for (auto i = selectedDevices.begin(); i != selectedDevices.end(); ++i)
|
||||
{
|
||||
if (i->name_.starts_with("hw:")) {
|
||||
bool migrated = false;
|
||||
for (const auto &port: sequencerPorts)
|
||||
{
|
||||
if (i->name_ == port.rawMidiDevice)
|
||||
{
|
||||
// Found a matching sequencer port.
|
||||
i->name_ = port.rawMidiDevice;
|
||||
i->description_ = port.name;
|
||||
migrated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!migrated) {
|
||||
i = selectedDevices.erase(i);
|
||||
if (i == selectedDevices.end())
|
||||
{
|
||||
break;
|
||||
}
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const std::exception&e) {
|
||||
Lv2Log::error(SS("Failed to migrate MIDI settings. " << e.what()));
|
||||
// ick.
|
||||
channelSelection.GetInputMidiDevices().clear();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Storage::LoadChannelSelection()
|
||||
{
|
||||
auto fileName = this->GetChannelSelectionFileName();
|
||||
@@ -745,11 +788,11 @@ void Storage::LoadChannelSelection()
|
||||
json_reader reader(s);
|
||||
reader.read(&this->jackChannelSelection);
|
||||
this->isJackChannelSelectionValid = true;
|
||||
MigrateRawMidiToAlsaSequencer(this->jackChannelSelection);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Lv2Log::error("I/O error reading %s: %s", fileName.c_str(), e.what());
|
||||
throw PiPedalStateException("Unexpected error reading Jack settings file.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+37
-19
@@ -48,8 +48,9 @@
|
||||
#include "AudioFiles.hpp"
|
||||
|
||||
#include <systemd/sd-daemon.h>
|
||||
#include "AlsaSequencer.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
using namespace pipedal;
|
||||
|
||||
#ifdef __ARM_ARCH_ISA_A64
|
||||
#define AARCH64
|
||||
@@ -81,9 +82,9 @@ static bool isJackServiceRunning()
|
||||
return std::filesystem::exists(path);
|
||||
}
|
||||
|
||||
|
||||
#if ENABLE_BACKTRACE
|
||||
void segvHandler(int sig) {
|
||||
void segvHandler(int sig)
|
||||
{
|
||||
void *array[10];
|
||||
|
||||
// Get void*'s for all entries on the stack
|
||||
@@ -92,26 +93,40 @@ void segvHandler(int sig) {
|
||||
|
||||
// Print out all the frames to stderr
|
||||
const char *message = "Error: SEGV signal received.\n";
|
||||
auto _ = write(STDERR_FILENO,message,strlen(message));
|
||||
auto _ = write(STDERR_FILENO, message, strlen(message));
|
||||
|
||||
backtrace_symbols_fd(array+2, size-2, STDERR_FILENO);
|
||||
backtrace_symbols_fd(array + 2, size - 2, STDERR_FILENO);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
static void EnableBacktrace()
|
||||
{
|
||||
signal(SIGSEGV, segvHandler);
|
||||
|
||||
signal(SIGSEGV, segvHandler);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool TryGetLogLevel(const std::string&strLogLevel,LogLevel*result)
|
||||
static bool TryGetLogLevel(const std::string &strLogLevel, LogLevel *result)
|
||||
{
|
||||
if (strLogLevel == "debug") {*result= LogLevel::Debug; return true;}
|
||||
if (strLogLevel == "info") {*result= LogLevel::Info; return true;}
|
||||
if (strLogLevel == "warning") {*result= LogLevel::Warning; return true;}
|
||||
if (strLogLevel == "error") { *result= LogLevel::Error; return true;}
|
||||
if (strLogLevel == "debug")
|
||||
{
|
||||
*result = LogLevel::Debug;
|
||||
return true;
|
||||
}
|
||||
if (strLogLevel == "info")
|
||||
{
|
||||
*result = LogLevel::Info;
|
||||
return true;
|
||||
}
|
||||
if (strLogLevel == "warning")
|
||||
{
|
||||
*result = LogLevel::Warning;
|
||||
return true;
|
||||
}
|
||||
if (strLogLevel == "error")
|
||||
{
|
||||
*result = LogLevel::Error;
|
||||
return true;
|
||||
}
|
||||
*result = LogLevel::Info;
|
||||
return false;
|
||||
}
|
||||
@@ -140,7 +155,7 @@ int main(int argc, char *argv[])
|
||||
parser.AddOption("-h", &help);
|
||||
parser.AddOption("--help", &help);
|
||||
parser.AddOption("-systemd", &systemd);
|
||||
parser.AddOption("-log-level",&logLevel);
|
||||
parser.AddOption("-log-level", &logLevel);
|
||||
parser.AddOption("-port", &portOption);
|
||||
parser.AddOption("-test-extra-device", &testExtraDevice); // advertise two different devices (for testing multi-device connect)
|
||||
|
||||
@@ -220,11 +235,12 @@ int main(int argc, char *argv[])
|
||||
if (logLevel.length() != 0)
|
||||
{
|
||||
LogLevel lv2LogLevel;
|
||||
if (TryGetLogLevel(logLevel,&lv2LogLevel))
|
||||
if (TryGetLogLevel(logLevel, &lv2LogLevel))
|
||||
{
|
||||
Lv2Log::log_level(lv2LogLevel);
|
||||
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
Lv2Log::error(SS("Invalid log level: " << logLevel));
|
||||
return EXIT_SUCCESS; // indicate to systemd that we don't want a restart.
|
||||
}
|
||||
@@ -237,9 +253,10 @@ int main(int argc, char *argv[])
|
||||
|
||||
// clean up orphaned temporary files.
|
||||
const std::filesystem::path webTempDirectory = "/var/pipedal/web_temp";
|
||||
if (!webTempDirectory.empty()) {
|
||||
if (!webTempDirectory.empty())
|
||||
{
|
||||
std::filesystem::remove_all(webTempDirectory); //// user must belong to the pipedald grop when debugging.
|
||||
std::filesystem::create_directories(webTempDirectory);
|
||||
std::filesystem::create_directories(webTempDirectory);
|
||||
}
|
||||
|
||||
// configure AudiDirectoryInfo to use the correct directories.
|
||||
@@ -273,6 +290,7 @@ int main(int argc, char *argv[])
|
||||
return EXIT_SUCCESS; // indiate to systemd that we don't want a restart.
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user