Codificare e decodificare un flusso audio Opus mediante la libreria Libopus

Da Gambas-it.org - Wikipedia.

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.9.0 ".

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 codificato nuovamente in un file di tipo wav.

Private Const CANALI As Integer = 2
Private Const MAX_PACKET_SIZE As Integer = 3828
Private Const MAX_FRAME_SIZE As Integer = 5760
Private Const FRAME_SIZE As Integer = 960

Library "libopus:0.9.0"

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 i, nb, frames As Integer
 Dim fen, fex As File
 Dim buffen As New Short[FRAME_SIZE * CANALI]
 Dim buffex As New Short[MAX_FRAME_SIZE * CANALI]
 Dim cbits As New Byte[MAX_PACKET_SIZE]
 Dim pcm As Byte[]
 
 wav = "/percorso/del/file.wav"
 
' Crea il codificatore in formato "opus":"
 enco = opus_encoder_create(48000, CANALI, OPUS_APPLICATION_AUDIO, VarPtr(i))
 If enco = 0 Then Error.Raise("Errore: " & opus_strerror(i))
 
 fen = Open wav For Read
 pcm = New Byte[MAX_FRAME_SIZE * CANALI * SizeOf(gb.Short)]
' Crea il file finale wav che dovrà contenere i dati audio del flusso "opus" ri-decodificato::
 fex = Open "/tmp/dati" For Write Append
 
' Crea il decodificatore:
 deco = opus_decoder_create(48000, CANALI, VarPtr(i))
 If deco = 0 Then Error.Raise("Errore: " & opus_strerror(i))

 Repeat
   pcm = New Byte[MAX_FRAME_SIZE * CANALI * SizeOf(gb.Short)]
   
''''' Fase di codifica audio Opus '''''
   
' Legge i dati audio dal file wav iniziale:
   pcm.Read(fen, 0, (FRAME_SIZE * CANALI) * SizeOf(gb.Short))
 
' Effettua la conversione dall'ordine Little-Endian:
 For i = 0 To (CANALI * FRAME_SIZE) - 1
   buffen[i] = (pcm[2 * i + 1] * CInt(2 ^ 8)) Or pcm[2 * i]
 Next
 
 nb = opus_encode(enco, buffen.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, buffex.Data, MAX_FRAME_SIZE, 0)
   If frames < 0 Then Error.Raise("Errore: " & opus_strerror(frames))
   
   For i = 0 To frames * CANALI - 1
     Write #fex, buffex[i] And &FF As Byte
     Write #fex, (buffex[i] \ CInt(2 ^ 8)) And &FF As Byte
   Next
   
 Until Lof(fen) - Seek(fen) < FRAME_SIZE
   
 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")
 
' 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)
 
End


Riferimenti