Gestione del MIDI con O.S.S. - Uso del dispositivo /dev/sequencer

Da Gambas-it.org - Wikipedia.

Il dispositivo: /dev/sequencer

Gli eventi Midi possono essere inviati come flusso di dati (stream) al dispositivo " /dev/sequencer ".
Il numero identificativo di tale device è 0 .

E' necessario che il dispositivo (device) sia connesso al softsynth. Il comando per stabilire tale connessione è "aconnect" seguito dal numero identificativo del client " MidiThrough " e della sua porta, che solitamente è 14:0 , nonché del client softsynth e della sua porta. Il comando "aconnect" è attivato mediante il comando SHELL.

L'invio dei dati al device " /dev/sequencer " avviene mediante l'apertura del device medesimo in scrittura all'inizio del programma. In OSS il dispositivo non può essere aperto due volte.


Invio dati per la generazione di eventi Midi

Con " /dev/sequencer " il flusso di dati, che genera un evento midi, è formato sostanzialmente da gruppi di byte costituiti da 4 valori. Di ciascun gruppo (costituito da 4 byte/valori):

> il 1° valore è sempre = 5   (corrisponde all'evento SEQ_MIDIPUTC indicato nel file soundcard.h).

> il 2° valore corrisponde al comando Midi in sé, e può essere:
     - evento Midi+canale per il 1° gruppo di dati;
     - 2° parametro dell'evento Midi per il 2° gruppo;
     - 3° eventuale parametro dell'evento Midi per il 3° gruppo.

> il 3° valore è sempre = 0 (è il parametro del "device").

> il 4° valore è sempre = 0.

Il numero complessivo di dati da inviare dipende dal tipo di evento Midi da generare. Solitamente il numero di byte è 12; o più precisamente tre gruppi da 4 dati ciascuno.

Esempio:

 WRITE #file, Chr(5) & Chr(2° valore) & Chr(0) & Chr(0)&      ' 1° gruppo di 4 valori
              Chr(5) & Chr(2° valore) & Chr(0) & Chr(0)&      ' 2° gruppo di 4 valori
              Chr(5) & Chr(2° valore) & Chr(0) & Chr(0), 12   ' 3° gruppo di 4 valori
                                                              ' Totale:     12 valori presenti da inviare

Temporizzazione degli eventi Midi

Tre sono le possibili modalità per determinare dopo quanto tempo (Tempo Delta) un evento Midi avverrà rispetto a quello immediatamente precedente:

1 - Con un'istruzione da 4 byte

Codice:

WRITE #file, Chr(2), Chr(valore1) &  Chr(valore2) & Chr(valore3), 4

  'Chr(2)  = valore identificativo di TMR_WAIT_ABS (tempo assoluto)
  'valore1 = da 0 a 255. Qui 255 dovrebbe essere = 2,6 secondi (255/96 tick)
  'valore2 = da 0 a 255. Qui invece: 1 = 2,6 sec. , e 255 = 663 sec. (255 * 2,6)

NOTA: Con questa modalità di temporizzazione il primo valore dell'istruzione è uguale a 2 .

Esempio:

WRITE #file, Chr(2), Chr(255) &  Chr(2) & Chr(0), 4

 'durata dell'evento Midi = 2,6 + (2,6 * 2) = 7,8 secondi


2 - Con un'istruzione da 8 byte

Codice:

WRITE #file, Chr(&h81), Chr(2) & Chr(0) & Chr(0), & Chr(valore1), & Chr(valore2), & Chr(valore3), & Chr(valore4), 8


NOTA: le due istruzioni (da 4 e da 8 byte), sopra descritte, si comportano diversamente da WAIT, e per questo risultano essere più vantaggiose. Infatti, il programma Midi continua comunque a fluire, e la CPU può svolgere relativamente ad esso ogni altra operazione. Questo perché la gestione delle due istruzioni è demandata specificatamente a "/dev/sequencer".


3 - Con l'uso di Timer

In alternativa, si può usare un ciclo attivato da Timer, con il quale far accadere i vari eventi Midi ogni tot millesimi di secondo. Il Timer è abbastanza preciso, nonostante presenti, ad ogni tick, alcuni millesimi di imprecisione. Questo significa che ogni nota può iniziare o terminare con alcuni millisecondi di anticipo di o di ritardo, ma ciò può - volendo - essere considerato accettabile.

Un'altra idea potrebbe essere quella di combinare il TIMER con la funzione TIME, la quale ultima restituisce l'orario sino al millisecondo. Il programma assegnerebbe innanzitutto agli eventi Midi un vero e proprio orario, con definizione al millesimo di secondo, in cui ciascuno di essi deve accadere e, quindi, essere inviato al dispositivo /dev/sequencer. Timer si limiterebbe, in tal caso, ad effettuare semplicemente il ciclo per far verificare in ogni istante se è giunto l'orario prestabilito per l'invio dei dati che determineranno l'evento Midi. Ci si affiderebbe così alla precisione dell'orologio interno del computer; e la funzione TIME farebbe da punto di riferimento certo, superando in tal modo, qualora lo si richieda, l'incerta precisione millesimale della funzione Timer.


Esempio pratico dell'uso di /dev/sequencer

Codice:

  'Gambas class file

PRIVATE midi AS file

PUBLIC SUB Form_open()

  'definisce la variabile come stringa per contenere il percorso
  'ed il nome del dispositivo che sarà utilizzato
 DIM device AS String
 device = "/dev/sequencer"

  'apre il flusso di dati per la scrittura
  'da contenersi nella variabile denominata 'midi'  
 midi = OPEN device FOR WRITE

END

 PUBLIC SUB ButtonConnessione_Click()

  'presuppone il softsynth Timidity installato
  'e connette MidiThrough 14:0 a Timidity 128:0
SHELL "aconnect 14:0 128:0"

END

PUBLIC SUB ButtonInvioDati_Click()

'Invio dei gruppi di dati (in questo esempio i gruppi sono 4):

WRITE #midi, Chr(5) & Chr(&hC0) & Chr(0) & Chr(0)&     'definisce l'evento Program Change al canale 1
             Chr(5) & Chr(70) & Chr(0) & Chr(0), 8     'definisce lo strumento musicale

WRITE #midi, Chr(5) & Chr(&h90) & Chr(0) & Chr(0)&     'definisce l'evento Note ON al canale 1
             Chr(5) & Chr(60) & Chr(0) & Chr(0)&       'definisce la nota da suonare
             Chr(5) & Chr(100) & Chr(0) & Chr(0), 12   'definisce la velocità di tocco (velocity)
 
WRITE #midi, Chr(2) & Chr(255) &  Chr(2) & Chr(0), 4   'temporizzazione con istruzione da 4 byte
 
WRITE #midi, Chr(5) & Chr(&h80) & Chr(0) & Chr(0)&     'definisce l'evento Note OFF al canale 1
             Chr(5) & Chr(60) & Chr(0) & Chr(0)&       'definisce la nota da spegnere
             Chr(5) & Chr(100) & Chr(0) & Chr(0), 12   'definisce la velocità di tocco (velocity)

END

AVVERTENZA

Le cose con il rilascio della versione 4.0 di OSS sono cambiate per "/dev/sequencer".

Contattato al riguardo, Hannu Savolainen (che ha redatto soundcard.h) ha risposto: " The /dev/sequencer API is no longer part of OSS so it will not work. However you can use the /dev/midi API and write raw MIDI messages to it. The /dev/midi API can do the same than /dev/sequencer. All you need to do is to open /dev/midi (or /dev/midi##) and to write standard MIDI messages to it. The device doesn't provide any timing facilities but you can call usleep() to wait between messages. The /dev/sequencer API was removed because the whole concept was wrong and and it proved to be impossible to document it. There were some plans to develop a replacement but I don't think it will ever get implemented."

Inoltre, altrove in rete si legge:

" The third change in OSS 4.0 is that the development of the previous sequencer (/dev/sequencer and /dev/music) API has been halted. "

" The /dev/sequencer device interface for MIDI and synthesizer devices has been abandoned. This part of the OSS API was redundant with the official MIDI 1.0 specification by MMA. "