darkmanPPT Veteran
Joined: 13 Apr 2006 Posts: 1069 Location: vi/bo
|
Posted: Fri Jun 24, 2011 9:53 am Post subject: programmare midi devices con alsa e JACK |
|
|
Allora, come richiestomi nel relativo intervento
inizio a scrivere qui un piccolo how to.
Code: | /****JACK**********/
#include <jack/jack.h>
#include <jack/midiport.h>
/*******ALSA******/
#include <alsa/asoundlib.h> |
al più, se volete, aggiungete anche
Code: | #include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h> |
Il seguente programma crea un client JACK che prende in input un controller midi e poi ridirige l'output di un'altro device midi su determinati client midi.
notare che questo utilizza fortemente JACK, cambiando il grafo di connessione dei dispositivi/client che girano su jack
Code: | jack_client_t *client;
|
creiamo un puntatore che sarà il nostro client JACK.
Code: | if (( client = jack_client_new (NAMEAPP)) == 0)
{
fprintf (stderr, "Is the Jack server running? Turn it on and reload me\n");
return 1;
} |
dove NAMEAPP è una stringa che volete voi. se ritorna 0 vuol dire che non è riuscito a creare il vostro client JACK.
Code: | /* tell the JACK server to call `jack_shutdown()' if
it ever shuts down, either entirely, or if it just decides to stop calling us.*/
jack_on_shutdown (getClient(), close, 0);
|
dove la funzione getClient() semplicemente ritorna il nostro *client di prima e diciamo al gestore JACK che quando succederà qualcosa di anomalo o si pianterà, dovrà chiamare la funzione close() da me definita
Code: | jack_port_t* input_port;
if((input_port = jack_port_register(getClient(), "input", JACK_DEFAULT_MIDI_TYPE,JackPortIsInput,0)) == 0 )
{
fprintf (stderr, "ERROR while registering input port\n");
return 1;
} |
A questo punto dobbiamo registrare una porta di input per il nostro client.
allora, creiamo il puntatore di tipo jack_port_t e dopodichè chiamiamo la funzione per *registrare* la porta sul sistema jack.
registrare vuol dire che facciamo conoscere al sistema JACK che il nostro client avrà questa determinata porta, chiamata "input" e che sarà di tipo MIDI (una porta midi normale) e che sarà una porta di tipo input (= JackPortIsInput).
Code: | jack_port_t* output_port;
if((output_port = jack_port_register(getClient(), "output", JACK_DEFAULT_MIDI_TYPE,JackPortIsOutput,0)) == 0 )
{
fprintf (stderr, "ERROR while registering output port\n");
return 1;
} |
Analogamente, creiamo una porta MIDI di output che chiameremo "output".
Ora arriva la parte di ALSA. fino ad ora abbiam creato una "scatola nera" che interagisce con JACK. Andiamo ora a riempirla.
Code: | /*client input port creation*/
if(snd_seq_open(&handle_in,"default",SND_SEQ_OPEN_INPUT,SND_SEQ_NONBLOCK))
{
fprintf (stderr, "ERROR while registering input port\n");
return 1;
}
/*if everything goes ok, i'll set the client name in*/
snd_seq_set_client_name(handle_in, "Jack Midi Rute");
/*then i'll create an input port*/
if(snd_seq_create_simple_port(handle_in, "inputJMR",
SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE,
SND_SEQ_PORT_TYPE_MIDI_GENERIC))
{
fprintf (stderr, "ERROR while registering input port2\n");
return 1;
} |
Leggiamo passo passo.
Code: | if(snd_seq_open(&handle_in,"default",SND_SEQ_OPEN_INPUT,SND_SEQ_NONBLOCK)) |
chiamiamo la funzione per *aprire* una porta per un sequencer midi. passiamo il puntatore all'handler (quello che poi *sarà effettiamente* il puntatore alla nostra porta), la chiamiamo "default", la caratteriziamo come porta di input per un sequencer e la caratteriziamo come porta non bloccante (nel senso gli eventi in entrata non bloccheranno l'esecuzione del programma).
Code: | snd_seq_set_client_name(handle_in, "Jack Midi Rute"); |
Se va tutto a meraviglia, allora ci mettiamo un nome serio; Il client midi ALSA verrà chiamato "Jack Midi Rute".
Code: | if(snd_seq_create_simple_port(handle_in, "inputJMR",
SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE,
SND_SEQ_PORT_TYPE_MIDI_GENERIC)) |
Dopo avergli dato un nome al client, creiamo effettivamente la porta (finora l'avevamo solo *aperta*)
Passiamo il puntatore all'handler, il nome che vogliam dargli, diciamo che è una porta nella quale è possibile scrivere ed è una generica porta midi.
Analogamente facciamo per la porta d'uscita
Code: | if(snd_seq_open(&handle_out,"default",SND_SEQ_OPEN_OUTPUT,SND_SEQ_NONBLOCK ))
{
fprintf (stderr, "ERROR while registering output port\n");
return 1;
}
/*if everything goes ok, i'll set the client name in*/
snd_seq_set_client_name(handle_out, "Jack Midi Rute");
/*then i'll create an input port*/
if(snd_seq_create_simple_port(handle_out, "outputJMR",
SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
SND_SEQ_PORT_TYPE_MIDI_GENERIC))
{
fprintf (stderr, "ERROR while registering input port\n");
return 1;
} |
notare che i due handler (in e out) sono due client midi diversi. uno riceverà le cose in input; queste verranno elaborate dal programma e poi le sparerà in output.
Code: | snd_midi_event_new(MIDI_BUFFER_SIZE, &alsaDecoder);
if (!alsaDecoder) {
fprintf(stderr, "ERROR: Failed to initialize ALSA MIDI decoder\n");
return 1;
}
snd_midi_event_no_status(alsaDecoder, 1); |
Bene, ora stiamo dicendo quanto è grande il buffer, MIDI_BUFFER_SIZE è un define da me definito (1023 come size), e passiamo un puntatore alla variabile alsaDecoder da me definita come
Code: | snd_midi_event_t *alsaDecoder = 0 |
Vuol dire che ogni evento midi sarà allocato là. vedere anche http://www.alsa-project.org/alsa-doc/alsa-lib/group___m_i_d_i___event.html#g8a9a7658d59324aaeda36584086ae2ba
Code: | snd_midi_event_no_status(alsaDecoder, 1);
|
Questo devo vedere bene cosa faceva. Praticamente con questa funzione si abilita (0) o si disabilita (1) il merge dei comandi MIDI.
può infatti capitare che un controller midi dia più volte lo stesso egnale (tipo hai premuto il tasto <pippo>). Può essere che non facciate a tempo a leggere tale tasto premuto. Io avevo così disabilitato il merge. merge significa che se il programma trova <pippo><pippo>, farà il merge e voi leggerete solo <pippo>.
Code: | jack_set_process_callback (getClient(), process, 0);
|
dice a JACK quale sia la funzione di callback. JACK ha un ciclo principale. in questo modo sto dicendo che ad ogni ciclo interno di JACK, egli chiamerà la funzione process() (nella quale farò le mie operazioni).
Code: | if (jack_activate (getClient()))
{
fprintf (stderr, "cannot activate client");
return 1;
} |
attiviamo dunque il client (da questo punto in poi, il client viene inserito nel ciclo interno di JACK).
intanto ora è giusto un draft.
Continuerò tra una settimana. per i prossimi giorni sono sufficientemente impegnato. nel frattempo, chi è interessato scriva giù le domande.
Dimenticavo di dire che, anche se non è banale, alsa-lib ha le sue api online e nel caso si possono vedere le spiegazioni delle varie funzioni.
http://www.alsa-project.org/alsa-doc/alsa-lib/index.html _________________ Darkman |
|