Author Topic: Audio gen 32 bits flt - 1 output to audio device + 1 output to disk file  (Read 601 times)

RisingMoon

  • Posts: 11
Hello,

from an "on the fly stream" (fixed frequency), i want to send data to one audio device and at the same time record it disk file, but with differents formats.
For example, with stream created in 32 bits float format, I want to be able to send to audio device in 24 bits int and record in 16 bits int or 32 bits float format.

At this time, I can run audio gen stream (works well), send it to selected output sound device (I can hear it) and record it to disk (in the selected format trough a Bass encoder).
All this works OK only when audiogen stream is in 16 bits int format, no sound seems to be output on sound device when audiogen stream is in 32 bits float format.

Following code is not full, just show it for information (don't show errors handling).

Code: [Select]

var
  AudioGenStream: HSTREAM;
  StreamChan: HSTREAM = 0;
  RecordChan: HRECORD = 0;

function MakeWave(handle: HSTREAM; buffer: Pointer; length: DWORD; user: Pointer): DWORD; stdcall;
var
  buf: ^WORD;
  buf2: ^DOUBLE;
begin
  // make sinus
  // in 16 bits int (-3768..+32767 range) with ^WORD buf pointer <= works
  // in 32 bits float (-1..+1 range) with ^DOUBLE buf2 pointer <= don't works
end;

function RecordingCallback(channel: HRECORD; buffer: Pointer; length, user: DWORD): Boolean; stdcall;
begin
  Result := Bool(BASS_Encode_IsActive(channel)); // continue recording if encoder is alive
end;

procedure BASS_Init_main;
begin
  BASS_Init(FAudioGen.OutDeviceIdx, FAudioGen.SampleRate, BASS_SAMPLE_FLOAT, win, nil) then
end;

procedure BASS_Rec_Start;
begin
  RecordChan := BASS_RecordStart(iSR, iCh, BASS_RECORD_PAUSE or BASS_SAMPLE_FLOAT, @RecordingCallback, nil);
  wBassFlags := BASS_ENCODE_PCM or BASS_ENCODE_FP_24BIT or BASS_ENCODE_AUTOFREE {$IFDEF UNICODE} or BASS_UNICODE {$ENDIF};
  BASS_Encode_Start(AudioGenStream, pwidechar(FAudioRec.OutFileName), wBassFlags, nil, nil) = 0)
  BASS_ChannelPlay(RecordChan, FALSE); // resume recording
end;

procedure TfmMain.Rec_Stop;
begin
  BASS_Encode_Stop(AudioGenStream);
  BASS_ChannelStop(RecordChan);
  RecordChan := 0;
end;

procedure TfmMain.Gen_StreamCreate();
begin
  AudioGenStream := BASS_StreamCreate(FAudioGen.SampleRate, FAudioGen.Channels, GenFlags, @MakeWave, 0)
end;


What is the best method to achieve that ?
Knowing that the software must run under win7..win11, is it more judicious to opt for WASAPI now?
I admit that I'm a little lost... and I suspect error with pointer in my MakeWave proc.

Thank you for your suggestions,
Remy

Ian @ un4seen

  • Administrator
  • Posts: 26172
If you are generating/playing the sound with BASS in the same app, then you can attach the encoder(s) directly to that stream instead of creating a recording channel to capture it. Refering to the code above, it would mean using the "AudioGenStream" handle instead of "RecordChan" in the BASS_Encode_Start call. To write both 16-bit and floating-point files, you can start two encoders, one with the BASS_ENCODE_FP_16BIT flag and one without. For example:

Code: [Select]
  AudioGenStream := BASS_StreamCreate(FAudioGen.SampleRate, FAudioGen.Channels, BASS_SAMPLE_FLOAT, @MakeWave, 0);
  BASS_Encode_Start(AudioGenStream, pwidechar(FAudioRec.OutFileName1), BASS_ENCODE_PCM or BASS_ENCODE_AUTOFREE or BASS_ENCODE_QUEUE {$IFDEF UNICODE} or BASS_UNICODE {$ENDIF}, nil, nil);
  BASS_Encode_Start(AudioGenStream, pwidechar(FAudioRec.OutFileName2), BASS_ENCODE_PCM or BASS_ENCODE_FP_16BIT or BASS_ENCODE_AUTOFREE or BASS_ENCODE_QUEUE {$IFDEF UNICODE} or BASS_UNICODE {$ENDIF}, nil, nil);

Regarding WASAPI, BASS uses that by default on Vista and above (a DirectSound option is also available).

RisingMoon

  • Posts: 11
Thank you Ian for your answer.

I applied your suggestion about use the same handle for stream generator and for the recording stream.
This work as well as my old bad (not optimised) method with additionnal stream.

But the problem remain. That works whith a 16 bits stream, but not with the 32 bits flt stream.
I think problem is in my streamproc, that make usage of a ^word pointer for 16-bits data's, or that make usage of a ^double(/real) pointer for 32-bits-flt data's (depending on conditionnal).

Code: [Select]
function MakeWave(handle: HSTREAM; buffer: Pointer; length: DWORD; user: Pointer): DWORD; stdcall;
var
{$IFDEF BASS_STREAM_16_BITS}
  buf: ^WORD;
{$ENDIF}
{$IFDEF BASS_STREAM_32_BITS}
  buf: ^DOUBLE;
  //buf: ^WORD;
{$ENDIF}
  i, len: Integer;
  iAmp1_16bInt: word;
  iAmp1_32bFlt: real;
begin
  buf := buffer;
{$IFDEF BASS_STREAM_16_BITS}
  len := length div 2;
{$ENDIF}
{$IFDEF BASS_STREAM_32_BITS}
  len := length div 4;
{$ENDIF}

  for i := 0 to len - 1 do
  begin

    iAmp1_32bFlt := Sin(Wave1Pos * PI);  // range -1 .. +1
    iAmp1_16bInt := Trunc(Wave1Amp * iAmp1_32bFlt);  // range -32768 .. +32767

{$IFDEF BASS_STREAM_16_BITS}
    buf^ := iAmp1_16bInt;
{$ENDIF}
{$IFDEF BASS_STREAM_32_BITS}
    buf^ := iAmp1_32bFlt;
{$ENDIF}

    Inc(buf);
    Wave1Pos := Wave1Pos + (Wave1Freq / iSampleRate);

  end;
  Result := length;

end;

Does someone has an example of stream create with float 32 bits that I can compare with my code ?

Thanks again,
Remy

Ian @ un4seen

  • Administrator
  • Posts: 26172
The "double" type is 64-bit float. You should use "single" instead for 32-bit float. But use "double" for "Wave1Pos" to keep greater precision in that.

RisingMoon

  • Posts: 11
Argh ! Simply that !
All now works as expected.
Thank you very much, Ian !

Code: [Select]
function MakeWave(handle: HSTREAM; buffer: Pointer; length: DWORD; user: Pointer): DWORD; stdcall;
var
{$IFDEF BASS_STREAM_16_BITS}
  buf: ^WORD;
{$ENDIF}
{$IFDEF BASS_STREAM_32_BITS}
  buf: ^SINGLE;
{$ENDIF}
  i, len: Integer;
  iAmp1_16bInt: word;
  iAmp1_32bFlt: single;
begin
  buf := buffer;
{$IFDEF BASS_STREAM_16_BITS}
  len := length div 2;
{$ENDIF}
{$IFDEF BASS_STREAM_32_BITS}
  len := length div 4;
{$ENDIF}

  for i := 0 to len - 1 do
  begin

    iAmp1_32bFlt := Sin(Wave1Pos * PI) * Wave1AmpFlt;  // range -1 .. +1
    iAmp1_16bInt := Trunc(Sin(Wave1Pos * PI) * Wave1AmpInt);  // range -32768 .. +32767

{$IFDEF BASS_STREAM_16_BITS}
    buf^ := iAmp1_16bInt;
{$ENDIF}
{$IFDEF BASS_STREAM_32_BITS}
    buf^ := iAmp1_32bFlt;
{$ENDIF}

    Inc(buf);
    Wave1Pos := Wave1Pos + (Wave1Freq / iSampleRate);

  end;
  Result := length;

end;

Remy