FLAC Encoder by stream

Share your advanced PureBasic knowledge/code with the community.
User avatar
oryaaaaa
Addict
Addict
Posts: 825
Joined: Mon Jan 12, 2004 11:40 pm
Location: Okazaki, JAPAN

FLAC Encoder by stream

Post by oryaaaaa »

have fun 8)

Code: Select all

; Author oryaaaaa.  2009/11/7
;  ----------------------------------
;/  You need flac-1.2.1-devel-win.zip
;/   flac-1.2.1-devel-win\lib\libFLAC.lib and libFLAC.dll
;/   IF you use FLAC API LIB, your software applied LGPL License
;/   But Flac.exe and others are GPL License.

Enumeration ;/FLAC__StreamEncoderWriteStatus
  #FLAC__STREAM_ENCODER_WRITE_STATUS_OK
  #FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR
EndEnumeration

ImportC "libFLAC.lib"
  FLAC__stream_encoder_delete(encoder.l)
  FLAC__stream_encoder_finish(encoder.l)
  FLAC__stream_encoder_get_verify(encoder.l)
  FLAC__stream_encoder_init_stream(encoder.l, writecallback.l, seekcallback.l, tellcallback.l, Metacallback.l)
  FLAC__stream_encoder_new( )
  FLAC__stream_encoder_process(encoder.l, sample.l, len.l) ;/ crush
  FLAC__stream_encoder_process_interleaved(encoder.l, sample.l, len.l)
  FLAC__stream_encoder_set_bits_per_sample(encoder.l, bit.b)
  FLAC__stream_encoder_set_blocksize(encoder.l, block.l)
  FLAC__stream_encoder_set_channels(encoder.l, ch.l)
  FLAC__stream_encoder_set_compression_level( encoder.l, level.l)
  FLAC__stream_encoder_set_do_escape_coding(encoder.l, a.l)
  FLAC__stream_encoder_set_do_exhaustive_model_search(encoder.l, flg.b)
  FLAC__stream_encoder_set_do_md5(encoder.l, flg.b) ;/ no effect
  FLAC__stream_encoder_set_do_mid_side_stereo(encoder, flg.b)
  FLAC__stream_encoder_set_do_qlp_coeff_prec_search(encoder.l, flg.b)
  FLAC__stream_encoder_set_loose_mid_side_stereo(encoder, flg.b)
  FLAC__stream_encoder_set_max_lpc_order(encoder.l, flg.b)
  FLAC__stream_encoder_set_max_residual_partition_order(encoder.l, flg.l)
  FLAC__stream_encoder_set_metadata(encoder, meta.l)
  FLAC__stream_encoder_set_min_residual_partition_order(encoder.l, flg.l)
  FLAC__stream_encoder_set_rice_parameter_search_dist(encoder.l, flg.l)
  FLAC__stream_encoder_set_sample_rate(encoder.l, rate.l)
  FLAC__stream_encoder_set_streamable_subset(encoder.l, flg.b)
  FLAC__stream_encoder_set_total_samples_estimate(encoder.l, flg.l) ;/ INT64, Dont use
  FLAC__stream_encoder_set_verify(encoder.l, flg.b)
EndImport

Global callFile = CreateFile(#PB_Any, "test.flac")
Global File = ReadFile(#PB_Any, OpenFileRequester("","", "*.wav",0))

ProcedureC FLAC__StreamEncoderWriteStatus(*encoder, *buffer, Bytes, samples, current_frame, *cliend_data)
  If WriteData(callFile, *buffer, Bytes)
    ProcedureReturn #FLAC__STREAM_ENCODER_WRITE_STATUS_OK
  Else
    ProcedureReturn #FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR
  EndIf
EndProcedure

encoder.l = FLAC__stream_encoder_new()
FLAC__stream_encoder_set_channels(encoder, 2)
FLAC__stream_encoder_set_bits_per_sample(encoder, 16)
FLAC__stream_encoder_set_sample_rate(encoder, 44100)
FLAC__stream_encoder_set_blocksize(encoder, 4096)
FLAC__stream_encoder_set_compression_level(encoder, 8)
FLAC__stream_encoder_init_stream(encoder, @FLAC__StreamEncoderWriteStatus(), 0, 0, 0)

Global *FB = AllocateMemory(Lof(File)*2)
ReadData(File, *FB, Lof(File))
;/ INT16 to INT32 variable
For pos.l=Lof(File)-2 To 0  Step -2
  PokeL(*FB+pos*2, PeekW(*FB+pos))
Next

For pos=0 To Int(Lof(File)*2-7) Step 8
  FLAC__stream_encoder_process_interleaved(encoder, *FB+pos, 1)
Next

FLAC__stream_encoder_finish(encoder)
CloseFile(File)

;/ FLAC__stream_encoder_set_total_samples_estimate
Size.q = (i Dont know)
FileSeek(callFile, 22)
WriteAsciiCharacter(callFile, (Size >>32)&$FF)
WriteAsciiCharacter(callFile, (Size >>24)&$FF)
WriteAsciiCharacter(callFile, (Size >>16)&$FF)
WriteAsciiCharacter(callFile, (Size >>8)&$FF)
WriteAsciiCharacter(callFile, Size&$FF) 

CloseFile(callFile)
chris319
Enthusiast
Enthusiast
Posts: 782
Joined: Mon Oct 24, 2005 1:05 pm

Re: FLAC Encoder by stream

Post by chris319 »

This version fixes a problem when dealing with mono files and adds a console window to give the user feedback. Original version wouldn't compile due to problem with initialization of variable Size.q. Also changed variable encoder to pointer *encoder. Program now quits if user doesn't select a file. Also properly frees memory, closes files and releases encoder.

Code: Select all

    ; Author oryaaaaa.  2009/11/7
    ;  ----------------------------------
    ;/  You need flac-1.2.1-devel-win.zip
    ;/   flac-1.2.1-devel-win\lib\libFLAC.lib and libFLAC.dll
    ;/   IF you use FLAC API LIB, your software applied LGPL License
    ;/   But Flac.exe and others are GPL License.

;MODIFIED BY chris319 ON 2012/6/2

#CHANNELS = 2
#BIT_DEPTH = 16

Global bytesPerSample = (#BIT_DEPTH / 8) * #CHANNELS

    Enumeration ;/FLAC__StreamEncoderWriteStatus
      #FLAC__STREAM_ENCODER_WRITE_STATUS_OK
      #FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR
    EndEnumeration

    ImportC "libFLAC.lib"
      FLAC__stream_encoder_delete(encoder.l)
      FLAC__stream_encoder_finish(encoder.l)
      FLAC__stream_encoder_get_verify(encoder.l)
      FLAC__stream_encoder_init_stream(encoder.l, writecallback.l, seekcallback.l, tellcallback.l, Metacallback.l)
      FLAC__stream_encoder_new( )
      FLAC__stream_encoder_process(encoder.l, sample.l, len.l) ;/ crush
      FLAC__stream_encoder_process_interleaved(encoder.l, sample.l, len.l)
      FLAC__stream_encoder_set_bits_per_sample(encoder.l, bit.b)
      FLAC__stream_encoder_set_blocksize(encoder.l, block.l)
      FLAC__stream_encoder_set_channels(encoder.l, ch.l)
      FLAC__stream_encoder_set_compression_level( encoder.l, level.l)
      FLAC__stream_encoder_set_do_escape_coding(encoder.l, a.l)
      FLAC__stream_encoder_set_do_exhaustive_model_search(encoder.l, flg.b)
      FLAC__stream_encoder_set_do_md5(encoder.l, flg.b) ;/ no effect
      FLAC__stream_encoder_set_do_mid_side_stereo(encoder, flg.b)
      FLAC__stream_encoder_set_do_qlp_coeff_prec_search(encoder.l, flg.b)
      FLAC__stream_encoder_set_loose_mid_side_stereo(encoder, flg.b)
      FLAC__stream_encoder_set_max_lpc_order(encoder.l, flg.b)
      FLAC__stream_encoder_set_max_residual_partition_order(encoder.l, flg.l)
      FLAC__stream_encoder_set_metadata(encoder, meta.l)
      FLAC__stream_encoder_set_min_residual_partition_order(encoder.l, flg.l)
      FLAC__stream_encoder_set_rice_parameter_search_dist(encoder.l, flg.l)
      FLAC__stream_encoder_set_sample_rate(encoder.l, rate.l)
      FLAC__stream_encoder_set_streamable_subset(encoder.l, flg.b)
      FLAC__stream_encoder_set_total_samples_estimate(encoder.l, flg.l) ;/ INT64, Dont use
      FLAC__stream_encoder_set_verify(encoder.l, flg.b)
    EndImport
    
    Global callFile, File

    ProcedureC FLAC__StreamEncoderWriteStatus(*encoder, *buffer, Bytes, samples, current_frame, *client_data)
      If WriteData(callFile, *buffer, Bytes)
        ProcedureReturn #FLAC__STREAM_ENCODER_WRITE_STATUS_OK
      Else
        ProcedureReturn #FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR
      EndIf
    EndProcedure

    File = ReadFile(#PB_Any, OpenFileRequester("","", "*.wav",0))
    If file = 0: End: EndIf ;QUIT IF USER DOES NOT SELECT A FILE

    OpenConsole()

    callFile = CreateFile(#PB_Any, "test.flac")

    Print("Encoding ... ")

    *encoder = FLAC__stream_encoder_new()
    FLAC__stream_encoder_set_channels(*encoder, #CHANNELS)
;    FLAC__stream_encoder_set_bits_per_sample(*encoder, #BIT_DEPTH) ;NOT NEEDED -- ENCODER MUST HAVE 32-BIT SAMPLES GOING IN
    FLAC__stream_encoder_set_sample_rate(*encoder, 44100)
    ;FLAC__stream_encoder_set_blocksize(*encoder, 4096)
    FLAC__stream_encoder_set_compression_level(*encoder, 8)
    
    FLAC__stream_encoder_init_stream(*encoder, @FLAC__StreamEncoderWriteStatus(), 0, 0, 0)

    Global *FB = AllocateMemory(Lof(File) * 2)
    ReadData(File, *FB, Lof(File))

    ; INT16 to INT32 variable
    For pos.l = Lof(File) - 2 To 0  Step -2
      PokeL(*FB + (pos << 1), PeekW(*FB + pos))
    Next

;    For pos = 0 To (Lof(File) * 2 - 7) Step 8
;      FLAC__stream_encoder_process_interleaved(*encoder, *FB + pos, 1)
;    Next

    FLAC__stream_encoder_process_interleaved(*encoder, *FB, Lof(File) / bytesPerSample)
    FLAC__stream_encoder_finish(*encoder)
    FLAC__stream_encoder_delete(*encoder)

    CloseFile(File)

    ;/ FLAC__stream_encoder_set_total_samples_estimate
    Size.q = 0 ;(i Dont know)
    FileSeek(callFile, 22)
    WriteAsciiCharacter(callFile, (Size >> 32) &$ FF)
    WriteAsciiCharacter(callFile, (Size >> 24) &$ FF)
    WriteAsciiCharacter(callFile, (Size >> 16) &$ FF)
    WriteAsciiCharacter(callFile, (Size >>  8) &$ FF)
    WriteAsciiCharacter(callFile, Size & $FF)

    CloseFile(callFile)
    FreeMemory(*FB)

    PrintN(Chr(10) + Chr(10) + "Finished. Press any key to quit.")

    While Inkey() = "": Delay(10): Wend
    CloseConsole()
    End
chris319
Enthusiast
Enthusiast
Posts: 782
Joined: Mon Oct 24, 2005 1:05 pm

Now with ASM!

Post by chris319 »

The 16- to 32-bit converter now written in ASM. Turn off the debugger to speed it up. The development files are available here:

http://sourceforge.net/projects/flac/files/flac-win/

It is necessary to convert samples to 32 bits for FLAC to work; however, FLAC only supports up to 24-bit samples.

Code: Select all

        ; Author oryaaaaa.  2009/11/7
        ;  ----------------------------------
        ;/  You need flac-1.2.1-devel-win.zip
        ;/   flac-1.2.1-devel-win\lib\libFLAC.lib and libFLAC.dll
        ;/   IF you use FLAC API LIB, your software applied LGPL License
        ;/   But Flac.exe and others are GPL License.

;MODIFIED BY chris319 ON 2012/6/4

Global my_WFE.WAVEFORMATEX, subChunk2Size
Global bytesPerSample

        Enumeration ;/FLAC__StreamEncoderWriteStatus
          #FLAC__STREAM_ENCODER_WRITE_STATUS_OK
          #FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR
        EndEnumeration

        ImportC "libFLAC.lib"
          FLAC__stream_encoder_delete(encoder.l)
          FLAC__stream_encoder_finish(encoder.l)
          FLAC__stream_encoder_get_verify(encoder.l)
          FLAC__stream_encoder_init_stream(encoder.l, writecallback.l, seekcallback.l, tellcallback.l, Metacallback.l)
          FLAC__stream_encoder_new( )
          FLAC__stream_encoder_process(encoder.l, sample.l, len.l) ;/ crush
          FLAC__stream_encoder_process_interleaved(encoder.l, sample.l, len.l)
          FLAC__stream_encoder_set_bits_per_sample(encoder.l, bit.b)
          FLAC__stream_encoder_set_blocksize(encoder.l, block.l)
          FLAC__stream_encoder_set_channels(encoder.l, ch.l)
          FLAC__stream_encoder_set_compression_level( encoder.l, level.l)
          FLAC__stream_encoder_set_do_escape_coding(encoder.l, a.l)
          FLAC__stream_encoder_set_do_exhaustive_model_search(encoder.l, flg.b)
          FLAC__stream_encoder_set_do_md5(encoder.l, flg.b) ;/ no effect
          FLAC__stream_encoder_set_do_mid_side_stereo(encoder, flg.b)
          FLAC__stream_encoder_set_do_qlp_coeff_prec_search(encoder.l, flg.b)
          FLAC__stream_encoder_set_loose_mid_side_stereo(encoder, flg.b)
          FLAC__stream_encoder_set_max_lpc_order(encoder.l, flg.b)
          FLAC__stream_encoder_set_max_residual_partition_order(encoder.l, flg.l)
          FLAC__stream_encoder_set_metadata(encoder, meta.l)
          FLAC__stream_encoder_set_min_residual_partition_order(encoder.l, flg.l)
          FLAC__stream_encoder_set_rice_parameter_search_dist(encoder.l, flg.l)
          FLAC__stream_encoder_set_sample_rate(encoder.l, rate.l)
          FLAC__stream_encoder_set_streamable_subset(encoder.l, flg.b)
          FLAC__stream_encoder_set_total_samples_estimate(encoder.l, flg.l) ;/ INT64, Dont use
          FLAC__stream_encoder_set_verify(encoder.l, flg.b)
        EndImport
       
        Global callFile, File

ProcedureC FLAC__StreamEncoderWriteStatus(*encoder, *buffer, Bytes, samples, current_frame, *client_data)
;Debug BYTES        
  If WriteData(callFile, *buffer, Bytes)
            ProcedureReturn #FLAC__STREAM_ENCODER_WRITE_STATUS_OK
          Else
            ProcedureReturn #FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR
          EndIf
EndProcedure

        OpenConsole()

        file = ReadFile(0, OpenFileRequester("","", "*.wav",0))
        If file = 0: End: EndIf ;QUIT IF USER DOES NOT SELECT A FILE

Print("Encoding ... ")
fileLen = Lof(0)
chunkID$ = "": For ct = 1 To 4: chunkID$ = chunkID$ + Chr(ReadAsciiCharacter(0)):Next
chunkSize = ReadLong(0)
fmt$ = "": For ct = 1 To 4: fmt$ = fmt$ + Chr(ReadAsciiCharacter(0)):Next
subchunk1ID$ = "": For ct = 1 To 4: subchunk1ID$ = subchunk1ID$ + Chr(ReadAsciiCharacter(0)):Next
subchunk1Size = ReadLong(0)
my_WFE\wFormatTag = ReadWord(0)
my_WFE\nChannels = ReadWord(0)
my_WFE\nSamplesPerSec = ReadLong(0)
my_WFE\nAvgBytesPerSec = ReadLong(0)
my_WFE\nBlockAlign = ReadWord(0)
my_WFE\wBitsPerSample = ReadWord(0)
my_WFE\cbSize = ReadWord(0)
If my_WFE\cbSize = 22
  validBitsPerSample = ReadWord(0)
  channelMask = ReadLong(0)
  GUID$ = "": For ct = 1 To 16: GUID$ = GUID$ + Chr(ReadAsciiCharacter(0)):Next
ElseIf my_WFE\cbSize <> 0 ;NO cbSize PRESENT
  FileSeek(0, Loc(0) - 2)
EndIf
subChunk2ID$ = "": For ct = 1 To 4: subChunk2ID$ = subChunk2ID$ + Chr(ReadAsciiCharacter(0)):Next
subChunk2Size = ReadLong(0)
bytesPerSample = (my_WFE\wBitsPerSample / 8) * my_WFE\nChannels

callFile = CreateFile(#PB_Any, "test.flac")

        *FB = AllocateMemory(subChunk2Size * my_WFE\nBlockAlign)
        FB = *FB
        ReadData(0, *FB, subChunk2Size) ;LOAD FILE

        *encoder = FLAC__stream_encoder_new()
        FLAC__stream_encoder_set_channels(*encoder, my_WFE\nChannels)
        result = FLAC__stream_encoder_set_bits_per_sample(*encoder, my_WFE\wBitsPerSample)
        FLAC__stream_encoder_set_sample_rate(*encoder, 44100)
        ;FLAC__stream_encoder_set_blocksize(*encoder, 4096)
        FLAC__stream_encoder_set_compression_level(*encoder, 0)
      
        FLAC__stream_encoder_init_stream(*encoder, @FLAC__StreamEncoderWriteStatus(), 0, 0, 0)

;MUST CONVERT 16-BIT SAMPLES TO 32 BITS
esi_temp.l ;STORAGE FOR NON-VOLATILE REGISTER
eax_temp.l
EnableASM ;TURN OFF DEBUGGER FOR A REAL SPEED IMPROVEMENT!
MOV esi_temp,ESI ;SAVE NON-VOLATILE REGISTER
mov eax_temp,eax

MOV ESI,FB ;LOAD ESI REGISTER WITH BUFFER ADDRESS
MOV ECX,subChunk2Size ;OFFSET
SUB ECX,2

loop: MOV AX,word[ESI+ECX] ;ADDRESS IS ALREADY IN ESI REGISTER, CX HOLDS SAMPLE
CWDE ;CONVERT AX FROM 16 TO 32 BITS
MOV [ESI+ECX*2],EAX
SUB ECX,2
JGE l_loop

MOV ESI,esi_temp ;RESTORE NON-VOLATILE REGISTER
mov eax,eax_temp
DisableASM 

FLAC__stream_encoder_process_interleaved(*encoder, *FB, subChunk2Size / my_WFE\nBlockAlign)

FLAC__stream_encoder_finish(*encoder)
FLAC__stream_encoder_delete(*encoder)

before = Lof(0)
after = Lof(callFile)
PrintN(Chr(10) + Chr(10) + "Original file size:   " + Str(before))
CloseFile(0)
PrintN("Compressed file size: " + Str(after))
PrintN(Chr(10) + "Compressed file size is " + StrF(after / before * 100, 0) + " percent of uncompressed file size.")
        ;/ FLAC__stream_encoder_set_total_samples_estimate
         Size.q = 0 ;(i Dont know)
         FileSeek(callFile, 22)
         WriteAsciiCharacter(callFile, (Size >> 32) &$ FF)
         WriteAsciiCharacter(callFile, (Size >> 24) &$ FF)
         WriteAsciiCharacter(callFile, (Size >> 16) &$ FF)
         WriteAsciiCharacter(callFile, (Size >>  8) &$ FF)
         WriteAsciiCharacter(callFile, Size & $FF)

        CloseFile(callFile)
        FreeMemory(*FB)

        Print(Chr(10) + "Press any key to quit.")

        While Inkey() = "": Delay(10): Wend
        CloseConsole()
        End
Post Reply