Hello there,
I recently found UOS while searching for a sound library I can use in Lazarus. I looked at the examples and tried to build a small application myself that just plays an audio file(test file is mp3, but should work with any supported by UOS, right?) to multiple output devices. At first I tried it with only one Device and that worked, but after adding another Output, the audio laggs/skips, like it's struggling to keep up. I saw that there is a way to load a file into a buffer, so I tried that, but only got horrible noises and never got the file to play correctly, like the .flac file from the "consoleplaymemorybuffer" example. Even after trying every sample rate, it didn't work. How can I achieve this, because in theory it should work right? Here is my code, but it is basically just the simpleplayer example: unit Unit1; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ctypes, uos_flat; type { TForm1 } TForm1 = class(TForm) Button1: TButton; ComboBox1: TComboBox; Edit1: TEdit; ListBox1: TListBox; ToggleBox1: TToggleBox; procedure Button1Click(Sender: TObject); procedure ComboBox1Change(Sender: TObject); procedure FormClose(Sender: TObject; var CloseAction: TCloseAction); procedure FormCreate(Sender: TObject); procedure ToggleBox1Change(Sender: TObject); private public end; var Form1: TForm1; PA_FileName: String; SF_FileName: String; MPG_FileName: String; LibLoaded: Boolean; PlayerIndex, InputIndex, OutputIndex1, OutputIndex2: Integer; testbuffer: array of cfloat; testbufferinfos: TuosF_BufferInfos; const SoundFileName = 'test.mp3'; implementation {$R *.lfm} { TForm1 } procedure TForm1.FormCreate(Sender: TObject); var appDir: String; opath: String; res: Integer; begin appDir := Application.Location; //Load apropriate libraries from 'lib' sub directory Form1.Enabled := False; {$IFDEF Windows} {$if defined(cpu64)} PA_FileName := appDir + 'lib\Windows\64bit\LibPortaudio-64.dll'; SF_FileName := appDir + 'lib\Windows\64bit\LibSndFile-64.dll'; MPG_FileName := appDir + 'lib\Windows\64bit\LibMpg123-64.dll'; {$else} PA_FileName := appDir + 'lib\Windows\32bit\LibPortaudio-32.dll'; SF_FileName := appDir + 'lib\Windows\32bit\LibSndFile-32.dll'; MPG_FileName := appDir + 'lib\Windows\32bit\LibMpg123-32.dll'; {$endif} {$ENDIF} {$IFDEF freebsd} {$if defined(cpu64)} PA_FileName := appDir + 'lib/FreeBSD/64bit/libportaudio-64.so'; SF_FileName := appDir + 'lib/FreeBSD/64bit/libsndfile-64.so'; MPG_FileName := appDir + 'lib/FreeBSD/64bit/libmpg123-64.so'; {$else} PA_FileName := appDir + 'lib/FreeBSD/32bit/libportaudio-32.so'; SF_FileName := appDir + 'lib/FreeBSD/32bit/libsndfile-32.so'; MPG_FileName := appDir + 'lib/FreeBSD/32bit/libmpg123-32.so'; {$endif} {$ENDIF} {$IFDEF Darwin} {$IFDEF CPU32} opath := ordir; opath := copy(opath, 1, Pos('/uos', opath) - 1); PA_FileName := appDir + '/lib/Mac/32bit/LibPortaudio-32.dylib'; SF_FileName := appDir + '/lib/Mac/32bit/LibSndFile-32.dylib'; MPG_FileName := appDir + '/lib/Mac/32bit/LibMpg123-32.dylib'; {$ENDIF} {$IFDEF CPU64} opath := ordir; opath := copy(opath, 1, Pos('/uos', opath) - 1); PA_FileName := appDir + '/lib/Mac/64bit/LibPortaudio-64.dylib'; SF_FileName := appDir + '/lib/Mac/64bit/LibSndFile-64.dylib'; MPG_FileName := appDir + '/lib/Mac/64bit/LibMpg123-64.dylib'; {$ENDIF} {$ENDIF} {$if defined(CPUAMD64) and defined(linux) } PA_FileName := appDir + 'lib/Linux/64bit/LibPortaudio-64.so'; SF_FileName := appDir + 'lib/Linux/64bit/LibSndFile-64.so'; MPG_FileName := appDir + 'lib/Linux/64bit/LibMpg123-64.so'; {$ENDIF} {$if defined(cpu86) and defined(linux)} PA_FileName := appDir + 'lib/Linux/32bit/LibPortaudio-32.so'; SF_FileName := appDir + 'lib/Linux/32bit/LibSndFile-32.so'; MPG_FileName := appDir + 'lib/Linux/32bit/LibMpg123-32.so'; {$ENDIF} {$if defined(linux) and defined(cpuarm)} PA_FileName := appDir + 'lib/Linux/arm_raspberrypi/libportaudio-arm.so'; SF_FileName := appDir + 'lib/Linux/arm_raspberrypi/libsndfile-arm.so'; MPG_FileName := appDir + 'lib/Linux/arm_raspberrypi/libmpg123-arm.so'; {$ENDIF} {$if defined(linux) and defined(cpuaarch64)} PA_FileName := appDir + 'lib/Linux/aarch64_raspberrypi/libportaudio_aarch64.so'; SF_FileName := appDir + 'lib/Linux/aarch64_raspberrypi/libsndfile_aarch64.so'; MPG_FileName := appDir + 'lib/Linux/aarch64_raspberrypi/libmpg123_aarch64.so'; {$ENDIF} res := uos_LoadLib(PChar(PA_FileName), PChar(SF_FileName), PChar(MPG_FileName), nil, nil, nil); if res <> 0 then begin ShowMessage('Error while loading'); LibLoaded := False; end else LibLoaded := True; Form1.Enabled := True; ListBox1.Items.Add('Libraries Loaded: ' + BoolToStr(LibLoaded, True)); end; procedure TForm1.ToggleBox1Change(Sender: TObject); begin if uos_GetStatus(PlayerIndex) > 0 then begin uos_Stop(PlayerIndex); end; //uos_File2File(PChar(Application.Location + SoundFileName), PChar(Application.Location + 'out.wav'), -1, -1); //testbuffer := uos_File2Buffer(PChar(Application.Location + 'out.wav'), -1, testbufferinfos, -1, -1); PlayerIndex := 0; if uos_CreatePlayer(PlayerIndex) then begin ListBox1.Items.Add('Sound File Name: ' + Application.Location + SoundFileName); InputIndex := uos_AddFromFile(PlayerIndex, PChar(Application.Location + SoundFileName), -1, -1, -1); //InputIndex := uos_AddFromMemoryBuffer(PlayerIndex, testbuffer, testbufferinfos, -1, 1024); //InputIndex := uos_AddFromFileIntoMemory(PlayerIndex, PChar(Application.Location + SoundFileName)); ListBox1.Items.Add('Player Index: ' + IntToStr(PlayerIndex)); ListBox1.Items.Add('Input Index: ' + IntToStr(InputIndex)); if InputIndex > -1 then begin uos_InputAddDSPVolume(PlayerIndex, InputIndex, 0.1, 0.1); OutputIndex1 := uos_AddIntoDevOut(PlayerIndex, -1, -1, -1, -1, -1, -1, -1); ListBox1.Items.Add('Output Index 1: ' + IntToStr(OutputIndex1)); if OutputIndex1 > -1 then begin OutputIndex2 := uos_AddIntoDevOut(PlayerIndex, 14, -1, -1, -1, -1, -1, -1); ListBox1.Items.Add('Output Index 2: ' + IntToStr(OutputIndex2)); if OutputIndex2 > -1 then begin uos_Play(PlayerIndex); ListBox1.Items.Add('Player Status: ' + IntToStr(uos_GetStatus(PlayerIndex))); end; end; end; end; end; procedure TForm1.Button1Click(Sender: TObject); var i: Integer; begin uos_GetInfoDevice(); for i := 0 to High(uosDeviceInfos) do begin if uosDeviceInfos[i].DeviceType = 'Out' then ComboBox1.Items.Add('[' + InttoStr(uosDeviceInfos[i].DeviceNum) + '] ' + uosDeviceInfos[i].DeviceName); end; end; procedure TForm1.ComboBox1Change(Sender: TObject); begin end; procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction); begin uos_unloadlib(); uos_free(); end; end. I would appreciate any help I can get! |
Administrator
|
Hello and welcome to uos forum.
What OS are you using and what sound cards? If dev1 and dev2 are working devices, this should work: ..... InputIndex := uos_AddFromFile(PlayerIndex, PChar(Application.Location + SoundFileName), -1, -1, -1); if InputIndex > -1 then begin OutputIndex1 := uos_AddIntoDevOut(PlayerIndex, -1, -1, -1, -1, -1, -1, -1); OutputIndex2 := uos_AddIntoDevOut(PlayerIndex, dev1, -1, -1, -1, -1, -1, -1); OutputIndex3 := uos_AddIntoDevOut(PlayerIndex, dev2, -1, -1, -1, -1, -1, -1); ;;;; What strange noise do you get? Did you try each device alone (not all but one by one) ? Fre;D |
Administrator
|
Re-hello.
Not sure it will work good because there are many chance that all the sound cards works with not exactly same timers. Imho, if you dont need absolute synchro, you will have better result with a player with one output (one of the devices) and one input (the same file for each player) and start all the players together. Fre;D |
In reply to this post by fredvs
Hi,
I'm on Linux Mint Cinnamon 64bit, Kernel 5.4 The sound card I use is the onboard sound of my Motherboard(Asus Prime B350 Plus). The Strange noises are like when you try to play a file with the wrong playback settings(I tried different sample formats). The "best" one was when you could hear the sound kind off, but distorted. I did try each device seperately, one of them is my normal output(headphones) and the other one is a virtual sink. I tested it now using the device number directly for both of them instead of using -1 for the fisrt one since it's my default device and it works better, but there is still a tiny "skip" at the start, but that may not be a problem of UOS. This may have solved my problem, but i need to test it. Thank you for the help! Just a question, in your example, why dou you use the uos_AddIntoDevOut 3 times? |
Also, to add to this,
I tried another mp3 file, and it's more noticeable there, there is some kind of noise(not that audible), when hearing the audio compared to playing it in vlc. It's like static noise, but following the pattern of the sound. I don't really know hoe to explain it. I could maybe record it and upload it here? |
Administrator
|
In reply to this post by JoeJoeTV
> Just a question, in your example, why dou you use the uos_AddIntoDevOut 3 times?
Maybe I did not understand, I was thinking that you wanted to play a mp3 and output the sound in 3 different sound cards. So, sorry I dont catch what yo want. Do you want to have the choice of the different devices of the same audio card and play a mp3 with device wanted ? Fre;D |
Administrator
|
In reply to this post by JoeJoeTV
> I could maybe record it and upload it here?
Yes but give also the code you used. |
In reply to this post by fredvs
What I want to do is to play a sound file and output it to 2 devices simultaineusly.
I've attached the recording, but you need to listen carefully to hear it, it is a high pitched static noise and don't worry about the sound, it's the first thing I found to test this. :) upload.wav EDIT:The part with the second output is commented out to test if the noise also existed when using only one output and it does. This is the code I used now(only a few changes from before): unit Unit1; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ctypes, uos_flat; type { TForm1 } TForm1 = class(TForm) Button1: TButton; ComboBox1: TComboBox; Edit1: TEdit; ListBox1: TListBox; ToggleBox1: TToggleBox; procedure Button1Click(Sender: TObject); procedure ComboBox1Change(Sender: TObject); procedure FormClose(Sender: TObject; var CloseAction: TCloseAction); procedure FormCreate(Sender: TObject); procedure ToggleBox1Change(Sender: TObject); private public end; var Form1: TForm1; PA_FileName: String; SF_FileName: String; MPG_FileName: String; LibLoaded: Boolean; PlayerIndex, InputIndex, OutputIndex1, OutputIndex2: Integer; testbuffer: array of cfloat; testbufferinfos: TuosF_BufferInfos; const SoundFileName = 'test2.mp3'; implementation {$R *.lfm} { TForm1 } procedure TForm1.FormCreate(Sender: TObject); var appDir: String; opath: String; res: Integer; begin appDir := Application.Location; //Load apropriate libraries from 'lib' sub directory Form1.Enabled := False; {$IFDEF Windows} {$if defined(cpu64)} PA_FileName := appDir + 'lib\Windows\64bit\LibPortaudio-64.dll'; SF_FileName := appDir + 'lib\Windows\64bit\LibSndFile-64.dll'; MPG_FileName := appDir + 'lib\Windows\64bit\LibMpg123-64.dll'; {$else} PA_FileName := appDir + 'lib\Windows\32bit\LibPortaudio-32.dll'; SF_FileName := appDir + 'lib\Windows\32bit\LibSndFile-32.dll'; MPG_FileName := appDir + 'lib\Windows\32bit\LibMpg123-32.dll'; {$endif} {$ENDIF} {$IFDEF freebsd} {$if defined(cpu64)} PA_FileName := appDir + 'lib/FreeBSD/64bit/libportaudio-64.so'; SF_FileName := appDir + 'lib/FreeBSD/64bit/libsndfile-64.so'; MPG_FileName := appDir + 'lib/FreeBSD/64bit/libmpg123-64.so'; {$else} PA_FileName := appDir + 'lib/FreeBSD/32bit/libportaudio-32.so'; SF_FileName := appDir + 'lib/FreeBSD/32bit/libsndfile-32.so'; MPG_FileName := appDir + 'lib/FreeBSD/32bit/libmpg123-32.so'; {$endif} {$ENDIF} {$IFDEF Darwin} {$IFDEF CPU32} opath := ordir; opath := copy(opath, 1, Pos('/uos', opath) - 1); PA_FileName := appDir + '/lib/Mac/32bit/LibPortaudio-32.dylib'; SF_FileName := appDir + '/lib/Mac/32bit/LibSndFile-32.dylib'; MPG_FileName := appDir + '/lib/Mac/32bit/LibMpg123-32.dylib'; {$ENDIF} {$IFDEF CPU64} opath := ordir; opath := copy(opath, 1, Pos('/uos', opath) - 1); PA_FileName := appDir + '/lib/Mac/64bit/LibPortaudio-64.dylib'; SF_FileName := appDir + '/lib/Mac/64bit/LibSndFile-64.dylib'; MPG_FileName := appDir + '/lib/Mac/64bit/LibMpg123-64.dylib'; {$ENDIF} {$ENDIF} {$if defined(CPUAMD64) and defined(linux) } PA_FileName := appDir + 'lib/Linux/64bit/LibPortaudio-64.so'; SF_FileName := appDir + 'lib/Linux/64bit/LibSndFile-64.so'; MPG_FileName := appDir + 'lib/Linux/64bit/LibMpg123-64.so'; {$ENDIF} {$if defined(cpu86) and defined(linux)} PA_FileName := appDir + 'lib/Linux/32bit/LibPortaudio-32.so'; SF_FileName := appDir + 'lib/Linux/32bit/LibSndFile-32.so'; MPG_FileName := appDir + 'lib/Linux/32bit/LibMpg123-32.so'; {$ENDIF} {$if defined(linux) and defined(cpuarm)} PA_FileName := appDir + 'lib/Linux/arm_raspberrypi/libportaudio-arm.so'; SF_FileName := appDir + 'lib/Linux/arm_raspberrypi/libsndfile-arm.so'; MPG_FileName := appDir + 'lib/Linux/arm_raspberrypi/libmpg123-arm.so'; {$ENDIF} {$if defined(linux) and defined(cpuaarch64)} PA_FileName := appDir + 'lib/Linux/aarch64_raspberrypi/libportaudio_aarch64.so'; SF_FileName := appDir + 'lib/Linux/aarch64_raspberrypi/libsndfile_aarch64.so'; MPG_FileName := appDir + 'lib/Linux/aarch64_raspberrypi/libmpg123_aarch64.so'; {$ENDIF} res := uos_LoadLib(PChar(PA_FileName), PChar(SF_FileName), PChar(MPG_FileName), nil, nil, nil); if res <> 0 then begin ShowMessage('Error while loading'); LibLoaded := False; end else LibLoaded := True; Form1.Enabled := True; ListBox1.Items.Add('Libraries Loaded: ' + BoolToStr(LibLoaded, True)); end; procedure TForm1.ToggleBox1Change(Sender: TObject); begin if uos_GetStatus(PlayerIndex) > 0 then begin uos_Stop(PlayerIndex); end; PlayerIndex := 0; if uos_CreatePlayer(PlayerIndex) then begin ListBox1.Items.Add('Sound File Name: ' + Application.Location + SoundFileName); InputIndex := uos_AddFromFile(PlayerIndex, PChar(Application.Location + SoundFileName), -1, -1, -1); ListBox1.Items.Add('Player Index: ' + IntToStr(PlayerIndex)); ListBox1.Items.Add('Input Index: ' + IntToStr(InputIndex)); if InputIndex > -1 then begin //uos_InputAddDSPVolume(PlayerIndex, InputIndex, 1, 1); OutputIndex1 := uos_AddIntoDevOut(PlayerIndex, 11, -1, -1, -1, -1, -1, -1); ListBox1.Items.Add('Output Index 1: ' + IntToStr(OutputIndex1)); if OutputIndex1 > -1 then begin //OutputIndex2 := uos_AddIntoDevOut(PlayerIndex, 14, -1, -1, -1, -1, -1, -1); //ListBox1.Items.Add('Output Index 2: ' + IntToStr(OutputIndex2)); //if OutputIndex2 > -1 then //begin uos_Play(PlayerIndex); ListBox1.Items.Add('Player Status: ' + IntToStr(uos_GetStatus(PlayerIndex))); //end; end; end; end; end; procedure TForm1.Button1Click(Sender: TObject); var i: Integer; begin uos_GetInfoDevice(); for i := 0 to High(uosDeviceInfos) do begin if uosDeviceInfos[i].DeviceType = 'Out' then ComboBox1.Items.Add('[' + InttoStr(uosDeviceInfos[i].DeviceNum) + '] ' + uosDeviceInfos[i].DeviceName); end; end; procedure TForm1.ComboBox1Change(Sender: TObject); begin end; procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction); begin uos_unloadlib(); uos_free(); end; end. |
Administrator
|
In reply to this post by fredvs
Hello.
I think with 2 players it will work (if your sound card can play 2 devices together): PlayerIndex1 := 0; PlayerIndex2 := 1; uos_CreatePlayer(PlayerIndex1); uos_CreatePlayer(PlayerIndex2); InputIndex1 := uos_AddFromFile(PlayerIndex1, PChar(Application.Location + SoundFileName), -1, -1, -1); InputIndex2 := uos_AddFromFile(PlayerIndex2, PChar(Application.Location + SoundFileName), -1, -1, -1) OutputIndex1 := uos_AddIntoDevOut(PlayerIndex1, -1, -1, -1, -1, -1, -1, -1); OutputIndex2 := uos_AddIntoDevOut(PlayerIndex2, devx, -1, -1, -1, -1, -1, -1); uos_Play(PlayerIndex1); uos_Play(PlayerIndex2); |
Administrator
|
Ooops sorry, we are very synchro.
OK, I can hear a scratch in middle of sound. Does it the same sound withe the SimplePlayer uos demo? Fre;D |
In reply to this post by fredvs
Thanks, I'll try that.
Do you have a solution for the background noise or know what causes it? |
Administrator
|
> Do you have a solution for the background noise or know what causes it?
At the moment no, it is strange because it is not constant. Anyway, you may play with the uos_AddIntoDevOut parameters, mainly with latency ( try with 0.3 ) and size of buffers. |
In reply to this post by fredvs
Ok, I found a solution:
I tried it with the SimplePlayer example and there was no noise, so I treid different settings in the SimplePlayer GUI and noticed that the nise appeared again after selecting int16 or int32 for the sample format, so I changed the sample format in my program to float32 and the noise is gone Thank you for helping me so fast! |
Administrator
|
I am happy that you found a solution.
By default the sample format it set to int16 because old sound cards only accept it. But yes, now all recent audio card can deal with float32. Fre;D |
Free forum by Nabble | Edit this page |