Caricare documenti e articoli online 
INFtub.com è un sito progettato per cercare i documenti in vari tipi di file e il caricamento di articoli online.


 
Non ricordi la password?  ››  Iscriviti gratis
 

Riconoscimento vocale

informatica



Riconoscimento

vocale




Indice :



Pag. 3 La lingua parlata


Pag. 7 Sistemi di riconoscimento




Pag. 8 Come si può introdurre la voce in un calcolatore?


Pag. 9 Premessa per la realizzazione del programma

I file WAVE

Il formato RIFF

Pag. 10 Il formato audio

Pag. 11 La struttura MMCKINFO

Panoramica sui device audio


Pag. 12 Come aprire un device per il playback

Il playback


Pag. 13 Descrizione programma


Pag. 14 La rappresentazione grafica dei dati

Descrizione funzioni essenziali

Funzioni programma


Pag. 16 Funzionamento

Listato


Pag.25 Considerazioni finali

Risultati

Tempi di realizzazione


Pag.26 Miglioramenti

Bibliografia









LA LINGUA PARLATA


L'apparato vocale umano può essere schematizzato come un tubo acustico in grado di variare la propria forma nel tempo eccitato da una sorgente di energia . Questo tubo acustico ha in genere la funzione di filtrante sul segnale alimentato dalla sorgente di energia , che può essere individuata nei polmoni.

Questo responsabile dei suoni cosiddetti vocalizzati utilizza le corde vocali; l'apertura creata da queste è in grado di aprirsi e chiudersi a un ritmo variabile dagli 80 ai 200 periodi al secondo ,producendo così un segnale acustico periodico di forma quasi triangolare. Il segnale ,passando attraverso il tubo acustico ,viene filtrato acquistando caratteristiche spettrali determinate dalla forma assunta dal tubo acustico stesso.


La sezione sagittale dell'apparato fonatorio può essere rappresentata come un tubo acustico cilindrico a sezione variabile eccitato da due diverse sorgenti; la forma del tubo varia con l'evolversi del fenomeno acustico.

La sorgente periodica (individuabile nelle corde vocali ,le quali aprendosi e chiudendosi ritmicamente trasformano il flusso d'aria proveniente dai polmoni in un segnale periodico) è responsabile dei suoni vocalizzati (per esempio vocali , nasali e liquide). Un altro tipo di eccitazione è dovuto alla sorgente casuale (la turbolenza dell'aria che si crea in prossimità di una occasionale ostruzione della cavità orale genera un segnale le cui caratteristiche sono quelle del rumore bianco) responsabile dei suoni non vocalizzati .


Le vocali sono suoni o FOMENI vocalizzati. Esistono però anche alcune consonanti che possono essere considerate vocalizzate. I suoni elementari o fonemi si contraddistinguono dunque dalla forma che assume il tubo acustico durante la loro produzione e dal tipo di eccitazione.

Il fonema è un'astrazione teorica: non esiste come entità a sé stante. Infatti la parole sono formate da sequenze di suoni e le frasi da sequenze di parole; quindi ,nel passaggio da un suono all'altro il tubo acustico passa da una configurazione alla successiva in modo continuo ,producendo intervalli di segnale le cui caratteristiche spettrali variano in modo molto rapido(transizioni).Almeno nelle sue porzioni transitorie ,la realizzazione fisica del fonema è fortemente dipendente dal contesto fonetico in cui è inserita. Ad esempio, la 949f55j /m/ nella parola amo si realizza in un segnale acustico differente dalla /m/ della parola uomini; per questo si parla di due diversi <allofoni> dello stesso fonema /m/.

Il fenomeno che prende il nome di coarticlazione, aumenta in modo notevole il numero effettivo di eventi acustici distinti di una lingua parlata. Si possono comunque definire eventi acustici elementari che risultano abbastanza indipendenti dal contesto. Questi elementi sono chiamati DIFONI e sono definiti come segmenti di segnale acustico che vanno dalla metà della parte stazionaria di un fonema fino alla metà della parte stazionaria del fonema successivo. Per ciò i difoni comprendono interamente la transizione fra due fonemi e, se per esempio una lingua prevede 30 fonemi, si possono avere 30 X 30 = 900 difoni.

Un'altra sorgente di variabilità del segnale vocale è dovuta alle differenze di pronuncia che si verificano sia fra parlanti differenti , sia nelle frasi e parole pronunciate in tempi differenti dalla stessa persona: questo implica una variabilità spettrale nella voce prodotta da diverse persone. Inoltre anche la voce prodotta da uno stesso soggetto rivela una notevole varianza nelle caratteristiche di un dato suono pronunciato in tempi diversi.

La voce deve essere considerata quindi un fenomeno casuale e il suo trattamento richiede l'uso di tecniche di tipo statistico.

Un metodo efficiente per visualizzare l'evoluzione spettrale del segnale vocale è costituito dal SONOGRAMMA.

Il sonogramma è una rappresentazione su tre assi ; tipicamente l'asse orizzontale rappresenta il tempo, quello verticale la frequenza e il terzo l'intensità.  





E' possibile quindi seguire l'andamento dei contributi energetici alle varie frequenze durante la pronuncia di una data frase; in particolare è molto significativo l'andamento delle tipiche striature orizzontali (chiamate formanti)che corrispondono alle frequenze di risonanza del tratto vocale e sono quindi direttamente correlate all'evoluzione della sua configurazione articolatoria.


Le tecniche di riconoscimento vocale richiedono l'uso di un calcolatore numerico come unità di elaborazione. Per introdurre la voce in un calcolatore si trasforma il segnale elettrico fornito da un microfono in una successione di numeri in codice binario ,direttamente utilizzabili da un calcolatore.

La possibilità di effettuare questa numerizzazione dei segnali ci viene garantita dal teorema del campionamento formulato da H.Nyquist negli anni venti. Questo teorema afferma che un segnale continuo può essere completamente rappresentato e perfettamente ricostruito attraverso una serie di misure , o campioni, effettuate sulla sua ampiezza a regolari intervalli di tempo. L'intervallo fra tali campioni non deve però essere superiore al semiperiodo della più alta frequenza presente nel segnale stesso.




Occorre inoltre considerare che l'informazione contenuta nella forma d'onda del segnale vocale è affettata da una notevole RIDONDANZA. Gran parte di tale informazione può essere eliminata mantenendo inalterate le caratteristiche del segnale che rendono i vari suoni percettivamente diversi: ne risulta un'onda con una configurazione più semplice sola l'informazione discriminante i vari eventi fonetici. Una rappresentazione di questo tipo viene indicata con il termine PATTERN.

Esistono diversi metodi per estrarre una configurazione del segnale vocale : uno di questi è il cosiddetto metodo delle BANDE CRITICHE.

Il segnale vocale , dal punto di vista delle caratteristiche spettrali , può essere ritenuto stazionario con buona approssimazione in intervalli dei millisecondi. Supponiamo quindi di suddividere il segnale in intervalli consecutivi della durata di 10 millisecondi che chiameremo finestre( frame in inglese).

Ciascuna finestra di segnale possiede ben determinate caratteristiche spettrali, rilevabili calcolandone lo spettro di energia.(lo spettro di energia è una curva dell'energia in funzione della frequenza. L'area di tale curva fra due valori di frequenza f1 e f2 è proporzionale al contributo energetico al segnale dell'intervallo .





Quindi ogni finestra può essere descritta mediante i vari contributi energetici in ciascuna banda critica. Per fare un esempio ,nell'intervallo fra 300 e 3400 hertz sono individuabili 13 bande critiche (la prima fra 300 e 430, l'ultima fra 2968 e 3400 hertz.

Calcolando quindi lo spettro di energia possiamo ricavare il contributo energetico di ciascuna delle 13 bande e avere una descrizione spettrale della finestra in questione sotto forma di una lista di 13 numeri. In questo modo abbiamo ottenuto la configurazione ad un0unica finestra .Man mano che il segnale si evolve nel tempo, otteniamo così una successione di liste ( o vettori) una ogni 10 millisecondi .Chiameremo questa successione << rappresentazione parametrica>>del segnale vocale.


SISTEMI DI RICONOSCIMENTO:


- per parole isolate

Questo sistema ,inventato nei laboratori della CSELT(centro studi e laboratori telecomunicazioni ) è un riconoscitore delle di parole pronunciate separatamente, vale a dire interponendo pause di silenzio fra di loro. I sistemi di riconoscimento per parole isolate (IWR)sono molto più semplici dal punto di vista realizzativo rispetto a quelli che riconoscono il parlato continuo. Infatti ,essendo le parole separate da pause, il loro inizio e la loro fine sono più facilmente individuabili e per di più non è presente la coarticolazione fra le parole stesse .

Generalmente la determinazione dell'inizio e della fine si basa su misure di ampiezza o di energia del segnale in successivi segmenti temporali. Tuttavia i problemi nascono quando il rumore ambientale è abbastanza elevato , tale da non permettere alle misure di ampiezza di distinguere tra i suoni vocali a bassa intensità e le pause prodotte dal parlante.

Inoltre il parlante stesso può provocare rumori indesiderati all'inizio e alla fine della pronuncia delle parole causate per esempio dalla apertura e chiusura delle labbra, da respiro, dai movimenti meccanici del microfono o da eventuali colpi di tosse. In tal caso è necessario utilizzare misure che rendano distinguibile la voce dagli altri rumori.

Per poter riconoscere delle parole ,il sistema deve possedere in memoria una loro descrizione. Questa descrizione è fornita alla nostra macchina sotto forma di prototipi di parole pronunciate dal potenziale utente.

E' quindi necessario aver fissato in precedenza un vocabolario, cioè l'insieme delle parole che la macchina dovrà riconoscere. Fatto questo l'utente dovrà pronunciare almeno una volta tutte le parole del vocabolario(fase di addestramento).

Le loro rappresentazioni parametriche verranno quindi memorizzate ed etichettate , e andranno a costituire l'insieme dei prototipi. Una volta che verrà pronunciata una parola del vocabolario, questa verrà riconosciuta effettuando un confronto fra la sua rappresentazione parametrica e tutti i prototipi memorizzati durante l'addestramento.

- parlato continuo

Un sistema in grado di riconoscere il parlato continuo, cioè senza pause fra le parole, deve risolvere problemi ulteriori rispetto ad un iwr. Non solo non conosce l'esatta identità delle parole contenute in una frase, ma neppure il loro numero, né l'istante di inizio e di fine di ciascuna di esse. Se fossimo in grado di riconoscere il punto di separazione , nel segnale vocale , fra una parola e la successiva , potremmo applicare la tecnica delle parole isolate sulle varie porzioni di segnale corrispondenti alle singole parole. In realtà non esiste alcuna tecnica che permetta di effettuare questa operazione in modo affidabile, anche perché la separazione fra parole non è facilmente definibile a causa del fenomeno della coarticolazione.


COME SI PUO' INTRODURRE LA VOCE IN UN CALCOLATORE?


Innanzitutto si deve poter disporre di un microfono. Il segnale in uscita da un microfono viene essenzialmente campionato ad intervalli regolari. Immaginiamo che il nostro segnale possa assumere valori di tensione compresi fra -5 e +5 volt. Suddividiamo quindi quest'intervallo in N intervalli più piccoli. Possiamo quindi costruire un quantizzatore che ad ogni campione associa un numero intero (compreso tra 1 e N) corrispondente all'intervallo di tensione al quale il campione appartiene. Il campione può quindi essere rappresentato da questo numero. Quanto più alto è N , maggiore è la precisione con cui viene rappresentato ciascun campione.

Tipicamente vengono utilizzati 4096 intervalli ; quindi ciascun campione può essere espresso da un numero binario di 12 cifre (2^12=4096).

Un'apparecchiatura che svolge tutte le operazioni che vanno dal campionamento alla rappresentazione numerica in codice binario di ciascun campione viene chiamata convertitore A/D(analogico/digitale).Mediante un convertitore A/D è quindi possibile inviare alla memoria di un calcolatore una rappresentazione numerica del segnale acquisito mediante un microfono; il teorema del campionamento formulato da H.Nyquist ci assicura che durante questa operazione (a parte le piccole inesattezze introdotte dalla quantizzazione ) non si ha perdita di informazione ,e pertanto possiamo trattare il segnale numerico come se fosse il segnale reale.



.


Premessa per la realizzazione del programma


Le estensioni multimediali di Windows consentono al programmatore due differenti approcci: uno d'alto livello più semplice da implementare e uno di basso livello, molto più ostico, ma che permette di accedere ad un'infinità di dettagli altrimenti non raggiungibili.

Ad alto livello vi è l'interfaccia MCI (MEDIA CONTROL INTERFACE) che offre un certo grado d'indipendenza dall'hardware, un'estrema semplicità di fondo e la possibilità di controllare virtualmente ogni tipo di device hardware.

A meno di dover implementare funzioni molto particolari, MCI è più che sufficiente.

Però quando si vuole realizzare una funzione in grado di effettuare il playback di un file sono necessarie le API a basso livello. Usare le API multimediali è molto più complesso che usare l'interfaccia MCI, ma in questo modo è possibile accedere ai dati del file WAV durante l'esecuzione.




I file WAV


I Waveform Audio File (WAV) sono diventati lo standard de facto per quanto riguarda il sonoro. Il principale svantaggio di questo formato riguarda le notevoli dimensioni che i file possono raggiungere.

Essi, infatti , salvano al proprio interno la rappresentazione digitale della forma d'onda. Questo fatto da un lato aiuta le applicazioni che devono accedervi , dall'altro aumenta la dimensione dei file stessi.

I file WAV, hanno una struttura interna nota come RIFF (Resource Interchange File Format).



Il formato RIFF


Il formato RIFF è una struttura tagged-file, nel senso che le informazioni sono raggruppate all'interno del file secondo una serie d'etichette.

Il blocco base di un file RIFF è detto chunk ed è composto dai seguenti campi:


Chunk Id, codice di quattro caratteri

Chunk Size, DWORD che specifica la dimensione del campo dati

Data Field, campo dati


In un RIFF il primo Chunk deve avere come ID il valore "RIFF" ed è l'unico che può avere al suo interno dei sotto chunk. In effetti tutti gli altri blocchi sono sottoelementi del chunk principale (vedi il Riquadro uno). Il RIFF oltre a tutti i campi di un chunk normale ha un'ulteriore sezione di quattro byte all'inizio dell'area dati.

Questo campo, detto Form Type, identifica il formato dei dati memorizzati nel file (vedi sempre il Riquadro 1). Nel caso di file con estensione WAV, il tipo è "WAVE".



Riquadro 1 - Esempio di HEXDUMP di un file WAV


Nelle prime locazioni di un file WAV sono riconoscibili gli identificatori dei chunk RIFF, WAVE, fmt e data.


000000 52 49 46 46 28 3E 00 00 57 41 56 45 66 6D 74 20 RIFF(>ùùWAVEfmt


000010 10 00 00 00 01 00 01 00 22 56 00 00 22 56 00 00 ùùùù" Vùù" Vùù


000020 01 00 08 00 64 61 74 61 04 3E 00 00 80 80 80 80 ùùdataùù°°°°


000030 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80




Il formato audio


La struttura che descrive il formato di un file audio è la seguente :


typedef struct WAVEFORMATEX;


wFormatTag è il tipo di formato; attualmente è definito il solo WAVE_FORMAT_PCM.

NChannels specifica il numero di canali e varrà 1 per un formato mono, 2 per un formato stereo.

Il campo nSamplePerSec è il sample rate mentre nAvgBytesPerSec indica il data rate cioè il numero medio di byte per secondi. nBlockAlign è l'unità di dati che può essere passata ad un driver ( ad esempio 1 byte per il formato mono con 1 byte per sample, 4 byte per un formato stereo con 2 byte per sample). Infine wBitsPerSample è il numeri di bit che compongono un campione(sample) e cbSize la dimensione della struttura WAVEFORMATEX.

Il formato più semplice è detto "telephone quality" (11Khz,mono). Altri formati possibili sono il 22k noto come "radio quality" e il 44K chiamato "CD quality". Entrambi possono essere sia mono che stereo. Come suggerito dalla sigla, il formato 11K può risultare sufficiente per applicazioni vocali, mentre è indispensabile passare almeno al 22K per registrare della musica.

La struttura WAVEFORMATEX consente di memorizzare il formato da usare per suonare o registrare un file. Naturalmente nel caso del playback le informazioni relative al formato sono custodite all'interno del file stesso; bisogna, quindi, posizionarsi sul campo fmt del file , leggere la chunk e riempire la struttura.

I dati sono naturalmente memorizzati all'interno del file in conformità del formato scelto.

Nel caso di un formato mono a 8 bit, i sample sono costituiti da byte posti in successione. Nel caso di un formato stereo a 8 bit, i byte del formato stereo sono intercalati con quelli del canale sinistro, nel caso, infine, di un formato a 16 bit un sample sarà formato da un byte basso seguito da un byte alto( vedi Figura 1).


Figura

Memorizzazione dei samples a seconda del tipo di formato.  



La struttura MMCKINFO


Le estensioni multimediali di Windows comprendono numerose strutture dati. La più importante è MMCKINFO definita come nel listato 1.

Il tipo FOURCC rappresenta un codice di 4 caratteri che la funzione mmioStringToFOURCC ottiene a partire da una stringa. Per impostare un ID con il valore "WAVE" è sufficiente scrivere :


chunkID : FOURCC;

ChunkID:= mmioStringToFOURCC('WAVE",0);


Listato 1 - La struttura MMCKINFO


Typedef struct _ MMCKINFO

MMCKINFO;



Panoramica sui device audio


I device audio sono risorse esclusive; quindi non è detto che siano assegnate a un programma tutte le volte che ne fa richiesta. E' possibile che un altro programma lo abbia richiesto e non ancora rilasciato.

Per questo motivo è molto importante che ogni applicazione rilasci il più presto possibile le risorse che acquisisce. Le estensioni multimediali di Windiws mettono a disposizione quattro funzioni per la gestione dei device audio, due per il playback (waveOutOpen e waveOutClose).




Come aprire un device per il playback



Aprire un dispositivo per il playback significa invocare la funzione waveOutOpen() che ha il seguente prototipo :


UINT waveOutOpen (lpWavwOut,wDeviceID, lpFormat, dwInstance, dwFlag);


lpWaveOut è un buffer che la funzione riempirà con l'handle al device e che sarà usato in tutte le successive chiamate. wDeviceID identifica il dispositivo da aprire: usando la costante WAVE_MAPPER si obbliga la funzione a ricercare un device capace di supportare il formato dato.

lpFormat è un puntatore alla struttura WAVWFORMATEX, che contiene tutte le informazioni relative al formato.

dwCallBack specifica l'indirizzo di una funzione callback o l'handle di una finestra a cui il device potrà mandare dei messaggi. Attraverso dwFlags, infine, è possibile indicare le modalità di apertura del device. La costante WAVE_FORMAT_QUERY, per esempio, interroga semplicemente il device, per capire se supporta o meno il formato in questione.

Il flag CALLBACK_WINDOE, invece, apre il dispositivo e specifica che il parametro dwCallback è un handle di finestra.

Vediamo due esempi d'uso di questa funzione :


waveOutOpen(nil,lpWaveFormat,nil ,nil, WAVE_FORMAT_QUERY)


La riga di sopra specifica se il formato è riconosciuto.


Come si vede, in questo caso molti parametri non sono significativi e sono impostati a nil.

Il seguente frammento di codice, invece, apre il dispositivo per il playback.


WaveOutOpen( @WaveOut, WAVE_MAPPER, @format, hwnd, 0, CALL_BACK_WINDOW);


Il programma principale, una volta avviato il device, può terminare la sua esecuzione.

La riproduzione del file musicale avverrà in background ,in maniera asincrona. Il device invia dei messaggi che permettono di monitorare l'andamento delle operazioni sul file musicale.

Nel caso del playback i messaggi inviati sono :


WOM_OPEN

WOM_CLOSE

WOM_DONE   


Essi vengono inviati rispettivamente quando il device viene aperto, quando viene chiuso e quando ha terminato di eseguire il playback di un dato blocco dati.

Solitamente non è necessario intraprendere alcuna azione in risposta ai primi due messaggi, mentre nel terzo caso dovrebbe chiudere device e file.



Il playback


Il playback consiste nell'inviare al dispositivo i dati letti dal file. Anche per queta operazione esiste un'apposita funzione con il seguente prototipo :


MMRESULT waveOutWrite (HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh );


dove hwo è l'handle restituito dalla funzione waveOutOpen.

Il parametro pwh è un puntatore alla struttura WAVWHDR con le informazioni relative al blocco che si sta spedendo al device e cbwh è la dimensione della struttura.

Diamo un'occhiata alla struttura


Typedef struct

WAVEHDR


La struttura deve essere inizializzata prima dell'invio al device. Anche in questo caso esiste una funzione che svolge il particolare compito.

MMRESULT waveOutPrepareHeader ( HWAVEOUT LPWAVEHDR pwh, UINT cbwh);


Vediamo un piccolo esempio di utilizzo :


lpWaveHdr.lpData := lpData;

lpWaveHdr.dwBufferLength := dwDataSize;

lpWaveHdr.dwFalgs := 0;

lpWaveHdr.dwLoops := 0;

waveOutPrepareHeader( hWaveOut, lpWaveHdr, sizeof(WAVEHDR));


In lpData si copiano i dati del file WAV e in dwBufferLength la loro dimensione.

Una volta effettuate tutte queste operazioni, può avere inizio il playback attraverso la funzione waveOutWrite. Quest'ultima restituisce il controllo immediatamente, mentre il device comincerà a suonare il file in background.


Forma d'onda relativa al file

 


Descrizione programma :


Apri file

 







La rappresentazione grafica dei dati


Per rappresentare in modo grafico i dati vi è un certo numero di problemi da affrontare.Il primo di questi è rappresentato dalla necessità di sincronizzare il device che effettivamente produce il suono con la grafica. Per ottenere ciò, la funzione di playback attiva un timer, che periodicamente scatta e chiama la funzione Draw() che produce l'output grafico. Un altro problema è il formato scelto. Supportando un unico formato la routine grafica è molto semplice, ma in un programma commerciale dovrebbe essere in grado di adattarsi a tutti i tipi. I dati da rappresentare sono molto numerosi e bisogna fare attenzione a non disegnare una serie confusa di punti sul video. Una possibile soluzione è quella di integrarli, cioè sommare fra loro il valore di più punti e dividere il risultato per il numero di punti considerato. In sostanza, si sostituisce un certo numero di punti con la media dei loro valori. Quando l'output ha raggiunto il margine destro dello spazio riservato, si cancella tutto e si ricomincia dal bordo sinistro, creando un effetto simile a quello di un oscilloscopio. Più si spinge l'integrazione dei dati (cioè più punti si accorpano), più aumenta la persistenza della grafica sul video, creando un gradevole effetto; al tempo stesso, però, si perdono informazioni sulla forma d'onda da rappresentare.



Descrizione funzioni essenziali :


Apri file : viene utilizzato per selezionare il file da riprodurre

Riproduci file : riproduce per intero il file selezionato

Continua avanzamento : il file selezionato viene riprodotto un pezzo alla volta in modo da rendere meglio visibile la sua forma d'onda

Blocca avanzamento : blocca la riproduzione del file

Risultato riproduzione : viene mostrato il risultato dell'operazione, e in particolare se questo valore è uguale a zero la riproduzione è avvenuta correttamente, mentre in caso contrario si sono verificati degli errori.



Funzioni programma :


Le funzioni presenti nel file principale tstWave.pas sono:



TForm1 = class(TForm)


ResultEdit: TEdit; 

label dove viene inserito il risultato della riproduzione


OpenDialog1: TOpenDialog;

componente utilizzato per selezionare i file da aprire


FileEdit: TEdit;

label in cui viene inserito il nome del file selezionato


Wave1: TWave;

apre il componente twave


apri: TSpeedButton;


suona: TSpeedButton;


ferma: TSpeedButton;


esci: TSpeedButton;


continua: TSpeedButton;


procedure TForm1.apriClick(Sender: TObject);

Viene utilizzata per selezionare il file audio da riprodurre.


procedure TForm1.suonaClick(Sender: TObject);

Richiama la funzione per la riproduzione completa del file


procedure TForm1.fermaClick(Sender: TObject);

Richiama la funzione per bloccare la riproduzione


procedure TForm1.esciClick(Sender: TObject);

Chiude l'applicazione.

procedure TForm1.FormCreate(Sender: TObject);

Crea la form principale e inizializza le variabili.

procedure TForm1.continuaClick(Sender: TObject);

Chiama la funzione per disegnare il grafico a blocchi


Le funzioni presenti nel file principale Wave.pas sono:


procedure DrawBackGround;

Disegno sfondo


procedure WMTimer(var Message: TWMTimer); message WM_Timer;

Timer


procedure Stop(var Msg: UINT); message WOM_DONE;

Termina riproduzione


constructor Create(AOwner: TComponent); override;

Costruttore


procedure Paint; override;

Disegno lo sfondo


procedure Draw;

Disegno la forma d'onda completa


procedure Draw_pezzi;

Disegno la forma d'onda a pezzi


procedure SetFileName(Value: TFileName);

Seleziona il file da riprodurre


function Play: Integer;

riproduce il file selezionato  


procedure PlayStop;

ferma riproduzione


procedure ric;

Funzione per il riconoscimento vocale.


Funzionamento


Una volta selezionato il file da riprodurre e iniziata la riproduzione viene attivato il playback, invocando la funzione Playing(). Il playback apre il file di interesse con la funzione mmioOpen(), si accerta tramite mmioDescend() che il chunk RIFF contenga i caratteri "WAVE" e rintracciare il chunk fmt per leggere il formato con cui il file è stato memorizzato. Il passo successivo è quello di aprire il device in output con waveOutOpen(), passandogli un puntatore alla struttura dati appena riempito con i valori del formato. Come ulteriori parametri è necessario passare l'handle della finestra corrente e il flag WINDOW_CALLBACK in modo che il device possa inviare messaggi. A questo punto bisogna individuare il chunk data nel file WAV e ricavare i dati da inviare al device. La funzione waveOutPrepareHeader() serve per preparare i dati alla spedizione e waveOutWrite() per l'effettivo invio. Una volta invocata quest'ultima funzione, il device inizia a suonare il file in background, cioè in maniera asincrona rispetto al programma. Il playback continua fino a che

l'utente non lo interrompe (con la funzione PlayStop) oppure fino a che il device non termina i dati a disposizione. In quest'ultimo caso esso invia il messaggio WOM_DONE. Delphi permette di intercettare i messaggi di sistema nel modo seguente:


procedura stop(var Mag: UINT);

message WOM_DONS;


La funzione Stop chiama PlayStop( ) che chiude il file con mmioClose e il device con waveOutClose.



Listato


Listato file wave.pas


unit Wave;


interface


uses

Windows, MMSystem, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;


const

FOURCC_WAVE = $45564157;

FOURCC_FMT = $20746D66;

FOURCC_DATA = $61746164;

IDLE = 0;

PLAYWAVE = 1;

RECORDWAVE = 2;


type

TWave = class(TCustomControl)


private

FFileName: TFileName;

hmmio: HMMIO;

mmckinfoParent: TMMCKINFO;

mmckinfoSubchunk: TMMCKINFO;

mmckinfo: TMMCKINFO;

mmckinfoSub: TMMCKINFO;

hData: HGLOBAL;

hWaveHdr: HGlobal;

lpData: Pointer;

lpWaveHdr: PWaveHdr;

dwDataSize: LongInt;

hWaveOut: HWaveOut;

lpWAveOut: PHWaveOut;

hWaveIn: HWaveIn;

lpWaveIn: PHWaveIn;

Format: TWaveFormatEx;

pFormat: PWaveFormatEx;

scanx: Integer;  // index scan del file

curx: Integer;

hInBuffer: Integer;

InBuffer: Pointer;

blkSize: Integer; // block size

procedure DrawBackGround;



procedure WMTimer(var Message: TWMTimer); message WM_Timer;

procedure Stop(var Msg: UINT); message WOM_DONE;


public

status: Integer;

ind,l,ia:integer;

pippo : array [1..100000] of LongInt;

constructor Create(AOwner: TComponent); override;

procedure Paint; override;

procedure Draw;

procedure Draw_pezzi;

procedure SetFileName(Value: TFileName);

function Play: Integer;

procedure PlayStop;

property FileName: TFileName read FFileName write SetFileName;

procedure ric;

end;


//procedure Register;


implementation





constructor TWave.Create(AOwner: TComponent);

begin

inherited Create(AOwner);

Width:= 320;

Height:= 120;

status := IDLE;

end;





procedure TWave.Paint;

begin

DrawBackground;

end;





procedure TWave.SetFileName(Value: TFileName);

begin

if FFileName <> Value then

begin

FFileName := Value;

end;

end;





function TWave.Play: Integer;

var

dwFmtSize: LongInt;

hFormat: HLocal;

ret: Integer;

hwnd: DWORD;


begin

ind:=0;

//lung:=0;

ia:=0;

l:=0;

// -------- ----- ------ ----

// apre il file


hmmio := mmioOpen(PChar(FileName), nil, MMIO_READ or MMIO_ALLOCBUF);

if hmmio = 0 then

Result := 1;



// controlla che si tratti di un file wave


mmckinfoParent.fccType := FOURCC_WAVE;

if mmioDescend(hmmio, @mmckinfoParent, nil, MMIO_FINDRIFF)

<> MMSYSERR_NOERROR then

begin

mmioClose(hmmio, 0);

Result:= 2;

end;


// -------- ----- ------ ----

// trova il "fmt" chunk

// -------- ----- ------ ----

mmckinfoSubchunk.ckid := FOURCC_FMT;

if mmioDescend(hmmio, @mmckinfoSubchunk, @mmckinfoParent,

MMIO_FINDCHUNK) <> MMSYSERR_NOERROR then

begin

mmioClose(hmmio, 0);

Result:= 3;

exit;

end;



// ottiene la dimensione del "fmt" chunk

// -------- ----- ------ ----

dwFmtSize := mmckinfoSubchunk.cksize;


// -------- ----- ------ ----

// alloca memoria per esso


hFormat := LocalAlloc(LMEM_MOVEABLE,LOWORD(dwFmtSize));

if (hFormat = NULL) then

begin

mmioClose(hmmio,0);

Result:= 4;

exit;

end;



// chiude la memoria

// -------- ----- ------ ----

pFormat := LocalLock(hFormat);

if pFormat = nil then

begin

LocalFree(hFormat);

mmioClose(hmmio,0);

Result:= 5;

exit;

end;


// -------- ----- ------ ----

// legge il "fmt" chunk


if (mmioRead(hmmio, PChar(pFormat), dwFmtSize) <> dwFmtSize) then

begin

LocalFree(hFormat);

mmioClose(hmmio,0);

Result:= 6;

exit;

end;

blkSize:= pFormat.nSamplesPerSec;


// -------- ----- ------ ----- ----- ---------------

// apre un device waveform per l'output - usando il window callback

// -------- ----- ------ ----- ----- ---------------

ret := waveOutOpen(@hWaveOut,WAVE_MAPPER, pFormat, Handle, 0, CALLBACK_WINDOW);

if ret <> MMSYSERR_NOERROR then

begin

Result:= 7;

exit;

end;


// -------- ----- ------ ----

// cerca "data" chunk


mmckinfoSubchunk.ckid := FOURCC_DATA;

if mmioDescend(hmmio,@mmckinfoSubchunk,@mmckinfoParent,MMIO_FINDCHUNK)

<> MMSYSERR_NOERROR then

begin

Result:= 8;

exit;

end;



// ottiene la dimensione del "data" chunk

// -------- ----- ------ ----

dwDataSize := mmckinfoSubchunk.cksize;

if dwDataSize = 0 then

begin

Result:= 9;

exit;

end;



// alloca memoria per esso


hData := GlobalAlloc(GMEM_MOVEABLE or GMEM_SHARE,dwDataSize);

if hData = NULL then

begin

mmioClose(hmmio,0);

Result:= 10;

exit;

end;



// chiude la memoria

// -------- ----- ------ ----

lpData := nil;

lpData := GlobalLock(hData);

if lpData = nil then

begin

GlobalFree(hData);

mmioClose(hmmio,0);

Result:= 11;

exit;

end;


// -------- ----- ------ ----

// legge il waveform del subchunk dei dati


if mmioRead(hmmio,lpData, dwDataSize) <> dwDataSize then

begin

Result:= 12;

exit;

end;


// -------- ----- ------ ----

// alloca memoria per header

// -------- ----- ------ ----

hWaveHdr := GlobalAlloc(GMEM_MOVEABLE or GMEM_SHARE, sizeof(TWAVEHDR));

if hWaveHdr = NULL then

begin

GlobalUnlock(hData);

GlobalFree(hData);

Result:= 13;

exit

end;


// -------- ----- ------ ----

// lo chiude

// -------- ----- ------ ----

lpWaveHdr := nil;

lpWaveHdr := GlobalLock(hWaveHdr);

if lpWaveHdr = nil then

begin

GlobalUnlock(hData);

GlobalFree(hData);

Result:= 14;

exit

end;


// -------- ----- ------ ----

// Setta header


lpWaveHdr.lpData := lpData;

lpWaveHdr.dwBufferLength := dwDataSize;

lpWaveHdr.dwFlags := 0;

lpWaveHdr.dwLoops := 0;

ret := waveOutPrepareHeader(hWaveOut, lpWaveHdr, sizeof(TWAVEHDR));



// manda il blocco di dati al dispositivo di output

// -------- ----- ------ ----

ret := waveOutWrite(hWaveOut, lpWaveHdr, sizeof(TWAVEHDR));

if (ret <> 0) then

begin

waveOutUnprepareHeader(hWaveOut,lpWaveHdr,sizeof(TWAVEHDR));

GlobalUnlock(hData);

GlobalFree(hData);

Result:= 15;

exit;

end;

scanx:= 0;

curx:= 0;

DrawBackground;

SetTimer(Handle,1,1000,nil);



// rilascia la memoria

// -------- ----- ------ ----

LocalFree(hFormat);


// -------- ----- ------ ----

// aggiorna status


status := PLAYWAVE;

Result:= 0;


end;





procedure TWave.WMTimer(var Message: TWMTimer);

begin

Draw;

end;





procedure TWave.PlayStop;

begin

// Close playback device

mmioClose(hmmio,0);

waveOutUnprepareHeader(hWaveOut,lpWaveHdr,sizeof(TWAVEHDR));

waveOutClose(hWaveOut);

waveOutReset(hWaveOut);

GlobalUnlock(hData);

GlobalFree(hData);

KillTimer(Handle, 1);

status:= IDLE;

l:=1;

scanx:=1;

end;



// Stop --> blocca la riproduzione //


procedure TWave.Stop(var Msg: UINT);

begin

PlayStop;

end;





procedure TWave.DrawBackground;

begin

Canvas.Brush.Color := clBlack;

Canvas.FloodFill(Width div 2,Height div 2,clRed,fsBorder);

Canvas.MoveTo(0,Height div 2);

Canvas.Pen.Color := clGreen;

Canvas.LineTo(Width, Height div 2);

end;





procedure TWave.Draw;

var

cury: Integer;

curx: Integer;

intData: LongInt;

k: Integer;

ysil: Integer;

done: boolean;

fi: Integer; // Fattore di integrazione

ff: single; // fattore di forma

app: longInt;

nDataBytes: Integer;


begin

// Silenzio

curx:= 0;

ysil:= Height div 2;


// Valuta fattore di integrazione

fi:= blkSize div Width;


// Valuta fattore di forma

ff:= Height/256;


// Draw Background

DrawBackground;


done:= false;

while (done = false) do

begin

intData:=0;

for k:=1 to fi do

begin

//


ia:=ia+1;

//pippo[ia]:= Ord(lpWaveHdr.lpData[scanx]);


app:=intData;

//


intData:= intData + Ord(lpWaveHdr.lpData[scanx]);


scanx:= scanx+1;


if ia < 100000 then

pippo[ia] := intdata - app;

end;


intData:= (Trunc(intData / fi * ff));

intData:= Abs(IntData - ysil);


for cury:= ysil-IntData to ysil + IntData do

Canvas.Pixels[curx,cury] := clGreen;

curx:= curx+1;

if curx >= width then

done := True;

end;


end;






procedure TWave.Draw_pezzi;

var

cury: Integer;

curx: Integer;

intData: LongInt;

k: Integer;

ysil: Integer;

done: boolean;

fi: Integer; // Fattore di integrazione

ff: single; // fattore di forma

nDataBytes: Integer;


begin

// Silenzio

curx:= 0;

ysil:= Height div 2;


// Valuta fattore di integrazione

fi:= blkSize div Width;


// Valuta fattore di forma

ff:= Height/256;


// Draw Background

DrawBackground;


done:= false;

while ( done = false ) do

begin

intData:=0;

for k:=1 to fi do

begin

intData:= intData + pippo[scanx];

scanx:= scanx+1;

end;


intData:= (Trunc(intData / fi * ff));

intData:= Abs(IntData - ysil);

if scanx > ia then

begin

showmessage('Riproduzione terminata');

ric;

done:=true;

end;

if scanx < ia then

for cury:= ysil-IntData to ysil + IntData do

begin

Canvas.Pixels[curx,cury] := clGreen;

if cury > ind then

ind:=cury;

end;

curx:= curx+1;

if curx >= width then

done := True;

end;

end;


procedure TWave.ric;

var

i ,j,k: integer;

appo : array [1..100000] of integer;

begin

for j:=1 to ia do

begin

k:=0;

for i:=1 to 10 do

begin

if pippo[i] = 60 then

begin

inc(k);

end;

end;

appo[j]:=k;

end;


end;


end.

Listato file tstwave.pas


unit tstWave;


interface


uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

StdCtrls, Wave, Buttons, Mask;


type  

TForm1 = class(TForm)

ResultEdit: TEdit;

Label1: TLabel;

OpenDialog1: TOpenDialog;

FileEdit: TEdit;

Wave1: TWave;

apri: TSpeedButton;

suona: TSpeedButton;

ferma: TSpeedButton;

esci: TSpeedButton;

continua: TSpeedButton;

procedure apriClick(Sender: TObject);

procedure suonaClick(Sender: TObject);

procedure fermaClick(Sender: TObject);

procedure esciClick(Sender: TObject);

procedure FormCreate(Sender: TObject);

procedure continuaClick(Sender: TObject);

end;


var

Form1: TForm1;


implementation




procedure TForm1.apriClick(Sender: TObject);

begin

OpenDialog1.InitialDir:=GetCurrentDir;

opendialog1.FileName:='*.wav';

opendialog1.Filter:='File audio(wave)| *.wav';

if opendialog1.Execute then

begin

FileEdit.Text:= OpenDialog1.FileName;

suona.enabled:=true;

//ferma.enabled:=true;

//continua.enabled:=true;

end;


end;


procedure TForm1.suonaClick(Sender: TObject);

var

answ: Integer;

begin

Wave1.FileName := OpenDialog1.FileName;

answ:=Wave1.Play;

ferma.enabled:=true;

continua.enabled:=true;

ResultEdit.Text:= IntTosTr(answ);

end;


procedure TForm1.fermaClick(Sender: TObject);

begin

Wave1.PlayStop;

end;


procedure TForm1.esciClick(Sender: TObject);

begin

close;

end;


procedure TForm1.FormCreate(Sender: TObject);

begin

suona.enabled:=false;

ferma.enabled:=false;

continua.enabled:=false;

end;


procedure TForm1.continuaClick(Sender: TObject);

begin

wave1.draw_pezzi;

end;


end.



Considerazioni finali


Nella realizzazione di questo progetto sono sorte numerose difficoltà che ci hanno costretto a ridurre il nostro progetto iniziale. Sono sorti subito problemi per il recupero della documentazione, in quanto il materiale riguardante il riconoscimento vocale in circolazione è abbastanza generico e raramente siamo riusciti a reperire documentazione specifica. Inoltre anche la documentazione sulla scheda audio e sul formato wave sono sporadiche e questo è anche dovuto al fatto che i costruttori non sempre sono disposti a svelare i propri segreti se non a pagamento. Inoltre la scelta di un linguaggio visuale per l'implementazione del software (dettata soprattutto da motivi grafici), ci ha costretti ad ampliare le nostre conoscenze del linguaggio, con una conseguente perdita di tempo.

Inoltre in seguito alla sporadicità della documentazione presente sui libri siamo stati costretti a rivolgere la nostra ricerca su internet con un'ulteriore perdita di tempo che, fra l'altro, non ha dato i risultati previsti.

Risultati

Alla fine possiamo dire che i nostri obiettivi iniziali sono stati raggiunti solo in parte in quanto la fase del riconoscimento delle vocali si è rivelata più complicata del previsto e non è stata ancora portata a conclusione. Tuttavia questa esperienza si è rilevata istruttiva dal nostro punto di vista in quanto ci ha permesso di ampliare le nostre conoscenze sul Delphi, sull'utilizzo della grafica in ambiente Windows 95, delle applicazioni della API multimediale e sulla composizione dei suoni.

Tempi di realizzazione


La quantizzazione del tempo richiesto è abbastanza approssimativa soprattutto per quel che riguarda la ricerca e l'analisi, e in linea di massima si può così riassumere:


Ore a scuola Ore a casa Ore totali


Ricerca e 48 38 86

analisi materiale


Realizzazione 42 20 66

software e relazioni



Consultivo totale 100 58 152


Miglioramenti

I miglioramenti realizzabili sono tantissimi soprattutto per quel che riguarda il riconoscimento delle vocali. Inoltre si potrebbe pensare di implementare il programma per giungere a realizzare dei sistemi di dettatura abbastanza semplici.

Bibliografia


Computer programming luglio/agosto 1997 n.60.

Computer programming disk dicembre 1994 n.14.

Computer programming disk luglio/agosto 1995 n.60.

Guida alla multimedialità.

Enciclopedia dei formati grafici.

Macchine che riconoscono la lingua parlata di Roberto Pieraccini.









Privacy




Articolo informazione


Hits: 1930
Apprezzato: scheda appunto

Commentare questo articolo:

Non sei registrato
Devi essere registrato per commentare

ISCRIVITI



Copiare il codice

nella pagina web del tuo sito.


Copyright InfTub.com 2024