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

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 e a musica stereo di altissima qualità.

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

Codificare e decodificare un flusso audio Opus mediante la libreria Libopus

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)
 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


Ottenere la durata di un file Opus

Mostriamo un semplicissimo codice per ottenere la durata di un file Opus.
E' necessario avere installata nel sistema la libreria condivisa "libopusfile.so.0.4.5 ".

Library "libopusfile:0.4.5"

' OggOpusFile* op_open_file (const char *_path, int *_error)
' Open a stream from the given file path.
Private Extern op_open_file(_path As String, _error As Pointer) As Pointer

' ogg_int64_t op_pcm_total (const OggOpusFile *_of, int _li)
' Get the total PCM length (number of samples at 48 kHz) of the stream.
Private Extern op_pcm_total(_of As Pointer, _li As Integer) As Long


Public Sub Main()

 Dim p As Pointer
 Dim l, secondi As Long

 p = op_open_file("/percorso/del/file.opus", 0)

 l = op_pcm_total(p, -1)

 secondi = l / 48000

 Print "Durata del file: "; Time(0, 0, 0, secondi * 1000)

End


Riferimenti