La gestione dell'audio e dei file audio mediante le funzioni esterne del API di Opus

Da Gambas-it.org - Wikipedia.

Codificare e decodificare un flusso audio Opus mediante la libreria Libopus

Opus è un codec audio totalmente aperto, esente da diritti e altamente versatile.
Opus è in grado di gestire un'ampia gamma di applicazioni audio, tra cui Voice over IP, videoconferenze, chat in-game e persino esibizioni di musica dal vivo in remoto. Può passare dal parlato, a banda stretta, a basso bitrate a musica stereo di altissima qualità.

E' necessario avere installata nel sistema e richiamare in Gambas la libreria condivisa: "libopus.so.0.10.1 ".

Vediamo di seguito un breve esempio di codifica e decodifica audio usando Opus, per mostrare semplicemente come questa libreria funziona. In particolare da un file wav si otterrà un flusso audio codificato con Opus, e si decodificherà tale flusso opus codificandolo nuovamente in un file di tipo wav.

Private Const FRAME_SIZE As Integer = 960
Private Const SAMPLE_RATE As Integer = 48000
Private Const CANALI As Integer = 2
Private Const MAX_FRAME_SIZE As Integer = 6 * 960
Private Const MAX_PACKET_SIZE As Integer = 3 * 1276


Library "libopus:0.10.1"

Private Const OPUS_APPLICATION_VOIP As Integer = 2048
Private Const OPUS_APPLICATION_AUDIO As Integer = 2049
Private Const OPUS_APPLICATION_RESTRICTED_LOWDELAY As Integer = 2051

' OpusEncoder *opus_encoder_create( opus_int32 Fs, int channels, int application, int *error )
' Allocates and initializes an encoder state.
Private Extern opus_encoder_create(Fs As Integer, channels As Integer, _application As Integer, _error As Pointer) As Pointer

' const char *opus_strerror(int error)
' Converts an opus error code into a human readable string.
Private Extern opus_strerror(_error As Integer) As String

' opus_int32 opus_encode( OpusEncoder *st, const opus_int16 *pcm, int buffer, unsigned char *data, opus_int32 max_data_bytes)
' Encodes an Opus frame.
Private Extern opus_encode(st As Pointer, pcm As Pointer, buffer As Integer, data As Pointer, max_data_bytes As Integer) As Integer

' OpusDecoder *opus_decoder_create( opus_int32 Fs, int channels, int *error )
' Allocates and initializes a decoder state.
Private Extern opus_decoder_create(Fs As Integer, channels As Integer, _error As Pointer) As Pointer

' int opus_decode( OpusDecoder *st, const unsigned char *data, opus_int32 len, opus_int16 *pcm, int frame_size, int decode_fec)
' Decode an Opus packet.
Private Extern opus_decode(st As Pointer, data As Pointer, _len As Integer, pcm As Pointer, frame_size As Integer, decode_fec As Integer) As Integer

' void opus_encoder_destroy(OpusEncoder *st)
' Frees an OpusEncoder allocated by opus_encoder_create().
Private Extern opus_encoder_destroy(st As Pointer)


Public Sub Main()
 
 Dim wav As String
 Dim enco, deco As Pointer
 Dim c, i, nb, frames As Integer
 Dim fen, fex As File
 Dim buffIn As New Integer[FRAME_SIZE * CANALI]
 Dim buffOut As New Integer[MAX_FRAME_SIZE * CANALI]
 Dim cbits As New Short[MAX_PACKET_SIZE]
 Dim pcm_bytes As Short[]

 Write "\e[5mAttendere...\e[0m"
 Flush

 wav = "/percorso/del/file.wav"
 
' Crea il codificatore in formato "opus":"
 enco = opus_encoder_create(SAMPLE_RATE, CANALI, OPUS_APPLICATION_AUDIO, VarPtr(i))
 If enco = 0 Then Error.Raise("Errore: " & opus_strerror(i))
 
 fen = Open wav For Read

' Crea il file finale wav che dovrà contenere i dati audio del flusso "opus" ri-decodificato::
 fen = Open wav For Read

' Crea il decodificatore:
 deco = opus_decoder_create(SAMPLE_RATE, CANALI, VarPtr(i))
 If deco = 0 Then Error.Raise("Errore: " & opus_strerror(i))

' Crea il file finale wav che dovrà contenere i dati audio del flusso "opus" ri-decodificato:
 fex = Open "/tmp/dati.raw" For Create

 c = SizeOf(gb.integer) * CANALI * FRAME_SIZE

 Repeat
   pcm_bytes = New Short[MAX_FRAME_SIZE * CANALI]
   
''''' Fase di codifica audio Opus '''''

' Evita l'errore di "Fine del file":
   If (Lof(fen) - Seek(fen)) < FRAME_SIZE * CANALI * SizeOf(gb.Integer) Then c = Lof(fen) - Seek(fen)
' Legge i dati audio dal file wav iniziale:
   pcm_bytes.Read(fen, 0, c)
' Effettua la conversione dall'ordine Little-Endian:
   For i = 0 To (CANALI * FRAME_SIZE) - 1
     buffIn[i] = pcm_bytes[2 * i + 1] * CInt(2 ^ 8) Or pcm_bytes[2 * i]
   Next
' Codifica il frame audio:
   nb = opus_encode(enco, buffIn.Data, FRAME_SIZE, cbits.Data, MAX_PACKET_SIZE)
   If nb < 0 Then Error.Raise("Errore: " & opus_strerror(nb))

''''' Fase di decodifica audio da Opus in PCM grezzi '''''
   
' Decodifica i dati. In questo esempio, frame_size sarà costante poiché l'encoder utilizza una dimensione di frame costante.
' Tuttavia, potrebbe non essere il caso per tutti gli encoder, quindi il decoder deve sempre controllare la dimensione del frame restituita.
   frames = opus_decode(deco, cbits.Data, nb, buffOut.Data, MAX_FRAME_SIZE, 0)
   If frames < 0 Then Error.Raise("Errore: " & opus_strerror(frames))
' Effettua la conversione all'ordine Little-Endian:
   For i = 0 To (CANALI * frames) - 1
     pcm_bytes[SizeOf(gb.Integer) * i] = buffOut[i] And &FF
     pcm_bytes[SizeOf(gb.Integer) * i + 4] = (buffOut[i] \ CInt(2 ^ 8)) And &FF
   Next
' Scrive l'audio decodificato nel file di dati grezzi:
   pcm_bytes.Write(fex, 0, SizeOf(gb.Integer) * frames * CANALI)'pcm_bytes.Count)
 Until (Lof(fen) - Seek(fen)) < FRAME_SIZE * CANALI * SizeOf(gb.Integer)

 fen.Close
 fex.Close
   
 HeaderWav()
   
 opus_encoder_destroy(deco)
 opus_encoder_destroy(enco)
   
End


Private Procedure HeaderWav()    ' Crea l'header del file wav
 
 Dim hdr As Byte[]
 Dim b As Byte
 Dim s As String
 
' Imposta i dati principali dell'header del file WAV finale:
 hdr = [&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, &00, &00, &00, &00]
 
 s = File.Load("/tmp/dati.raw")
 
' Determina i valori a 32-bit, relativi alla dimensione del file wav, richiesti al 5° e al 41° byte dell'header:
 For b = 0 To 3
   hdr[4 + b] = Shr(Len(s) + 38, 8 * b) And &FF
   hdr[40 + b] = Shr(Len(s), 8 * b) And &FF
 Next
 
' Genera il file finale di tipo WAV dai dati Opus riconvertiti in PCM grezzi:
 File.Save("/tmp/file_finale.wav", hdr.ToString(0, hdr.Count) & s)

 Write "\rFile WAV creato !"

End


Riferimenti