La gestione dell'audio e dei file audio mediante le funzioni esterne del API di Opus
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.pcm" 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.pcm") ' 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
- http://opus-codec.org/
- https://jmvalin.ca/papers/aes135_opus_silk.pdf
- https://chromium.googlesource.com/chromium/deps/opus/+/1.1.1/doc/trivial_example.c
- https://stackoverflow.com/questions/51638654/how-to-encode-and-decode-audio-data-with-opus
- https://datatracker.ietf.org/doc/html/draft-ietf-codec-oggopus