Generare un'onda sinusoidale con le sole funzioni di Gambas
Vai alla navigazione
Vai alla ricerca
E' possibile generare un'onda sinusoidale con le sole risorse di Gambas.
Mostriamo di seguito tre esempi.
Creare un'onda sinusoidale e salvare i dati in un file WAV
Creiamo un'onda sinusoidale da 8000 hertz, 8-bit, 1 canale:
Private Const AMPIEZZA As Integer = 127
Private Const FREQUENZA As Integer = 110
Private Const CAMPIONAMENTO As Integer = 8000
Private Const DURATA As Integer = 10
Public Sub Main()
Dim dati As Byte[]
' Va a generare i dati dell'onda sinusoidale:
dati = Onda()
' Va a generare il file WAV contenente l'onda sinusoidale:
CreaFileWav(dati.ToString(0, dati.Count))
End
Private Function Onda() As Byte[]
Dim i As Integer
Dim b As Byte
Dim bb As New Byte[]
For i = 0 To (DURATA * 2 * CAMPIONAMENTO) - 1
b = 128 + AMPIEZZA * Sin(CFloat(i / CAMPIONAMENTO * FREQUENZA * (2 * Pi)))
bb.Push(b)
Next
Return bb
End
Private Procedure CreaFileWav(dati_grezzi As String)
Dim fl As File
Dim ini As String
Dim bh, bb As New Byte[]
Dim canali, risoluzione, blal As Byte
Dim i, i2, brps As Integer
' Vengono definiti gli elementi fondamentali del blocco d'intestazione del file wav:
canali = 1
risoluzione = 8
fl = Open "/tmp/file.wav" For Create
ini = "RIFF"
bb = Byte[].FromString(ini)
i = Len(dati_grezzi)
i2 = i + 36
' Imposta il valore dimensionale di 4 byte a partire dal 5° byte del futuro file:
bb.Push(i2 And &FF)
bb.Push(Shr(i2 And &FF00&, 8))
bb.Push(Shr(i2 And &FF0000&, 16))
bb.Push(Shr(i2 And &FF000000&, 24))
' Vengono aggiunti: il tipo di formato di file e l'identificativo del formato del blocco dei dati audio:
bb = bb.Insert(bh.FromString("WAVEfmt "))
' Viene aggiunto il valore della lunghezza dei dati del formato (in questo caso il PCM):
bh = [&10, &00, &00, &00]
bb = bb.Insert(bh)
' Viene aggiunto il valore del formato audio (1 = PCM):
bb = bb.Insert(bh.FromString(Chr(&01) & Chr(&00)))
' Viene aggiunto il numero dei canali di uscita:
bb = bb.Insert(bh.FromString(Chr(canali) & Chr(&00)))
' Viene aggiunto il valore della frequenza di campionamento:
bb.Push(CAMPIONAMENTO And &FF)
bb.Push(Shr(CAMPIONAMENTO And &FF00&, 8))
bb.Push(Shr(CAMPIONAMENTO And &FF0000&, 16))
bb.Push(Shr(CAMPIONAMENTO And &FF000000&, 24))
' Viene aggiunto il valore del "Byte rate per secondo":
brps = CAMPIONAMENTO * canali * (risoluzione / 8)
bb.Push(brps And &FF)
bb.Push(Shr(brps And &FF00&, 8))
bb.Push(Shr(brps And &FF0000&, 16))
bb.Push(Shr(brps And &FF000000&, 24))
' Viene aggiunto il valore del "Block Align":
blal = canali * risoluzione / 8
bb = bb.Insert(bh.FromString(Chr(blal) & Chr(&00)))
' Viene aggiunto il valore della risoluzione di campionamento:
bb = bb.Insert(bh.FromString(Chr(risoluzione) & Chr(&00)))
' Viene aggiunto l'identificativo del Blocco dei dati audio grezzi:
bb = bb.Insert(bh.FromString("data"))
' Imposta il valore dimensionale di 4 byte a partire dal 41° byte del futuro file
' e relativo alla dimensione dei dati audio grezzi:
bb.Push(i And &FF)
bb.Push(Shr(i And &FF00&, 8))
bb.Push(Shr(i And &FF0000&, 16))
bb.Push(Shr(i And &FF000000&, 24))
bb = bb.Insert(Byte[].FromString(dati_grezzi))
' Crea il nuovo file wav:
bb.Write(fl, 0, bb.Count)
fl.Close
End
Ricavare un'onda sinusoidale da un insieme di dati audio grezzi predefiniti in un vettore
Mostriamo un esempio (tratto da analogo codice scritto in C da Fabrice Bellard e convertito qui in Gambas con modifiche ed integrazioni), al termine del quale verrà creato un file di tipo WAV contenente i dati audio e che consentirà di eseguire l'onda sinusoidale campionata:
Private Const RISOL_BIT As Integer = 16
Private Const COST_TAB_VAL As Integer = 7
Private FRAZIONE As Integer = 1 * CInt(2 ^ RISOL_BIT)
Private CSHIFT As Integer = RISOL_BIT - COST_TAB_VAL - 2
Private Tabella_Valori As Integer[] = [&8000&, &7ffe, &7ff6, &7fea, &7fd9, &7fc2, &7fa7, &7f87,
&7f62, &7f38, &7f0a, &7ed6, &7e9d, &7e60, &7e1e, &7dd6,
&7d8a, &7d3a, &7ce4, &7c89, &7c2a, &7bc6, &7b5d, &7aef,
&7a7d, &7a06, &798a, &790a, &7885, &77fb, &776c, &76d9,
&7642, &75a6, &7505, &7460, &73b6, &7308, &7255, &719e,
&70e3, &7023, &6f5f, &6e97, &6dca, &6cf9, &6c24, &6b4b,
&6a6e, &698c, &68a7, &67bd, &66d0, &65de, &64e9, &63ef,
&62f2, &61f1, &60ec, &5fe4, &5ed7, &5dc8, &5cb4, &5b9d,
&5a82, &5964, &5843, &571e, &55f6, &54ca, &539b, &5269,
&5134, &4ffb, &4ec0, &4d81, &4c40, &4afb, &49b4, &486a,
&471d, &45cd, &447b, &4326, &41ce, &4074, &3f17, &3db8,
&3c57, &3af3, &398d, &3825, &36ba, &354e, &33df, &326e,
&30fc, &2f87, &2e11, &2c99, &2b1f, &29a4, &2827, &26a8,
&2528, &23a7, &2224, &209f, &1f1a, &1d93, &1c0c, &1a83,
&18f9, &176e, &15e2, &1455, &12c8, &113a, &0fab, &0e1c,
&0c8c, &0afb, &096b, &07d9, &0648, &04b6, &0324, &0192,
&0000, &0000]
Public Sub Main()
Dim fl As File
Dim filegrezzo As String
Dim i, a, v, j As Integer
Dim campionamento, canali, frequenza, volume As Integer
Dim durata As Single
filegrezzo = "/tmp/datigrezzi" ' File contenente i dati audio grezzi dell'onda
campionamento = 44100 ' Frequenza di campionamento dell'onda
canali = 2 ' Numero canali del file WAV
frequenza = 440 ' Frequenza dell'onda
durata = 3.0 ' Durata in secondi
volume = 10000 ' Volume audio dell'onda
fl = Open filegrezzo For Create
If IsNull(fl) Then Error.Raise("Impossibile creare il file dei dati audio grezzi !")
' Genera 3 secondi di un'onda sinusoidale alla frequenza di Hz 440:
For i = 0 To (durata * campionamento) - 1
v = (int_cos(a) * volume) \ CInt(2 ^ RISOL_BIT)
For j = 0 To canali - 1
Scrivi_File(fl, v)
Next
a += (frequenza * FRAZIONE) / campionamento
Next
Crea_File_WAV(fl)
fl.Close
Print "Fine creazione dell'onda."
End
Private Function Int_Cos(a As Integer) As Integer
Dim neg, v, f As Integer
Dim p As Integer[]
a = a And (FRAZIONE - 1)
If a >= (FRAZIONE / 2) Then a = FRAZIONE - a
neg = 0
If a > (FRAZIONE / 4) Then
neg = -1
a = (FRAZIONE / 2) - a
Endif
p = Tabella_Valori.Copy(a \ CInt(2 ^ CSHIFT), Tabella_Valori.Count - a \ CInt(2 ^ CSHIFT))
' Interpolazione lineare:
f = a And ((1 * CInt(2 ^ CSHIFT)) - 1)
v = p[0] + (((p[1] - p[0]) * f + (1 * CInt(2 ^ (CSHIFT - 1)))) / CInt(2 ^ CSHIFT))
v = (v Xor neg) - neg
v = v * CInt(2 ^ (RISOL_BIT - 15))
Return v
End
Private Procedure Scrivi_File(sf As File, v As Integer)
Write #sf, CByte(v And &ff) As Byte
Write #sf, CByte((v \ CInt(2 ^ 8)) And &ff) As Byte
End
Private Procedure Crea_File_WAV(dg As File)
Dim intes As File
Dim bb As Byte[] = [&52, &49, &46, &46, &00, &00, &00, &00, &57, &41, &56,
&45, &66, &6D, &74, &20, &10, &00, &00, &00, &01, &00,
&02, &00, &44, &AC, &00, &00, &10, &B1, &02, &00, &04,
&00, &10, &00, &64, &61, &74, &61]
' Scrive innanzitutto il blocco di intestazione del file WAV:
intes = Open "/tmp/blocco_header" For Create
bb.Write(intes, 0, 4)
Write #intes, Lof(dg) + 36 As Integer
bb.Write(intes, 8, 32)
Write #intes, Lof(dg) As Integer
intes.Close
' Unisce il blocco di intestazione del file WAV ai dati audio grezzi presenti nel file "datigrezzi"
' (in questo esempio il file WAV finale viene scritto nella cartella "/tmp"):
File.Save("/tmp/onda.wav", File.Load("/tmp/blocco_header") & File.Load("/tmp/datigrezzi"))
End
Ricavare un'onda sinusoidale convertendo una nota Midi nella corrispondente frequenza sonora
Quest'altra modalità prevede il calcolo della frequenza sonora da una nota Midi. Come si sa, il sistema Midi numera le note da 0 a 127.
Verrà, dunque, calcolata la frequenza di una nota Midi e dai risultanti dati audio grezzi verrà creato un file wav:
Public Sub Main()
Dim fl As File
Dim filegrezzo As String
Dim campionamento, durata, volume As Integer
Dim NotaMidi As Byte
Dim dati_grezzi As Short[]
filegrezzo = "/tmp/datigrezzi" ' Percorso del file che conterrà i dati audio grezzi dell'onda
campionamento = 44100 ' Frequenza di campionamento dell'onda
NotaMidi = 60 ' Numero della nota Midi
durata = 300000 ' Durata
volume = 10000 ' Volume audio dell'onda
fl = Open filegrezzo For Create
If IsNull(fl) Then Error.Raise("Impossibile creare il file dei dati audio grezzi !")
' Genera i dati audio grezzi:
dati_grezzi = CreaDatiAudio(NotaMidi, durata, volume, campionamento)
dati_grezzi.Write(fl, 0, dati_grezzi.Count)
' Crea il file wav finale:
CreaFileWAV(fl)
fl.Close
Print "Fine creazione dell'onda e del file wav."
End
Private Function CreaDatiAudio(nota As Byte, dur As Integer, ampiezza As Integer, freq_camp As Integer) As Short[]
Dim campione As Short
Dim frequenza As Float
Dim ciclo As Integer
Dim dati_audio As New Short[]
' Ciclo per ciascun dato campione audio:
For ciclo = 1 To dur
' Calcola la frequenza della nota Midi:
frequenza = CalcolaFrequenza(nota)
' Calcola il dato del campione audio:
campione = ampiezza * Sin(2 * Pi * ciclo * frequenza / freq_camp)
dati_audio.Push(campione)
Next
Return dati_audio
End
Private Function CalcolaFrequenza(numero_nota As Byte) As Float
' Converte la nota Midi nella corrispondente frequenza sonora.
' La nota di riferimento è il "Do centrale" (hz 261,63 -- nota Midi: 60).
Return 261.63 * (2 ^ (1 / 12)) ^ (numero_nota - 60)
End
Private Procedure CreaFileWAV(wav As File)
Dim intes As File
Dim bi As Byte[] = [&52, &49, &46, &46, &00, &00, &00, &00, &57, &41, &56,
&45, &66, &6D, &74, &20, &10, &00, &00, &00, &01, &00,
&02, &00, &44, &AC, &00, &00, &10, &B1, &02, &00, &04,
&00, &10, &00, &64, &61, &74, &61]
' Scrive innanzitutto il blocco di intestazione del file WAV:
intes = Open "/tmp/blocco_header" For Create
If IsNull(intes) Then Error.Raise("Impossibile scrivere il blocco d'intestazione !")
bi.Write(intes, 0, 4)
Write #intes, Lof(wav) + 36 As Integer
bi.Write(intes, 8, 32)
Write #intes, Lof(wav) As Integer
intes.Close
' Unisce il blocco di intestazione del file WAV ai dati audio grezzi presenti nel file "datigrezzi"
' (in questo esempio il file WAV finale viene scritto nella cartella "/tmp"):
File.Save("/tmp/onda.wav", File.Load("/tmp/blocco_header") & File.Load("/tmp/datigrezzi"))
End