Differenze tra le versioni di "La gestione dell'audio e dei file audio mediante le funzioni esterne del API di Opus"
(Creata pagina con "* Codificare e decodificare un flusso audio Opus mediante la libreria Libopus") |
|||
Riga 1: | Riga 1: | ||
− | + | =Codificare e decodificare un flusso audio Opus mediante la libreria Libopus= | |
+ | '''Opus''' è un codec audio totalmente aperto, esente da diritti e altamente versatile. | ||
+ | <BR>''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 | ||
+ | |||
+ | <FONT Color=gray>' ''OpusEncoder *opus_encoder_create( opus_int32 Fs, int channels, int application, int *error )'' | ||
+ | ' ''Allocates and initializes an encoder state.''</font> | ||
+ | Private Extern opus_encoder_create(Fs As Integer, channels As Integer, _application As Integer, _error As Pointer) As Pointer | ||
+ | |||
+ | <FONT Color=gray>' ''const char *opus_strerror(int error)'' | ||
+ | ' ''Converts an opus error code into a human readable string.''</font> | ||
+ | Private Extern opus_strerror(_error As Integer) As String | ||
+ | |||
+ | <FONT Color=gray>' ''opus_int32 opus_encode( OpusEncoder *st, const opus_int16 *pcm, int buffer, unsigned char *data, opus_int32 max_data_bytes)'' | ||
+ | ' ''Encodes an Opus frame.''</font> | ||
+ | Private Extern opus_encode(st As Pointer, pcm As Pointer, buffer As Integer, data As Pointer, max_data_bytes As Integer) As Integer | ||
+ | |||
+ | <FONT Color=gray>' ''OpusDecoder *opus_decoder_create( opus_int32 Fs, int channels, int *error )'' | ||
+ | ' ''Allocates and initializes a decoder state.''</font> | ||
+ | Private Extern opus_decoder_create(Fs As Integer, channels As Integer, _error As Pointer) As Pointer | ||
+ | |||
+ | <FONT Color=gray>' ''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.''</font> | ||
+ | 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 | ||
+ | |||
+ | <FONT Color=gray>' ''void opus_encoder_destroy(OpusEncoder *st)'' | ||
+ | ''' Frees an OpusEncoder allocated by opus_encoder_create().''</font> | ||
+ | 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 = "<FONT Color=darkgreen>''/percorso/del/file.wav''</font>" | ||
+ | |||
+ | <FONT Color=gray>' ''Crea il codificatore in formato "opus":''</font>" | ||
+ | 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 | ||
+ | |||
+ | <FONT Color=gray>' ''Crea il file finale wav che dovrà contenere i dati audio del flusso "opus" ri-decodificato:''</font>: | ||
+ | fen = Open wav For Read | ||
+ | |||
+ | <FONT Color=gray>' ''Crea il decodificatore:''</font> | ||
+ | deco = opus_decoder_create(SAMPLE_RATE, CANALI, VarPtr(i)) | ||
+ | If deco = 0 Then Error.Raise("Errore: " & opus_strerror(i)) | ||
+ | |||
+ | <FONT Color=gray>' ''Crea il file finale wav che dovrà contenere i dati audio del flusso "opus" ri-decodificato:''</font> | ||
+ | fex = Open "/tmp/dati.raw" For Create | ||
+ | |||
+ | c = SizeOf(gb.integer) * CANALI * FRAME_SIZE | ||
+ | |||
+ | Repeat | ||
+ | pcm_bytes = New Short[MAX_FRAME_SIZE * CANALI] | ||
+ | |||
+ | <FONT Color=gray>''''''''''</font> <FONT Color=#B22222>Fase di codifica audio Opus</font> <FONT Color=gray>''''''''''</font> | ||
+ | |||
+ | <FONT Color=gray>' ''Evita l'errore di "Fine del file":''</font> | ||
+ | If (Lof(fen) - Seek(fen)) < FRAME_SIZE * CANALI * SizeOf(gb.Integer) Then c = Lof(fen) - Seek(fen) | ||
+ | <FONT Color=gray>' ''Legge i dati audio dal file wav iniziale:''</font> | ||
+ | pcm_bytes.Read(fen, 0, c) | ||
+ | <FONT Color=gray>' ''Effettua la conversione dall'ordine Little-Endian:''</font> | ||
+ | For i = 0 To (CANALI * FRAME_SIZE) - 1 | ||
+ | buffIn[i] = pcm_bytes[2 * i + 1] * CInt(2 ^ 8) Or pcm_bytes[2 * i] | ||
+ | Next | ||
+ | <FONT Color=gray>' ''Codifica il frame audio:''</font> | ||
+ | nb = opus_encode(enco, buffIn.Data, FRAME_SIZE, cbits.Data, MAX_PACKET_SIZE) | ||
+ | If nb < 0 Then Error.Raise("Errore: " & opus_strerror(nb)) | ||
+ | |||
+ | <FONT Color=gray>''''''''''</font> <FONT Color=#B22222>Fase di decodifica audio da Opus in PCM grezzi</font> <FONT Color=gray>'''''''''' | ||
+ | |||
+ | ' ''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.''</font> | ||
+ | frames = opus_decode(deco, cbits.Data, nb, buffOut.Data, MAX_FRAME_SIZE, 0) | ||
+ | If frames < 0 Then Error.Raise("Errore: " & opus_strerror(frames)) | ||
+ | <FONT Color=gray>' ''Effettua la conversione all'ordine Little-Endian:''</font> | ||
+ | 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 | ||
+ | <FONT Color=gray>' ''Scrive l'audio decodificato nel file di dati grezzi:''</font> | ||
+ | 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() <FONT Color=gray>' ''Crea l'header del file wav''</font> | ||
+ | |||
+ | Dim hdr As Byte[] | ||
+ | Dim b As Byte | ||
+ | Dim s As String | ||
+ | |||
+ | <FONT Color=gray>' ''Imposta i dati principali dell'header del file WAV finale:''</font> | ||
+ | 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") | ||
+ | |||
+ | <FONT Color=gray>' ''Determina i valori a 32-bit, relativi alla dimensione del file wav, richiesti al 5° e al 41° byte dell'header:''</font> | ||
+ | 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 | ||
+ | |||
+ | <FONT Color=gray>' ''Genera il file finale di tipo WAV dai dati Opus riconvertiti in PCM grezzi:''</font> | ||
+ | File.Save("/tmp/file_finale.wav", hdr.ToString(0, hdr.Count) & s) | ||
+ | |||
+ | Write "\rFile WAV creato !" | ||
+ | |||
+ | 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 |
Versione delle 01:43, 22 lug 2024
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