Estrarre da un file sf2 i dati audio di ciascun suono campionato e creare un file wav
Un file banco di suoni di formato sf2 è semplicemente una libreria di suoni per la riproduzione di dati Midi che si basa su tabelle di suoni campionati (wavetable).
Il file contiene, dunque, dati audio di suoni campionati che possono essere successivamente manipolati dal calcolatore per generare le restanti frequenze sonore appartenenti a quel timbro originario.
Il file .sf2 è costituito, fra l'altro, da diversi blocchi comprendenti specifiche informazioni di carattere generale sul file stesso. In particolare il sub-blocco denominato "shdr" contiene i nomi di ciascun suono campionato e la posizione iniziale e finale dei suoi dati audio grezzi all'interno del sub-blocco "sdtasmpl". Quest'ultimo contiene i dati audio grezzi, dunque, di tutti i suoni campionati contenuti nel file sf2.
Mostriamo un possibile codice per estrarre da un file sf2 i dati audio grezzi di ciascun suono campionato, presenti nel file, con i quali si generarà per ciascun suono un distinto file audio wav:
Public Struct sfSample achSampleName[20] As Byte dwStart As Integer dwEnd As Integer dwStartLoop As Integer dwEndLoop As Integer dwSampleRate As Integer byOriginalPitch As Byte chPitchCorrection As Byte wSampleLink As Short sfSampleType As Short End Struct Public Sub Main() Dim fl As File Dim sf2, s As String Dim bb As Byte[] Dim i, j, i2 As Integer Dim sfS As SfSample Dim ssff As SfSample[] sf2 = "/percorso/del/file.sf2" fl = Open sf2 For Read If IsNull(fl) Then Error.Raise("Impossibile caricare il file '" & sf2 & " ' !") With bb = New Byte[Lof(fl)] .Read(fl, 0, Lof(fl)) s = .ToString(0, bb.Count) If IsNull(s) Then Error.Raise("Il file caricato non contiene dati !") End With bb.Clear ' Individua la posizione del sub-Blocco 'shdr': i = InStr(s, "shdr") If i = 0 Then Error.Raise("Impossibile trovare il sub-blocco 'shdr' !") ' Individua la posizione del sub-blocco "sdtasmpl": i2 = InStr(s, "sdtasmpl") If i2 = 0 Then Error.Raise("Impossibile trovare il sub-blocco 'sdtasmpl' !") ' Individua la quantità di byte che compongono l'intero blocco 'shdr': Seek #fl, i + 3 j = Read #fl As Integer ' Raccoglie ad ogni ciclo 46 byte contenenti i vari elementi del suono campionato, rappresentati dai membri della "Struttura". ' In particolare serviranno in seguito i seguenti 4 membri: nome del suono campionato, byte iniziale del suono campionato dall'inizio dei dati audio del sotto-blocco "sdtasmpl", ' byte finale del suono campionato e frequenza di campionamento dei dati del campione. Va precisato che la dimensione del suono campionato, data dalla sottrazione ' dei membri della Struttura ".dWEnd" e ".dwStart" è espressa in dati di campionamento, i quali hanno sempre una risoluzione a 16 bit: ssff = New SfSample[] While Seek(fl) < j + i sfS = New SfSample With sfS .achSampleName.Read(fl, 0, 20) .dwStart = Read #fl As Integer .dwEnd = Read #fl As Integer .dwStartLoop = Read #fl As Integer .dwEndLoop = Read #fl As Integer .dwSampleRate = Read #fl As Integer .byOriginalPitch = Read #fl As Byte .chPitchCorrection = Read #fl As Byte .wSampleLink = Read #fl As Short .sfSampleType = Read #fl As Short End With ssff.Add(sfS) Wend ' Elimina l'ultima variabile di tipo "Struttura", poiché contenente i dati terminale del blocco 'shdr' che sono non significativi: ssff.Remove(ssff.Max) ' Mostra i nomi ad alcune caratteristiche dei suoni campionati presenti nel file sf2: Print "Suoni campionati presenti nel file: "; File.Name(sf2) For j = 0 To ssff.Max Print "\nSuono campionato n. "; j; ":" With ssff[j] Print " - nome assegnato: "; .achSampleName.ToString() Print " - frequenza campionamento: "; .dwSampleRate;; "hertz" Print " - dimensione: "; (.dwEnd - .dwStart) * 2;; "byte" End With CreaFileWAV(fl, ssff[j]) Next fl.Close End Private Procedure CreaFileWAV(camp As File, sf As SfSample) Dim wav As File Dim bb As Byte[] Dim dmn As Integer wav = Open "/tmp" &/ sf.achSampleName.ToString() & ".wav" For Create dmn = (sf.dwEnd - sf.dwStart) * SizeOf(gb.Short) Seek #camp, sf.dwStart * SizeOf(gb.Short) ' Comincia a scrivere il blocco d'intestazione del futuro file wav: Write #wav, "RIFF" ' Continua ad aggiungere altri elementi costituenti il blocco d'intestazione del futuro file wav: Write #wav, (dmn) + 36 As Integer bb = New Byte[] bb = [&57, &41, &56, &45, &66, &6D, &74, &20, 16, 0, 0, 0, 1, 0, 1, 0] bb.Write(wav, 0, bb.Count) bb.Clear Write #wav, sf.dwSampleRate As Integer Write #wav, sf.dwSampleRate * 2 As Integer bb = [2, 0, 16, 0, &64, &61, &74, &61] bb.Write(wav, 0, bb.Count) bb.Clear Write #wav, dmn As Integer bb = New Byte[dmn * 2] bb.Read(camp, 0, dmn) bb.Write(wav, 0, bb.Count) ' Al termine dei dati audio di ciascun suono campionato facciamo avanzare il puntatore interno del file di 92 byte (46 * 16 bit), ' poiché il valore espresso dal membro ".dwEnd" della Struttura rappresenta il primo di 46 byte di valore zero, posti dopo i dati audio del suono campionato. ' Come già detto in precedenza tali valori, indicanti quantità di byte all'interno del sotto-blocco "sdtasmpl", vanno moltiplicati per i bit (16 bit)della risoluzione di ogni campione audio. Seek #camp, Seek(camp) + 92 End