Skip to content

Commit 1aa2b8e

Browse files
committed
Linux ALSA sequencer midi driver code
Linux ALSA sequencer midi driver code this time with the actual code :P Revert "Linux ALSA sequencer midi driver code" This reverts commit 4e3a96c.
1 parent d881dcd commit 1aa2b8e

1 file changed

Lines changed: 323 additions & 0 deletions

File tree

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
#include "Fuji_Internal.h"
2+
3+
#if MF_MIDI == MF_DRIVER_ALSA
4+
#include "MFMidi_Internal.h"
5+
#include <alsa/asoundlib.h>
6+
7+
struct MFMidiLinux_MidiDevice
8+
{
9+
snd_seq_addr_t send;
10+
snd_seq_port_subscribe_t *subscription;
11+
MFMidiDeviceInfo info;
12+
bool bBuffered;
13+
14+
MFMidiEvent *pEvents;
15+
uint32 numEvents;
16+
uint32 numAllocated;
17+
uint32 numEventsRead;
18+
19+
struct ChannelState
20+
{
21+
uint8 lastNotes[128];
22+
uint8 notes[128];
23+
uint8 control[128];
24+
uint8 program;
25+
uint8 channel_pressure;
26+
uint16 pitch;
27+
} channels[16];
28+
};
29+
30+
31+
32+
static int numDevices = 0;
33+
static MFMidiLinux_MidiDevice *pDevices = NULL;
34+
35+
snd_seq_t *sequencer;
36+
snd_seq_event_t *event;
37+
char * port_name = "fuji";
38+
snd_seq_addr_t dest;
39+
static const int read_bits = SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
40+
41+
inline int MFMidi_GetNumPorts(snd_seq_client_info_t * client_info, snd_seq_port_info_t * port_info)
42+
{
43+
int ports = 0, count;
44+
MFDebug_Warn(1,"ALSA MIDI looking for devices\n");
45+
46+
snd_seq_client_info_set_client(client_info, -1);
47+
while (snd_seq_query_next_client(sequencer, client_info) >= 0) {
48+
49+
//filter clients we are not interested in
50+
if ( MFString_Compare(snd_seq_client_info_get_name(client_info),"System") == 0||
51+
MFString_Compare(snd_seq_client_info_get_name(client_info),"Midi Through") == 0 ||
52+
snd_seq_client_info_get_client(client_info) == dest.client ||
53+
snd_seq_client_info_get_type(client_info) == SND_SEQ_USER_CLIENT ) { continue; }
54+
55+
MFDebug_Warn(3,MFStr("ALSA MIDI client: %s (%i)\n",snd_seq_client_info_get_name(client_info),snd_seq_client_info_get_client(client_info)));
56+
snd_seq_port_info_set_client(port_info, snd_seq_client_info_get_client(client_info));
57+
snd_seq_port_info_set_port(port_info, -1);
58+
59+
count = 0;
60+
while (snd_seq_query_next_port(sequencer,port_info) >= 0) {
61+
if ((snd_seq_port_info_get_capability(port_info) & read_bits) == read_bits) { count++; }
62+
}
63+
ports += count;
64+
}
65+
return ports;
66+
}
67+
68+
void MFMidi_InitModulePlatformSpecific()
69+
{
70+
71+
if ( snd_seq_open(&sequencer, "hw", SND_SEQ_OPEN_INPUT, 0) < 0) { MFDebug_Error("Can't open alsa MIDI\n"); exit(1); }
72+
snd_seq_set_client_name(sequencer,port_name);
73+
74+
dest.client = snd_seq_client_id(sequencer);
75+
snd_seq_set_client_name(sequencer,port_name);
76+
dest.port = snd_seq_create_simple_port( sequencer, "listen:in",
77+
SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE,
78+
SND_SEQ_PORT_TYPE_APPLICATION);
79+
80+
snd_seq_client_info_t * client_info;
81+
snd_seq_port_info_t * port_info;
82+
int count;
83+
84+
snd_seq_client_info_malloc(&client_info);
85+
snd_seq_port_info_malloc(&port_info);
86+
87+
numDevices = MFMidi_GetNumPorts(client_info,port_info);
88+
89+
MFDebug_Warn(3,MFStr("ALSA MIDI found %i devices",numDevices));
90+
91+
if (numDevices) pDevices = (MFMidiLinux_MidiDevice*)MFHeap_Alloc(sizeof(MFMidiLinux_MidiDevice)*numDevices);
92+
93+
int deviceNum = 0;
94+
snd_seq_client_info_set_client(client_info, -1);
95+
96+
while (snd_seq_query_next_client(sequencer, client_info) >= 0) {
97+
98+
// filter clients we are not interested in including us and currently any
99+
if ( MFString_Compare(snd_seq_client_info_get_name(client_info),"System") == 0||
100+
MFString_Compare(snd_seq_client_info_get_name(client_info),"Midi Through") == 0 ||
101+
snd_seq_client_info_get_client(client_info) == dest.client ||
102+
snd_seq_client_info_get_type(client_info) == SND_SEQ_USER_CLIENT ) { continue; }
103+
104+
snd_seq_port_info_set_client(port_info, snd_seq_client_info_get_client(client_info));
105+
snd_seq_port_info_set_port(port_info, -1);
106+
107+
while (snd_seq_query_next_port(sequencer,port_info) >= 0) {
108+
109+
if ((snd_seq_port_info_get_capability(port_info) & read_bits) == read_bits) {
110+
MFString_Copy(pDevices[deviceNum].info.name,snd_seq_client_info_get_name(client_info));
111+
pDevices[deviceNum].info.status = MFMS_Available;
112+
pDevices[deviceNum].send.client = snd_seq_port_info_get_client(port_info);
113+
pDevices[deviceNum].send.port = snd_seq_port_info_get_port(port_info);
114+
printf("device %i client %i port %i\n", deviceNum, pDevices[deviceNum].send.client, pDevices[deviceNum].send.port);
115+
deviceNum++;
116+
}
117+
}
118+
}
119+
120+
snd_seq_client_info_free(client_info);
121+
snd_seq_port_info_free(port_info);
122+
}
123+
124+
void MFMidi_DeinitModulePlatformSpecific()
125+
{
126+
for (int i=0; i < numDevices; ++i)
127+
{
128+
if (pDevices[i].info.status > MFMS_Available)
129+
{
130+
MFDebug_Warn(1, MFStr("MIDI device not closed: %s", pDevices[i].info.name));
131+
// reset midi
132+
// close midi
133+
}
134+
}
135+
}
136+
137+
void MFMidi_Process (snd_seq_event_t *event) {
138+
printf("event->type %i sender %i.%i\n",event->type,event->source.client,event->source.port);
139+
140+
MFMidiLinux_MidiDevice * targetDevice = NULL;
141+
for (int i=0; i < numDevices; i++) {
142+
if (event->source.client == pDevices[i].send.port && event->source.port == pDevices->send.port) {
143+
targetDevice = pDevices[i];
144+
break;
145+
}
146+
}
147+
148+
149+
if (targetDevice == NULL) { return; }
150+
151+
switch (event->type)
152+
{
153+
case SND_SEQ_EVENT_NOTEON:
154+
targetDevice->channels[event->data.control.channel].notes[event->data.note.note] = event->data.note.velocity;
155+
break;
156+
case SND_SEQ_EVENT_NOTEOFF:
157+
targetDevice->channels[event->data.control.channel].notes[event->data.note.note] = 0;
158+
break;
159+
case SND_SEQ_EVENT_CONTROLLER:
160+
targetDevice->channels[event->data.control.channel].control[event->data.control.param] = event->data.control.value;
161+
break;
162+
case SND_SEQ_EVENT_CHANPRESS:
163+
targetDevice->channels[event->data.control.channel].channel_pressure = event->data.control.value;
164+
break;
165+
case SND_SEQ_EVENT_PGMCHANGE:
166+
targetDevice->channels[event->data.control.channel].program = event->data.control.value;
167+
break;
168+
case SND_SEQ_EVENT_PITCHBEND:
169+
targetDevice->channels[event->data.control.channel].pitch = event->data.control.value;
170+
break;
171+
}
172+
}
173+
174+
void MFMidi_UpdateInternal()
175+
{
176+
// TODO reimpliment this in a separate thread with a ring buffer in order to work with lower latencies.
177+
if (snd_seq_event_input_pending (sequencer,1))
178+
{
179+
snd_seq_event_t *event = NULL;
180+
snd_seq_event_input(sequencer,&event);
181+
do { MFMidi_Process(event); }
182+
while (snd_seq_event_input_pending (sequencer,0));
183+
}
184+
}
185+
186+
MF_API int MFMidi_GetNumDevices()
187+
{
188+
return numDevices;
189+
}
190+
191+
MF_API const char *MFMidi_GetDeviceName(int deviceId)
192+
{
193+
MFDebug_Assert(deviceId < numDevices, "Invalid device ID!");
194+
return pDevices[deviceId].info.name;
195+
}
196+
197+
MF_API MFMidiDeviceStatus MFMidi_GetStatus(int deviceId)
198+
{
199+
MFDebug_Assert(deviceId < numDevices, "Invalid device ID!");
200+
return pDevices[deviceId].info.status;
201+
}
202+
203+
MF_API MFMidiInput *MFMidi_OpenInput(int deviceId, bool bBuffered, MFMidiEventCallback *pEventCallback)
204+
{
205+
MFDebug_Assert(deviceId < numDevices, "Invalid device ID!");
206+
MFMidiLinux_MidiDevice *pDevice = &pDevices[deviceId];
207+
//TODO open midi device
208+
209+
pDevice->bBuffered = bBuffered;
210+
pDevice->numAllocated = 256;
211+
pDevice->pEvents = (MFMidiEvent*)MFHeap_Alloc(sizeof(MFMidiEvent) * pDevice->numAllocated);
212+
213+
pDevice->info.status = MFMS_Ready;
214+
215+
snd_seq_port_subscribe_t * subscription;
216+
snd_seq_port_subscribe_malloc(&subscription);
217+
snd_seq_port_subscribe_set_dest(subscription,&dest);
218+
snd_seq_port_subscribe_set_queue(subscription, bBuffered);
219+
snd_seq_port_subscribe_set_exclusive(subscription, 0);
220+
snd_seq_port_subscribe_set_time_update(subscription, 0);
221+
snd_seq_port_subscribe_set_time_real(subscription, 0);
222+
223+
printf ("numDevices %i\n", numDevices);
224+
for (int i = 0; i < numDevices; i++) {
225+
snd_seq_port_subscribe_set_sender(subscription,&(pDevices[i].send));
226+
MFDebug_Warn(3,MFStr("ALSA MIDI: connecting to %i client %i port %i\n",i,pDevices[i].send.client,pDevices[i].send.port));
227+
if (snd_seq_get_port_subscription(sequencer, subscription) == 0) { MFDebug_Warn (3,"Connection is already subscribed\n"); continue; }
228+
if (snd_seq_subscribe_port(sequencer, subscription) < 0) { MFDebug_Warn(1,MFStr("Connection failed (%s)\n", snd_strerror(errno)));}
229+
}
230+
231+
snd_seq_port_subscribe_free(subscription);
232+
233+
return (MFMidiInput*) pDevice;
234+
}
235+
236+
MF_API void MFMidi_CloseInput(MFMidiInput *pMidiInput)
237+
{
238+
MFMidiLinux_MidiDevice *pDevice = (MFMidiLinux_MidiDevice*)pMidiInput;
239+
240+
MFHeap_Free(pDevice->pEvents);
241+
242+
// reset device
243+
// close device
244+
// for linux I am just going to unsubscribe.
245+
246+
snd_seq_unsubscribe_port(sequencer,pDevice->subscription);
247+
snd_seq_port_subscribe_free(pDevice->subscription);
248+
249+
pDevice->info.status = MFMS_Available;
250+
251+
}
252+
253+
MF_API uint32 MFMidi_GetState (MFMidiInput *pMidiInput, MFMidiDataType type, int channel, int note)
254+
{
255+
MFMidiLinux_MidiDevice *pDevice = (MFMidiLinux_MidiDevice *)pMidiInput;
256+
257+
switch(type)
258+
{
259+
case MFMD_Note:
260+
return pDevice->channels[channel].notes[note];
261+
case MFMD_Controller:
262+
return pDevice->channels[channel].control[note];
263+
case MFMD_Program:
264+
return pDevice->channels[channel].program;
265+
case MFMD_PitchWheel:
266+
return pDevice->channels[channel].pitch;
267+
}
268+
return 0;
269+
}
270+
271+
MF_API uint32 MFMidi_WasPressed(MFMidiInput *pMidiInput, int channel, int note)
272+
{
273+
MFMidiLinux_MidiDevice *pDevice = (MFMidiLinux_MidiDevice*)pMidiInput;
274+
275+
return pDevice->channels[channel].notes[note] && !pDevice->channels[channel].lastNotes[note];
276+
}
277+
278+
MF_API uint32 MFMidi_WasReleased(MFMidiInput *pMidiInput, int channel, int note)
279+
{
280+
MFMidiLinux_MidiDevice *pDevice = (MFMidiLinux_MidiDevice*)pMidiInput;
281+
282+
return !pDevice->channels[channel].notes[note] && pDevice->channels[channel].lastNotes[note];
283+
}
284+
285+
MF_API bool MFMidi_Start(MFMidiInput *pMidiInput)
286+
{
287+
MFMidiLinux_MidiDevice *pDevice = (MFMidiLinux_MidiDevice*)pMidiInput;
288+
289+
MFDebug_Assert(pDevice->info.status == MFMS_Ready, MFStr("Midi device not ready: %s", pDevice->info.name));
290+
291+
pDevice->numEvents = pDevice->numEventsRead = 0;
292+
pDevice->info.status = MFMS_Active;
293+
return true;
294+
}
295+
296+
297+
MF_API void MFMidi_Stop(MFMidiInput *pMidiInput)
298+
{
299+
MFMidiLinux_MidiDevice *pDevice = (MFMidiLinux_MidiDevice*)pMidiInput;
300+
pDevice->info.status = MFMS_Ready;
301+
}
302+
303+
MF_API size_t MFMidi_GetEvents(MFMidiInput *pMidiInput, MFMidiEvent *pEvents, size_t maxEvents, bool bPeek)
304+
{
305+
MFMidiLinux_MidiDevice *pDevice = (MFMidiLinux_MidiDevice*)pMidiInput;
306+
307+
if(pDevice->numEvents == 0)
308+
return 0;
309+
310+
uint32 toRead = MFMin((uint32)maxEvents, pDevice->numEvents - pDevice->numEventsRead);
311+
MFCopyMemory(pEvents, pDevice->pEvents + pDevice->numEventsRead, sizeof(MFMidiEvent)*toRead);
312+
313+
if(!bPeek)
314+
{
315+
pDevice->numEventsRead += toRead;
316+
if(pDevice->numEventsRead == pDevice->numEvents)
317+
pDevice->numEvents = pDevice->numEventsRead = 0;
318+
}
319+
320+
return toRead;
321+
}
322+
323+
#endif // MF_MIDI == MF_DRIVER_LINUX

0 commit comments

Comments
 (0)