1

I am playing with BASS from http://www.un4seen.com/.

I need to create a flac file(16bits) or flac stream from user speaking on Microphone.

I have seen this demo in BASS source code. There is a bassenc_flac.dll as well with these functions:

function BASS_Encode_FLAC_Start(handle:DWORD; options:PChar; flags:DWORD; proc:ENCODEPROCEX; user:Pointer): HENCODE; {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF}; external bassencflacdll;
function BASS_Encode_FLAC_StartFile(handle:DWORD; options:PChar; flags:DWORD; filename:PChar): HENCODE; {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF}; external bassencflacdll;

How could I change the next code to encode the audio to flac file or stream?

From RecordTest BASS demo

(* This is called while recording audio *)
function RecordingCallback(Handle: HRECORD; buffer: Pointer; length: DWORD; user: Pointer): boolean; stdcall;
var level:dword;
begin
    level:=BASS_ChannelGetLevel(Handle);
    // Copy new buffer contents to the memory buffer
    Form1.WaveStream.Write(buffer^, length);
    // Allow recording to continue
    Result := True;
end;

(* Start recording to memory *)
procedure TForm1.StartRecording;
begin
    if ComboBox1.ItemIndex < 0 then Exit;
    if WaveStream.Size > 0 then
    begin   // free old recording
        BASS_StreamFree(chan);
        WaveStream.Clear;
    end;
    // generate header for WAV file
    with WaveHdr do
    begin
        riff := 'RIFF';
        len := 36;
        cWavFmt := 'WAVEfmt ';
        dwHdrLen := 16;
        wFormat := 1;
        wNumChannels := 2;
        dwSampleRate := 44100;
        wBlockAlign := 4;
        dwBytesPerSec := 176400;
        wBitsPerSample := 16;
        cData := 'data';
        dwDataLen := 0;
    end;
    WaveStream.Write(WaveHdr, SizeOf(WAVHDR));
    // start recording @ 44100hz 16-bit stereo
    rchan := BASS_RecordStart(44100, 2, 0, @RecordingCallback, nil);
    if rchan = 0 then
    begin
        MessageDlg('Couldn''t start recording!', mtError, [mbOk], 0);
        WaveStream.Clear;
    end
    else
    begin
        bRecord.Caption := 'Stop';
        bPlay.Enabled := False;
        bSave.Enabled := False;
    end;
end;


(* Stop recording *)
procedure TForm1.StopRecording;
var
    i: integer;
  he:BassEnc.HENCODE;
begin
    BASS_ChannelStop(rchan);
    bRecord.Caption := 'Record';
    // complete the WAV header
    WaveStream.Position := 4;
    i := WaveStream.Size - 8;
    WaveStream.Write(i, 4);
    i := i - $24;
    WaveStream.Position := 40;
    WaveStream.Write(i, 4);
    WaveStream.Position := 0;
    // create a stream from the recorded data
    chan := BASS_StreamCreateFile(True, WaveStream.Memory, 0, WaveStream.Size, 0);
    if chan <> 0 then
    begin
        // enable "Play" & "Save" buttons
        bPlay.Enabled := True;
        bSave.Enabled := True;
    end
    else
        MessageDlg('Error creating stream from recorded data!', mtError, [mbOk], 0);
    if SaveDialog.Execute then
       WaveStream.SaveToFile(SaveDialog.FileName);

end;
Luiz Alves
  • 2,575
  • 4
  • 34
  • 75
  • Hello, Luiz! Perhaps, you should check out in `Bass` help (in a case it is exists) what functions `BASS_Encode_FLAC_Start` and `BASS_Encode_FLAC_StartFile` actually do. I cannot see any relations between these functions and that piece of code you have provided. – Josef Švejk Sep 08 '18 at 14:47
  • @Dima I don´t found sample or more info in BASS forum about how to use it. – Luiz Alves Sep 08 '18 at 15:04
  • Surely, there are people who already had dealt with `Bass` and `FLAC` encoding, but while they are not here, I would recommend you to read help for `Bass` library. What I found (as I can understand `Bass`'s help): call `BASS_Encode_FLAC_Start` function and specifiy callback-function. Go to this [help link](http://www.un4seen.com/doc/#bassenc_flac/BASS_Encode_FLAC_Start.html) and read all info related with `FLAC`. [How to prepare callback-function](http://www.un4seen.com/doc/#bass/RECORDPROC.html). – Josef Švejk Sep 08 '18 at 15:15
  • I hope you familar with `Bass` library enough to do all the things yourself, otherwise it will not be a trivial task (writing it from my *little expirience* with that library in past). – Josef Švejk Sep 08 '18 at 15:19
  • No, I am not familiar with BASS. I am just using the demos. The demos has the callback implementation. I need to generate the audio file to send to google speech recognition. – Luiz Alves Sep 08 '18 at 15:25
  • It accepts: WAVe is not supported. The formats are documented as supported though: LINEAR16 Uncompressed 16-bit signed little-endian samples. FLAC This is the recommended encoding for speech.syncrecognize and StreamingRecognize because it uses lossless compression; therefore recognition accuracy is not compromised by a lossy codec. Only 16-bit samples are supported. MULAW 8-bit samples that compand 14-bit audio samples using G.711 PCMU/mu-law. AMR Adaptive Multi-Rate Narrowband codec. sampleRate must be 8000 Hz. AMR_WB Adaptive Multi-Rate Wideband codec. sampleRate must be 16000 Hz. – Luiz Alves Sep 08 '18 at 15:26
  • How I will send the file/stream by http to google speech recoginition. I think FLAC is ok, because the size file is small. – Luiz Alves Sep 08 '18 at 15:28
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/179683/discussion-between-dima-and-luiz-alves). – Josef Švejk Sep 08 '18 at 15:28

1 Answers1

1

I have updated code because of comments that show incorrect work of previous encoder version. And I am totally agree with these comments.

In order to create an encoder to FLAC we should go to un4seen web-site and download the next files:

  1. BASS audio library 2.4
  2. BASSFLAC 2.4.4
  3. BASSenc 2.4.14
  4. BASSenc_FLAC 2.4.1.1

Go through these folders and look for the next files:

  1. bass.pas
  2. bassenc.pas
  3. bassenc_flac.pas

Now place these pas-files into one folder and add it to Library via Delphi's options.
After this step create new project, save it in separate folder.
Then go through BASS_XXX folders and look for *.dll files.
Combine them together in the folder where you have saved your project!

Now let's write some code.

Add to the uses clause bass.pas, bassenc.pas and bassenc_flac.pas. Then copy the code shown below.

  uses ..., BASS, BASSEnc, BASSEnc_FLAC;

  ...

  TForm1 = class(TForm)
    ProgressBar1: TProgressBar;      
  public
    { Public declarations }
    procedure StartEncode(SourceFileName, OutputFileName: String);
    procedure StopEncode;
  end;

...

procedure TForm1.StartEncode(SourceFileName, OutputFileName: String);
var
  PercentDone: Cardinal;
  Buffer: array [0..1024] of Byte;
begin
  Channel := BASS_StreamCreateFile(false, PChar(SourceFileName), 0, 0, BASS_MUSIC_DECODE or BASS_UNICODE);
  BASSEnc_FLAC.BASS_Encode_FLAC_StartFile(Channel, 0, BASS_ENCODE_FP_AUTO or BASS_UNICODE, PChar(OutputFileName));
  while BASS_ChannelIsActive(Channel) > 0 do
  begin
    BASS_ChannelGetData(Channel, @Buffer, 1024);
    PercentDone := Trunc(100 * (BASS_ChannelGetPosition(Channel, BASS_POS_BYTE) / BASS_ChannelGetLength(Channel, BASS_POS_BYTE)));
    ProgressBar1.Position := PercentDone;
  end;
  StopEncode;
end;

procedure TForm1.StopEncode;
begin
  BASS_Encode_Stop(Channel);
  BASS_StreamFree(Channel);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  BASS_Init(-1, 44100, 0, Application.Handle, nil);
  try   
    // Set name of file to convert it to FLAC and save it with output name
    StartEncode('SourceFileName', 'OutputFileName');
  finally
    BASS.BASS_Free;
  end;
end;

One notice:
Indeed, file encoded with previous version of the code had incorrect header (I could see it when opened file in Notepad.exe). After code has been updated I can see valid header (in Notepad, of course, because I have no professional instruments for work with audio-files).
Now you even have no need to add plugin to BASS as I did earlier. Since this I think that the encoder works as it was expected.

Josef Švejk
  • 1,047
  • 2
  • 12
  • 23
  • Thank you. You help me too much. I used your code. The flac file is created, but when I send it to Google API, I have an "400 Bad Request". I suspect the flac file generated is too big. I don't know if it is valid. If I use a external tool to convert the wave file to flac, all works well. – Luiz Alves Sep 08 '18 at 19:09
  • 1
    I'm guessing that the problem is the fact that while the above code could generate audio stream supported by FLAC and save it to file what above code probably does not do is also save the mandatory metadata that needs to be present on the beginning of the file. This could then result in Google of rejecting your file as being of wrong type. – SilverWarior Sep 08 '18 at 21:28
  • I strongly recommend you read more about FLAC file format on their official website https://xiph.org/flac/format.html – SilverWarior Sep 08 '18 at 21:29
  • @SilverWarior if mandatory metadata is not present then the flac encoder doesn´t work ok. – Luiz Alves Sep 08 '18 at 22:03
  • @SilverWarior, yes, output file wan't actual FLAC file. I opened encoded file in Notepad and its signature was `RIFF`, not `fLaC` as become now. – Josef Švejk Sep 09 '18 at 09:10
  • @LuizAlves, I have updated the code so it should work. Try it now. – Josef Švejk Sep 09 '18 at 09:11
  • Thank you, the flac is ok. Is there a way to create the microphone audio stream flac encoded directly without wait to create a wave audio file to encode? – Luiz Alves Sep 09 '18 at 19:52
  • @LuizAlves, I think the answer is 'Yes'. As long as you can obtain `HRECORD` you will be able to pass it to `BASS_Encode_FLAC_StartFile`. According to help, the last function takes `HSTREAM, HMUSIC, or HRECORD` as argument. But this is just a thought. The real solution could be more complicated. – Josef Švejk Sep 10 '18 at 07:39