Author Topic: Audio Output to Separate Speakers [Solved]  (Read 321 times)

onurcode

  • Posts: 9
Audio Output to Separate Speakers [Solved]
« on: 10 May '24 - 11:04 »
Hi,
I am a Delphi user. I want to send different sounds to the speakers of the same or different sound cards installed in the computer, but I was not successful. I would like to point out that I am very new to this business.
I've been dealing with database applications before, but I'm very new to hardware management.
I am sharing my sample codes below. I would be very grateful if you could guide me.

Best Regards

Code: [Select]
unit unitMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, cxGraphics, cxControls, cxLookAndFeels, cxLookAndFeelPainters, dxStatusBar, bass, cxContainer, cxEdit,
  Vcl.Menus,
  Vcl.StdCtrls, cxButtons, cxTextEdit;

type
  TForm1 = class(TForm)
    dxStatusBar1: TdxStatusBar;
    cxTextEdit1: TcxTextEdit;
    btnPlay1: TcxButton;
    btnStop1: TcxButton;
    btnStop2: TcxButton;
    btnPlay2: TcxButton;
    cxTextEdit2: TcxTextEdit;
    procedure FormCreate(Sender: TObject);
    procedure btnPlay1Click(Sender: TObject);
    procedure btnStop1Click(Sender: TObject);
    procedure btnPlay2Click(Sender: TObject);
    procedure btnStop2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    FChannel1: HSTREAM;
    FChannel2: HSTREAM;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnPlay1Click(Sender: TObject);
begin
  FChannel1 := BASS_StreamCreateFile(False, PChar(cxTextEdit1.Text), 0, 0, BASS_SPEAKER_REAR2RIGHT); //FRONT, REAR, CENLFE, SIDE, LEFT, RIGHT ETC.
  BASS_ChannelPlay(FChannel1, False);
end;

procedure TForm1.btnStop1Click(Sender: TObject);
begin
  BASS_ChannelStop(FChannel1);
  BASS_StreamFree(FChannel1);
end;

procedure TForm1.btnStop2Click(Sender: TObject);
begin
  BASS_ChannelStop(FChannel2);
  BASS_StreamFree(FChannel2);
end;

procedure TForm1.btnPlay2Click(Sender: TObject);
begin
  FChannel2 := BASS_StreamCreateFile(False, PChar(cxTextEdit2.Text), 0, 0, BASS_UNICODE); //only BASS_UNICODE works. but it's also stereo. I want to send separate sound for each speaker in mono.
  BASS_ChannelPlay(FChannel2, False);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  BASS_Init(-1, 44100, 0, 0, NIL);
end;

end.
« Last Edit: 14 May '24 - 07:59 by onurcode »

Falcosoft

  • Posts: 181
Re: Audio Output to Separate Speakers
« Reply #1 on: 10 May '24 - 15:30 »
Hi,
First of all if you use a unicode Delphi version (Delphi 2009+) you always have to use the BASS_UNICODE flag when you provide PChar(string) as an argument.
So if you use regular stereo wave files you should try these first:


Code: [Select]
FChannel1 := BASS_StreamCreateFile(False, PChar(cxTextEdit1.Text), 0, 0, BASS_SPEAKER_REAR or BASS_UNICODE);
...
FChannel2 := BASS_StreamCreateFile(False, PChar(cxTextEdit2.Text), 0, 0, BASS_SPEAKER_FRONT or BASS_UNICODE);

These calls should play the stereo wave files on the Back and Front speaker pairs respectively.

onurcode

  • Posts: 9
Re: Audio Output to Separate Speakers
« Reply #2 on: 11 May '24 - 08:35 »
Hi @Falconsoft,
Thank you for your interest, I am using Delphi 10.3 Rio version.

As you mentioned, I added the BASS_UNICODE flag, but I think I got sound from both speakers because it was stereo.
I have more than one sound card in my computer and I want to play separate audio files using mono outputs of each sound card.
I continue to research on this subject, but I think I also need the BASSMix plugin for this application. I'm sure it's not too complicated, but it's a very new system for me.

Best Regards.

Chris

  • Posts: 2208
Re: Audio Output to Separate Speakers
« Reply #3 on: 11 May '24 - 15:49 »
Hi
  In Your case (you want to play on only ONe SPEAKER.

Code: [Select]
FChannel1 := BASS_StreamCreateFile(False, PChar(cxTextEdit1.Text), 0, 0,BASS_SPEAKER_FRONTLEFT or BASS_UNICODE);
...
FChannel2 := BASS_StreamCreateFile(False, PChar(cxTextEdit2.Text), 0, 0, BASS_SPEAKER_FRONTRIGHT or BASS_UNICODE);

IF YOU WANT TO PLAY ON DIFFERENT Devices then just

Code: [Select]
BASS_Init(1, 44100, 0, 0, NIL);// first device
BASS_Init(2, 44100, 0, 0, NIL);//  second device

.........


Bass_SetDevice(1)
FChannel1 := BASS_StreamCreateFile(False, PChar(cxTextEdit1.Text), 0, 0,BASS_SPEAKER_FRONTLEFT or BASS_UNICODE);
// will play FChannel1 on the left front speaker on Device 1

Bass_SetDevice(2)
FChannel2 := BASS_StreamCreateFile(False, PChar(cxTextEdit2.Text), 0, 0,BASS_SPEAKER_FRONTLEFT or BASS_UNICODE);
// will play FChannel2 on the left front speaker on Device 2


onurcode

  • Posts: 9
Re: Audio Output to Separate Speakers
« Reply #4 on: 13 May '24 - 07:59 »
Hi @Chris,

There are 2 sound cards in my computer.

1-Realtek High Definition Audio (ONBOARD)
2-Sound Blaster Play!3 (USB)

(BASS_SPEAKER_FRONTLEFT or BASS_UNICODE) When I use this flag, no sound is played, but when I use only (BASS_UNICODE), I get the same sound from both speakers.
I think the sound cards I use are not incompatible, but I don't fully understand why the problem occurs.

First of all, I cannot proceed to the second sound card stage because I cannot figure out how to get separate sound from the speakers connected to the same sound card.

Best Regards


Code: [Select]
unit unitMain;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, cxGraphics, cxControls, cxLookAndFeels, cxLookAndFeelPainters, dxStatusBar, bass, cxContainer, cxEdit,
  Vcl.Menus,
  Vcl.StdCtrls, cxButtons, cxTextEdit;

type
  TForm1 = class(TForm)
    dxStatusBar1: TdxStatusBar;
    cxTextEdit1: TcxTextEdit;
    btnPlay1: TcxButton;
    btnStop1: TcxButton;
    btnStop2: TcxButton;
    btnPlay2: TcxButton;
    cxTextEdit2: TcxTextEdit;
    ListBox1: TListBox;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure btnPlay1Click(Sender: TObject);
    procedure btnStop1Click(Sender: TObject);
    procedure btnPlay2Click(Sender: TObject);
    procedure btnStop2Click(Sender: TObject);

  private
    { Private declarations }
  public
    { Public declarations }
    FChannel1: HSTREAM;
    FChannel2: HSTREAM;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnPlay1Click(Sender: TObject);
begin
  FChannel1 := BASS_StreamCreateFile(False, PChar(cxTextEdit1.Text), 0, 0, BASS_SPEAKER_FRONTLEFT or BASS_UNICODE);
  BASS_ChannelPlay(FChannel1, False);
end;

procedure TForm1.btnStop1Click(Sender: TObject);
begin
  BASS_ChannelStop(FChannel1);
  BASS_StreamFree(FChannel1);
end;

procedure TForm1.btnStop2Click(Sender: TObject);
begin
  BASS_ChannelStop(FChannel2);
  BASS_StreamFree(FChannel2);
end;

procedure TForm1.btnPlay2Click(Sender: TObject);
begin
  FChannel2 := BASS_StreamCreateFile(False, PChar(cxTextEdit2.Text), 0, 0, BASS_SPEAKER_FRONTRIGHT or BASS_UNICODE);
  BASS_ChannelPlay(FChannel2, False);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  BASS_Init(-1, 44100, 0, 0, NIL);
end;

Chris

  • Posts: 2208
Re: Audio Output to Separate Speakers
« Reply #5 on: 13 May '24 - 08:55 »
just change it to

Code: [Select]
procedure TForm1.FormCreate(Sender: TObject);
begin
  BASS_Init(1, 44100, 0, 0, NIL);
  BASS_Init(2, 44100, 0, 0, NIL);
end;

procedure TForm1.btnPlay1Click(Sender: TObject);
begin
  Bass_SetDevice(1);
  FChannel1 := BASS_StreamCreateFile(False, PChar(cxTextEdit1.Text), 0, 0, BASS_SPEAKER_FRONTLEFT or BASS_UNICODE);
  BASS_ChannelPlay(FChannel1, False);
end;

procedure TForm1.btnPlay2Click(Sender: TObject);
begin
  Bass_SetDevice(2);
  FChannel2 := BASS_StreamCreateFile(False, PChar(cxTextEdit1.Text), 0, 0, BASS_SPEAKER_FRONTRIGHT or BASS_UNICODE);
  BASS_ChannelPlay(FChannel2, False);
end;

onurcode

  • Posts: 9
Re: Audio Output to Separate Speakers
« Reply #6 on: 13 May '24 - 09:15 »
Hi @Chris,

I could only get sound on the 1st card by using the BASS_UNICODE flag.
2.music didn't work, it didn't work with any flags.

 :( ???

BASS.pas 2.4
BASS.dll 2 .4.17.0


Code: [Select]
rocedure TForm1.btnPlay1Click(Sender: TObject);
begin
  Bass_SetDevice(1);
  FChannel1 := BASS_StreamCreateFile(False, PChar(cxTextEdit1.Text), 0, 0, BASS_SPEAKER_FRONTLEFT or BASS_UNICODE);
  BASS_ChannelPlay(FChannel1, False);
end;

procedure TForm1.btnStop1Click(Sender: TObject);
begin
  BASS_ChannelStop(FChannel1);
  BASS_StreamFree(FChannel1);
end;

procedure TForm1.btnStop2Click(Sender: TObject);
begin
  BASS_ChannelStop(FChannel2);
  BASS_StreamFree(FChannel2);
end;

procedure TForm1.btnPlay2Click(Sender: TObject);
begin
  Bass_SetDevice(2);
  FChannel2 := BASS_StreamCreateFile(False, PChar(cxTextEdit2.Text), 0, 0, BASS_SPEAKER_FRONTRIGHT or BASS_UNICODE);
  BASS_ChannelPlay(FChannel2, False);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  BASS_Init(1, 44100, 0, 0, NIL);
  BASS_Init(2, 44100, 0, 0, NIL);
end;

Chris

  • Posts: 2208
Re: Audio Output to Separate Speakers
« Reply #7 on: 13 May '24 - 09:52 »
try this
Code: [Select]
procedure TForm1.btnPlay1Click(Sender: TObject);
begin
  Bass_SetDevice(1);
  if fChannel1 <> 0 then
  Bass_StreamFree(fChannel1);
  FChannel1 := BASS_StreamCreateFile(False, PChar(cxTextEdit1.Text), 0, 0, BASS_SPEAKER_FRONTLEFT or BASS_UNICODE);
  If FChannel1 = 0 then  // Error
     ShowMessage('Error File1 '+Bass_ErrorGetCode.ToString);
  BASS_ChannelPlay(FChannel1, False);
end;

procedure TForm1.btnStop1Click(Sender: TObject);
begin
  Bass_SetDevice(1); 
  BASS_ChannelStop(FChannel1);
end;

procedure TForm1.btnStop2Click(Sender: TObject);
begin
 Bass_SetDevice(2);
 BASS_ChannelStop(FChannel2);
end;

procedure TForm1.btnPlay2Click(Sender: TObject);
begin
  Bass_SetDevice(2);
   if fChannel2 <> 0 then
  Bass_StreamFree(fChannel2);
  FChannel2 := BASS_StreamCreateFile(False, PChar(cxTextEdit2.Text), 0, 0, BASS_SPEAKER_FRONTRIGHT or BASS_UNICODE);
  If FChannel2 = 0 then  // Error
     ShowMessage('Error File 2'+Bass_ErrorGetCode.ToString);
  BASS_ChannelPlay(FChannel2, False);
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  s1,s2: string;
  FInfo : Bass_Info;
begin
  BASS_Init(1, 44100, 0, 0, NIL);
  BASS_GetInfo(Finfo);
  s1 := Finfo.Speakers.ToString;
  BASS_Init(2, 44100, 0, 0, NIL);
  BASS_GetInfo(Finfo);
  s2 := Finfo.Speakers.ToString;
  Caption := 'Device 1 SpeakerCount '+s1+'----'+ 'Device 2 SpeakerCount '+s2;
end;

« Last Edit: 13 May '24 - 10:03 by Chris »

onurcode

  • Posts: 9
Re: Audio Output to Separate Speakers
« Reply #8 on: 13 May '24 - 10:02 »
Hi @Chris

I received the following error warnings.

btnPlay1Click : Error File1 42
btnPlay2Click : Error File 242

Chris

  • Posts: 2208
Re: Audio Output to Separate Speakers
« Reply #9 on: 13 May '24 - 10:05 »
that will mean unavailable speaker
by the way your os is win10 ?
just recheck the bass_init Code to
Code: [Select]
procedure TForm1.FormCreate(Sender: TObject);
var
  s1,s2: string;
  FInfo : Bass_Info;
begin
  BASS_Init(1, 44100, BASS_DEVICE_SPEAKERS, 0, NIL);
  BASS_GetInfo(Finfo);
  s1 := Finfo.Speakers.ToString;
  BASS_Init(2, 44100, BASS_DEVICE_SPEAKERS, 0, NIL);
  BASS_GetInfo(Finfo);
  s2 := Finfo.Speakers.ToString;
  Caption := 'Device 1 SpeakerCount '+s1+'----'+ 'Device 2 SpeakerCount '+s2;
end;
« Last Edit: 13 May '24 - 10:11 by Chris »

onurcode

  • Posts: 9
Re: Audio Output to Separate Speakers
« Reply #10 on: 13 May '24 - 10:12 »
I just saw your updated message, yes I am using W10.

Output:
Device 1 SpeakerCount 2----Device 2 SpeakerCount 2

Does it mean the hardware is incompatible?

Chris

  • Posts: 2208
Re: Audio Output to Separate Speakers
« Reply #11 on: 13 May '24 - 10:29 »
you can try to change

BASS_SPEAKER_FRONTLEFT  to -> BASS_SPEAKER_LEFT

BASS_SPEAKER_FRONTRIGHT to -> BASS_SPEAKER_RIGHT


onurcode

  • Posts: 9
Re: Audio Output to Separate Speakers
« Reply #12 on: 13 May '24 - 10:42 »
Device 1 produced stereo sound
Device 2 produced no sound

Code: [Select]
procedure TForm1.btnPlay1Click(Sender: TObject);
begin
  begin
    Bass_SetDevice(1);
    if FChannel1 <> 0 then
      Bass_StreamFree(FChannel1);
    FChannel1 := BASS_StreamCreateFile(False, PChar(cxTextEdit1.Text), 0, 0, BASS_SPEAKER_LEFT or BASS_UNICODE);
    If FChannel1 = 0 then // Error
      ShowMessage('Error File1 ' + Bass_ErrorGetCode.ToString);
    BASS_ChannelPlay(FChannel1, False);
  end;
end;

procedure TForm1.btnPlay2Click(Sender: TObject);
begin
  Bass_SetDevice(2);
   if fChannel2 <> 0 then
  Bass_StreamFree(fChannel2);
  FChannel2 := BASS_StreamCreateFile(False, PChar(cxTextEdit2.Text), 0, 0, BASS_SPEAKER_RIGHT or BASS_UNICODE);
  If FChannel2 = 0 then  // Error
     ShowMessage('Error File 2'+Bass_ErrorGetCode.ToString);
  BASS_ChannelPlay(FChannel2, False);
end;

Falcosoft

  • Posts: 181
Re: Audio Output to Separate Speakers
« Reply #13 on: 13 May '24 - 13:23 »
According to documentation you can only play mono channel with mono speaker assignment flags. Are you sure your waw/mp3/ogg files are only 1 channel/mono files?
https://www.un4seen.com/doc/#bass/speaker.html

Quote
The stereo speaker assignment flags can also be used with mono channels, so that, for example, a mono channel can be played on both the front speakers.
But mono speaker assignment flags cannot be used with stereo channels, so, for example, it is not possible to play a stereo channel on just the center speaker.

If not you can try the BASS_SAMPLE_MONO flag. That is:
Code: [Select]
FChannel1 := BASS_StreamCreateFile(False, PChar(cxTextEdit1.Text), 0, 0, BASS_SPEAKER_FRONTLEFT or BASS_SAMPLE_MONO or BASS_UNICODE);
But the documentation of this flag is not absolutely clear about if this flag works only for compressed formats (OGG/MP3/MP2/MP1) or also works for uncompressed wav files.
https://www.un4seen.com/doc/#bass/BASS_StreamCreateFile.html

« Last Edit: 13 May '24 - 13:35 by Falcosoft »

onurcode

  • Posts: 9
Re: Audio Output to Separate Speakers
« Reply #14 on: 13 May '24 - 14:59 »
Dear @Chris and Dear @Falcosoft,

Thank you very much for your support. Without your guidance, I think it would have taken me longer to figure this out.
I realized that the problem was in the notebook computer I was using for development purposes. I tested on a different computer. I managed to get sound separately from the individual speakers of each sound card.

Thank you again for sparing your valuable time for me.

I have one last request from you. How can I get a list of sound cards installed in the computer?
Can I set the relevant sound card and speaker selection as runtime?  ::)

Best Regards.

Chris

  • Posts: 2208
Re: Audio Output to Separate Speakers
« Reply #15 on: 13 May '24 - 16:10 »
Hi, something like this

Code: [Select]
procedure ListDevices;
var
DevInfo BASS_DEVICEINFO ;
a: integer;
begin
  a:= 1;
  while BASS_GetDeviceInfo(a, DevInfo) do
  begin
    if (DevInfo.flags and BASS_DEVICE_ENABLED) = BASS_DEVICE_ENABLED then // will only display Enabled Playback-Devices
      ComboBox1.Items.add(string(DevInfo.name));
    inc(a);
  end;
end;

Falcosoft

  • Posts: 181
Re: Audio Output to Separate Speakers
« Reply #16 on: 13 May '24 - 16:17 »
...
I have one last request from you. How can I get a list of sound cards installed in the computer?
Can I set the relevant sound card and speaker selection as runtime?  ::)

Best Regards.

1.
You can use BASS_GetDeviceInfo() in a loop to get the list of available devices:
https://www.un4seen.com/doc/#bass/BASS_GetDeviceInfo.html
   
Code: [Select]

    CbOutputDev: TComboBox;
    BassDevInfo: BASS_DEVICEINFO;
    Valid: LongBool;
    i: Integer;
    ...

    i := 1;
    repeat
      Valid := BASS_GetDeviceInfo(i, BassDevInfo);
      if Valid then CbOutputDev.Items.Add(BassDevInfo.name);
      Inc(i);
    until (not Valid);

2. Yes, and Chris has already shown how you can init devices and how you can set the active one at runtime with Bass_SetDevice().
« Last Edit: 13 May '24 - 17:48 by Falcosoft »

onurcode

  • Posts: 9
Re: Audio Output to Separate Speakers
« Reply #17 on: 14 May '24 - 07:59 »
Hi,

I would like to thank you both individually. You have really been a great support to me in this regard.
I hope this topic will be a good guide for beginners. I hope you live a successful life.

Best Regards.