Convertire un file WAV in formato AAC con l'API di Libvo-aacenc

Da Gambas-it.org - Wikipedia.

La libreria Libvo-aacenc consente di comprimere i dati PCM in flussi compatibili con il formato AAC standard. Il codificatore AAC si basa sul profilo AAC LC. I formati di uscita supportati sono ADTS e dati AAC grezzi con canali mono e stereo.


Poiché l'uso della libreria esterna Libvo-aacenc prevede il richiamo diretto ed indiretto di numerose Strutture e di funzioni poste all'interno di una Struttura, ed al fine di poter gestire dette Strutture e funzioni esterne in modo assolutamente sicuro, ci serviremo di una libreria esterna scritta in C, che realizzeremo ad hoc e che richiameremo all'interno del codice Gambas. Questa libreria ad hoc, costituita sostanzialmente da due funzioni, conterrà tutte le funzioni dell'API della libreria Libvo-aacenc:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include </usr/include/vo-aacenc/voAAC.h>
#include </usr/include/vo-aacenc/voAudio.h>
#include </usr/include/vo-aacenc/voMem.h>
#include </usr/include/vo-aacenc/cmnMemory.h>

VO_AUDIO_CODECAPI codecapi;
VO_HANDLE handle = 0;


int codifica_parametri(int freq, int can) {

 VO_MEM_OPERATOR mem_operator;
 VO_CODEC_INIT_USERDATA dati;
 AACENC_PARAM parametri;

 voGetAACEncAPI(&codecapi);
 mem_operator.Alloc = cmnMemAlloc;
 mem_operator.Copy = cmnMemCopy;
 mem_operator.Free = cmnMemFree;
 mem_operator.Set = cmnMemSet;
 mem_operator.Check = cmnMemCheck; 
 dati.memflag = VO_IMF_USERMEMOPERATOR;
 dati.memData = &mem_operator; 
 codecapi.Init(&handle, VO_AUDIO_CodingAAC, &dati);

 parametri.sampleRate = freq;
 parametri.bitRate = 64000;
 parametri.nChannels = can;
 parametri.adtsUsed = 1;

 if (codecapi.SetParam(handle, VO_PID_AAC_ENCPARAM, &parametri) != VO_ERR_NONE) {
   return -1;
 }

}


int scrive(uint8_t* immissioBuf, int16_t* buffConv, int letti, FILE *ex) {

 VO_CODECBUFFER immissio, emissio;
 VO_AUDIO_OUTPUTINFO emissio_info;
 int i;
 uint8_t exbuf[20480];

 for (i = 0; i < letti/2; i++) {
    const uint8_t* in = &immissioBuf[2*i];
    buffConv[i] = in[0] | (in[1] << 8);
 }

 immissio.Buffer = (uint8_t*) buffConv;
 immissio.Length = letti;
 codecapi.SetInputData(handle, &immissio);

 emissio.Buffer = exbuf;
 emissio.Length = sizeof(exbuf);
 if (codecapi.GetOutputData(handle, &emissio, &emissio_info) != VO_ERR_NONE) {
    return -1;
 }
 
 fwrite(exbuf, 1, emissio.Length, ex);

 return (0);

}

Tale codice in linguaggio C in questo esempio sarà posto nella cartella Dati dell'applicativo Gambas, e andrà poi trasformato in una libreria condivisa .so , che - come già accennato - sarà richiamata nel codice Gambas.


Il codice Gambas potrà essere il seguente:

Library "/tmp/codecapi"

Private Extern codifica_parametri(fr As Integer, ca As Integer) As Integer

Private Extern scrive(data As Pointer, conbuf As Pointer, legit As Integer, exitus As Pointer) As Integer


Library "libc:6"

Private Const SEEK_SET As Integer = 0

' FILE *fopen(const char *path, const char *mode);
' Apre il file path associandolo ad uno stream.
Private Extern fopen(outfile As String, wb As String) As Pointer

' int fseek(FILE *stream, long offset, int whence)
' Imposta l'indicatore di posizione del file.
Private Extern fseek(strP As Pointer, offset As Long, whence As Integer) As Integer
 
' size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
' Legge da stream nmemb elementi, ciascuno di dimensione size.
' La funzione fread() ritorna il numero di elementi letti.
Private Extern fread(ptr As Pointer, size As Integer, nmemb As Integer, strP As Pointer) As Integer

' int fclose(FILE *stream)
' Chiude il file associato a stream durante l'apertura con fopen().
Private Extern fclose(strP As Pointer) As Integer


Public Sub Main()

 Dim percorsoFileWav As String = "/percorso/del/file.wav"
 Dim percorsoFileAcc As String = "/tmp/audioACC.acc"
 Dim err, frequenza, canali, introDim, letti As Integer
 Dim inP, outP As Pointer
 Dim dati_intest As Byte[]
 Dim introBuff, conversioBuff As Pointer
 
 
' Viene generata la libreria ad hoc da noi scritta:
  Shell "gcc -o /tmp/codecapi.so " & Application.Path &/ "codecapi.c -shared -fPIC /usr/lib/x86_64-linux-gnu/libvo-aacenc.so.0.0.3" Wait

 
  dati_intest = estraiDatiWAV(percorsoFileWav)
  canali = dati_intest[22]
  frequenza = Val("&" & Hex(dati_intest[27]) & Hex(dati_intest[26]) & Hex(dati_intest[25]) & Hex(dati_intest[24]))

  introDim = canali * 2 * 1024
  introBuff = Alloc(introDim)
  conversioBuff = Alloc(introDim)

  err = codifica_parametri(frequenza, canali)
  If err = -1 Then Error.Raise("Impossibile impostare i parametri di codifica !")    

  inP = fopen(percorsoFileWav, "rb")

  outP = fopen(percorsoFileAcc, "wb")
  If IsNull(outP) Then Error.Raise("Impossibile aprire il file !")
   
  fseek(inp, 44, SEEK_SET)
   
  While True
     
    letti = fread(introBuff, 1, introDim, inP)

    If letti < introDim Then Break

    err = scrive(introBuff, conversioBuff, letti, outP)
    If err = -1 Then Error.Raise("Impossibile codificare il frame !")
     
  Wend
   
' va in chiusura:
  Free(conversioBuff)
  Free(introBuff)
  fclose(outP)
  fclose(inp)

End


Private Function estraiDatiWAV(fileWav As String) As Byte[]
 
 Dim fl As File
 Dim bb As Byte[]
 Dim solo_dati, freq As Integer

  
  fl = Open fileWav For Read
 
  bb = New Byte[44]
 
  bb.Read(fl, 0, 44)
 
  fl.Close()
 
  If bb.ToString(0, 4) <> "RIFF" Then Error.Raise("Il file caricato non è di tipo 'RIFF' !")
 
  If bb.ToString(8, 4) <> "WAVE" Then Error.Raise("Il file caricato non è di tipo 'WAVE' !")
 
  If bb.ToString(12, 4) <> "fmt " Then Error.Raise("Il file caricato non è di tipo 'fmt ' !")
 
  Print "== Caratteristiche del file: "; File.Name(fileWav); " ==\n"
 
  Print "Dimensioni totali del file: "; Stat(fileWav).Size; " byte"
 
  solo_dati = Stat(fileWav).Size - 44
  Print "Dimensione dei soli dati audio grezzi: "; solo_dati; " byte"
 
  If bb[20] = 1 Then 
    Print "Formato audio: PCM"
  Else
    Print "Formato audio: "; bb[20]
  Endif
 
  Print "Canali di uscita audio: "; bb[22]
 
  freq = Val("&" & Hex(bb[27]) & Hex(bb[26]) & Hex(bb[25]) & Hex(bb[24]))
  Print "Frequenza di campionamento: Hz "; freq
 
  Print "Byte rate: "; Val("&" & Hex(bb[31]) & Hex(bb[30]) & Hex(bb[29]) & Hex(bb[28])); " Bps"
 
  Print "Risoluzione di campionamento a "; bb[34]; " bit"
 
  Print "Durata del brano: "; Date(0, 0, 0, 0, 0, 0, Fix((solo_dati * 8) / (freq * bb[34] * bb[22])) * 1000)
 
  Return bb

End