Author Topic: How to play sound from a WAV file instantaneously?  (Read 238 times)

RolandCh

  • Posts: 4
Hello!

I try to use the BASS library in a Lazarus project (a snake game) to play sounds from WAV files.

The first time a sound is played, there is a little delay that slows down my animation. How to solve that problem?

Here is my code:

Code: [Select]
uses
  ..., Bass;

type
  { TForm1 }
  TForm1 = class(TForm)
    ...
  private
    var
      ...
      stream1: HSTREAM;
  end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ...
  BASS_Init(-1, 44100, 0, Handle, nil);
  stream1 := BASS_StreamCreateFile(FALSE, pchar('sounds\lemmings\yippee.wav'), 0, 0, 0{$IFDEF UNICODE} or BASS_UNICODE{$ENDIF});
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  ...
  BASS_Free;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if (snake[0].x + direction.x = apple.x) and (snake[0].y + direction.y = apple.y) then
  begin
    BASS_ChannelPlay(stream1, FALSE);
    ...
  end;

Ian @ un4seen

  • Administrator
  • Posts: 20764
Re: How to play sound from a WAV file instantaneously?
« Reply #1 on: 29 Jan '18 - 15:03 »
If they are sound effects that will be played many times, it may be better to use the sample functions (eg. BASS_SampleLoad) instead of the stream functions (eg. BASS_StreamCreateFile) to play them. The sample functions will pre-load the entire sample data to memory and play it from there. For example, you could modify the code something like this:

Code: [Select]
uses
  ..., Bass;

type
  { TForm1 }
  TForm1 = class(TForm)
    ...
  private
    var
      ...
      sample1: HSAMPLE;
  end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ...
  BASS_Init(-1, 44100, 0, Handle, nil);
  sample1 := BASS_SampleLoad(FALSE, pchar('sounds\lemmings\yippee.wav'), 0, 0, maxplays, 0{$IFDEF UNICODE} or BASS_UNICODE{$ENDIF});
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  ...
  BASS_Free;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if (snake[0].x + direction.x = apple.x) and (snake[0].y + direction.y = apple.y) then
  begin
    BASS_ChannelPlay(BASS_SampleGetChannel(sample1, FALSE), FALSE);
    ...
  end;

If you would prefer to stick with using streams, another thing that may help is calling BASS_ChannelUpdate straight after BASS_StreamCreateFile, to pre-buffer some data.

RolandCh

  • Posts: 4
Re: How to play sound from a WAV file instantaneously?
« Reply #2 on: 30 Jan '18 - 06:46 »
Thank you very much for your answer.  :)

I tried your two propositions. I am sorry but in both cases, there is still a delay when the sound is played for the first time. Maybe didn't I use the good parameters?

Code: [Select]
procedure TForm1.FormCreate(Sender: TObject);
begin
  BASS_Init(-1, 44100, 0, Handle, nil);
  stream1 := BASS_StreamCreateFile(FALSE, pchar('sounds\lemmings\yippee.wav'), 0, 0, 0);
  BASS_ChannelUpdate(stream1, 0);
  //sample1 := BASS_SampleLoad(FALSE, pchar('sounds\lemmings\yippee.wav'), 0, 0, 1, 0);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if (snake[0].x + direction.x = apple.x) and (snake[0].y + direction.y = apple.y) then
  begin
    BASS_ChannelPlay(stream1, FALSE);
    //BASS_ChannelPlay(BASS_SampleGetChannel(sample1, FALSE), FALSE);

Ian @ un4seen

  • Administrator
  • Posts: 20764
Re: How to play sound from a WAV file instantaneously?
« Reply #3 on: 30 Jan '18 - 13:31 »
That's strange. I'm not sure what could be causing the delay then. If you time the BASS_ChannelPlay calls, does that confirm a delay in the first call compared to the rest? Another thing you could try is enabling the BASS_CONFIG_DEV_NONSTOP option before calling BASS_Init:

Code: [Select]
  BASS_SetConfig(BASS_CONFIG_DEV_NONSTOP, 1);
  BASS_Init(-1, 44100, 0, Handle, nil);

RolandCh

  • Posts: 4
Re: How to play sound from a WAV file instantaneously?
« Reply #4 on: 30 Jan '18 - 17:19 »
If you time the BASS_ChannelPlay calls, does that confirm a delay in the first call compared to the rest?

Thank you for your answer. Here is the confirmation:

Code: [Select]
    count := GetTickCount64;
    BASS_ChannelPlay(stream1, FALSE);
    //BASS_ChannelPlay(BASS_SampleGetChannel(sample1, FALSE), FALSE);
    count := GetTickCount64 - count;
    log.Append(Format('%d ms', [count]));

For each test, the snake eats five apples.

Quote
Version 1 (BASS_StreamCreateFile)

469 ms
0 ms
0 ms
0 ms
16 ms

Version 2 (BASS_StreamCreateFile + BASS_ChannelUpdate)

453 ms
15 ms
0 ms
0 ms
15 ms

Version 3 (BASS_SampleLoad)

484 ms
0 ms
0 ms
0 ms
0 ms

Another thing you could try is enabling the BASS_CONFIG_DEV_NONSTOP option before calling BASS_Init:

I tried but unfortunately it doesn't solve the problem.

I attach the Lazarus project, in case someone would test it. (It uses BGRABitmap and, of course, BASS libraries.)
« Last Edit: 30 Jan '18 - 17:23 by RolandCh »

Ian @ un4seen

  • Administrator
  • Posts: 20764
Re: How to play sound from a WAV file instantaneously?
« Reply #5 on: 30 Jan '18 - 17:55 »
I notice in the uploaded project that you're currently using an old BASS.DLL version. Please try upgrading to the latest version and see if you still get the problem then. If you do still get the problem, please also confirm what Windows version you are running on.

Xire

  • Posts: 276
Re: How to play sound from a WAV file instantaneously?
« Reply #6 on: 30 Jan '18 - 17:59 »
Compiled with Lazarus 1.8.0 r56594 FPC 3.0.4 x86_64-win64-win32/win64 and latest 64bit version of bass.dll - no delays for me.

RolandCh

  • Posts: 4
Re: How to play sound from a WAV file instantaneously?
« Reply #7 on: 30 Jan '18 - 18:34 »
@Ian,Xire

Indeed, replacing BASS.DLL solves the problem. Thank you very much!