Riporto di seguito lo studio che ho compiuto su O.O.S.:
Invio dati relativi agli eventi Midi con O.O.S.Uso del dispositivo: /dev/sequencerGli eventi Midi devono essere inviati come flusso di dati (
stream) al dispositivo "
/dev/sequencer ".Tale dispositivo rappresenta, quindi, il dispositivo in grado di supportare il multimediale Midi, e che permette di programmare il sintetizzatore FM o wavetable o dispositivi esterni sul MIDI bus. Il parametro "device" serve proprio per indirizzare l'evento Midi su una certa periferica, corrisponde al MIDI output da usare ed il suo numero identificativo è 0.
Per la sintesi sonora, laddove le schede audio non posseggano il sintetizzatore interno, è necessario servirsi di un
softsynth che gestisca la riproduzione sonora appunto a livello software. Softsynth sono, per esempio, Timidity e Fluidsynth. I Softsynth sono, dunque, programmi specifici (client) aventi il compito di interfacciare i sequencer con la scheda audio gestendo preliminarmente la parte sonora vera e propria.
Ovviamente è necessario che il dispositivo 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 " può essere attivato mediante il comando SHELL.
Senza questo accorgimento non si potrebbero inviare eventi Midi dal sequencer al softsynth, il quale sulla base dei dati Midi ricevuti gestisce i campioni audio del soundbank e li suona sul dispositivo PCM della scheda audio.
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.
Con "
/dev/sequencer " il flusso di dati, che genera un evento midi, è distinguibile sostanzialmente in gruppi di byte costituiti da 4 valori. Di
ciascun gruppo:
- il 1° valore è sempre = 5 e corrisponde a SEQ_MIDIPUTC.
- il 2° valore corrisponde al comando midi in sé:
- Evento Midi+canale per il 1°gruppo di dati;
- 2° parametro dell'evento per il 2° gruppo;
- 3° eventuale parametro dell'evento 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.
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
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)
' 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
WRITE #file, Chr(&h81), Chr(2) & Chr(0) & Chr(0), & Chr(valore1), & Chr(valore2), & Chr(valore3), & Chr(valore4), 8
NOTA: le due istruzioni, 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 - 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 : ' Gambas class file
PRIVATE midi AS file
PUBLIC SUB Form_open()
DIM device AS String
device = "/dev/sequencer"
' apre il flusso di dati per la scrittura
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:
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
N O T A :
le cose con la versione 4.0 di OSS sono cambiate per "/dev/sequencer".
Da me specificamente contattato, 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, in http://manuals.opensound.com 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. >>