Il Linux OpenSound System

download Il Linux OpenSound System

If you can't read please download the document

description

Reverse engineering che ho fatto del primo sottosistema audio di Linux nel 1999. Magari a qualcuno può tornare utile per motivi storici. :-)

Transcript of Il Linux OpenSound System

  • 1. IL LINUX OPEN SOUND SYSTEM ANTONIO TRINGALImaggio 1999

2. ii 3. Indice1 LA SCHEDA AUDIO E OSS 41.1 Modello della scheda audio . . . . . . . . . . . . . . . . . . . . . .41.2 Linterfaccia di programmazione di OSS . . . . . . . . . . . . . .61.3 Inizializzazione di OSS . . . . . . . . . .. . . . . . . . . . . . . .91.3.1 Gestione degli errori . . . . . . .. . . . . . . . . . . . . . 101.3.2 Lexit handler . . . . . . . . . . . . . . . . . . . . . . . . . 121.3.3 Il signal handler . . . . . . . . . .. . . . . . . . . . . . . . 13 1.4Scrittura di codice portabile . . . . . . .. . . . . . . . . . . . . . 14 1.5Anatomia del driver . . . . . . . . . . . .. . . . . . . . . . . . . . 161.5.1 Scrittura sul buer DMA . . . . .. . . . . . . . . . . . . . 171.5.2 Lettura dal buer DMA . . . . .. . . . . . . . . . . . . . 18 1.6Schede audio ISA e PCI . . . . . . . . . . . . . . . . . . . . . . . 19 1.7File system e device le . . . . . . . . . . . . . . . . . . . . . . . . 20 1.8Le versioni di OSS . . . . . . . . . . . . . . . . . . . . . . . . . . 212 IL MIXER E LA GESTIONE DEI CANALI232.1 Descrizione di /dev/mixer . . . . . . . . . . .. . . . . . . . . . . 232.2 I canali del mixer . . . . . . . . . . . . . . . . . . . . . . . . . . . 242.2.1 Lettura della congurazione . . . . . .. . . . . . . . . . . 262.2.2 Selezione del canale da campionare . . . . . . . . . . . . . 272.3 Livelli di volume dei canali . . . . . . . . . . . . . . . . . . . . . . 282.4 Dipendenza dallhardware . . . . . . . . . . . . . . . . . . . . . . 292.5 Nuove caratteristiche . . . . . . . . . . . . . .. . . . . . . . . . . 302.6 Esempio di programma . . . . . . . . . . . . . . . . . . . . . . . . 313 CAMPIONAMENTO E RIPRODUZIONE 363.1 I device le audio . . . . . . . . . . . . . . . . . . . . . . . . . . . 363.2 Il buer audio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363.3 Parametri di campionamento . . . . . . . . . . . . . . . . . . . . . 373.3.1 Reset dei parametri . . . . . . . . . .. . . . . . . . . . . . 383.3.2 Pause nelle operazioni . . . . . . . . . . . . . . . . . . . . 393.4 Il campionamento . . . . . . . . . . . . . . . . . . . . . . . . . . . 393.5 La riproduzione . . . . . . . . . . . . . . . .. . . . . . . . . . . . 403.6 Il formato audio . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41iii 4. 3.6.1 Little e big endian . . . . . .. . . . . . . . . . . . . . . . 423.6.2 La codica lineare . . . . . . . . . . . . . . . . . . . . . . 43 3.7Il tempo reale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443.7.1 Le capacit` del driver . . . . .a. . . . . . . . . . . . . . . . 453.7.2 Gestione del buer DMA . . . . . . . . . . . . . . . . . . . 463.7.3 I/O non bloccante . . . . . .. . . . . . . . . . . . . . . . 483.7.4 La sincronizzazione . . . . . .. . . . . . . . . . . . . . . . 523.7.5 Accesso diretto al buer DMA . . . . . . . . . . . . . . . . 53 3.8Il full duplex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 3.9Uso di coprocessori . . . . . . . . . .. . . . . . . . . . . . . . . . 56 3.10 Nuove caratteristiche . . . . . . . . .. . . . . . . . . . . . . . . . 58 3.11 Esempio di programma . . . . . . . . . . . . . . . . . . . . . . . . 594 SINTETIZZATORI E MIDI 644.1 I device le del sequencer . . . . . . . . . . . . . . . . . . . . . . .64 4.1.1 I chip sintetizzatori . . . . . . . . . . . . . . . . . . . . . .65 4.1.2 I sintetizzatori MIDI . . . . . . . . . . . . . . . . . . . . .664.2 Il buer degli eventi . . . . . . . . . . . . . . .. . . . . . . . . . .674.3 Lettura della congurazione . . . . . . . . . .. . . . . . . . . . .69 4.3.1 Parametri generali . . . . . . . . . . .. . . . . . . . . . .70 4.3.2 Le porte MIDI . . . . . . . . . . . . . . . . . . . . . . . .714.4 Predisposizione dei chip sintetizzatori . . . . .. . . . . . . . . . .72 4.4.1 Caricamento degli algoritmi FM . . . .. . . . . . . . . . .73 4.4.2 Caricamento delle patch wavetable . . . . . . . . . . . . .76 4.4.3 Caricamento delle sysex patch . . . . . . . . . . . . . . . .814.5 La temporizzazione . . . . . . . . . . . . . . . . . . . . . . . . . .81 4.5.1 La sincronizzazione per /dev/music . .. . . . . . . . . . .83 4.5.2 La sincronizzazione per /dev/sequencer. . . . . . . . . . .854.6 Output degli eventi . . . . . . . . . . . . . . .. . . . . . . . . . .86 4.6.1 Messaggi di canale . . . . . . . . . . .. . . . . . . . . . .86 4.6.2 Messaggi di sistema . . . . . . . . . . . . . . . . . . . . . .89 4.6.3 Controllo della coda degli eventi . . . . . . . . . . . . . . .914.7 Formato degli eventi . . . . . . . . . . . . . . . . . . . . . . . . .924.8 Input degli eventi . . . . . . . . . . . . . . . . . . . . . . . . . . .94 4.8.1 Eventi per /dev/music . . . . . . . . . . . . . . . . . . . .95 4.8.2 Eventi per /dev/sequencer . . . . . . . . . . . . . . . . . .974.9 Nuove caratteristiche . . . . . . . . . . . . . .. . . . . . . . . . .97 4.9.1 SoftOSS . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 4.9.2 OSSlib . . . . . . . . . . . . . . . . . .. . . . . . . . . . . 1014.10 Esempio di programma . . . . . . . . . . . . .. . . . . . . . . . . 102 iv 5. 5 IL MIDI A BASSO LIVELLO1065.1 Descrizione dei device le MIDI .. . . . . . . . . . . . . . . . . . 1065.2 Lettura dalla porta MIDI . . . . . . . . . . . . . . . . . . . . . . . 1075.3 Scrittura sulla porta MIDI . . . . . . . . . . . . . . . . . . . . . . 1095.4 Programmazione della MPU401 . . . . . . . . . . . . . . . . . . 1095.5 Esempio di programma . . . . . . . . . . . . . . . . . . . . . . . . 110A GENERAL MIDI 121A.1 Timbri strumentali . . . . . . . . . . . . . . . . . . . . . . . . . . 121A.2 Timbri delle percussioni . . . . . . . . . . . . . . . . . . . . . . . 125 1 6. SommarioQuesto lavoro si propone come tutorial per la programmazione della scheda audiotramite lOpen Sound System (OSS) per il sistema operativo Linux, ponendoparticolare enfasi sulle strategie di programmazione pi` opportune per la sintesi uaudio in tempo reale.Capitolo 1 Si introduce un modello medio delle varie schede audio presentisul mercato; dopo sono descritte la visione orientata a UNIX che ne ha OSS(in particolare alla ne descrivendo il funzionamento interno del driver), lasua inizializzazione e la gestione delle uscite da programma e dei segnali inun ambiente multiutente e multiprogrammato, con qualche riferimento allascrittura di programmi portabiliCapitolo 2 Il mixer gestisce i canali di ingresso/uscita facenti capo alla schedaaudio; ` spiegato come scoprire le capacit` di questultima, selezionare ieacanali e regolare i livelli di volume per campionamento e riproduzione`Capitolo 3 E spiegato come campionare da un qualsiasi canale del mixer e ripro-durre i campioni sintetizzati o immagazzinati su un le, eventualmente inmaniera non bloccanteCapitolo 4 Ci si occupa della programmazione ad alto livello, cio` in maniera eindipendente dai dispositivi, del chip sintetizzatore interno alla scheda audioe delle porte MIDICapitolo 5 Si tratta della programmazione MIDI a basso livello, cio` a livello edi I/O di byte dei messaggi `Appendice A E riportata una tabella di corrispondenza fra i preset timbricideniti nello standard General MIDI level 1 e i nomi delle patch wavetableper la Gravis UltraSound 7. INTRODUZIONE ALLOPEN SOUND SYSTEM Allinizio degli anni 90 Hannu Savolainen scrisse la prima versione di un driverper la scheda audio Sound Blaster 1.5 sotto Minix386; nel 1992 ne fece il portingsotto Linux: era nato il Linux Sound Driver (LSD). Al crescere del numero di versione e delle funzionalit` implementate, nonchaeallaumentare del numero di porting ad altre variet` di UNIX, il driver cambi` a oil nome in VoxWare; sfortunatamente era omonimo della VoxWare Incorporat-ed, quindi per problemi di copyright per un certo periodo rest` il Temporarily oAnonymous Sound Driver (TASD). Poco dopo la 4Front Technologies, con la collaborazione dello stesso HannuSavolainen, svilupp` lUnix Sound System (USS), avente qualche caratteristica in opi` rispetto al driver freeware e un maggior numero di schede audio supportate; ulo stesso Savolainen continuava indipendentemente a sviluppare il driver freeware(il cui codice sorgente era dierente da quello di USS), noto ora come USS/Lite. In ossequio a POSIX e alla gran variet` di sistemi di tipo UNIX al quale `aestato portato, il nome ` cambiato nuovamente e il driver ` commercializzato dallaee4Front Technologies come Open Sound System, mentre nella versione freeware` noto come OSS/Free. Di questo Savolainen non mantiene pi` il codice: laeuresponsabilit` ` passata a Alan Cox dagli inizi del 1998.ae Dora in poi ci si riferir` a questi driver globalmente come OSS, sottinten-adendo che ci` che sar` detto vale per entrambi (evidenziando, ove necessario,o ale dierenze); la versione assunta come riferimento ` la 3.5.4. A seguire daleCapitolo 2, la penultima Sezione di ogni Capitolo elenca le dierenze di program-mazione introdotte dalla versione 3.6 in poi e lultima Sezione ospita un breveprogramma di esempio in C che illustra le caratteristiche di OSS introdotte nelCapitolo stesso. Delle chiamate alla libreria di OSS elencate in soundcard.h, si` scelto di riportare solo quelle pienamente supportate.e OSS ` in pratica il primo tentativo di unicare larchitettura di audio digitale eper UNIX: alle capacit` di campionamento e riproduzione (piuttosto articolate arispetto a quanto prima disponibile per questo ambiente) sono aancate le possi-bilit` del MIDI, con il supporto dellaudio sincronizzato alla riproduzione video.a`E allo stato attuale un insieme modulare di device driver che garantiscono unin-terfaccia di programmazione uniforme e compatibile a livello di codice sorgenteper tutte le piattaforme su cui ` stato portato, tanto che per queste ` valido la e equasi totalit` di ci` che ` scritto nel seguito di questo lavoro. aoe Una panoramica generica delle caratteristiche supportate sia dallOSS com-merciale (OSS/Linux) che da OSS/Free ` la seguente:e supporto di vari formati audio per i campioni (8 e 16 bit signed e unsigned, 8 bit Law e A-Law, IMAADPCM) supporto di canali stereo e mono frequenze di campionamento comprese tra 4kHz e 48kHz2 8. supporto half e full duplex (con apposite schede audio) possibilit` di accesso diretto al buer audio DMA (per applicazioni con a richieste temporali pi` stringenti, come i giochi) u possibilit` di accesso indipendente dallhardware a MIDI e a sintetizzatori a FM o wavetable sulla scheda audio caricamento delle patch in maniera indipendente dallhardware supporto per SMPTE/MTC supporto per UART di tipo MP401, Sound Blaster MIDI e XG MIDI supporto di IBM/Motorola PowerPC (bus ISA e PCI), 386/486/Pentium (bus ISA, EISA e PCI), Sun SPARC e DEC Alpha/AXP (bus ISA e PCI) Rispetto a OSS/Free, OSS/Linux ha in pi` le seguenti caratteristiche:u supporto diretto dello standard PnP (senza dover inizializzare precedente- mente le schede con il comando isapnp) supporto di un maggior numero di schede audio non si deve ricompilare il kernel ad ogni cambio di congurazione o driver (questo ` un modulo separato) e supporto delle schede Sound Blaster 16/32/64/AWE in full duplex supporto delle patch wavetable Emu SoundFont 2.0 librerie DirectAudio per laccesso diretto ai chip sintetizzatori FM e alle porte MIDIsi ` scelto di non trattare gli ultimi due punti, in quanto di interesse marginale eper la sintesi in tempo reale.CONVENZIONI TIPOGRAFICHEPer tutto il testo sono seguite per le parole le seguenti convenzioni: corsivo indica una sigla o un termine tecnico rilevante introdotto per la primavolta neretto indica un identicatore o una variabile (con specicato il tipo) perlinterfaccia di programmazione di OSS o di Linux spaziatura fissa indica una parola chiave appartenente alla libreria diOSS o codice sorgente di esempio; se le parole sono contenute tra parentesi an-golate, come ad esempio per , si intende che il programmatore debbasostituirvi un adeguato valore numerico 3 9. Capitolo 1LA SCHEDA AUDIO E OSS1.1 Modello della scheda audioUna scheda audio ha diverse funzioni: converte i suoni immagazzinati nel sistema(ad esempio, su le) dalla forma digitale allanalogica anch li si possa udireeo registrare, converte da opportuni canali di ingresso (LineIn1 , microfono, CDaudio) i segnali da analogico a digitale anch possano essere immagazzinati oemanipolati dal computer, pu` consentire essa stessa di creare nuovi suoni tramiteoeventuali sintetizzatori interni. Secondo le speciche MPC2 deve anche essere dotata di una porta MIDI percheil computer possa controllare strumenti musicali o sintetizzatori esterni con talicapacit`, invece di generare da s i suoni. Il PC diventa in pratica un sequencer,a ecio` un direttore dorchestra allo stato solido; virtualmente non ` pi` di unaee umemoria e un sistema di messaggi dalla e alla strumentistica, con capacit` diaediting. LOSS cerca di orire una visione idealizzata della scheda audio al program-matore, nascondendo le dierenze tecniche fra le varie schede presenti sul mercato;essa pu` essere vista come un mixer, di cui il programmatore (il Disc Jockey) haopossibilit` di controllare ogni canale3 . a Per la riproduzione (conversione D/A) la sorgente ` da le (brano digitaliz- ezato o MIDI) o i campioni sono creati con un opportuno algoritmo di sintesi, daCD audio4 (il segnale ` semplicemente spedito alla sezione analogica della schedae 1`E un ingresso con livelli simili a quelli dingresso per un amplicatore HIFI, utile percampionare il segnale proveniente da un registratore analogico o da un CD player esterni2Speciche rilasciate da Microsoft e Intel nel 1991 per denire una congurazione hardwareminima anch un computer potesse essere denito multimediale: si doveva cio` disporreeealmeno di un PC 386SX/16MHz con 4MB di RAM e 40MB di hard disk, una VGA a colori, unmouse, una scheda audio e un CDROM player3Indipendentemente o meno dagli altri; relativamente alle capacit` della scheda audio stessa, acon il programmatore che pu` interrogare OSS sulle capacit` di questultima oa4Non ` presa in considerazione la programmazione del drive CD in questo tutorial, solo come esi fa a campionare o riprodurre il segnale che ` presente sulleventuale canale CD Audio In dellaescheda audio4 10. CAPITOLO 1. LA SCHEDA AUDIO E OSS 1.1. MODELLO DELLA SCHEDA AUDIOaudio senza che sia stato campionato prima), da LineIn e da microfono. In cam-pionamento (conversione A/D) si pu` acquisire dagli ingressi CD Audio, LineIn oe microfono o si possono acquisire eventi MIDI.La coppia di convertitori A/D e D/A viene spesso chiamata PCM, DSP,CODEC o ADC/DAC ; nella Tabella seguente sono elencate le massime frequenzedi campionamento5 per le varie generazioni di schede audio che OSS supporta (laminima ` sempre intorno ai 5 kHz); la fc ` generata dividendo lalta frequenza di eeun oscillatore di riferimento, per cui non ` possibile ottenere tutte le frequenzeedellintervallo: le dierenze di qualche percento dovrebbero essere ignorate perchesolitamente non avvertibili (OSS cerca di ottenere dalla scheda audio la frequenzapi` vicina a quella richiesta). La risoluzione di campionamento pu` essere di 8 ouo16 bit, in mono o stereo.Generazione Max fcNote1a22.05 kHz in riproduzione11.025 kHzin campionamento2a 44.1 kHz mono22.05 kHz stereo3a 44.1 kHz qualit` audio CDa48 kHzqualit` DATa4a96 kHzUltra HiFiOltre alle succitate possibilit` di sintesi FM6 e di gestione dei dispositivi MI-aDI, alcune schede consentono la wavetable synthesis e la possibilit` di caricare acampioni di uno strumento (patch) in una speciale memoria della scheda stessa.In gura 1.1 ` evidenziata la visione che possiamo dare della nostra scheda eaudio idealizzata come output mixer per quanto riguarda la riproduzione, che bensi conf` alle caratteristiche pi` comuni riscontrabili nelle schede audio attualmente aupresenti sul mercato (1999). In gura 1.2 ` invece schematizzato linput mixer aecui fare riferimento per quanto riguarda il campionamento.Infatti dentro una scheda audio possono esserci realmente due mixer che gestis-cono separatamente i casi di campionamento e riproduzione, ma OSS gestisceautomaticamente il passaggio dalluno allaltro in base ai comandi del program-matore di lettura o scrittura dalla/alla scheda audio. In particolare, se tali attivit`asono implementate dalla scheda audio come mutuamente esclusive ` denita halfeduplex, mentre ` denita full duplex se possono aver luogo contemporaneamente. e 5Alcune vecchie schede permettono di scegliere solo fra frequenze di campionamento sse:11025 Hz, 22050 Hz, 32000 Hz e 44100 Hz 6`E sfruttata la tecnica degli operatori per produrre pi` voci: i chip classici sono lo YamahauOPL2 (a due operatori, nove voci con timbri non realistici) e OPL3 (a quattro operatori,diciotto voci): aumentando il numero di operatori per voce migliora la qualit` della sintesia5 11. 1.2. LINTERFACCIA DI PROGRAMMAZIONE DI OSSCAPITOLO 1. LA SCHEDA AUDIO E OSSMemoria del computero Hard-DiskLINE-OUT ALTOPARLANTI LRL RL RLRL R L RL R STEREO MONOSTEREO MONO TREBLE BASS RECORDCD Audio LINE-INMICMASTERDAT CD Audio LINE-INMICMIDI ON/OFF VOLUMEON/OFF ON/OFF ON/OFFMIDI ON/OFFVOLUMEON/OFFON/OFF ON/OFF ON/OFFFigura 1.1: Modello come mixer della Figura 1.2: Modello come mixerscheda audio in riproduzione della scheda audio in campiona- mento1.2Linterfaccia di programmazione di OSSPer gestire il mixer virtuale delle gure 1.1 e 1.2 OSS sfrutta la visione orientataal lesystem che Linux ha di ogni device 7 . I canali del mixer, campionamentoe riproduzione possono quindi essere gestiti manipolando degli speciali le chesi trovano nella directory /dev tramite le primitive di sistema open(), ioctl(),read(), write() e close().In gura 1.3 sono evidenziati i device le relativi alla manipolazione dei varicanali per la riproduzione, mentre in gura 1.4 c` lo schema equivalente per ilecampionamento.Questorganizzazione ` conveniente, poich in tal modo sono schermate sia le e ecomplessit` dellhardware e del software sottostante (la scheda audio, ma ancheala gestione del DMA, della memoria virtuale e del multitasking), sia le diversit`afra le varie schede audio in commercio.Si pu` ora procedere alla descrizione di ogni device le:o/dev/mixer Questa ` linterfaccia di accesso alle funzioni del mixer e pu`eoessere un link simbolico a /dev/mixer0; se ` presente un secondo mixer, ci esi riferisce ad esso come /dev/mixer1/dev/dsp Identica il DSP della scheda (per default con codica lineare 8 bitunsigned) e pu` essere un link simbolico a /dev/dsp0; in genere ` presenteo e 7` E uninterfaccia a un dispositivo hardware (dischi, linee seriali, etc.) o a entit` a cuianon corrisponde un vero e proprio dispositivo hardware (memoria di sistema, kernel, etc.); a undevice sico come la scheda audio corrisponde un device driver come OSS che hail compito di pilotarlo 6 12. CAPITOLO 1. LA SCHEDA AUDIO E OSS 1.2. LINTERFACCIA DI PROGRAMMAZIONE DI OSS = Link simbolico OSS driverOSS driver Memoria del computer o Hard-Disk /dev/dsp /dev/dsp0/dev/dsp1MIDI in/dev/dsp /dev/dspW/dev/dspW0/dev/dspWMIDI file (out) /dev/dspW1/dev/audio/dev/audio/dev/audio0 CD Audio /dev/music/dev/audio1/dev/sequencerCD Audio/dev/midi00Memoria del computer/dev/midi01o Hard-Disk/dev/sndstat /dev/sndstat /dev/midi02/dev/sndprocLine-In /dev/music/dev/sndproc /dev/midi03 Line-In /dev/sequencer /dev/midi00 /dev/midi01 MIXER MIXER /dev/midi02 /dev/midi03 MicMic /dev/mixer/dev/mixer0/dev/mixer/dev/mixer0 /dev/mixer1/dev/mixer1 Figura 1.3: Visione della scheda au- Figura 1.4: Visione della scheda dio in riproduzione che OSS d` al audio in campionamento che OSS a programmatored` al programmatore a anche /dev/dsp1, che pu` identicare un secondo DSP o lo stesso con unao diversa funzione/dev/dspW Se presente (dipende dalla versione di OSS), si riferisce al DSP concodica lineare 16 bit signed e little endian8 ; pu` essere un link simbolico oa /dev/dspW0 ed eventualmente ` presente anche /dev/dspW1e`/dev/audio E presente per limitata compatibilit` con le workstation Sun (non asono supportati cambiamenti da 8 kHz/mono/8 bit) e utilizza la codicaLaw9 ; ` disponibile in mutua esclusione con /dev/dsp e generalmente ` e eun link a /dev/audio0, potendo essere presente anche /dev/audio1/dev/music Permette di accedere al sintetizzatore interno alla scheda audio ealle porte MIDI in maniera indipendente dal dispositivo (sono trattati allostesso modo dal punto di vista dellinterfaccia di programmazione), conmodalit` di temporizzazione piuttosto articolate; pu` essere presente il linka osimbolico ad esso /dev/sequencer2 (obsoleto)`/dev/sequencer E un device le a pi` basso livello di /dev/music, rispetto aluquale ha capacit` limitate di temporizzazione, sincronizzazione e gestioneaautomatica del chip sintetizzatore interno alla scheda audio 8 Little endian e big endian si riferiscono a come sono conservati i campioni in memoria dallaCPU: per il primo lindirizzo del dato corrisponde allindirizzo del byte meno signicativo (Intel,Alpha), per il secondo al byte pi` signicativo (Motorola, Sparc, PowerPC, HPPA) u 9 Un campione a 12 o 16 bit ` compresso logaritmicamente a 8 bit: OSS in riproduzioneenon eettua loperazione opposta, ma converte in un campione a 8 bit lineare prima di inviareal device audio (sono introdotti un overhead per il calcolo e della distorsione); ` un formato ederivante dalla tecnologia telefonica digitale7 13. 1.2. LINTERFACCIA DI PROGRAMMAZIONE DI OSS CAPITOLO 1. LA SCHEDA AUDIO E OSS`/dev/midi00 E uninterfaccia a basso livello al canale MIDI, orientata ai carat-teri come tty (raw mode) e indirizzata ad applicazioni che non richiedonouna sincronizzazione in tempo reale come i sequencer (pu` essere usata oper inviare sysex, per caricare campioni sugli strumenti o per eettuare ildump dei canali); se presenti, possono essere gestite altre porte MIDI con/dev/midi01, /dev/midi02 e /dev/midi03/dev/sndproc Rappresenta linterfaccia interna di accesso a un eventuale co-processore presente sulla scheda audio; pu` essere una soluzione tempo-oranea, da eliminare in seguito/dev/sndstat Questo, a dierenza degli altri, ` un device le a sola lettura: estampa informazioni diagnostiche riguardo la congurazione di OSS in for-ma leggibile agli umani, ma se ne sconsiglia lutilizzo da parte dei programmiperch` in futuro il formato delle informazioni da esso fornite potrebbe cam-ebiare; non ci si far` pi` riferimento dora in poi. Loutput ha una sezione a uper ogni categoria di dispositivi, e questi sono numerati nellordine in cui ildriver li inizializza (che non ` sso, per cui ` meglio non fare assunzioni a e epriori); un esempio del suo utilizzo ` il seguente:e ~>cat /dev/sndstat Sound Driver:3.5.4-960630 Kernel: Linux papo 2.0.32 #1 Wed Nov 19 00:46:45 EST 1997 i486 Config options: 0 Installed drivers: Card config: Audio devices: 0: Sound Blaster 16 (4.13) Synth devices: 0: Yamaha OPL-3 Midi devices: Timers: 0: System clock Mixers: 0: Sound Blaster OSS consente di avere pi` schede audio installate nel computer, il che si utraduce in un maggior numero di device le indirizzabili, elencati nelloutput8 14. CAPITOLO 1. LA SCHEDA AUDIO E OSS1.3. INIZIALIZZAZIONE DI OSSdi /dev/sndstat: lesempio sopra rivela che il computer ha solo /dev/dsp0,ma in generale con pi` DSP li si potrebbe indirizzare operando su /dev/dspn; uanalogamente per il mixer su /dev/mixern, etc.1.3Inizializzazione di OSSIl minimo insieme di header le da includere ` rappresentato da stdio.h (per le efunzioni di libreria tipo printf() e perror()), unistd.h, fcntl.h (per open(),ioctl(), read(), write() e close()) e sys/soundcard.h (le denizioni vere eproprie per la libreria di OSS).Adesso si riporter` uno scheletro di codice C per lapertura di un device le di aOSS (nellesempio ` /dev/dsp, ma potrebbe essere /dev/mixer, . . . ); nei succes- esivi Capitoli lo si dar` come sottinteso man mano che si introducono le primitive aper una gestione vieppi` sosticata della scheda audio.u#include #include #include #include int main(){ int dspfd;/* File descriptor per /dev/dsp */dspfd = open("/dev/dsp", O_WRONLY);/* Apre il device*/if (dspfd == -1) {perror("/dev/dsp"); /* Gestione errore*/exit(-1);} /* Qui ci puo andare tutto il codice per sfruttare il */ /* device file, con le ioctl() necessarie a configurare*/ /* il driver poste prima delle read() o write()*/close(dspfd);/* Chiusura del device file */return 0;} Il secondo argomento di open() ` la modalit` di accesso al device le, che nele acaso di OSS pu` essere una fra le seguenti: oO RDONLY Accesso in sola lettura (read() o ioctl())O WRONLY Accesso in sola scrittura (write() o ioctl())O RDWR Accesso in lettura/scrittura (read(), write() o ioctl()) 9 15. 1.3. INIZIALIZZAZIONE DI OSS CAPITOLO 1. LA SCHEDA AUDIO E OSSPer read() e write() i ag O_NDELAY o O_NONBLOCK, anch le operazioni dieI/O sul device le non blocchino il processo chiamante, non hanno eetto: se ilprocesso eettua una read() e i campioni richiesti non sono ancora disponibili,questo viene messo in wait nch loperazione ` completata (analogamente cone euna write(), se il buer del driver non ha spazio suciente per i campioni chesi vogliono riprodurre).Per la open() il funzionamento ` sempre del tipo O_NDELAY; se fallisce il ele descriptor ` posto uguale a -1 e la macro errno ` impostata a un oppor-e etuno valore (questa ` messa a disposizione del programmatore per mezzo die#include ).1.3.1 Gestione degli erroriI codici di errore pi` comuni riportati da errno sono i seguenti: uENOENT Il device le che si ` tentato di aprire non ` presente in /deve eENODEV Esiste il device le in /dev, ma il driver non ` stato caricato dal e kernel (si pu` controllare con cat /dev/sndstat o dmesg | more)oENOSPC Esiste il device le in /dev, ma il driver non ` stato in grado die allocare la memoria per il buer DMA tramite sound_mem_init() durante il boot del sistema; il modulo del driver dovrebbe essere uno dei primi ad esser caricato dal kernel, in modo da garantire tale operazione anche in caso di poca memoria installataENXIO Esiste il device le in /dev e il driver ` presente nel kernel, ma non e esiste il dispositivo hardware che si tenta di indirizzare (ad esempio, perche la congurazione del driver non corrisponde allhardware audio)EINVAL Uno degli argomenti della chiamata a una funzione non ha un valorevalidoEBADF Il le descriptor non si riferisce a un device le aperto, una read() `e stata rivolta a un device le aperto con O_WRONLY o una write() ` stata e rivolta a un device le aperto con O_RDONLYEBUSY Solo un processo alla volta pu` gestire /dev/dspn e /dev/audion, pero cui si verica se un altro processo (anche appartenente allo stesso utente) tenta di accedervi (si verica anche se lIRQ o il canale DMA sono occupati); pu` essere gestito dal programma tentando di riaprire il device le che hao causato lerrore dopo qualche tempo, ma non ` garantito che esso divenga e mai disponibile`EINTR E ritornato da read() o write() qualora queste siano risultate bloccan- ti e durante lo stato di wait il processo utente abbia ricevuto un signal() 10 16. CAPITOLO 1. LA SCHEDA AUDIO E OSS 1.3. INIZIALIZZAZIONE DI OSSEAGAIN La risorsa a cui si ` cercato di accedere ` temporaneamente non e e disponibile (ad esempio, si ` cercato di scrivere su un buer pieno o leggere e da un buer vuoto con il device le aperto in O_NONBLOCK)EACCES Per i device /dev/dspn e /dev/audion laccesso ` consentito solo al e root (a meno che il sistema sia stato congurato in altro modo), e questo errore si verica se un utente normale cerca di accedere a tali device le; questa ` una misura di sicurezza per impedire che, ad esempio, qualora il e computer sia connesso in rete e abbia un microfono qualcuno possa ascoltare remotamente le conversazioni che si svolgono nella stanza ove si trova il computer Un error handler un po pi` sosticato potrebbe rassomigliare a: uif ((dspfd = open("/dev/dsp2", O_WRONLY)) == -1) {switch (errno) {case ENOENT: perror("/dev/dsp2"); exit(-1);case EBUSY: close(dspfd);/* Aspetta un po per riaprire */ sleep(10); if (dspfd = open("/dev/dsp2", O_WRONLY)) == -1) { perror("/dev/dsp2"); exit(-1); } break;default: perror("Altro tipo di errore");fprintf(stderr, "Errore numero: %dn", errno);exit(-1);}} Per quanto invece riguarda il seguito di questo lavoro, si demander` la gestioneadegli errori a una semplice routine del tipo:void errore(const char *msgerr){perror(msgerr);exit(-1);}Sarebbe corretto mettere un error handler non solo dopo una open(), ma an-che dopo read() o write() per vericare se siano stati letti o scritti il numerocorretto di byte; tuttavia nella sintesi in tempo reale ci` tende a rappresentare ocicli di CPU sprecati. Invece dopo una ioctl() conviene controllare quasi ob-bligatoriamente il valore ritornato dal driver nellultimo dei suoi argomenti, pervedere cosa si riesce a ottenere rispetto a quanto richiesto dal programmatore.11 17. 1.3. INIZIALIZZAZIONE DI OSS CAPITOLO 1. LA SCHEDA AUDIO E OSS Un altro buon accorgimento di programmazione ` di installare un exit handler ee/o un signal handler subito dopo una open() riuscita: il primo pu` essere utile o10per operazioni di routine alla chiusura del programma (regolare o in seguito aexit()), il secondo pu` gestire i segnali impostati da altri processi, dal kernel oo dal processo stesso. Per una dettagliata descrizione di questi argomenti siveda [10].1.3.2Lexit handlerUn exit handler richiede la funzione di libreria atexit(), disponibile in segui-to a #include . Essa registra le funzioni di tipo void f() date inargomento come exit handler (max 32), venendo richiamate nellordine inversorispetto a quello con cui sono state registrate; atexit() restituisce 0 se la regis-trazione ` stata possibile, altrimenti -1 con errno==ENOMEM (memoria insucienteeper aggiungere la funzione).Nellesempio seguente si dimostra lutilizzo di atexit() chiudendo un devicele alluscita dal programma (anche se ci` non ` strettamente necessario per o equanto prima aermato):#include#include#include#include#includeint dspfd; /* Variabile globale */void Messaggio()/* Chiamata per prima */{ puts("Premi per chiudere /dev/dsp..."); getchar();}void ChiudiTutto() /* Chiamata per ultima */{ close(dspfd); puts("Fine del programma");}void errore(const char *msgerr) /* Gestione degli errori */{perror(msgerr);10Linux svuota i buer di I/O e chiude automaticamente i device le rimasti eventualmenteaperti prima che il processo termini, tramite librerie a livello utente o tramite kernel12 18. CAPITOLO 1. LA SCHEDA AUDIO E OSS1.3. INIZIALIZZAZIONE DI OSS exit(-1);}int main(){ if ((dspfd = open("/dev/dsp", O_RDONLY)) == -1) errore("/dev/dsp"); if (atexit(ChiudiTutto) == -1) /* Registrata per prima */ errore("ChiudiTutto()"); if (atexit(Messaggio) == -1) /* Registrata per ultima */ errore("Messaggio()"); return 0;/* Uscita dal programma */}Lexit handler ` richiamato in seguito ad exit(), abort()11 , return o alla econsegna di un segnale la cui azione di default ` di uccidere il processo; loutputedel programma sarebbe:Premi per chiudere /dev/dsp...Fine del programma Per evitare la chiamata allexit handler lo standard POSIX.1 (1990) prevede_exit(), che inoltre evita lo svuotamento dei buer prima della chiusura dei le.1.3.3 Il signal handlerUn signal handler funziona in modo molto simile ad un exit handler: previo#include , si usa la funzione di libreria signal() per registrare dellefunzioni (non di libreria) che hanno il compito di reagire a segnali provenienti daaltri processi o dallo stesso processo12 (rispettivamente generati tramite le funzionidi libreria kill() e raise()).In signum.h sono deniti una trentina di segnali che si conformano ad ANSI,POSIX, System V e BSD 4.2, per i quali esistono delle disposizioni di default(ci` equivale a signal(, SIG_DFL)); se si vuole ignorare un partico- olare segnale si pu` porre nel codice signal(, SIG_IGN). La signal() oritorna il valore precedente del signal handler o SIG_ERR se si verica un errore.11 Viene impostato un segnale SIGABRT al processo chiamante: non ` ignorabile o bloccabileeda un signal handler12 Un segnale ` denibile come uninterruzione asincrona software nel usso di un processo:eesso ` impostato dal processo che lo genera ed ` consegnato al processo che lo riceve; il eemodo in cui questultimo reagisce al segnale si chiama disposizione 13 19. 1.4. SCRITTURA DI CODICE PORTABILE CAPITOLO 1. LA SCHEDA AUDIO E OSS Per esempio, se si vuole che il processo reagisca a un segnale di interruzione(SIGINT) e a un segnale di terminazione del programma (SIGTERM) con unadisposizione diversa dalla predenita, allinizio di main() si potr` porre: asignal(SIGINT, SignalHandler);/* Le funzioni possono essere */signal(SIGTERM, SignalHandler); /* uguali o diverse */ove SignalHandler ` una funzione del tipo:evoid SignalHandler(int segnale) /* Il segnale invocante */{ /* e passato allhandler */switch (segnale) {case SIGINT:case SIGTERM: puts("SIGINT o SIGTERM"); exit(0);default:}}il processo relativo termina se da shell di comando si digita kill -s SIGINT o kill -s SIGTERM , ove ` lidenticatore del processo (visualizz-eabile tramite il comando ps).Sono da tenere presenti i seguenti fatti: non possono essere variate le disposizioni di SIGKILL e SIGSTOP (signal() ritorna EINVAL) il comportamento di un programma ` indenito se sono ignorati SIGFPE,e SIGILL o SIGSEGV ignorare il segnale derivante dalla divisione intera per zero ha un compor- tamento indenito, che potrebbe condurre a un blocco del computer per certi segnali la disposizione di default implica la terminazione del pro- cesso e il salvataggio dellarea dati e heap in un le core a ni di debug; altri provocano la semplice terminazione del processo o sono ignorati a dierenza di BSD, Linux non ridispone il signal handler a SIG_DFL dopo la consegna di un segnale Si veda man 7 signal per una dettagliata descrizione dei segnali e della lorodisposizione di default, e in ogni caso [10] per una gestione pi` sosticata.u1.4 Scrittura di codice portabileDi seguito sono elencati alcuni consigli per la scrittura di programmi che sianoportabili sotto i vari sistemi operativi per cui sia stato portato anche OSS: 14 20. CAPITOLO 1. LA SCHEDA AUDIO E OSS 1.4. SCRITTURA DI CODICE PORTABILE Come si vedr` nei successivi Capitoli, conviene usare delle speciche macro a per impostare dei parametri, ad esempio del mixer, tramite ioctl() (queste schermano i dettagli dellimplementazione dei parametri per le future ver- sioni del driver); in particolare bisognerebbe controllare se ` adeguato ile valore ritornato nellultimo argomento (per le chiamate che lo prevedono) ` E meglio riferirsi a un link simbolico per un device piuttosto che utilizzare un riferimento assoluto, ad esempio usando /dev/dsp al posto di /dev/dsp0; ci` d` essibilit` allutente per poter far puntare i link simbolici ad altri o aa device le, se questi garantiscono migliori risultati (i programmi dovrebbero sfruttare i nomi veri solo se resi facilmente congurabili) Non bisogna sfruttare delle caratteristiche non documentate (in quanto obsolete scompariranno in futuro o non ancora ben testate) Lappesantimento di un programma con caratteristiche al di fuori dellessen- ziale o con trucchi pu` compromettere la compatibilit` del codice con leo a future versioni del driver Se si utilizza la risoluzione a 16 bit bisogna fare attenzione che la CPU memorizzi i campioni come il DSP della scheda audio, cio` coincida per e entrambi la codica big endian o little endian; in tal senso bisogna evitare di accedere ai campioni a 16 bit ciecamente come signed short Non bisogna darsi delle impostazioni di default di un device (anche perch` e potrebbero essere state modicate da un precedente processo); ad esempio, anche se il default per /dev/dsp ` 8 kHz/8 bit unsigned/mono, molte schedee non supportano la frequenza di campionamento di 8 kHz e si corre il rischio di ottenere solo rumore con le future schede audio a 24 bit; analogamente, non bisogna assumere per /dev/sequencer il clock di default di 100 Hz (Linux/Alpha ha un clock di 1024 Hz), mentre non ci sono valori di de- fault per /dev/music (bisogna sempre impostare per primi i parametri di temporizzazione) Non si devono scrivere programmi che funzionano solo a 16 bit, poich molte e schede vecchie sono a 8 bit: campioni da 16 bit riprodotti su queste danno luogo solo a rumore ad alto volume Non si deve dare per scontato che per ogni scheda audio ci sia /dev/mixer (non lo possiedono le pi` vecchie, o quelle non ancora pienamente support- u ate, o le schede audio completamente digitali); non tutti i mixer hanno un controllo di master volume (ma se ce lhanno bisogna tenere presente che questo inuenza il volume di tutti i canali), inoltre si deve sempre testare la presenza di un canale prima di cercare di indirizzarlo (ad esempio, non tutte le schede possiedono un sintetizzatore interno e/o una porta MIDI)15 21. 1.5. ANATOMIA DEL DRIVERCAPITOLO 1. LA SCHEDA AUDIO E OSS Non si deve usare il full duplex senza prima controllare che la scheda audio supporti tale modalit`a I device audio non devono essere tenuti aperti quando non sono richiesti, altrimenti altri programmi non vi possono accedere; in tal senso un pro- gramma dovrebbe gestire essibilmente le situazioni di EBUSY, ad esempio riprovando ad accedere dopo qualche tempo al device le1.5 Anatomia del driverOSS sfrutta il Direct Memory Access (DMA) per trasferire i campioni dalla schedaaudio a unopportuna area di RAM e viceversa: questa in genere non coincidecon il buer del processo che li elabora, per cui il driver deve copiare i campionidal/al buer DMA a/da questultimo13 .Nei PCcompatibili della copia se ne occupa la CPU, dal momendo che ilDMA Controller (DMAC) compatibile Intel 8237 ha dei pesanti limiti: non pu`oeettuare copie fra le porte di I/O o fra memoria e memoria; inoltre il buerDMA deve risiedere al di sotto dei primi 16 MB di RAM per le schede audioISA (non per le PCI), poich questo ` il limite di indirizzamento del bus, e devee eessere un blocco di memoria non frammentato che inizia e nisce nella stessapagina DMA. Questultima ha dimensione di 64 kB per i canali 03 a 8 bit e128 kB per i canali 57 a 16 bit: ci` rende dicile usare direttamente il buer olocale del processo come buer DMA con le schede ISA e pi` di 16 MB, poich u edovrebbe risiedere al di sotto del limite dei 16 MB; tuttavia i nuovi controller nelleperiferiche bypassano lIntel 8237 completamente (yby), per cui in particolaricondizioni si pu` arrivare a mappare il buer DMA allinterno dellarea dati del oprocesso (ci` ` sempre possibile con le schede PCI).oeLelaborazione dei campioni da parte del processo deve avvenire almeno unpo pi` velocemente del ritmo al quale il DMAC trasferisce i campioni, anch u enon ci siano pause in fase di campionamento o riproduzione. Se ci` si verica obisogner` usare una frequenza di campionamento inferiore allattuale, o usare un aformato audio pi` compatto per i campioni, in modo da ridurre la quantit` diu abyte trasferiti dal DMAC.Linux ha il problema di essere multiutente e multiprogrammato, per cuii processi competono per lutilizzo della CPU e un processo a pi` alta pri-uorit` potrebbe porre il processo che sfrutta i servizi di OSS in stato di waitaper diversi ms: in tal modo il tempo che questo ha per elaborare i campioni13Alcune schede possono autoiniziare il trasferimento senza attendere risposta dal driver,usando il DMAC in modalit` autorestart (ci` non ` supportato da tutti i sistemi operativi, ada oeesempio da BSD) 16 22. CAPITOLO 1. LA SCHEDA AUDIO E OSS1.5. ANATOMIA DEL DRIVERdiminuisce. In pratica ci deve essere abbastanza spazio nel buer DMA pergarantire loperativit` per il tempo di wait14 .a OSS gestisce il buer DMA con la tecnica del multibuering: in praticaquesto ` diviso in frammenti di uguale dimensione, per default calcolata dal driverein modo tale che la latenza15 sia attorno a 0.5s per la riproduzione e attorno a 0.1sper il campionamento. In tal modo ` possibile aumentare la dimensione del buer esenza inuire sulla latenza stessa poich il DMAC lavora solo su un frammento eper volta, mentre lapplicazione legge o scrive sul resto del buer. memoriawait Scheda audioKernel Processo 1111 0000 DMAC 1111 0000 bus ISA o PCI OSSbuffer audiodriver read() owrite() buffer DMA Figura 1.5: Schema di utilizzo del multibuering1.5.1Scrittura sul buer DMAIn fase di riproduzione, quando il programma chiama write() per la prima voltadopo lapertura del device, si vericano i seguenti eventi: il driver programma la scheda audio con i parametri di campionamentopredisposti (risoluzione, numero di canali e frequenza) di default ` calcolata la dimensione adeguata per un frammento, se tramite eunopportuna chiamata ioctl() non se ne ` stabilita unaltrae viene iniziato il riempimento del primo frammento del buer con i datipassati dalla write() se il primo frammento ` stato riempito completamente, il DMAC ne inizia eil trasferimento alla scheda audio14 Alcune schede dispongono di RAM locale per la riproduzione, ma prima che questa possaavvenire i campioni devono esservi trasferiti (per la Gravis UltraSound ci sono 256 kB percanale, ovvero sono coperti 2.9 s di suono continuo in modalit` 16 bit/stereo/44.1 kHz)a15 La latenza ` il tempo che il processo deve aspettare per avere accesso a un frammentoein campionamento o perch questo venga suonato in riproduzione quando il buer ` pieno;eeessa dipende dal data rate, che ` la quantit` di dati che il DMAC deve trasferire nellunit` die aatempo (per esempio, con un campionamento 16 bit/stereo/44.1 kHz il data rate ` 2 2 44.1 =e176.4 kB/s), nonch dalla dimensione del frammentoe 17 23. 1.5. ANATOMIA DEL DRIVER CAPITOLO 1. LA SCHEDA AUDIO E OSS il driver copia il resto dei dati nel buer, eventualmente riempiendo altri frammenti; se tutti i frammenti del buer sono stati riempiti il processo relativo al programma che ha chiamato la write() ` messo in stato di waite nch non ` libero almeno un frammento (condizione di overrun) e eAlle successive chiamate di write() i dati sono immagazzinati nel buersecondo la disponibilit` di frammenti liberi.aLoverrun si verica normalmente per un processo che scriva i campioni nelbuer pi` velocemente di quanto vengano riprodotti. Se al contrario il processo u` leggermente pi` lento a scrivere i campioni rispetto alla velocit` con la qualeeuasono riprodotti si verica la condizione di underrun, per uno dei seguenti motivi: lapplicazione ` troppo lenta nellelaborazione dei campioni (perch la CPUe e ` troppo lenta rispetto al data rate richiesto o ci sono troppi processi in e esecuzione che competono per la CPU) ci sono leggere variazioni nel tempo di CPU ricevuto (unapplicazione gen- eralmente ben funzionante pu` occasionalmente andare in underrun) o lapplicazione tenta di lavorare troppo in tempo reale (frammenti pi` piccoli u decrescono la latenza, tuttavia bisogna sempre scrivere altri campioni prima che il buer si svuoti)Un underrun provoca in genere un difetto udibile nel segnale riprodotto: pu`oessere una breve pausa, un click o la ripetizione di una parte del segnale(looping); se questultima si verica con frequenza uniforme si avvertir` un tonoasovrapposto al segnale riprodotto, con frequenza pari a quella con cui si vericalunderrun.1.5.2Lettura dal buer DMAIn fase di campionamento, quando il programma chiama read() per la primavolta dopo lapertura del device, si vericano i seguenti eventi: il driver programma la scheda audio con i parametri di campionamento predisposti (risoluzione, numero di canali e frequenza) di default ` calcolata la dimensione adeguata per un frammento, se tramitee unopportuna chiamata ioctl() non se ne ` stabilita unaltra e sono attivati il processo di campionamento da parte della scheda audio e il trasferimento dei campioni nel primo frammento del buer il processo ` messo in wait nch non ` riempito un numero di frammenti e e e che forniscono globalmente una quantit` di campioni maggiore o uguale aa quella richiesta 18 24. CAPITOLO 1. LA SCHEDA AUDIO E OSS1.6. SCHEDE AUDIO ISA E PCI i campioni richiesti sono copiati nel buer del processo; gli eventuali cam- pioni in pi` rimangono nel buer DMAuLe read() successive funzionano come sopra, senza che sia necessario ripredis-porre la scheda audio.Un overrun in campionamento si verica se il buer ` completamente riempito: ein tal caso gli ulteriori campioni sono scartati; le ragioni per cui si verica sonosimili a quelle per cui si verica in riproduzione.1.6Schede audio ISA e PCILapproccio seguito in questo lavoro ` di essere il pi` indipendenti possibile dal- eulhardware, in modo da poter creare dei programmi che girino su ogni piattaformaper cui ` stato portato OSS con tuttal pi` una semplice ricompilata del codice e usorgente. Tuttavia si vogliono elencare i motivi per i quali le schede audio PCIsono superiori alle schede audio ISA (al di l` della qualit` audio); al momento ina acui si scrive (1999) la quasi totalit` delle schede audio in commercio sono PCI, ma ano a un anno fa erano quasi tutte ISA: un musicista professionista dovrebbe pren-dere in considerazione lacquisto di una scheda audio PCI, anche se attualmenteOSS non supporta del tutto le nuove caratteristiche, come laudio 3D.Si elencheranno ora le dierenze fra ISA e PCI pertinenti le schede audio: Il bus ISA ha un clock nominale di 8 MHz, il che darebbe un throughput teorico di 16 MB/s; in realt`, a causa di overhead vari nella gestione dei seg-a nali, nonch il fatto che sono richiesti due cicli di clock per il trasferimentoe dei dati, il throughput si aggira attorno ai 5 MB/s. Se si guarda la quantit`a di dati da trasferire per una scheda che campiona a 16 bit/stereo/44.1 kHz (circa 176 kB/s) questo throughput appare adeguato, ma si pu` vericareo che il DMA blocchi laccesso della CPU al bus durante il trasferimento dei campioni o se la richiesta dinterrupt ` occupata (con ISA le IRQ non sono e condivisibili), il che pu` causare click nel suono in sistemi pesantementeo caricati. La capacit` di indirizzamento massima per ISA ` di 16 MB, per ae cui sono dicilmente applicabili le tecniche di allocazione del buer DMA nel buer del processo, che dovrebbe risiedere al di sotto di tale limite anche per sistemi con pi` RAM. Pu` inoltre risultare dicile la congurazione di uo una scheda audio, soprattutto con OSS/Free se ` PnP.e Le speciche PCI 2.1 consentono un clock sul bus no a 66 MHz, con un throughput teorico di 264 MB/s, mentre in pratica ci si aggira intorno ai 108 MB/s; con un clock sul bus di 33 MHz questi valori si dimezzano. Gli interrupt sono condivisibili, per cui con la tecnica dellinterrupt binding, se si vericano pi` interrupt contemporaneamente, questi vengono raggrup- u pati e serviti in base alla priorit` (e Linux distingue fra fast interrupt a quelli che richiedono un salvataggio del contesto parziale, come la richiesta19 25. 1.7. FILE SYSTEM E DEVICE FILECAPITOLO 1. LA SCHEDA AUDIO E OSSdi un servizio DMA e gli interrupt normali, con salvataggio completo delcontesto riducendo in tal modo loverhead). Il PCI consente il busmas-tering multiplo (con due busmaster), ovvero due arbitri nella gestione delbus, il che si traduce nellaccesso contemporaneo di CPU e DMA al bus sele aree di memoria interessate non coincidono; lindirizzamento ` a 32 bit,eper cui sono applicabili le tecniche di allocazione del buer DMA nel buer`del processo. E inoltre pi` semplice la congurazione delle periferiche PCI, uin quanto dopo il boot queste negoziano fra loro, in pratica autoallocandosi. Queste ed altre considerazioni, come la tendenza delle nuove schede audioad incrementare la frequenza di campionamento, spingono a concludere che unascheda audio PCI pu` risultare no a dieci volte pi` eciente di una scheda ISA. o u1.7File system e device leCome si ` avuto occasione di aermare precedentemente, OSS ` un driver che eefornisce al programmatore la possibilit` di gestire audio e MIDI tramite opportuni adevice le inseriti nella struttura dellalbero monolitico del le system di Linux.Ogni device le ` caratterizzato da un major number e da un minor number : ileprimo ` utilizzato come indice in una tabella del kernel per identicare il tipo edi driver che deve gestire un dispositivo hardware, il secondo ` passato al driver estesso per identicare lunit` su cui agire (classe del device).a In Linux il major number per OSS ` 14, ma per altri sistemi operativi potrebbeeessere diverso. Il minor number pu` essere codicato tramite un solo byte: inotal caso, come da gura 1.6, i quattro bit meno signicativi identicano la classedel dispositivo, mentre i quattro bit pi` signicativi identicano un dispositivo uallinterno di una stessa classe; ne consegue che ci possono essere no a sedicidispositivi dello stesso tipo per ogni classe. Tipo deviceClasse mixer 0 sequencer 1 midi27 4 3 0 dsp 3num. deviceclasseaudio 4 dspW5 1 byte sndstat 6 riservato 7 music 8 sndproc 9 Figura 1.6: Codica del minor number di un device le di OSS20 26. CAPITOLO 1. LA SCHEDA AUDIO E OSS 1.8. LE VERSIONI DI OSSAd esempio, /dev/midi00 identica il primo dispositivo di classe 2 (MIDI),per cui ` il numero del device ` 0: ci` implica che il minor number per il device lee e orelativo ` 0x02 (2 in decimale). Analogamente per /dev/midi01 il minor number eper il device le relativo sar` 0x12 (18 in decimale), per /dev/midi02 sar` 0x22a a(34 in decimale) e per /dev/midi03 sar` 0x32 (50 in decimale).aAnche se nelle odierne distribuzioni di Linux non ci dovrebbero essere problemidel genere, se un device le dovesse mancare ` possibile crearlo eettuando il login ecome root e dando il seguente comando:mknod -m c 14 ad esempio, il nome del device le potrebbe essere /dev/music e la classe sarebbe 8; ` un numero ottale che predispone i permessi di accesso al le, che epu` essere posto pari a 666 per accesso in lettura/scrittura da parte di tutti glioutenti (vedere man chmod).1.8Le versioni di OSSIl riconoscimento della versione di OSS in uso varia secondo che si stia utilizzandouna versione precedente o successiva alla 3.6. Ad esempio, la versione 3.5.4 disoundcard.h denisce le seguenti macro:#define SOUND_VERSION 350#define UNIX_SOUND_SYSTEMmentre nella versione 3.8.2 sono denite le seguenti altre macro:#define SOUND_VERSION 0x030802#define OPEN_SOUND_SYSTEMSOUND_VERSION contiene il numero di versione, con formato che varia secondo chesia denito UNIX_SOUND_SYSTEM o OPEN_SOUND_SYSTEM.Dalla versione 3.6 in poi ` possibile usare il seguente frammento di codice, cheeinterroga direttamente il driver per ricavare il numero di versione (` pi` adabile e uche ricavarlo da SOUND_VERSION):int versione;if (ioctl(dspfd, OSS_GETVERSION, &versione) == -1) {/* Versione precedente alla 3.6: errno==EINVAL */} Il seguente frammento di codice ricava i numeri di versione e release indipen-dentemente dalla versione di OSS utilizzata:int versione, release1, release2, tmpver;#ifdef UNIX_SOUND_SYSTEM 21 27. 1.8. LE VERSIONI DI OSSCAPITOLO 1. LA SCHEDA AUDIO E OSS versione = SOUND_VERSION / 100; release1 = (SOUND_VERSION - versione*100) / 10; release2 = SOUND_VERSION - versione*100 - release1*10;#else /* e definito OPEN_SOUND_SYSTEM */ if (ioctl(dspfd, OSS_GETVERSION, &tmpver) == -1) errore("OSS_GETVERSION"); versione = (tmpver & 0x00ff0000) >> 16; release1 = (tmpver & 0x0000ff00) >> 8; release2 = tmpver & 0x000000ff;#endif22 28. Capitolo 2IL MIXER E LA GESTIONEDEI CANALI2.1Descrizione di /dev/mixerNon tutte le schede audio possiedono un mixer: nella fattispecie possono nonaverlo le schede pi` vecchie, quelle non ancora pienamente supportate, quelleuprofessionali e le completamente digitali. Anche se il mixer ` mancante si pu` e osempre aprire /dev/mixer, ma uneventuale ioctl() ritorna errno==ENXIO.Nella Sezione 1.3 si ` fornito uno scheletro di codice C per lapertura e laechiusura di un device le; si ` scritto che fra la open() e la close() di questoeci possono essere delle read(), write() o ioctl(). /dev/mixer ` un device le eatipico, in quanto non accetta operazioni di read() o write() e lunica primitivautilizzabile ` ioctl(): infatti il mixer svolge solo un lavoro di gestione dei canali ecambiando la congurazione della scheda audio, praticamente non impiegandorisorse di calcolo per operare.A dierenza di /dev/dsp e /dev/audio, pi` di un processo alla volta pu` u oaprire /dev/mixer; generalmente si usa O_RDONLY come argomento di open().Le modiche eettuate alla congurazione del mixer permangono anche dopola chiusura dellultimo processo modicante, no a quando un eventuale altroprocesso non eettuer` nuovi cambiamenti o no al reboot del computer. Allatto adel boot ` il kernel che si occupa di congurare la scheda audio con dei valori edi default ragionevoli, ma che non dovrebbero comunque essere dati per scontatiper non creare programmi inadabili.Solo per il primo mixer, luso delle ioctl() per /dev/mixer su questo o su unaltro device di OSS sono equivalenti: ad esempio, se si ` aperto /dev/sequencer ` e einutile aprire anche /dev/mixer per variare la congurazione della scheda audio,basta usare gli ioctl() che si sarebbero utilizzati col secondo direttamente colprimo.`E importante vericare le capacit` del mixer prima di usarne i canali, in mo-ado da creare programmi che siano portabili per quasi tutte le schede audio. Le 23 29. 2.2. I CANALI DEL MIXERCAPITOLO 2. IL MIXER E LA GESTIONE DEI CANALIdierenze di comportamento riscontrate dovrebbero essere indicate nella docu-mentazione, evitando descrizioni troppo speciche nei confronti dei canali, chepossono avere caratteristiche diverse con schede diverse.2.2I canali del mixerPer i canali del mixer ci si pu` rifare alla metafora delle gure 1.1 e 1.2, per cui oun canale identica la classe del dispositivo (CD, microfono, . . . ) che a questo` connesso; il programmatore pu` eettuarne la selezione per la riproduzione oeoil campionamento, regolandone il livello di volume (se il dispositivo ` stereo ci esono due livelli di volume indipendentemente controllabili, il che consente di real-izzare il balance). Il volume principale pu` essere mancante (Gravis UltraSound,oMicrosoft Sound System).Sono deniti SOUND_MIXER_NRDEVICES canali, a cui ` associato un numero dae0 a SOUND_MIXER_NRDEVICES-1; un programma non dovrebbe cercare di accederea numeri di canale superiori a questultimo.OSS mette a disposizione del programmatore dei nomi simbolici per ognicanale; quelli deniti in soundcard.h versione 3.5.4 si trovano nella tabella dellaprossima pagina.Ad ogni canale ` associato un int, nella cui rappresentazione in binario (bit- emask ) ` posto a 1 il bit di posizione corrispondente al numero del canale; leebitmask sono utili con i comandi di gestione della congurazione del mixer.Sempre in soundcard.h sono deniti dei nomi simbolici da dare ai canalitramite le macro:#define SOUND_DEVICE_LABELS {"Vol ", "Bass ","Trebl", "Synth","Pcm ", "Spkr ","Line ","Mic ", "CD ","Mix ","Pcm2 ","Rec ", "IGain","OGain","Line1","Line2","Line3"}#define SOUND_DEVICE_NAMES {"vol", "bass", "treble", "synth", "pcm", "speaker", "line", "mic", "cd", "mix", "pcm2", "rec", "igain", "ogain", "line1", "line2", "line3"}la dierenza fra i due ` che il primo formato ` adatto per la stampa dei nomi ae evideo quali etichette dei canali, mentre del secondo formato ` pi` adatto lutilizzo e uquando i nomi dei device audio sono forniti sulla riga di comando di una shell.24 30. CAPITOLO 2. IL MIXER E LA GESTIONE DEI CANALI2.2. I CANALI DEL MIXER Nomi dei canali Bitmask associateDescrizione SOUND_MIXER_VOLUMESOUND_MASK_VOLUMELivello volume principale SOUND_MIXER_BASSSOUND_MASK_BASSRegolazione toni bassi principale SOUND_MIXER_TREBLESOUND_MASK_TREBLERegolazione toni acuti principale SOUND_MIXER_SYNTH SOUND_MASK_SYNTH Livello di uscita sintetizzatoreinterno (FM, wavetable); per alcuneschede ne controlla anche il livellodi campionamento SOUND_MIXER_PCM SOUND_MASK_PCM Livello di uscita di /dev/dspe /dev/audio SOUND_MIXER_SPEAKER SOUND_MASK_SPEAKER Livello di uscita del segnaleallaltoparlantino nel PC (seconnesso alla scheda audio);su altre schede pu` essere unogenerico ingresso mono conqualche altra funzione SOUND_MIXER_LINESOUND_MASK_LINELivello di ingresso per LineIn SOUND_MIXER_MIC SOUND_MASK_MIC Livello del segnale microfonico incampionamento o inviato a cue eLineOut; qualche volta il microfononon ` connesso a questo canale, ma ea un line level input della scheda SOUND_MIXER_CDSOUND_MASK_CDLivello del CD AudioIn SOUND_MIXER_IMIXSOUND_MASK_IMIXRecording monitor campionamento;durante tale fase, su alcune schedecontrolla il volume delle cue SOUND_MIXER_ALTPCMSOUND_MASK_ALTPCMLivello di un DSP secondario; nellaPro Audio Spectrum 16 ` il canale edellemulazione della Sound Blaster SOUND_MIXER_RECLEVSOUND_MASK_RECLEVLivello di campionamento per tuttii canali (nella Sound Blaster 16 sihanno solo quattro livelli possibili) SOUND_MIXER_IGAIN SOUND_MASK_IGAIN Livello di guadagno di ingresso SOUND_MIXER_OGAIN SOUND_MASK_OGAIN Livello di guadagno di uscita SOUND_MIXER_LINE1 SOUND_MASK_LINE1 Canale generico 1 (aux1); i codecAD1848 e compatibili hanno treline level input a cui diversicostruttori assegnano funzionidiverse, per cui tali nomi siusano quando il signicato precisodi un canale sico ` sconosciuto e SOUND_MIXER_LINE2 SOUND_MASK_LINE2 Canale generico 2 (aux2) SOUND_MIXER_LINE3 SOUND_MASK_LINE3 Canale generico 3 (line) 25 31. 2.2. I CANALI DEL MIXER CAPITOLO 2. IL MIXER E LA GESTIONE DEI CANALI Se in un programma si eettuano le assegnazioni:const char *nome_canale[] = SOUND_DEVICE_LABELS;const char *nome_cmline[] = SOUND_DEVICE_NAMES;allora, ad esempio, nome_canale[SOUND_MIXER_CD] corrisponde a CD , men-tre nome_cmline[SOUND_MIXER_CD] corrisponde a cd.Il mixer consente la selezione dei canali da cui eettuare il campionamento,che per buona parte delle schede avviene in mutua esclusione.Il canale di default dopo il boot ` quello del microfono, ma non dovrebbeeessere dato per scontato, poich il driver non altera le predisposizioni del mixerea meno di un comando da programma; un qualche altro processo dopo il bootpotrebbe averle modicate, con tali modiche che permangono anche dopo la suaterminazione.Linsieme di canali disponibili non ` sso, ma dipende dalla scheda audio; si epu` vericare che ai canali dello stesso chip mixer costruttori diversi assegnino ofunzioni diverse, per cui bisogner` vericarne caso per caso il reale signicato.aSarebbe meglio non includere funzionalit` di mixer nei programmi se si vuole ala massima portabilit` del proprio codice, demandandole a programmi specializ-azati per le varie schede audio. Nel caso si volesse realizzare un tale programmamixer bisogna ben documentare le sue capacit` se ` sviluppato per una precisa aescheda, altrimenti ` meglio evitare di essere troppo specici nella documentazioneeper non trarre in inganno gli utenti: potrebbero credere che la propria schedaaudio sia diversa da come ` realmente, basandosi su ci` che il programma fa e ovedere.2.2.1 Lettura della congurazioneCome ` stato visto sopra, converrebbe eettuare il controllo sia delle capacit`e adella scheda audio che dellesistenza o meno dei canali di interesse prima di in-traprendere qualsiasi altra azione; unoperazione di ioctl() fallita (ad esempio,perch il canale non esiste) ritorna -1 e errno==EINVAL.ePer eettuare la lettura della congurazione dei canali del mixer il codice `esimile per tutti i comandi a disposizione:int bitmask;if (ioctl(mixfd, SOUND_MIXER_READ_****, &bitmask) == -1) {/* Il mixer e mancante - errno==ENXIO */}ove SOUND_MIXER_READ_**** ` lidenticatore del comando di lettura che ritornaein bitmask una maschera di bit; questa pu` essere esaminata per determinare le ocapacit` di un canale o del mixer in base al comando dato. a26 32. CAPITOLO 2. IL MIXER E LA GESTIONE DEI CANALI2.2. I CANALI DEL MIXER Per i canali il controllo si pu` eettuare con:oif (bitmask & (1 8; Inversamente, per costituire una parola di volume:int volume;volume = (vol_destro 100) ? 100 : valore;}int main(int argc, char *argv[]){ int mixfd; mixer_info info; int canale, volume, num_canali = 0, num_camp = 0; int c_bmask, dev_bmask, canst_bmask, camp_bmask, catt_bmask; int caratteristiche[SOUND_MIXER_NRDEVICES][6]; const char *nome[] = SOUND_DEVICE_LABELS;/* Nomi dei canali */ const char *cm_nome[] = SOUND_DEVICE_NAMES; if ((mixfd = open("/dev/mixer", O_RDONLY)) == -1) errore("/dev/mixer");32 38. CAPITOLO 2. IL MIXER E LA GESTIONE DEI CANALI 2.6. ESEMPIO DI PROGRAMMA /* Dopo lapertura del device, raccolta di informazioni */ if (ioctl(mixfd, SOUND_MIXER_INFO, &info) == -1) errore("SOUND_MIXER_INFO"); if (ioctl(mixfd, SOUND_MIXER_READ_DEVMASK, &dev_bmask) == -1) errore("SOUND_MIXER_READ_DEVMASK"); if (ioctl(mixfd, SOUND_MIXER_READ_STEREODEVS, &canst_bmask) == -1) errore("SOUND_MIXER_READ_STEREODEVS"); if (ioctl(mixfd, SOUND_MIXER_READ_RECMASK, &camp_bmask) == -1) errore("SOUND_MIXER_READ_RECMASK"); if (ioctl(mixfd, SOUND_MIXER_READ_RECSRC, &catt_bmask) == -1) errore("SOUND_MIXER_READ_RECSRC"); for (canale=0; canale> 8; if (caratteristiche[num_canali][CAMPIONABILE]) num_camp++;/* Numero dei canali campionabili */ num_canali++; } } /* In base al numero di argomenti varia il comportamento del mixer */ switch (argc) {/* mixer invocato senza argomenti - visualizza informazioni */case 1: if (ioctl(mixfd, SOUND_MIXER_READ_CAPS, &c_bmask) == -1)errore("SOUND_MIXER_READ_CAPS");printf("nScheda %s, %d canali campionabili ", info.name,num_camp);(c_bmask & SOUND_CAP_EXCL_INPUT) ? puts("in mutua esclusione.") : puts("simultaneamente.");printf("nCanale NomeCampionabile " "Selezionato Tipo VolSx VolDxn");printf("--------------------------------" "--------------------------------n");for (canale=0; canale> 8); else printf("%d%n", volume & 0x000000ff); } break;/* Numero sbagliato di argomenti */default: printf("uso: %s [livSx%] [livDx%]n", argv[0]);}close(mixfd);return 0;}Di seguito ` riportato un esempio di utilizzo del programma: e$ mixer ?Canale non disponibile.$ mixer ? ? ? ? ?uso: mixer [livSx%] [livDx%]$ mixerScheda Sound Blaster, 4 canali campionabili simultaneamente.Canale Nome Campionabile Selezionato Tipo VolSx VolDx----------------------------------------------------------------Volvol NONO Stereo75 75Bass bassNONO Stereo75 75TrebltrebleNONO Stereo75 75Synthsynth SINO Stereo75 75Pcmpcm NONO Stereo75 75Spkr speaker NONOMono75Line lineSINO Stereo75 75Micmic SISIMono16CD cdSISI Stereo75 75IGainigain NONO Stereo75 75OGainogain NONO Stereo75 75$ mixer cdcd deselezionato.$ mixer mic 0Volume di mic a 0%$ mixer cd 75 40Volume di cd a 75% / 40%$ mixer cd 80Volume di cd a 80% / 80%$ mixer cdcd selezionato. 35 41. Capitolo 3CAMPIONAMENTO ERIPRODUZIONE3.1I device le audioNel Capitolo precedente si ` appreso come selezionare un canale e impostarne ilelivello di volume per il campionamento e la riproduzione. Linterfaccia di accessoal DSP ` rappresentata dai seguenti device le (possono essere link simbolici): e/dev/dsp codica 8 bit unsigned/dev/dspW codica 16 bit signed/dev/audio codica 8 bit Lawla frequenza di campionamento predenita ` di 8 kHz/mono (per /dev/audio e` lunica possibile), il canale predenito ` il microfono (con le solite avvertenzeeeriguardo i parametri di default). Un metodo alternativo ` di procedere predispo- enendo il formato opportuno per i campioni tramite ioctl() su /dev/dsp, maluso di uno di questi device le equivale a sceglierlo automaticamente.I device le dovrebbero essere aperti in readonly (O_RDONLY) o in writeonly (O_WRONLY) per motivi di ecienza di OSS; per le schede audio che nonsupportano il full duplex aprire in readonly il device le, richiudere e riaprire inwriteonly, e cos` via, ` pi` eciente dellapertura in readwrite (O_RDWR).e u3.2Il buer audioIl campionamento eettuato da una scheda audio ` uniforme; il risultato ` unaeesequenza di campioni (ampiezze del segnale in ingresso al canale negli istanti dicampionamento, opportunamente codicate), che costituisce una quantit` di bytea 36 42. CAPITOLO 3. CAMPIONAMENTO E RIPRODUZIONE 3.3. PARAMETRI DI CAMPIONAMENTOdipendente dalla risoluzione di campionamento (8 o 16 bit), dal numero di canalicoinvolti1 (mono o stereo) e dalla frequenza di campionamento.In generale, bisogna predisporre un opportuno buer per ospitare i campionida riprodurre o letti da un canale; un esempio di codice per dichiarare un buerdi dimensione DIM_BUF ` il seguente:econst int DIM_BUF = 4096;unsigned char audio_buffer[DIM_BUF];ove si presupponga di utilizzare /dev/dsp o /dev/audio, poich il buer ` dieeunsigned char.`E possibile ridurre loverhead nellI/O aumentando DIM_BUF, quindi passandopi` campioni nelle write() o richiedendone di pi` nelle read(). Buer pi` cortiu u udanno migliori risultati nel caso di elaborazione simultanea al campionamento deidati: per un uso normale2 una dimensione indicativa per DIM_BUF ` compresaefra 1024 e 4096 per un campionamento mono a 8 bit (tali limiti raddoppiano incaso di campionamento a 16 bit e quadruplicano se ` anche stereo).eSe il campionamento ` da un canale stereo i campioni per ambo i canali siepresentano alternati in sequenza nel buer con prima il canale sinistro (L) deldestro (R): LRLRLRLRLRLR. . . , ove L o R avranno dimensione di un byte se ilcampionamento ` a 8 bit o di due byte se il campionamento ` a 16 bit. Quantoe edetto no ad ora ` da tenere presente anche nel caso di riproduzione di campioni, eper cui il buer deve essere riempito con le stesse regole.Si deve decidere se in unapplicazione sia eettivamente il caso di campionarea 16 bit invece che a 8 bit: in generale ci sono molti casi in cui i primi non portanoa un miglioramento evidente della qualit` audio, a fronte invece di un raddoppio adei dati trasferiti dal DMA nel buer. Un modo per vedere quali dei due siameglio utilizzare (oltre lascolto diretto) ` di guardare i bit meno signicativi edei campioni: se pi` di quattro sono sempre nulli, il campionamento a 8 bit ` u epreferibile a quello a 16 bit.3.3 Parametri di campionamentoIl default per OSS ` un campionamento a 8kHz/mono/8 bit, ma ` ovvio che possa e enon essere soddisfacente per tutte le applicazioni. I parametri di campionamentoe riproduzione possono essere cambiati con delle opportune chiamate a ioctl()dopo lapertura del device audio, ma prima di qualsiasi read() o write() aquestultimo, altrimenti il comportamento della scheda audio ` indenito. Inoltreei parametri devono essere predisposti nel seguente ordine: risoluzione di campionamento (8 o 16 bit) 1La prossima generazione di schede audio (1999) sar` a 24 bit, con 4 o 6 canali di uscita perail surround; la massima frequenza di campionamento ` di 96 kHz (Ultra HiFidelity)e2Il normale ` qui inteso nel senso di utilizzare una frequenza di campionamento relativamente ebassa, ove il programma non abbia stringenti requisiti di elaborazione in tempo reale 37 43. 3.3. PARAMETRI DI CAMPIONAMENTO CAPITOLO 3. CAMPIONAMENTO E RIPRODUZIONE numero di canali (1 per mono, 2 per stereo) frequenza di campionamento (in Hz)utilizzando il seguente frammento di codice:int parametro = ;ioctl(dspfd, SNDCTL_DSP_****, &parametro);if ( /* parametro e molto diverso da */) {/* la scheda audio non puo supportare *//* il valore richiesto per parametro */}infatti il driver ritorna in parametro il valore che pu` supportare rispetto a quanto orichiesto; SNDCTL_DSP_**** ` uno dei seguenti:e Comandi AliasPer cambiare. . . SNDCTL_DSP_SAMPLESIZE SOUND_PCM_WRITE_BITS Risoluzione (8, 16) SNDCTL_DSP_SETFMT SOUND_PCM_SETFMT SNDCTL_DSP_CHANNELS SOUND_PCM_WRITE_CHANNELS Numero di canali (1, 2) SNDCTL_DSP_SPEEDSOUND_PCM_WRITE_RATE Frequenza (Hz)per scegliere se il campionamento deve essere mono o stereo esiste anche un altromodo: si usa SNDCTL_DSP_STEREO, con parametro posto a 0 se si vuole mono oa 1 se si vuole stereo.Al solito la ioctl() restituisce -1 e cambia errno se non ` stata in grado dieportare a termine lazione richiesta. Il programmatore dovrebbe inoltre vericarese parametro, dopo la chiamata a ioctl(), soddisfa i requisiti voluti3 .Per interrogare il driver riguardo il valore per un parametro di campionamento,con signicato analogo ai relativi comandi di predisposizione, si pu` usare iloseguente frammento di codice:int parametro;ioctl(dspfd, SOUND_PCM_READ_****, &parametro);ove SOUND_PCM_READ_**** ` uno fra i seguenti comandi: SOUND_PCM_READ_BITS,eSOUND_PCM_READ_CHANNELS e SOUND_PCM_READ_RATE.3.3.1Reset dei parametriDopo la prima read() o write() i parametri di campionamento non sono pi`umodicabili a meno di resettare il DSP interno alla scheda; il metodo per comu-nicare a OSS questazione ` il seguente:e 3Ad esempio, il divisore del clock interno alla scheda audio pu` non supportare una certaofrequenza di campionamento, per cui il driver ne sceglier` una il pi` vicino possibile; possonoa unon essere supportati anche il campionamento a 16 bit o i canali stereo per le schede pi` vecchieu 38 44. CAPITOLO 3. CAMPIONAMENTO E RIPRODUZIONE3.4. IL CAMPIONAMENTOioctl(dspfd, SNDCTL_DSP_****);ove SNDCTL_DSP_**** ` uno fra i comandi:eComandiAlias AzioneSNDCTL_DSP_SYNCSOUND_PCM_SYNCSvuota i buer e resettaSNDCTL_DSP_RESET SOUND_PCM_RESET Resetta senza svuotare i buerIl primo comando fa attendere lapplicazione nch lultimo byte scritto sul edevice ` stato riprodotto (il che pu` richiedere diversi secondi, in funzione della e oquantit` di campioni nel buer non ancora riprodotti); dopo il controllo ritorna aal programma chiamante. SNDCTL_DSP_SYNC ` invocata automaticamente dalla eclose() del device audio.Il secondo comando ferma immediatamente il device, ponendolo nello stato incui pu` essere ripredisposto, se in fase di riproduzione; in campionamento, dopoolultima read(), ` utile se non si ha intenzione di chiudere immediatamente iledevice, in modo che si prevenga il driver dalla visualizzazione di un messaggio dierrore non necessario riguardo la condizione di overrun in campionamento.Entrambe queste chiamate possono essere utilizzate per passare al volo dacampionamento a riproduzione; in tal caso il device audio dovr` essere apertoacon O_RDWR. Si ricorda tuttavia, per le schede audio che non supportano il fullduplex (vedi Sezione 3.8), che per passare da campionamento a riproduzione unagestione pi` eciente del buer di I/O ` ottenuta chiudendo il device (che eraueaperto in modalit` O_RDONLY) e riaprendolo nella modalit` O_WRONLY, e viceversa.aa3.3.2Pause nelle operazioniQualora sia necessario eettuare una pausa relativamente lunga nelloutput con-tinuo dei campioni, ` possibile informare di ci` il driver anch la gestione e o edi questevento sia eettuata in maniera pi` intelligente; ci` si eettua con iluoseguente comando:ioctl(dspfd, SNDCTL_DSP_POST);(lalias ` SOUND_PCM_POST) che ` una versione pi` leggera di SNDCTL_DSP_SYNC. e e uLe circostanze nelle quali questo comando risulta utile sono quelle in cui si `eriprodotta una serie di campioni (come un eetto sonoro in un gioco) e non si vuoleriprodurre immediatamente unaltra serie, oppure prima di iniziare operazionimolto lunghe (come lI/O da tastiera o da disco). Si veda la Sezione 3.10 perulteriori informazioni su questargomento.3.4 Il campionamentoPer campionare da un certo canale (che si suppone sia stato precedentementeselezionato tramite il mixer, nonch di cui sia stato ssato un certo livello di ecampionamento) si pu` usare il seguente frammento di codice: o39 45. 3.5. LA RIPRODUZIONECAPITOLO 3. CAMPIONAMENTO E RIPRODUZIONEint num_ric = , num_camp;if ((num_camp = read(dspfd, audio_buffer, num_ric)) == -1)errore("read()");ove num_camp ` il numero di byte che vengono eettivamente campionati rispettoeal numero richiesto num_ric; questultimo devessere un multiplo intero delladimensione in byte del campione (ci` fa funzionare meglio il buer interno usatoo nda OSS): num_ric = 2 , con num_ric 8) & 0x00ff);}write(dspfd, audio_buffer, DIM_BUF * 2); /* Riproduzione */ove DIM_BUF ` una dimensione per il buer secondo i suggerimenti della Sezione 3.2. eUno stralcio di codice che compie loperazione opposta ` invece:eread(dspfd, audio_buffer, DIM_BUF * 2);/* Campionamento */j = 0;for (i=0; i) {/* Il driver supporta tale capacita */}SOUND_PCM_GETCAPS ` un alias di SNDCTL_DSP_GETCAPS; ` unoe edegli identicatori seguenti:Identicatore Capacit` di. . . aDSP_CAP_DUPLEXfull duplex: bisogna abilitarlo prima, altrimenti il driverpotrebbe ritornare che non ` supportato (vedi Sezione 3.8) eDSP_CAP_REALTIMEtempo reale: lhardware o Linux hanno una precisioneattorno a un paio di campioni nel riportare la posizionedel puntatore al buer DMA in riproduzione usandoSNDCTL_DSP_GETOPTR; altrimenti essa ` precisaeattorno a un frammentoDSP_CAP_BATCH la scheda audio ha un buer locale in campionamentoe/o riproduzione: risulta inaccurata SNDCTL_DSP_GETxPTRDSP_CAP_COPROCcoprocessore programmabile (potrebbe essere un DSP):attualmente questo bit ` riservato per uso futuroeDSP_CAP_TRIGGER triggering: sincronizzazione fra il campionamento e lariproduzione o fra audio e MIDIDSP_CAP_MMAPsupporto di mmap(): accesso diretto al buer DMA inriproduzione e/o campionamento Per determinare la versione di questa chiamata si usa:int versione = cap & DSP_CAP_REVISION;in versione dovrebbe essere ritornato un numero tra 0 e 255; attualmente ` eritornato versione==1. 45 51. 3.7. IL TEMPO REALECAPITOLO 3. CAMPIONAMENTO E RIPRODUZIONE3.7.2 Gestione del buer DMACome ` stato visto nel Capitolo 1, il buer DMA agisce dietro le quinte pereadattare la velocit` di campionamento/riproduzione allI/O eettuato sui device ale dal processo che sfrutta i servizi di OSS. In base a ci` in casi normali nonoci si cura del numero di campioni scritti o letti, ma se ci sono stringenti requisitidi elaborazione in tempo reale, il driver pu` funzionare meglio se ` letto o scrittooeun frammento per volta.Per determinare la dimensione in byte di un frammento ` possibile usare ilecodice seguente:int dim_frammento;ioctl(dspfd, SNDCTL_DSP_GETBLKSIZE, &dim_frammento);Questa chiamata calcola la dimensione del frammento nel caso non fosse statofatto prima, per cui la si deve usare solo dopo aver predisposto i parametri dicampionamento, o esplicitamente la dimensione dei frammenti (nonch il loro enumero nel buer DMA) con la seguente:int frammento = 0xNNNNDDDD;ioctl(dspfd, SNDCTL_DSP_SETFRAGMENT, &frammento);ove frammento ha nella word pi` signicativa (NNNN) il massimo numero di fram- umenti voluti nel buer (minimo 2, massimo 0x7f), mentre nella word menosignicativa (DDDD) si ha codicato lesponente a cui si deve elevare 2 per averela dimensione in byte del frammento:min 16 byte (DDDD = 4)2DDDD =max dim. totale buer 2in frammento il driver ritorna i valori che esso ` stato capace di fornire rispetto ea quanto richiesto.Esiste unaltra chiamata, oramai obsoleta rispetto alla precedente, con loscopo di stabilire la dimensione/numero dei frammenti nel buer DMA; ques-ta richiede come parametro di ioctl() un valore di divisore, che consente dipredisporre la dimensione del frammento da un massimo di 4 kB a un minimodi 1 kB, se il kernel ` stato congurato per una dimensione del buer DMA die64 kB (il numero dei frammenti varia di conseguenza da 16 a 64). La chiamataha la forma:int div = ;ioctl(dspfd, SNDCTL_DSP_SUBDIVIDE, &div);ove SOUND_PCM_SUBDIVIDE ` un alias di SNDCTL_DSP_SUBDIVIDE, mentre epu` essere 1, 2, 4. oIl primo uso di read() o write() blocca il numero di frammenti nel buer ela loro dimensione; per cambiarli ` necessario chiudere e riaprire il device audio,e 46 52. CAPITOLO 3. CAMPIONAMENTO E RIPRODUZIONE3.7. IL TEMPO REALEper cui SNDCTL_DSP_SETFRAGMENT o SNDCTL_DSP_SUBDIVIDE dovrebbero essereposte subito dopo la open(). Non c` un metodo universale per calcolare la dimensione di un frammento,ea parte la prova sul campo; nella maggior parte dei casi potrebbe essere utile laseguente formula empirica:numero canali byte/campione fcdim. frammento [byte] =eventi/sove eventi/s ` il numero di eventi che bisognerebbe gestire nellunit` di tempo:e anella simulazione di uno strumento musicale questi potrebbero essere gli eventiMIDI, a cui bisogna prontamente rispondere con la sintesi dei campioni relativi,ad esempio, a un NOTE ON o a un NOTE OFF. Anche se con una macchinaveloce si pu` scendere alla dimensione minima di 16 byte, un frammento non odovrebbe essere cos` piccolo da rischiare di incorrere in condizioni di underrun:indicativamente con meno di 128 o 256 byte i frammenti diventano critici dagestire con una CPU lenta o con molti processi che competono per questa.Per ottenere informazioni sullevoluzione della situazione di I/O nel buer(frammenti e byte elaborati) si possono utilizzare le seguenti chiamate:audio_buf_info IO_info;ioctl(dspfd, SNDCTL_DSP_GETISPACE, &IO_info);per il buer di input, e analogamente per il buer di output:audio_buf_info IO_info;ioctl(dspfd, SNDCTL_DSP_GETOSPACE, &IO_info);SOUND_PCM_GETISPACE e SOUND_PCM_GETOSPACE sono alias rispettivamente diSNDCTL_DSP_GETISPACE e SNDCTL_DSP_GETOSPACE; la struct audio_buf_info` cos` composta:eint fragments numero di frammenti che possono essere letti/scritti senza bloc-care (sono esclusi i parzialmente riempiti); questo campo ` adabile soloese lapplicazione legge/scrive interi frammenti per voltaint fragstotal numero totale di frammenti allocati per il buerint fragsize dimensione di un frammento in byte (valore ritornato dalla chiama-ta SNDCTL_DSP_GETBLKSIZE)int bytes numero di byte che possono essere letti/scritti immediatamente sen- za bloccare, tenendo conto anche dei frammenti parzialmente riempiti; di conseguenza si pu` vericare che bytes>fragments*fragsize oqueste chiamate possono essere sfruttate per scrivere applicazioni non bloccanti.La dimensione del buer (secondo la chiamata, di input o di output) ` pari a eIO_info.fragstotal * IO_info.fragsize. 47 53. 3.7. IL TEMPO REALE CAPITOLO 3. CAMPIONAMENTO E RIPRODUZIONEPer avere informazioni sullevoluzione dinamica dei puntatori DMA al buersono disponibili due chiamate con forma analoga alle SNDCTL_DSP_GETxSPACE; ivalori ritornati hanno una precisione attorno a un paio di campioni in difetto,quando funzionano: infatti buer locali nelle schede audio e imprecisioni nel-lhardware possono contribuire a ridurre la precisione attorno a un frammento, einoltre alcuni sistemi operativi non consentono di ottenere i valori dei puntatoriDMA7 . Le chiamate sono:count_info cinfo;ioctl(dspfd, SNDCTL_DSP_GETIPTR, &cinfo);per il buer di input, e analogamente per il buer di output:count_info cinfo;ioctl(dspfd, SNDCTL_DSP_GETOPTR, &cinfo);ove le SOUND_PCM_GETIPTR e SOUND_PCM_GETOPTR sono alias rispettivamente diSNDCTL_DSP_GETIPTR e SNDCTL_DSP_GETOPTR; la struct count_info ` cos` com-e posta:int bytes numero di byte elaborati dallapertura del device; ` un valore preciso e se ` preciso il valore determinabile del puntatore DMAeint blocks numero di transizioni di frammento (interrupt hardware) dalla prece- dente chiamata a SNDCTL_DSP_GETxPTR (il valore di questo campo ` azzer- e ato dopo questultima ed ` valido solo se si usa laccesso diretto al buer e DMA); potrebbe essere usato per rilevare condizioni di underrunint ptr puntatore in byte alla posizione corrente nel buer DMA dal suo inizio;nel caso non sia possibile accedere al puntatore DMA attuale questo valore` troncato al bordo del frammentoebytes 1000 fornisce un timer (in ms) abbastanza preciso,numero canalibyte/campione fcin teoria: fatte salve le considerazioni riguardo alla disponibilit` dei puntatoriaDMA, la precisione ` compromessa dal vericarsi di condizioni di overrun, under- erun e dalle chiamate a SNDCTL_DSP_RESET, SNDCTL_DSP_POST e SNDCTL_DSP_SYNC.3.7.3I/O non bloccanteLa possibilit` di eettuare lI/O non bloccante sui device audio ` una caratter-aeistica fondamentale ove si voglia creare un sistema eciente di sintesi in temporeale: infatti quel tempo che il processo eventualmente avrebbe perso in stato diwait per ottenere la disponibilit` del device, pu` essere impiegato utilmente ina oaltre attivit`, ad esempio calcolando nuovi campioni. aEssenzialmente ci sono tre metodi per gestire lI/O non bloccante: 7Queste chiamate possono di conseguenza essere non portabili fra i vari sistemi operativi,n compatibili con tutte le schede audio e 48 54. CAPITOLO 3. CAMPIONAMENTO E RIPRODUZIONE3.7. IL TEMPO REALE uso della chiamata SNDCTL_DSP_NONBLOCK, per la riproduzione di piccolieetti audio uso delle chiamate a SNDCTL_DSP_GETxSPACE o a SNDCTL_DSP_GETxPTR, perpoter calibrare accuratamente la quantit` di byte da porre in I/O tramitearead()/write() uso della funzione di libreria select(), per eettuare un polling con timeout sui device audio Il primo metodo funziona solo in riproduzione, la read() ritorna sempre senzaaver eseguito il campionamento; nel usso di un programma pu` essere postaoovunque la chiamata:ioctl(dspfd, SNDCTL_DSP_NONBLOCK);ove SOUND_PCM_NONBLOCK ` un alias di SNDCTL_DSP_NONBLOCK; essa istruisce il edriver a far ritornare immediatamente la write() facendo trasferire nel buerDMA una quantit` massima di campioni pari allo spazio attualmente disponibile. aCi` implica che una sequenza troppo ravvicinata di write(), o la scrittura di otroppi campioni, potrebbe saturare il buer DMA8 ; in tal caso la scrittura cheprovoca questevento ` troncata secondo lo spazio disponibile nel buer, mentreele seguenti sono ignorate nch ` disponibile nuovo spazio libero.eeIl secondo metodo consiste nellottenere informazioni dal driver sullevoluzionedella situazione nel buer DMA; il trasferimento dei campioni pu` essere eseguito osecondo la quantit` di spazio disponibile nel buer: aint dspfd;unsigned char buf[DIM_BUF]; /* DIM_BUF qualunque */unsigned char *p_buf = buf;const unsigned char *p_fine_buf = buf + sizeof(buf);audio_buf_info ainfo;/* Apre /dev/dsp e riempie il buffer *//* del processo buf[] di campioni*/while (p_buf < p_fine_buf) {ioctl(dspfd, SNDCTL_DSP_GETOSPACE, &ainfo);if (ainfo.bytes) {/* Ce spazio disponibile */if (ainfo.bytes > (p_fine_buf - p_buf))ainfo.bytes = p_fine_buf - p_buf;write(dspfd, p_buf, ainfo.bytes); 8Questa chiamata ` quindi usata in quei casi in cui bisogna riprodurre piccoli eetti sonorieabbastanza distanziati luno dallaltro in termini temporali; labbastanza dipende dallafrequenza di campionamento, dallo spazio nel buer DMA e dalla quantit` di campioni scritti a49 55. 3.7. IL TEMPO REALECAPITOLO 3. CAMPIONAMENTO E RIPRODUZIONEp_buf += ainfo.bytes; } else { /* Fa qualcosaltro - il device e occupato */ }}Un altro modo consiste nel trasferire un frammento per volta:int dspfd, nfram = 0;unsigned char buf[DIM_BUF]; /* DIM_BUF dim. di un frammento */audio_buf_info ainfo;/* Apre /dev/dsp e predispone la dimensione *//* di un frammento a DIM_BUF*/while (nfram < NUM_FRAMMENTI) {ioctl(dspfd, SNDCTL_DSP_GETOSPACE, &ainfo);if (ainfo.fragments && ) {write(dspfd, buf, DIM_BUF); /* Scrive un frammento */nfram++;}else { /* Fa qualcosaltro - il device e occupato */ /* oppure non ci sono campioni in buf[] */}}ove NUM_FRAMMENTI ` il numero di frammenti di dimensione DIM_BUF da ripro-edurre. Gli esempi per il campionamento sono analoghi, sostituendo alla chiamataSNDCTL_DSP_GETOSPACE la SNDCTL_DSP_GETISPACE.In generale ` possibile aermare che la prima metodologia ` preferibile quandoe ebisogna trasferire una grande quantit` di campioni gi` pronti al buer DMA per a ala riproduzione, in quanto verranno generate meno interruzioni del usso delprogramma (viene copiata inizialmente una gran quantit` di dati, poi quantoabasta per tenere il buer pieno); la seconda metodologia ` preferibile quando iecampioni siano sintetizzati in tempo reale (specie se la dimensione dei frammentidiventa piccola), in quanto la prima metodologia potrebbe generare dei frammentisolo parzialmente riempiti, con uno sfruttamento non ottimale del buer DMA.Questi discorsi rimangono validi anche per il campionamento, con gli opportuniadattamenti del caso.La gestione di campionamento/riproduzione per frammenti impegna di pi` lauCPU di quanto faccia la prima metodologia, con questa tendenza che aumenta aldiminuire della dimensione dei frammenti; tuttavia essa garantisce al sistema unarisposta pi` pronta, a patto che lintero processo di consumazione/creazione di ucampioni sia abbastanza veloce da non generare condizioni di overrun/underrun. 50 56. CAPITOLO 3. CAMPIONAMENTO E RIPRODUZIONE3.7. IL TEMPO REALEIl terzo metodo implica lutilizzo di select() per eettuare il polling (bloc-cante o meno no a un determinato tempo massimo) sui device le di OSS alne di testare la disponibilit` alla lettura e/o scrittura oppure al ne di rilevarealinsorgere di eccezioni. Volendo sfruttare il timeout ` necessario includere glieheader le time.h e types.h; un esempio di utilizzo ` il seguente:eint retval;retval = select(fd+1, &readset, &writeset, &exceptset, &timeout);essa opera su degli insiemi di le descriptor, cio` pu` essere usata per controllaree opi` device le contemporaneamente: readset raccoglie quelli aperti in lettura,uwriteset quelli aperti in scrittura e exceptset quelli per i quali si voglia cat-turare linsorgere di uneccezione. Questultima ` pi` facile che insorga su le e udescriptor che si riferiscano a pipe o socket, non a device audio. Se uno o duedei tre insiemi sono mancanti, vi si pu` sostituire NULL. Nel seguente esempio diocodice ` mostrato come aggiungere il le descriptor dspfd, aperto in lettura, aereadset:fd_set readset;FD_ZERO(&readset); /* Azzera linsieme */FD_SET(dspfd, &readset); /* Aggiunge dspfd */nel caso dspfd sia aperto con O_RDWR, readset ` inuenzato solo nel caso diedisponibilit` di campioni nel buer di input, separato da quello di output; peratestare la disponibilit` alla scrittura su questultimo bisogner` aggiungere dspfd aaa un eventuale writeset.Quando un device le ` causa di un evento che fa ritornare select() laemacro FD_ISSET(filedes, &filedes_set) ritorna un valore diverso da zero; intal caso retval ` il numero di le descriptor che hanno cambiato stato alluscita eda select(), mentre fd ` il massimo fra i le descriptor dei tre insiemi. La macroeFD_CLR(filedes, &filedes_set) rimuove il le descriptor filedes dallinsiemefiledes_set.Il tempo massimo che select() attende per il vericarsi di un evento su unodegli insiemi ` stabilito dalla struct timeout come nel seguente esempio:estruct timeval timeout;timeout.tv_sec = 1;/* secondi*/timeout.tv_usec = 500; /* microsecondi */in questo caso select() attenderebbe al massimo 1.5 secondi prima di ritornare(con retval==0) non vericandosi un evento. In Linux la struct timeout ` emodicata alluscita con la quantit` di tempo non atteso: questo non ` il com-a eportamento della versione POSIX di select(), quindi per produrre programmiportabili bisogner` assumere che timeout abbia un valore indenito alluscita da aquesto, reinizializzandola prima del riutilizzo. Un NULL al posto di &timeout faattendere indenitamente il vericarsi di un evento sugli insiemi, mentre il polling` non bloccante ponendo:e51 57. 3.7. IL TEMPO REALECAPITOLO 3. CAMPIONAMENTO E RIPRODUZIONEtimeout.tv_sec = timeout.tv_usec = 0; Un esempio dellutilizzo di select() ` riportato a pagina 108.e3.7.4La sincronizzazionePer sincronizzazione si intende la possibilit` di fare andare in sincrono eventi aaudio con altri eventi, esterni o interni a OSS: ad esempio I/O da disco, eventiMIDI, campionamento e riproduzione.Prima di applicare le capacit` di sincronizzazione (triggering) del driver,abisogna controllare se queste siano supportate o meno testando DSP_CAP_TRIGGER,altrimenti il comportamento di OSS ` indenito. Nel caso particolare del full du- eplex, se campionamento e riproduzione devono andare in sincrono, bisogna testareanche DSP_CAP_DUPLEX come descritto nella Sezione 3.8.Per impostare il trigger si usa il seguente frammento di codice:int trigger = ;if (ioctl(dspfd, SNDCTL_DSP_SETTRIGGER, &trigger) == -1)errore("SNDCTL_DSP_SETTRIGGER");ove SOUND_PCM_SETTRIGGER ` un alias di SNDCTL_DSP_SETTRIGGER; la bitmaske ` una fra PCM_ENABLE_INPUT e PCM_ENABLE_OUTPUT, per abil-eitare rispettivamente campionamento e riproduzione. Un OR di queste due abili-ta contemporaneamente campionamento e riproduzione nel caso di full duplex,mentre se trigger ` posto a 0 vengono disabilitate entrambe; per disabilitarle esingolarmente si usano ~PCM_ENABLE_INPUT e ~PCM_ENABLE_OUTPUT.Un esempio di utilizzo ` di disabilitare la riproduzione, scrivere sul deviceeaudio dei campioni e riabilitarla, con write() che ritorna -1 (errno==EAGAIN)se il buer DMA si riempie; la read() ritorna sempre -1 (errno==EAGAIN) se ilcampionamento non ` abilitato. Dopo lapertura del device audio con O_RDWR ecampionamento e riproduzione sono abilitate per default.Per conoscere lo stato di abilitazione di campionamento e riproduzione `edisponibile la seguente chiamata:int trigger;ioctl(dspfd, SNDCTL_DSP_GETTRIGGER, &trigger);if (trigger & ) {/* Sono abilitati campionamento o riproduzione *//* secondo lidentificatore */}SOUND_PCM_GETTRIGGER ` un alias di SNDCTL_DSP_GETTRIGGER e e` uno fra gli identicatori PCM_ENABLE_INPUT, PCM_ENABLE_OUTPUT o un ORearitmetico di questi.Per sincronizzare campionamento e/o riproduzione con gli eventi MIDI out sipu` sfruttare la seguente chiamata: o 52 58. CAPITOLO 3. CAMPIONAMENTO E RIPRODUZIONE3.7. IL TEMPO REALEioctl(dspfd, SNDCTL_DSP_SETSYNCRO);ove SOUND_PCM_SETSYNCRO ` un alias di SNDCTL_DSP_SETSYNCRO; questa disat-etiva gli eventi audio, che vengono riattivati solo quando ` utilizzata la macroeSEQ_PLAYAUDIO(). Sfortunatamente questultima, pur essendo elencata in soundcard.h,non ` ancora stata implementata. e3.7.5 Accesso diretto al buer DMAMappare il buer DMA nellarea di memoria del processo ` una tecnica che pu` eoottimizzare i tempi del driver: soprattutto se i campioni sono stati prodotti da unalgoritmo di sintesi in tempo reale, pu` essere pi` eciente eliminarne la copiao udal/al buer del p