ALSA Sequencer Interim checkin.

This commit is contained in:
Robin E. R. Davies
2025-06-28 18:41:44 -04:00
parent acb668853b
commit 862d5b6315
17 changed files with 801 additions and 434 deletions
+45 -294
View File
@@ -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);