Skip to content

Commit 85f57ef

Browse files
committed
AlsaMIDIDevice and CoreMIDIDevice small refactor
1 parent cf9be31 commit 85f57ef

2 files changed

Lines changed: 161 additions & 193 deletions

File tree

source/mididevices/music_alsa_mididevice.cpp

Lines changed: 73 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -57,29 +57,30 @@ class AlsaMIDIDevice : public MIDIDevice
5757
int GetTechnology() const override;
5858
int SetTempo(int tempo) override;
5959
int SetTimeDiv(int timediv) override;
60-
int StreamOut(MidiHeader *data) override;
61-
int StreamOutSync(MidiHeader *data) override;
60+
int StreamOut(MidiHeader* data) override;
61+
int StreamOutSync(MidiHeader* data) override;
6262
int Resume() override;
6363
void Stop() override;
64-
bool FakeVolume() override { return true; }; //Not sure if we even can control the volume this way with Alsa, so make it fake.
64+
bool FakeVolume() override { return true; }; // Not sure if we even can control the volume this way with Alsa, so make it fake.
6565
bool Pause(bool paused) override;
6666
void InitPlayback() override;
6767
bool Update() override;
68-
bool CanHandleSysex() const override { return true; } //Assume we can, let Alsa sort it out.
69-
void PrecacheInstruments(const uint16_t *instruments, int count) override;
68+
bool CanHandleSysex() const override { return true; } // Assume we can, let Alsa sort it out.
69+
void PrecacheInstruments(const uint16_t* instruments, int count) override;
7070

7171
protected:
7272
bool PullEvent();
7373
void PlayerLoop();
7474
void HandleEvent(snd_seq_event_t &event, uint tick);
7575

7676
AlsaSequencer &sequencer;
77-
MidiHeader *Events = nullptr;
77+
MidiHeader* Events = nullptr;
7878
snd_seq_event_t Event;
79+
std::array<uint8_t, 3> ShortMsgBuffer;
7980
snd_midi_event_t* Coder = nullptr;
8081
uint32_t Position = 0;
8182
uint32_t PositionOffset;
82-
uint NextEventTickDelta;
83+
uint CurrentEventTickDelta;
8384

8485
const static int IntendedPortId = 0;
8586
bool Connected = false;
@@ -91,12 +92,12 @@ class AlsaMIDIDevice : public MIDIDevice
9192
int Technology;
9293
bool Precache;
9394

94-
int InitialTempo = 480000;
95+
int InitialTempo = 500000;
9596
int Tempo;
96-
int TimeDiv = 480;
97+
int Division = 100; // PPQN
9798

9899
std::thread PlayerThread;
99-
volatile bool Exit = false;
100+
volatile bool Exit;
100101
std::mutex Mutex;
101102
std::condition_variable ExitCond;
102103
};
@@ -125,7 +126,7 @@ int AlsaMIDIDevice::Open()
125126

126127
if (PortId < 0)
127128
{
128-
snd_seq_port_info_t *pinfo;
129+
snd_seq_port_info_t* pinfo;
129130
snd_seq_port_info_alloca(&pinfo);
130131

131132
snd_seq_port_info_set_port(pinfo, IntendedPortId);
@@ -200,12 +201,12 @@ int AlsaMIDIDevice::SetTempo(int tempo)
200201

201202
int AlsaMIDIDevice::SetTimeDiv(int timediv)
202203
{
203-
TimeDiv = timediv;
204+
Division = timediv;
204205
return 0;
205206
}
206207

207208
// This is meant to mirror WinMIDIDevice::PrecacheInstruments
208-
void AlsaMIDIDevice::PrecacheInstruments(const uint16_t *instruments, int count)
209+
void AlsaMIDIDevice::PrecacheInstruments(const uint16_t* instruments, int count)
209210
{
210211
// Setting snd_midiprecache to false disables this precaching, since it
211212
// does involve sleeping for more than a miniscule amount of time.
@@ -215,7 +216,6 @@ void AlsaMIDIDevice::PrecacheInstruments(const uint16_t *instruments, int count)
215216
}
216217
uint8_t bank[16] = {0};
217218
uint8_t i, chan;
218-
std::array<uint8_t, 3> message;
219219

220220
for (i = 0, chan = 0; i < count; ++i)
221221
{
@@ -227,29 +227,29 @@ void AlsaMIDIDevice::PrecacheInstruments(const uint16_t *instruments, int count)
227227
{
228228
if (bank[9] != banknum)
229229
{
230-
message = { MIDI_CTRLCHANGE | 9, 0, banknum };
231-
snd_midi_event_encode(Coder, message.data(), 3, &Event);
230+
ShortMsgBuffer = { MIDI_CTRLCHANGE | 9, 0, banknum };
231+
snd_midi_event_encode(Coder, ShortMsgBuffer.data(), 3, &Event);
232232
HandleEvent(Event, 0);
233233
bank[9] = banknum;
234234
}
235-
message = { MIDI_NOTEON | 9, instr, 1 };
236-
snd_midi_event_encode(Coder, message.data(), 3, &Event);
235+
ShortMsgBuffer = { MIDI_NOTEON | 9, instr, 1 };
236+
snd_midi_event_encode(Coder, ShortMsgBuffer.data(), 3, &Event);
237237
HandleEvent(Event, 0);
238238
}
239239
else
240240
{ // Melodic
241241
if (bank[chan] != banknum)
242242
{
243-
message = { MIDI_CTRLCHANGE | 9, 0, banknum };
244-
snd_midi_event_encode(Coder, message.data(), 3, &Event);
243+
ShortMsgBuffer = { MIDI_CTRLCHANGE | 9, 0, banknum };
244+
snd_midi_event_encode(Coder, ShortMsgBuffer.data(), 3, &Event);
245245
HandleEvent(Event, 0);
246246
bank[chan] = banknum;
247247
}
248-
message = { (uint8_t)(MIDI_PRGMCHANGE | chan), (uint8_t)instruments[i] };
249-
snd_midi_event_encode(Coder, message.data(), 2, &Event);
248+
ShortMsgBuffer = { (uint8_t)(MIDI_PRGMCHANGE | chan), (uint8_t)instruments[i] };
249+
snd_midi_event_encode(Coder, ShortMsgBuffer.data(), 2, &Event);
250250
HandleEvent(Event, 0);
251-
message = { (uint8_t)(MIDI_NOTEON | chan), 60, 1 };
252-
snd_midi_event_encode(Coder, message.data(), 3, &Event);
251+
ShortMsgBuffer = { (uint8_t)(MIDI_NOTEON | chan), 60, 1 };
252+
snd_midi_event_encode(Coder, ShortMsgBuffer.data(), 3, &Event);
253253
HandleEvent(Event, 0);
254254
if (++chan == 9)
255255
{ // Skip the percussion channel
@@ -265,8 +265,8 @@ void AlsaMIDIDevice::PrecacheInstruments(const uint16_t *instruments, int count)
265265
for (chan = 15; chan-- != 0; )
266266
{
267267
// Turn all notes off
268-
message = { (uint8_t)(MIDI_CTRLCHANGE | chan), 123, 0 };
269-
snd_midi_event_encode(Coder, message.data(), 3, &Event);
268+
ShortMsgBuffer = { (uint8_t)(MIDI_CTRLCHANGE | chan), 123, 0 };
269+
snd_midi_event_encode(Coder, ShortMsgBuffer.data(), 3, &Event);
270270
HandleEvent(Event, 0);
271271
}
272272
// And now chan is back at 0, ready to start the cycle over.
@@ -277,13 +277,11 @@ void AlsaMIDIDevice::PrecacheInstruments(const uint16_t *instruments, int count)
277277
{
278278
if (bank[i] != 0)
279279
{
280-
message = { MIDI_CTRLCHANGE | 9, 0, 0 };
281-
snd_midi_event_encode(Coder, message.data(), 3, &Event);
280+
ShortMsgBuffer = { MIDI_CTRLCHANGE | 9, 0, 0 };
281+
snd_midi_event_encode(Coder, ShortMsgBuffer.data(), 3, &Event);
282282
HandleEvent(Event, 0);
283283
}
284284
}
285-
// Wait until all events are processed
286-
snd_seq_sync_output_queue(sequencer.handle);
287285
}
288286

289287
bool AlsaMIDIDevice::PullEvent()
@@ -314,8 +312,8 @@ bool AlsaMIDIDevice::PullEvent()
314312
return false;
315313
}
316314

317-
uint32_t *event = (uint32_t *)(Events->lpData + Position);
318-
NextEventTickDelta = event[0];
315+
uint32_t* event = (uint32_t*)(Events->lpData + Position);
316+
CurrentEventTickDelta = event[0];
319317

320318
// Get event size to advance Position
321319
if (event[2] < 0x80000000)
@@ -331,31 +329,27 @@ bool AlsaMIDIDevice::PullEvent()
331329
switch (MEVENT_EVENTTYPE(event[2]))
332330
{
333331
case MEVENT_TEMPO:
334-
{
335-
int tempo = MEVENT_EVENTPARM(event[2]);
336-
snd_seq_ev_set_queue_tempo(&Event, QueueId, tempo);
332+
snd_seq_ev_set_queue_tempo(&Event, QueueId, MEVENT_EVENTPARM(event[2]));
337333
break;
338-
}
339-
case MEVENT_LONGMSG: // SysEx messages...
334+
case MEVENT_LONGMSG: // SysEx message...
340335
{
341-
uint8_t* data = (uint8_t *)&event[3];
342-
int len = MEVENT_EVENTPARM(event[2]);
343-
if (len > 2 && data[0] == 0xF0 && data[len - 1] == 0xF7)
336+
int long_msg_len = MEVENT_EVENTPARM(event[2]);
337+
uint8_t* long_msg_data = (uint8_t*)&event[3];
338+
// Ensure valid sysex message
339+
if (long_msg_len > 2 && long_msg_data[0] == 0xF0 && long_msg_data[long_msg_len - 1] == 0xF7)
344340
{
345-
snd_seq_ev_set_sysex(&Event, len, (void*)data);
341+
snd_seq_ev_set_sysex(&Event, long_msg_len, (void*)long_msg_data);
346342
break;
347343
}
348344
}
349345
case 0: // Short MIDI event
350-
{
351-
uint8_t status = event[2] & 0xFF;
352-
uint8_t param1 = (event[2] >> 8) & 0x7f;
353-
uint8_t param2 = (event[2] >> 16) & 0x7f;
354-
uint8_t message[] = {status, param1, param2};
355-
// This silently ignore extra bytes, so no message length logic is needed.
356-
snd_midi_event_encode(Coder, message, 3, &Event);
346+
ShortMsgBuffer = { (uint8_t)(event[2] & 0xff), // Status
347+
(uint8_t)((event[2] >> 8) & 0xff), // Data 1
348+
(uint8_t)((event[2] >> 16) & 0xff) }; // Data 2
349+
350+
// This silently ignores extra bytes, so no message length logic is needed.
351+
snd_midi_event_encode(Coder, ShortMsgBuffer.data(), 3, &Event);
357352
break;
358-
}
359353
default: // We didn't really recognize the event, treat it as a NOP
360354
Event.type = SND_SEQ_EVENT_NONE;
361355
snd_seq_ev_set_fixed(&Event);
@@ -374,10 +368,10 @@ void AlsaMIDIDevice::PlayerLoop()
374368
const std::chrono::microseconds buffer_step(40000);
375369

376370
// TODO: fill in error handling throughout this.
377-
snd_seq_queue_tempo_t *tempo;
371+
snd_seq_queue_tempo_t* tempo;
378372
snd_seq_queue_tempo_alloca(&tempo);
379373
snd_seq_queue_tempo_set_tempo(tempo, InitialTempo);
380-
snd_seq_queue_tempo_set_ppq(tempo, TimeDiv);
374+
snd_seq_queue_tempo_set_ppq(tempo, Division);
381375
snd_seq_set_queue_tempo(sequencer.handle, QueueId, tempo);
382376

383377
snd_seq_start_queue(sequencer.handle, QueueId, NULL);
@@ -386,7 +380,7 @@ void AlsaMIDIDevice::PlayerLoop()
386380
Tempo = InitialTempo;
387381
int buffered_ticks = 0;
388382

389-
snd_seq_queue_status_t *status;
383+
snd_seq_queue_status_t* status;
390384
snd_seq_queue_status_malloc(&status);
391385

392386
while (!Exit)
@@ -399,34 +393,36 @@ void AlsaMIDIDevice::PlayerLoop()
399393
}
400394

401395
// Figure out if we should sleep (the event is too far in the future for us to care), and for how long
402-
int next_event_tick = buffered_ticks + NextEventTickDelta;
396+
int current_event_tick = buffered_ticks + CurrentEventTickDelta;
403397
snd_seq_get_queue_status(sequencer.handle, QueueId, status);
404398
int queue_tick = snd_seq_queue_status_get_tick_time(status);
405-
int tick_delta = next_event_tick - queue_tick;
406-
auto usecs = std::chrono::microseconds(tick_delta * Tempo / TimeDiv);
407-
auto schedule_time = std::max(std::chrono::microseconds(0), usecs - buffer_step);
399+
int ticks_until_current_ev = current_event_tick - queue_tick;
400+
auto time_until_current_ev = std::chrono::microseconds(ticks_until_current_ev * Tempo / Division);
401+
auto schedule_time = time_until_current_ev - buffer_step;
408402
if (schedule_time >= buffer_step)
409403
{
410-
ExitCond.wait_for(lock, schedule_time);
411-
continue;
404+
if (ExitCond.wait_for(lock, schedule_time) == std::cv_status::no_timeout)
405+
{
406+
break;
407+
}
412408
}
413-
if (tick_delta < 0)
409+
if (ticks_until_current_ev < 0)
414410
{ // Can be triggered on rare occasions on playback start.
415411
// Message shouldn't be shown by default like other midi backends here.
416-
ZMusic_Printf(ZMUSIC_MSG_NOTIFY, "Alsa sequencer underrun: %d ticks!\n", tick_delta);
412+
ZMusic_Printf(ZMUSIC_MSG_NOTIFY, "Alsa sequencer underrun: %d ticks!\n", ticks_until_current_ev);
417413
}
418414

419415
// We found an event worthy of sending to the sequencer
420-
HandleEvent(Event, next_event_tick);
421-
buffered_ticks = next_event_tick;
416+
HandleEvent(Event, current_event_tick);
417+
buffered_ticks = current_event_tick;
422418
Position += PositionOffset;
423419
}
424420

425421
snd_seq_ev_clear(&Event); // Event is cleared to be used in reset messages in Stop()
426422
snd_seq_queue_status_free(status);
427423
}
428424

429-
// Requires QueueId to be started first for non-zero tick position
425+
// Requires QueueId to be started first for non-zero tick positioned events.
430426
void AlsaMIDIDevice::HandleEvent(snd_seq_event_t &event, uint tick)
431427
{
432428
snd_seq_ev_set_source(&event, PortId);
@@ -468,7 +464,10 @@ void AlsaMIDIDevice::Stop()
468464
{
469465
Exit = true;
470466
ExitCond.notify_all();
471-
PlayerThread.join();
467+
if (PlayerThread.joinable())
468+
{
469+
PlayerThread.join();
470+
}
472471
snd_seq_drop_output(sequencer.handle); // This drops events in the sequencer, the sequencer is still usable
473472

474473
// Reset all channels to prevent hanging notes
@@ -489,37 +488,36 @@ bool AlsaMIDIDevice::Pause(bool paused)
489488
}
490489

491490

492-
int AlsaMIDIDevice::StreamOut(MidiHeader *header)
491+
int AlsaMIDIDevice::StreamOut(MidiHeader* data)
493492
{
494-
header->lpNext = NULL;
495-
if (Events == NULL)
493+
data->lpNext = nullptr;
494+
if (Events == nullptr)
496495
{
497-
Events = header;
496+
Events = data;
498497
Position = 0;
499498
}
500499
else
501500
{
502-
MidiHeader **p;
503-
504-
for (p = &Events; *p != NULL; p = &(*p)->lpNext)
501+
MidiHeader** p;
502+
for (p = &Events; *p != nullptr; p = &(*p)->lpNext)
505503
{ }
506-
*p = header;
504+
*p = data;
507505
}
508506
return 0;
509507
}
510508

511509

512-
int AlsaMIDIDevice::StreamOutSync(MidiHeader *header)
510+
int AlsaMIDIDevice::StreamOutSync(MidiHeader* data)
513511
{
514-
return StreamOut(header);
512+
return StreamOut(data);
515513
}
516514

517515
bool AlsaMIDIDevice::Update()
518516
{
519517
return true;
520518
}
521519

522-
MIDIDevice *CreateAlsaMIDIDevice(int mididevice)
520+
MIDIDevice* CreateAlsaMIDIDevice(int mididevice)
523521
{
524522
return new AlsaMIDIDevice(mididevice, miscConfig.snd_midiprecache);
525523
}

0 commit comments

Comments
 (0)