diff options
Diffstat (limited to 'sound/synth/emux/emux_seq.c')
-rw-r--r-- | sound/synth/emux/emux_seq.c | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/sound/synth/emux/emux_seq.c b/sound/synth/emux/emux_seq.c new file mode 100644 index 000000000000..e41b28d9bf52 --- /dev/null +++ b/sound/synth/emux/emux_seq.c | |||
@@ -0,0 +1,428 @@ | |||
1 | /* | ||
2 | * Midi Sequencer interface routines. | ||
3 | * | ||
4 | * Copyright (C) 1999 Steve Ratcliffe | ||
5 | * Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | #include "emux_voice.h" | ||
23 | #include <linux/slab.h> | ||
24 | |||
25 | |||
26 | /* Prototypes for static functions */ | ||
27 | static void free_port(void *private); | ||
28 | static void snd_emux_init_port(snd_emux_port_t *p); | ||
29 | static int snd_emux_use(void *private_data, snd_seq_port_subscribe_t *info); | ||
30 | static int snd_emux_unuse(void *private_data, snd_seq_port_subscribe_t *info); | ||
31 | static int get_client(snd_card_t *card, int index, char *name); | ||
32 | |||
33 | /* | ||
34 | * MIDI emulation operators | ||
35 | */ | ||
36 | static snd_midi_op_t emux_ops = { | ||
37 | snd_emux_note_on, | ||
38 | snd_emux_note_off, | ||
39 | snd_emux_key_press, | ||
40 | snd_emux_terminate_note, | ||
41 | snd_emux_control, | ||
42 | snd_emux_nrpn, | ||
43 | snd_emux_sysex, | ||
44 | }; | ||
45 | |||
46 | |||
47 | /* | ||
48 | * number of MIDI channels | ||
49 | */ | ||
50 | #define MIDI_CHANNELS 16 | ||
51 | |||
52 | /* | ||
53 | * type flags for MIDI sequencer port | ||
54 | */ | ||
55 | #define DEFAULT_MIDI_TYPE (SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |\ | ||
56 | SNDRV_SEQ_PORT_TYPE_MIDI_GM |\ | ||
57 | SNDRV_SEQ_PORT_TYPE_MIDI_GS |\ | ||
58 | SNDRV_SEQ_PORT_TYPE_MIDI_XG |\ | ||
59 | SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE) | ||
60 | |||
61 | /* | ||
62 | * Initialise the EMUX Synth by creating a client and registering | ||
63 | * a series of ports. | ||
64 | * Each of the ports will contain the 16 midi channels. Applications | ||
65 | * can connect to these ports to play midi data. | ||
66 | */ | ||
67 | int | ||
68 | snd_emux_init_seq(snd_emux_t *emu, snd_card_t *card, int index) | ||
69 | { | ||
70 | int i; | ||
71 | snd_seq_port_callback_t pinfo; | ||
72 | char tmpname[64]; | ||
73 | |||
74 | sprintf(tmpname, "%s WaveTable", emu->name); | ||
75 | emu->client = get_client(card, index, tmpname); | ||
76 | if (emu->client < 0) { | ||
77 | snd_printk("can't create client\n"); | ||
78 | return -ENODEV; | ||
79 | } | ||
80 | |||
81 | if (emu->num_ports < 0) { | ||
82 | snd_printk("seqports must be greater than zero\n"); | ||
83 | emu->num_ports = 1; | ||
84 | } else if (emu->num_ports >= SNDRV_EMUX_MAX_PORTS) { | ||
85 | snd_printk("too many ports." | ||
86 | "limited max. ports %d\n", SNDRV_EMUX_MAX_PORTS); | ||
87 | emu->num_ports = SNDRV_EMUX_MAX_PORTS; | ||
88 | } | ||
89 | |||
90 | memset(&pinfo, 0, sizeof(pinfo)); | ||
91 | pinfo.owner = THIS_MODULE; | ||
92 | pinfo.use = snd_emux_use; | ||
93 | pinfo.unuse = snd_emux_unuse; | ||
94 | pinfo.event_input = snd_emux_event_input; | ||
95 | |||
96 | for (i = 0; i < emu->num_ports; i++) { | ||
97 | snd_emux_port_t *p; | ||
98 | |||
99 | sprintf(tmpname, "%s Port %d", emu->name, i); | ||
100 | p = snd_emux_create_port(emu, tmpname, MIDI_CHANNELS, | ||
101 | 0, &pinfo); | ||
102 | if (p == NULL) { | ||
103 | snd_printk("can't create port\n"); | ||
104 | return -ENOMEM; | ||
105 | } | ||
106 | |||
107 | p->port_mode = SNDRV_EMUX_PORT_MODE_MIDI; | ||
108 | snd_emux_init_port(p); | ||
109 | emu->ports[i] = p->chset.port; | ||
110 | emu->portptrs[i] = p; | ||
111 | } | ||
112 | |||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | |||
117 | /* | ||
118 | * Detach from the ports that were set up for this synthesizer and | ||
119 | * destroy the kernel client. | ||
120 | */ | ||
121 | void | ||
122 | snd_emux_detach_seq(snd_emux_t *emu) | ||
123 | { | ||
124 | if (emu->voices) | ||
125 | snd_emux_terminate_all(emu); | ||
126 | |||
127 | down(&emu->register_mutex); | ||
128 | if (emu->client >= 0) { | ||
129 | snd_seq_delete_kernel_client(emu->client); | ||
130 | emu->client = -1; | ||
131 | } | ||
132 | up(&emu->register_mutex); | ||
133 | } | ||
134 | |||
135 | |||
136 | /* | ||
137 | * create a sequencer port and channel_set | ||
138 | */ | ||
139 | |||
140 | snd_emux_port_t * | ||
141 | snd_emux_create_port(snd_emux_t *emu, char *name, | ||
142 | int max_channels, int oss_port, | ||
143 | snd_seq_port_callback_t *callback) | ||
144 | { | ||
145 | snd_emux_port_t *p; | ||
146 | int i, type, cap; | ||
147 | |||
148 | /* Allocate structures for this channel */ | ||
149 | if ((p = kcalloc(1, sizeof(*p), GFP_KERNEL)) == NULL) { | ||
150 | snd_printk("no memory\n"); | ||
151 | return NULL; | ||
152 | } | ||
153 | p->chset.channels = kcalloc(max_channels, sizeof(snd_midi_channel_t), GFP_KERNEL); | ||
154 | if (p->chset.channels == NULL) { | ||
155 | snd_printk("no memory\n"); | ||
156 | kfree(p); | ||
157 | return NULL; | ||
158 | } | ||
159 | for (i = 0; i < max_channels; i++) | ||
160 | p->chset.channels[i].number = i; | ||
161 | p->chset.private_data = p; | ||
162 | p->chset.max_channels = max_channels; | ||
163 | p->emu = emu; | ||
164 | p->chset.client = emu->client; | ||
165 | #ifdef SNDRV_EMUX_USE_RAW_EFFECT | ||
166 | snd_emux_create_effect(p); | ||
167 | #endif | ||
168 | callback->private_free = free_port; | ||
169 | callback->private_data = p; | ||
170 | |||
171 | cap = SNDRV_SEQ_PORT_CAP_WRITE; | ||
172 | if (oss_port) { | ||
173 | type = SNDRV_SEQ_PORT_TYPE_SPECIFIC; | ||
174 | } else { | ||
175 | type = DEFAULT_MIDI_TYPE; | ||
176 | cap |= SNDRV_SEQ_PORT_CAP_SUBS_WRITE; | ||
177 | } | ||
178 | |||
179 | p->chset.port = snd_seq_event_port_attach(emu->client, callback, | ||
180 | cap, type, max_channels, | ||
181 | emu->max_voices, name); | ||
182 | |||
183 | return p; | ||
184 | } | ||
185 | |||
186 | |||
187 | /* | ||
188 | * release memory block for port | ||
189 | */ | ||
190 | static void | ||
191 | free_port(void *private_data) | ||
192 | { | ||
193 | snd_emux_port_t *p; | ||
194 | |||
195 | p = private_data; | ||
196 | if (p) { | ||
197 | #ifdef SNDRV_EMUX_USE_RAW_EFFECT | ||
198 | snd_emux_delete_effect(p); | ||
199 | #endif | ||
200 | kfree(p->chset.channels); | ||
201 | kfree(p); | ||
202 | } | ||
203 | } | ||
204 | |||
205 | |||
206 | #define DEFAULT_DRUM_FLAGS (1<<9) | ||
207 | |||
208 | /* | ||
209 | * initialize the port specific parameters | ||
210 | */ | ||
211 | static void | ||
212 | snd_emux_init_port(snd_emux_port_t *p) | ||
213 | { | ||
214 | p->drum_flags = DEFAULT_DRUM_FLAGS; | ||
215 | p->volume_atten = 0; | ||
216 | |||
217 | snd_emux_reset_port(p); | ||
218 | } | ||
219 | |||
220 | |||
221 | /* | ||
222 | * reset port | ||
223 | */ | ||
224 | void | ||
225 | snd_emux_reset_port(snd_emux_port_t *port) | ||
226 | { | ||
227 | int i; | ||
228 | |||
229 | /* stop all sounds */ | ||
230 | snd_emux_sounds_off_all(port); | ||
231 | |||
232 | snd_midi_channel_set_clear(&port->chset); | ||
233 | |||
234 | #ifdef SNDRV_EMUX_USE_RAW_EFFECT | ||
235 | snd_emux_clear_effect(port); | ||
236 | #endif | ||
237 | |||
238 | /* set port specific control parameters */ | ||
239 | port->ctrls[EMUX_MD_DEF_BANK] = 0; | ||
240 | port->ctrls[EMUX_MD_DEF_DRUM] = 0; | ||
241 | port->ctrls[EMUX_MD_REALTIME_PAN] = 1; | ||
242 | |||
243 | for (i = 0; i < port->chset.max_channels; i++) { | ||
244 | snd_midi_channel_t *chan = port->chset.channels + i; | ||
245 | chan->drum_channel = ((port->drum_flags >> i) & 1) ? 1 : 0; | ||
246 | } | ||
247 | } | ||
248 | |||
249 | |||
250 | /* | ||
251 | * input sequencer event | ||
252 | */ | ||
253 | int | ||
254 | snd_emux_event_input(snd_seq_event_t *ev, int direct, void *private_data, | ||
255 | int atomic, int hop) | ||
256 | { | ||
257 | snd_emux_port_t *port; | ||
258 | |||
259 | port = private_data; | ||
260 | snd_assert(port != NULL && ev != NULL, return -EINVAL); | ||
261 | |||
262 | snd_midi_process_event(&emux_ops, ev, &port->chset); | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | |||
268 | /* | ||
269 | * increment usage count | ||
270 | */ | ||
271 | int | ||
272 | snd_emux_inc_count(snd_emux_t *emu) | ||
273 | { | ||
274 | emu->used++; | ||
275 | if (!try_module_get(emu->ops.owner)) | ||
276 | goto __error; | ||
277 | if (!try_module_get(emu->card->module)) { | ||
278 | module_put(emu->ops.owner); | ||
279 | __error: | ||
280 | emu->used--; | ||
281 | return 0; | ||
282 | } | ||
283 | return 1; | ||
284 | } | ||
285 | |||
286 | |||
287 | /* | ||
288 | * decrease usage count | ||
289 | */ | ||
290 | void | ||
291 | snd_emux_dec_count(snd_emux_t *emu) | ||
292 | { | ||
293 | module_put(emu->card->module); | ||
294 | emu->used--; | ||
295 | if (emu->used <= 0) | ||
296 | snd_emux_terminate_all(emu); | ||
297 | module_put(emu->ops.owner); | ||
298 | } | ||
299 | |||
300 | |||
301 | /* | ||
302 | * Routine that is called upon a first use of a particular port | ||
303 | */ | ||
304 | static int | ||
305 | snd_emux_use(void *private_data, snd_seq_port_subscribe_t *info) | ||
306 | { | ||
307 | snd_emux_port_t *p; | ||
308 | snd_emux_t *emu; | ||
309 | |||
310 | p = private_data; | ||
311 | snd_assert(p != NULL, return -EINVAL); | ||
312 | emu = p->emu; | ||
313 | snd_assert(emu != NULL, return -EINVAL); | ||
314 | |||
315 | down(&emu->register_mutex); | ||
316 | snd_emux_init_port(p); | ||
317 | snd_emux_inc_count(emu); | ||
318 | up(&emu->register_mutex); | ||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | /* | ||
323 | * Routine that is called upon the last unuse() of a particular port. | ||
324 | */ | ||
325 | static int | ||
326 | snd_emux_unuse(void *private_data, snd_seq_port_subscribe_t *info) | ||
327 | { | ||
328 | snd_emux_port_t *p; | ||
329 | snd_emux_t *emu; | ||
330 | |||
331 | p = private_data; | ||
332 | snd_assert(p != NULL, return -EINVAL); | ||
333 | emu = p->emu; | ||
334 | snd_assert(emu != NULL, return -EINVAL); | ||
335 | |||
336 | down(&emu->register_mutex); | ||
337 | snd_emux_sounds_off_all(p); | ||
338 | snd_emux_dec_count(emu); | ||
339 | up(&emu->register_mutex); | ||
340 | return 0; | ||
341 | } | ||
342 | |||
343 | |||
344 | /* | ||
345 | * Create a sequencer client | ||
346 | */ | ||
347 | static int | ||
348 | get_client(snd_card_t *card, int index, char *name) | ||
349 | { | ||
350 | snd_seq_client_callback_t callbacks; | ||
351 | snd_seq_client_info_t cinfo; | ||
352 | int client; | ||
353 | |||
354 | memset(&callbacks, 0, sizeof(callbacks)); | ||
355 | callbacks.private_data = NULL; | ||
356 | callbacks.allow_input = 1; | ||
357 | callbacks.allow_output = 1; | ||
358 | |||
359 | /* Find a free client, start from 1 as the MPU expects to use 0 */ | ||
360 | client = snd_seq_create_kernel_client(card, index, &callbacks); | ||
361 | if (client < 0) | ||
362 | return client; | ||
363 | |||
364 | memset(&cinfo, 0, sizeof(cinfo)); | ||
365 | cinfo.client = client; | ||
366 | cinfo.type = KERNEL_CLIENT; | ||
367 | strcpy(cinfo.name, name); | ||
368 | snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); | ||
369 | |||
370 | return client; | ||
371 | } | ||
372 | |||
373 | |||
374 | /* | ||
375 | * attach virtual rawmidi devices | ||
376 | */ | ||
377 | int snd_emux_init_virmidi(snd_emux_t *emu, snd_card_t *card) | ||
378 | { | ||
379 | int i; | ||
380 | |||
381 | emu->vmidi = NULL; | ||
382 | if (emu->midi_ports <= 0) | ||
383 | return 0; | ||
384 | |||
385 | emu->vmidi = kcalloc(emu->midi_ports, sizeof(snd_rawmidi_t*), GFP_KERNEL); | ||
386 | if (emu->vmidi == NULL) | ||
387 | return -ENOMEM; | ||
388 | |||
389 | for (i = 0; i < emu->midi_ports; i++) { | ||
390 | snd_rawmidi_t *rmidi; | ||
391 | snd_virmidi_dev_t *rdev; | ||
392 | if (snd_virmidi_new(card, emu->midi_devidx + i, &rmidi) < 0) | ||
393 | goto __error; | ||
394 | rdev = rmidi->private_data; | ||
395 | sprintf(rmidi->name, "%s Synth MIDI", emu->name); | ||
396 | rdev->seq_mode = SNDRV_VIRMIDI_SEQ_ATTACH; | ||
397 | rdev->client = emu->client; | ||
398 | rdev->port = emu->ports[i]; | ||
399 | if (snd_device_register(card, rmidi) < 0) { | ||
400 | snd_device_free(card, rmidi); | ||
401 | goto __error; | ||
402 | } | ||
403 | emu->vmidi[i] = rmidi; | ||
404 | //snd_printk("virmidi %d ok\n", i); | ||
405 | } | ||
406 | return 0; | ||
407 | |||
408 | __error: | ||
409 | //snd_printk("error init..\n"); | ||
410 | snd_emux_delete_virmidi(emu); | ||
411 | return -ENOMEM; | ||
412 | } | ||
413 | |||
414 | int snd_emux_delete_virmidi(snd_emux_t *emu) | ||
415 | { | ||
416 | int i; | ||
417 | |||
418 | if (emu->vmidi == NULL) | ||
419 | return 0; | ||
420 | |||
421 | for (i = 0; i < emu->midi_ports; i++) { | ||
422 | if (emu->vmidi[i]) | ||
423 | snd_device_free(emu->card, emu->vmidi[i]); | ||
424 | } | ||
425 | kfree(emu->vmidi); | ||
426 | emu->vmidi = NULL; | ||
427 | return 0; | ||
428 | } | ||