Encoder simple demo

Next Topic
 
classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Encoder simple demo

trustfm
Hello to the forum !
I managed to make an encoder that can also encode only a portion of input file (wav ogg opus mp3)
The procedure has three operations  
*CutEnd
*CutStart
*Encode

The demo works with libsndfile library and portaudio
The only problem that i do not know why works like this is the following portion
I had to add a multiplier in the case of encoding ogg or mp3
The multiplier is equal to 1 on the wav encoding
 

    if RadioButtonWav.Checked then begin
      outformatst := '.wav';
      outformat := 0;
      multiplier:=1;
    end else if RadioButtonOgg.Checked then begin
      outformatst := '.ogg';
      outformat := 3;
      multiplier:=numchan;
    end else if RadioButtonMp3.Checked then begin
      outformatst := '.mp3';
      outformat := 4;
      multiplier:=numchan;
    end;

    //FramesCount : default: -1 (65536) 64chunks*1024 - max 32*multikier
    InputIndex1 := uos_AddFromFile(PlayerIndex1, PChar(songIn),-1,sampleformat,32*1024*multiplier);

    numchan := uos_InputGetChannels(PlayerIndex1, InputIndex1);
    samplerate:=uos_InputGetSampleRate(PlayerIndex1, InputIndex1);


    if RadioButtonWav.Checked then begin
       uos_AddIntoFile(PlayerIndex1, PChar('out_final'+outformatst),samplerate,numchan,sampleformat,32*1024, outformat); //-1
    end else begin //ogg or mp3
       uos_AddIntoFile(PlayerIndex1, PChar('out_final'+outformatst),samplerate,numchan,sampleformat,32*1024, outformat,FloatSpinEditVBREncoding.Value,FloatSpinEditCompressionLevel.Value); //-1
    end;  


https://trim.uk.to/my-simple-encoder.zip
The file is large since has demo audios
Hope someone explain the multiplier issue
Thanks

 
Reply | Threaded
Open this post in threaded view
|

Re: Encoder simple demo

fredvs
Administrator
This post was updated on .
Hello.

Is it working with your "multiplier" workaround?

In uos it uses also a "ratio" for libmpg123 to be compatible with code used for libsndfile.
One needs the length of the buffer, the other the length of the frame (size of the buffer div channels).

And libsndfile uses the code of libmpg123 to decode/encode mp3 format (see their readme.txt).

Decoding of mp3 is recent in libsndfile, so it seems that they did not adapt it to use the same way as previous other format, like ogg, flac, wav (and now in trunk opus).

Sorry I can not explain that "multiplier" but if it works for you, better to be happy and maybe somebody will explain why one day.
Reply | Threaded
Open this post in threaded view
|

Re: Encoder simple demo

trustfm
Hello yes the demo is fully functional
If the multiplier is removed the generated tracks (mp3,ogg) are twice long and say track 1 and not track2
Notice that the multiplier is applied only at
InputIndex1 := uos_AddFromFile(PlayerIndex1, PChar(songIn),-1,sampleformat,32*1024*multiplier);
and NOT at
uos_AddIntoFile(PlayerIndex1, PChar('out_final'+outformatst),samplerate,numchan,sampleformat,32*1024, outformat,FloatSpinEditVBREncoding.Value,FloatSpinEditCompressionLevel.Value); //-1

Now i am thinking how to do a join of two files
Reply | Threaded
Open this post in threaded view
|

Re: Encoder simple demo

fredvs
Administrator
But you are using only libsndfile to decode ogg and mp3 files, dont you?

So uos threat the input files the same way depending of the library used, no matter if it is a ogg, flac, wav and mp3 if only libsndfile is used.

Now if libsndfile needs different parameters depending of the format, it is sad (but it seems to be the case).
Reply | Threaded
Open this post in threaded view
|

Re: Encoder simple demo

trustfm
yes this might be the case
Reply | Threaded
Open this post in threaded view
|

Re: Encoder simple demo

trustfm
In reply to this post by trustfm
Hello !
Here is a function that can join two or more audiofiles into one single file
based on this example
https://github.com/libsndfile/libsndfile/blob/ea9ff560b4c2086c2f1cae3f02287768a0de4673/programs/sndfile-concat.c
The joined files must have the same samplerate and number of channels 


//////////////////////////////////////////////////////
//////////////////////////////////////////////////////
//////////////////////////////////////////////////////
uos_libsndfile_aux.pas 
//Made By TrustFm
//https://github.com/libsndfile/libsndfile/blob/ea9ff560b4c2086c2f1cae3f02287768a0de4673/programs/sndfile-concat.c
unit uos_libsndfile_aux;  

{$mode objfpc}{$H+}  

interface

uses
  classes,sysutils,
  uos,
  uos_flat,
  uos_libsndfile,
  Dialogs,
  Buttons,
  Forms,
  ctypes;
 
const
    BUFFER_LEN = 65536;


function JoinAudioFiles(var FilesInStringList:TStringList; FileOutString:string; StartObj: TObject=nil; AbortObj: TObject=nil):string;
 
   
implementation


////////////////////////////////////////////////////
////////////////////////////////////////////////////


{
static void concat_data_int (SNDFILE *wfile, SNDFILE *rofile, int channels)
        static int data [BUFFER_LEN] ; //declare an array of BUFFER_LEN integers
        int frames, readcount ;

        frames = BUFFER_LEN / channels ;
        readcount = frames ;

        sf_seek (wfile, 0, SEEK_END) ;

        while (readcount > 0)
        { readcount = (int) sf_readf_int (rofile, data, frames) ;
                sf_writef_int (wfile, data, readcount) ;
                } ;

        return ;
/* concat_data_int */
}

procedure concat_data_int(wfile: TSNDFILE_HANDLE; rofile: TSNDFILE_HANDLE; channels: ctypes.cint; AbortObj: TObject=nil);
var
  //buffer: array of ctypes.cint;
  //data: ctypes.pcint;
  buffer: TDArFloat;
  frames,readcount: Tuos_count_t;
begin

  //SetLength(buffer, BUFFER_LEN);
  //data := @buffer;

  SetLength(buffer, BUFFER_LEN);

  frames := BUFFER_LEN div channels;
  readcount:= frames;

  sf_seek (wfile, 0, SEEK_END) ;

  while (readcount > 0) do begin
    readcount := sf_readf_int(rofile, @buffer[0], frames);  //data[0]
    sf_writef_int(wfile, @buffer[0], readcount);
    if AbortObj is TBitBtn then begin
      if (not TBitBtn(AbortObj).Enabled) then begin
        break;
      end;
    end;
  end;

end;

{
static void
concat_data_fp (SNDFILE *wfile, SNDFILE *rofile, int channels)
{ static double data [BUFFER_LEN] ;
        int frames, readcount ;

        frames = BUFFER_LEN / channels ;
        readcount = frames ;

        sf_seek (wfile, 0, SEEK_END) ;

        while (readcount > 0)
        { readcount = (int) sf_readf_double (rofile, data, frames) ;
                sf_writef_double (wfile, data, readcount) ;
                } ;

        return ;
} /* concat_data_fp */
}
procedure concat_data_fp(wfile: TSNDFILE_HANDLE; rofile: TSNDFILE_HANDLE; channels: ctypes.cint; AbortObj: TObject=nil);
var
  //buffer: array of ctypes.cdouble;
  //data : ctypes.pcdouble;
  buffer: TDArFloat;
  frames,readcount: Tuos_count_t;
begin

  //SetLength(buffer, BUFFER_LEN);
  //data := @buffer;

  SetLength(buffer, BUFFER_LEN);

  frames := BUFFER_LEN div channels;
  readcount:= frames;

  sf_seek (wfile, 0, SEEK_END) ;

  while (readcount > 0) do begin
    readcount := sf_readf_double(rofile, @buffer[0], frames);
    sf_writef_double(wfile, @buffer[0], readcount);
    if (not TBitBtn(AbortObj).Enabled) then begin
      break;
    end;
  end;

end;


{
int
main (int argc, char *argv [])
{ const char *progname, *outfilename ;
        SNDFILE *outfile, **infiles ;
        SF_INFO sfinfo_out, sfinfo_in ;
        void (*func) (SNDFILE*, SNDFILE*, int) ;
        int k ;

        progname = program_name (argv [0]) ;

        if (argc < 4)
                usage_exit (progname) ;

        argv ++ ;
        argc -- ;

        argc -- ;
        outfilename = argv [argc] ;

        if ((infiles = calloc (argc, sizeof (SNDFILE*))) == NULL)
        { printf ("\nError : Malloc failed.\n\n") ;
                exit (1) ;
                } ;

        memset (&sfinfo_in, 0, sizeof (sfinfo_in)) ;

        if ((infiles [0] = sf_open (argv [0], SFM_READ, &sfinfo_in)) == NULL)
        { printf ("\nError : failed to open file '%s'.\n\n", argv [0]) ;
                exit (1) ;
                } ;

        sfinfo_out = sfinfo_in ;

        for (k = 1 ; k < argc ; k++)
        { if ((infiles [k] = sf_open (argv [k], SFM_READ, &sfinfo_in)) == NULL)
                { printf ("\nError : failed to open file '%s'.\n\n", argv [k]) ;
                        exit (1) ;
                        } ;

                if (sfinfo_in.channels != sfinfo_out.channels)
                { printf ("\nError : File '%s' has %d channels (should have %d).\n\n", argv [k], sfinfo_in.channels, sfinfo_out.channels) ;
                        exit (1) ;
                        } ;
                } ;

        if ((outfile = sf_open (outfilename, SFM_WRITE, &sfinfo_out)) == NULL)
        { printf ("\nError : Not able to open input file %s.\n", outfilename) ;
                puts (sf_strerror (NULL)) ;
                exit (1) ;
                } ;

        if ((sfinfo_out.format & SF_FORMAT_SUBMASK) == SF_FORMAT_DOUBLE ||
                        (sfinfo_out.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT)
                func = concat_data_fp ;
        else
                func = concat_data_int ;

        for (k = 0 ; k < argc ; k++)
        { func (outfile, infiles [k], sfinfo_out.channels) ;
                sf_close (infiles [k]) ;
                } ;

        sf_close (outfile) ;
        free (infiles) ;

        return 0 ;
} /* main */

}


function JoinAudioFiles(var FilesInStringList:TStringList; FileOutString:string; StartObj: TObject=nil; AbortObj: TObject=nil):string;
var k:integer;
    infiles: array of TSNDFILE_HANDLE;
    outfile: TSNDFILE_HANDLE;
    sfinfo_in, sfinfo_out : TSF_INFO;
begin

 SetLength(infiles, FilesInStringList.Count-1);

 result:='';
 infiles[0] := sf_open (FilesInStringList[0], SFM_READ, sfinfo_in);
 if infiles[0]=nil then begin

    result:='Error: failed to open file 1';

 end else begin // infiles[0] opened

   sfinfo_out:=sfinfo_in;

   for k:=1 to FilesInStringList.Count-1 do begin

      infiles[k] := sf_open(FilesInStringList[k], SFM_READ, sfinfo_in);
      if infiles[k]=nil then begin
        result:='Error: failed to open file ' + IntToStr(k+1);
        break;
      end else begin

  if (sfinfo_in.channels <> sfinfo_out.channels) then begin
          result:='Error: Chanels missmatch at file ' + IntToStr(k+1);
          break;
        end;

      end;

   end; //for


   if result = '' then begin

     outfile := sf_open (FileOutString, SFM_WRITE, sfinfo_out);

     if outfile=nil then begin

       result:='Error : failed to create the out file';

     end else begin //outfile opened successfully --> everything is ok


      if StartObj is TBitBtn then begin
         TBitBtn(StartObj).Enabled:=false;
      end;
      if AbortObj is TBitBtn then begin
         TBitBtn(AbortObj).Enabled:=true;
      end;

       for k:=0 to FilesInStringList.Count-1 do begin

         if StartObj is TBitBtn then begin
            TBitBtn(StartObj).Caption:='Joining ' + IntToStr(k+1) + '/' + IntToStr(FilesInStringList.Count);
         end;
         if AbortObj is TBitBtn then begin
            if (not TBitBtn(AbortObj).Enabled) then begin
               break;
            end;
         end;
         sleep(10);
         Application.Processmessages;

         if ( (sfinfo_out.format and  SF_FORMAT_SUBMASK) = SF_FORMAT_DOUBLE ) or
              ( (sfinfo_out.format and SF_FORMAT_SUBMASK) = SF_FORMAT_FLOAT )  then begin
              concat_data_fp(outfile, infiles[k], sfinfo_out.channels,AbortObj) ;
         end else begin
              concat_data_int(outfile, infiles[k], sfinfo_out.channels,AbortObj) ;
         end;

         sf_close(infiles[k]) ;

       end; //for k

       if StartObj is TBitBtn then begin
          TBitBtn(StartObj).Caption:='Join Audio Files';
          TBitBtn(StartObj).Enabled:=true;
       end;
       if AbortObj is TBitBtn then begin
          TBitBtn(AbortObj).Enabled:=false;
       end;

     end;

     sf_close (outfile) ;

   end;

 end; //infile1


end;

end.

//////////////////////////////////////////////////////
//////////////////////////////////////////////////////
//////////////////////////////////////////////////////
usage
procedure TFormMain.BitBtnJoinAudioFilesClick(Sender: TObject);
var InputFileList : TStringList;
    error:string;
begin
    InputFileList := TStringList.Create;
    InputFileList.Add('input.mp3');
    InputFileList.Add('input.ogg');
    InputFileList.Add('input2.mp3');
    error:=JoinAudioFiles(InputFileList,'out.mp3',BitBtnJoinAudioFiles,BitBtnJoinAudioFilesAbort);
    if error<>'' then begin
      ShowMessage(error);
    end;
    InputFileList.Free;
end;

procedure TFormMain.BitBtnJoinAudioFilesAbortClick(Sender: TObject);
begin
  BitBtnJoinAudioFilesAbort.Enabled:= not BitBtnJoinAudioFilesAbort.Enabled;
  if not BitBtnJoinAudioFilesAbort.Enabled then begin
    ShowMessage('aborted');
    BitBtnJoinAudioFiles.Enabled:=true;
  end;
end;  
Reply | Threaded
Open this post in threaded view
|

Re: Encoder simple demo

trustfm
Small request . Would be nice to have at the AddFromFile function a starting delay . Would be useful at the multiinput example  
Reply | Threaded
Open this post in threaded view
|

Re: Encoder simple demo

fredvs
Administrator
Hello.

Sorry for late answer, I am super busy...
Please for request, use https://github.com/fredvs/uos/issues or better do a pull-request https://github.com/fredvs/uos/pulls