Codifica del parlato mediante le funzioni esterne del API di Speex

Da Gambas-it.org - Wikipedia.

Le risorse di Speex consentono di codificare il "parlato", mediante la compressione dei dati audio. Speex non è progettato per la telefonia cellulare, quanto piuttosto per le reti a pacchetto e per il voice over IP (VoIP). E' supportata anche la compressione su file. Il codificatore Speex è progettato per essere molto flessibile e supportare una vasta gamma di qualità vocale (sia a banda stretta con frequenza di campionamento a 8 kHz, sia a banda larga con frequenza di campionamento a 16 kHz) e di bit-rate (da 2 kbit/s a 44 kbit/s).

Uso delle risorse di Speex mediante la libreria libspeex

L'uso di Speex si sostanzia nella codifica del "parlato" e nella successiva decodifica dei dati audio precedentemente codificati.

Per poter utilizzare le risorse e le funzioni di Speex in Gambas è necessario richiamare la libreria (nella sua attuale versione): libspeex.so.1.5.2


La codifica del "parlato"

Speex è in grado di codificare il "parlato" sia in dati a banda stretta che a banda larga e fornire diverse velocità di campionamento (bit-rates).

Di seguito mostriamo un semplice codice per effettuare la codifica del "parlato", i cui dati audio sono contenuti in un file wav a 2 canali, 44100 Hz, 16 bit.
Sarà necessario scrivere e utilizzare nell'applicazione Gambas, una libreria esterna condivisa .so, da noi appositamente scritta in C, per poter gestire con sicurezza la funzione

void *speex_encoder_init(const SpeexMode *mode)

la quale, diversamente, determina la sollevazione di un errore.

' Speex License

' Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

   ' Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
   ' Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
   ' Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

' THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
' AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
' (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
' STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Private Const FRAME_SIZE As Byte = 160

Library "libspeex:1.5.2"

Public Struct SpeexBits 
   chars As Pointer     ' "raw" data 
   nbBits As Integer    ' Total number Of bits stored In the stream 
   charPtr As Integer   ' Position Of the byte "cursor" 
   bitPtr As Integer    ' Position Of the bit "cursor" within the current char 
   owner As Integer     ' Does the struct "own" the "raw" buffer(member "chars")
   overflow As Integer  ' Set To one If we Try To Read past the valid data 
   buf_size As Integer  ' Allocated size For buffer 
   reserved1 As Integer ' Reserved For future use 
   reserved2 As Integer ' Reserved For future use 
End Struct

Private Const SPEEX_SET_QUALITY As Byte = 4  ' Set quality value. The quality is an integer value ranging from 0 to 10

' int speex_encoder_ctl(void *state, int request, void *ptr)
' Used like the ioctl function to control the encoder parameters
Private Extern speex_encoder_ctl(stP As Pointer, request As Integer, ptr As Pointer) As Integer

' void speex_bits_init(SpeexBits *bits)
' Initializes and allocates resources for a SpeexBits struct
Private Extern speex_bits_init(sbSt As SpeexBits)

' void speex_bits_reset(SpeexBits *bits)
' Resets bits to initial value (just after initialization, erasing content)
Private Extern speex_bits_reset(sbSt As SpeexBits)

' int speex_encode(void *state, float *in, SpeexBits *bits)
' Uses an existing encoder state to encode one frame of speech pointed to by "in".
' The encoded bit-stream is saved in "bits".
Private Extern speex_encode(stP As Pointer, infl As Single[], sbSt As SpeexBits) As Integer

' int speex_bits_write(SpeexBits *bits, char *bytes, int max_len)
' Write the content of a bit-stream to an area of memory
Private Extern speex_bits_write(sbSt As SpeexBits, bytes As Byte[], max_len As Integer) As Integer

' void speex_encoder_destroy(void *state)
' Frees all resources associated to an existing Speex encoder state.
Private Extern speex_encoder_destroy(stP As Pointer)

' void speex_bits_destroy(SpeexBits *bits)
' Frees all resources associated to a SpeexBits struct.
Private Extern speex_bits_destroy(sbSt As SpeexBits)


Library "/tmp/libspeexcod"  ' Richiama la libreria da noi realizzata per la gestione sicura della funzione "speex_encoder_init()" di libspeex
' char * encoder_init()
Private Extern encoder_init() As Pointer


Public Sub Main()

 Dim state As Pointer
 Dim inFile, exFile As String
 Dim fin, fex As File
 Dim inSh As New Short[FRAME_SIZE]
 Dim inSi As New Single[FRAME_SIZE]
 Dim cbits As New Byte[200]
 Dim nbBytes, i, tmp As Integer
 Dim bits As New SpeexBits
 Dim j As Byte
 Dim s As Short

' Verifica se già è presente la libreria ad hoc per la gestione sicura della funzione "speex_encoder_init()" di libspeex:
 If Exist("/tmp/libspeexcod.so") = False Then creaSpeexCod()

 inFile = "/percorso/del/file.wav"
 exFile = "/tmp/codificato.sw"

 fin = Open inFile For Read
 fex = Open exFile For Create

' Invoca la funzione esterna della libreria ad hoc:
 state = encoder_init()

' Imposta la qualità a 8 (15 kbps):
 tmp = 8
 speex_encoder_ctl(state, SPEEX_SET_QUALITY, VarPtr(tmp))

' Inizializza la Struttura contenente i bit:
 speex_bits_init(bits)

 Do
' Legge un frame audio con campionamento a 16 bit:
   For j = 0 To FRAME_SIZE - 1
     If Eof(fin) Then Break
     Read #fin, s
     inSh[j] = s
   Next
   If Eof(fin) Then Break
' Copia i valori a 16 bit nella variabile di tipo "Single":
   For i = 0 To FRAME_SIZE - 1
     inSi[i] = inSh[i]
   Next
' Scrive tutti i bit nella Struttura per poter codificare un nuovo frame audio:
   speex_bits_reset(bits)
' Codifica il frame:
   speex_encode(state, inSi, bits)
' Copia i bit in un vettore di tipo Byte:
   nbBytes = speex_bits_write(bits, cbits, 200)
' Scrive innanzitutto la dimensione del frame:
   Write #fex, nbBytes As Integer
' Scrive i dati audio compressi nel file finale:
   cbits.Write(fex, 0, nbBytes)
 Loop

 Print "Fine codificazione"
 
' Va in chiusura.

' Distrugge lo stato di codificatore:
 speex_encoder_destroy(state)
' Elimina la Struttura contenente i bit:
 speex_bits_destroy(bits)
' Chiude i file:
 fex.Close
 fin.Close

End


' Crea l'apposita libreria per la gestione sicura della funzione "speex_encoder_init()" di libspeex:
Private Procedure creaSpeexCod()
 
 File.Save("/tmp/libspeexcod.c", "#include <speex/speex.h>\n\n" &
           "   const SpeexMode speex_nb_mode;\n\n" &
           "// Crea un nuovo stato del codificatore in modalità \"narrowband\":\n" &
           "char * encoder_init() {\n\n" &
           "   return speex_encoder_init(&speex_nb_mode);\n\n}")
 
 Shell "gcc -o /tmp/libspeexcod.so /tmp/libspeexcod.c -shared -fPIC -lspeex" Wait
 
End


Note

[1] Nell'ambito della codifica del "parlato" il bit-rate si definisce come il numero di bit per unità di tempo necessario per codificare il "parlato". Esso viene misurato in "bit per secondo" (bps), o in generale in "kilobit al secondo" (kbps).
Non va confuso il "kilobit" al secondo (kbps) con il "kilobyte" al secondo (Kbps) !


Riferimenti

[1] Il sito di Speex