Recent

Author Topic: [SOLVED] Capture sound from line in or microphone  (Read 7655 times)

CM630

  • Hero Member
  • *****
  • Posts: 1191
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: Capture sound from line in or microphone
« Reply #30 on: June 10, 2024, 02:18:38 pm »
Hi Fred Vs,

...
But in a previous post you said that it was working with the workaround using myarray:
Code: Pascal  [Select][+][-]
  1. myarray := uos_File2Buffer(pchar(Edit3.Text), 0 , BufferInfo, -1, -1);

 
Did you try to use thebuffer without splitting it, like you did for the workaround?
Code: Pascal  [Select][+][-]
  1.   uos_AddIntoMemoryBuffer(PlayerIndex1, @thebuffer, -1, 0, -1, -1);


thebuffer and myarray should be the same (here it is he case). 

Also, could you check the result of num input channel, you could do something like this:
...

It still does not work without splitting, as seen on the image attached.

nchan =0;
f = 44100;
from the code below:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button3Click(Sender: TObject);
  2. var
  3.   i, j, nchan : integer;
  4.   arrayleft, arrayright : array of double;
  5.   f : double = 1{/44100};
  6.   arraydouble: array of double;
  7. begin
  8.   nchan := uos_InputGetChannels(PlayerIndex1, In1Index); // this to check nb of channels
  9.   f:= uos_InputGetSampleRate(PlayerIndex1,In1Index);
  10.   uos_Stop(PlayerIndex1);
  11.   ClosePlayer1;
  12.   Chart1LineSeriesLeft.Clear;
  13.   Chart1LineSeriesRight.Clear;
  14.  
  15.   SetLength(arraydouble,length(thebuffer));
  16.   for i := 0 to high(thebuffer) do
  17.   begin
  18.     arraydouble [i]:=thebuffer[i];
  19.     Chart1LineSeriesLeft.AddXY(i/f,arraydouble[i],'',clRed);
  20.   end;
  21.  
  22.   WaveformGraph1.ClearPlots;
  23.   WaveformGraph1.Plot(arraydouble,f,clRed);
  24. end;                              



Hi Tron,
Thanks fot the link.
I am aware of that things, what I was not aware of is how this info is stored in the input data.

Hi CM630,
If I understood you correctly then you could perhaps take a look at this.


Hi Thaddy, ironically, the idea is to demonstrate a control based on TAchart using the audio input...
TAchart is way to slow to keep up with live audio anyway. I usualy draw on a canvas directly, most of my KOL audio display stuff works like that.
« Last Edit: June 10, 2024, 02:25:25 pm by CM630 »
Лазар 4,0RC1 32 bit (sometimes 64 bit); FPC3,2,2

Fred vS

  • Hero Member
  • *****
  • Posts: 3410
    • StrumPract is the musicians best friend
Re: Capture sound from line in or microphone
« Reply #31 on: June 10, 2024, 02:41:37 pm »
Hi CM630

Very, very strange.

In your code you used array of double vs single but maybe there is no problem with this.
May I ask you (sorry for the lot of) to compare the 2 arrays?
I checked uos_AddIntoFile() + uos_AddIntoMemoryBuffer() works here and both arrays are equal.
I propose that we explore your working workaround to adapt it with save to buffer:
https://forum.lazarus.freepascal.org/index.php/topic,67510.msg519772.html#msg519772

So in code,

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button2Click(Sender: TObject);
  2. ...
  3.    uos_AddIntoFile(PlayerIndex1, PChar(edit3.Text), -1, -1, -1, 4096, outformat); // save to file
  4.  
  5.    SetLength(thebuffer, 0);
  6.    uos_AddIntoMemoryBuffer(PlayerIndex1, @thebuffer, -1, 0, -1, -1); // save to buffer    
  7.  

And in close player:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button3Click(Sender: TObject);
  2. var
  3. ..., myarray : array of single;
  4.  
  5. ...
  6.  ClosePlayer1;
  7.  myarray := uos_File2Buffer(pchar(Edit3.Text), 0 , BufferInfo, -1, -1); // your workaround
  8. ...

And then compare myarray with thebuffer.
If they are the same, try using the code you used for the workaround to render the shape and that worked.
https://forum.lazarus.freepascal.org/index.php/topic,67510.msg519772.html#msg519772

And so we can understand what is wrong.
« Last Edit: June 10, 2024, 03:48:54 pm by Fred vS »
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

CM630

  • Hero Member
  • *****
  • Posts: 1191
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: Capture sound from line in or microphone
« Reply #32 on: June 10, 2024, 04:14:00 pm »

...
May I ask you (sorry for the lot of) to compare the 2 arrays?
...

We should have started with that.
Both arrays are not equal (as seen from the attached lazsound.7z)


Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button2Click(Sender: TObject);
  2. var
  3.    outformatst : string;
  4.    outformat :integer;
  5. begin
  6.   if (checkbox1.Checked = True) or (checkbox2.Checked = True) then
  7.   begin
  8.  
  9.  
  10.     PlayerIndex1 := 0;
  11.     // PlayerIndex : from 0 to what your computer can do ! (depends of ram, cpu, ...)
  12.     // If PlayerIndex exists already, it will be overwritten...
  13.  
  14.  
  15.     uos_CreatePlayer(PlayerIndex1);
  16.     //// Create the player.
  17.     //// PlayerIndex : from 0 to what your computer can do !
  18.     //// If PlayerIndex exists already, it will be overwriten...
  19.  
  20.  
  21.     uos_AddIntoFileFromMem(PlayerIndex1, Pchar(edit3.Text));
  22.     //// add Output into wav file (save record) from TMemoryStream  with default parameters
  23.  
  24.  
  25.      if bwav.checked then
  26.      begin
  27.     outformatst := '.wav';
  28.     outformat := 0;
  29.     end else
  30.     begin
  31.     outformatst := '.ogg';
  32.     outformat := 3;
  33.     end;
  34.  
  35.  
  36.      edit3.Text := 'rec_' +  UTF8Decode(formatdatetime('YY_MM_DD_HH_mm_ss', now)) + outformatst ;
  37.  
  38.  
  39.     // saving in a file using a File-Stream:
  40.  
  41.  
  42.      uos_AddIntoFile(PlayerIndex1, PChar(edit3.Text), -1, -1, -1, 4096, outformat);
  43.  
  44.  
  45.  //   function uos_AddIntoFile(PlayerIndex: cint32; Filename: PChar; SampleRate: cint32;
  46.  // Channels: cint32; SampleFormat: cint32 ; FramesCount: cint32 ; FileFormat: cint32): cint32;
  47. // Add a Output into audio wav file with custom parameters from TFileStream
  48. // PlayerIndex : Index of a existing Player
  49. // FileName : filename of saved audio wav file
  50. // SampleRate : delault : -1 (44100)
  51. // Channels : delault : -1 (2:stereo) (1:mono, 2:stereo, ...)
  52. // SampleFormat : default : -1 (2:Int16) (1:Int32, 2:Int16)
  53. // FramesCount : default : -1 (= 65536)
  54. // FileFormat : default : -1 (wav) (0:wav, 1:pcm, 2:custom, 3:ogg);
  55.  
  56.  
  57.     //// add Output into wav or ogg file (save record) from TFileStream
  58.  
  59.  
  60.     // saving in a Memory-Buffer:  
  61.     SetLength(thebuffer, 0);
  62.     uos_AddIntoMemoryBuffer(PlayerIndex1, @thebuffer, -1, 0, -1, -1); // save to buffer
  63.  
  64.  
  65.     //     uos_AddIntoMemoryBuffer(PlayerIndex1, @thebuffer);
  66.  
  67.  
  68.     // saving in a Memory-Stream:  
  69.     // if thememorystream = nil then thememorystream := tmemorystream.create;
  70.     // uos_AddIntoMemoryStream(PlayerIndex1, (thememorystream),-1,-1,-1,-1);
  71.  
  72.  
  73.     // saving in a file using a Menory-Stream:  
  74.     // uos_AddIntoFileFromMem(PlayerIndex1, Pchar(filenameEdit4.filename));
  75.     //// add Output into wav file (save record)  with default parameters
  76.  
  77.  
  78.       {$if defined(cpuarm) or defined(cpuaarch64)}  // need a lower latency
  79.     out1index := uos_AddIntoDevOut(PlayerIndex1, -1, 0.8, -1, -1, -1, -1) ;
  80.        {$else}
  81.     out1index := uos_AddIntoDevOut(PlayerIndex1);
  82.        {$endif}
  83.  
  84.  
  85.     uos_outputsetenable(PlayerIndex1, out1index, checkbox1.Checked);
  86.  
  87.  
  88.      // uos_AddIntoDevOut(PlayerIndex1, -1, -1, 8000, -1, -1,65536, -1);   //// add a Output into device with custom parameters
  89.     //////////// PlayerIndex : Index of a existing Player
  90.     //////////// Device ( -1 is default Output device )
  91.     //////////// Latency  ( -1 is latency suggested ) )
  92.     //////////// SampleRate : delault : -1 (44100)
  93.     //////////// Channels : delault : -1 (2:stereo) (0: no channels, 1:mono, 2:stereo, ...)
  94.     //////////// SampleFormat : -1 default : Int16 : (0: Float32, 1:Int32, 2:Int16)
  95.     //////////// FramesCount : -1 default : 65536
  96.     // ChunkCount : default : -1 (= 512)
  97.  
  98.  
  99.    In1Index := uos_AddFromDevIn(PlayerIndex1);
  100.  
  101.  
  102.     //// add input from mic with custom parameters
  103.     //////////// PlayerIndex : Index of a existing Player
  104.     //////////// Device ( -1 is default Input device )
  105.     //////////// Latency  ( -1 is latency suggested ) )
  106.     //////////// SampleRate : delault : -1 (44100)
  107.     //////////// OutputIndex : OutputIndex of existing Output // -1 : all output, -2: no output, other integer : existing output)
  108.     //////////// SampleFormat : -1 default : Int16 : (0: Float32, 1:Int32, 2:Int16)
  109.     //////////// FramesCount : -1 default : 4096   ( > = safer, < =  better latency )
  110.  
  111.  
  112.    uos_InputAddDSPVolume(PlayerIndex1, In1Index, 1, 1);
  113.     ///// DSP Volume changer
  114.     //////////// PlayerIndex : Index of a existing Player
  115.     ////////// In1Index : InputIndex of a existing input
  116.     ////////// VolLeft : Left volume
  117.     ////////// VolRight : Right volume
  118.  
  119.  
  120.     uos_InputSetDSPVolume(PlayerIndex1, In1Index, TrackBar1.position / 100,
  121.     TrackBar3.position / 100, True); /// Set volume
  122.  
  123.  
  124.     /////// procedure to execute when stream is terminated
  125.     //   uos_EndProc(PlayerIndex1, @ClosePlayer1);
  126.     ///// Assign the procedure of object to execute at end
  127.     //////////// PlayerIndex : Index of a existing Player
  128.     //////////// ClosePlayer1 : procedure of object to execute inside the loop
  129.  
  130.  
  131.     uos_Play(PlayerIndex1);  /////// everything is ready to play...
  132.  
  133.  
  134.     Button2.Enabled   := False;
  135.     Button3.Enabled   := True;
  136.     Button4.Enabled   := False;
  137.     //CheckBox1.Enabled := False;
  138.     CheckBox2.Enabled := False;
  139.   end;
  140. end;        
  141.  
Code: Pascal  [Select][+][-]
  1.  
  2. procedure TForm1.Button3Click(Sender: TObject);
  3. var
  4.   i, j, nchan : integer;
  5.   arrayleft, arrayright : array of double;
  6.   f : double = 1{/44100};
  7.   arraydouble: array of double;
  8.   myarray : array of single;
  9.   BufferInfo : Tuos_BufferInfos;
  10.   doublearray: array of double;
  11. begin
  12.   nchan := uos_InputGetChannels(PlayerIndex1, In1Index); // this to check nb of channels
  13.   f:= uos_InputGetSampleRate(PlayerIndex1,In1Index);
  14.   uos_Stop(PlayerIndex1);
  15.   ClosePlayer1;
  16.   myarray := uos_File2Buffer(pchar(Edit3.Text), 0 , BufferInfo, -1, -1); // your workaround
  17.  
  18.  
  19.   StringGrid1.RowCount := max (Length(thebuffer), length(myarray));
  20.   for i:= 0 to high(thebuffer) do
  21.   begin
  22.     StringGrid1.Cells[0,i] := IntToStr(i);
  23.     StringGrid1.Cells[1,i] := FloatToStr(thebuffer[i]);
  24.   end;
  25.  
  26.  
  27.  
  28.  
  29.   for i:= 0 to high(myarray) do
  30.     StringGrid1.Cells[2,i] := FloatToStr(myarray[i]);
  31.  
  32.  
  33.   StringGrid1.SaveToCSVFile('d:\temp\lazsound.txt',#9);
  34. end;  
...I checked uos_AddIntoFile() + uos_AddIntoMemoryBuffer() works here and both arrays are equal....

Could you share your working project?
« Last Edit: June 10, 2024, 04:17:39 pm by CM630 »
Лазар 4,0RC1 32 bit (sometimes 64 bit); FPC3,2,2

Fred vS

  • Hero Member
  • *****
  • Posts: 3410
    • StrumPract is the musicians best friend
Re: Capture sound from line in or microphone
« Reply #33 on: June 10, 2024, 04:24:01 pm »

...
May I ask you (sorry for the lot of) to compare the 2 arrays?
...

We should have started with that.
Both arrays are not equal (as seen from the attached lazsound.7z)

Grrrrrr.

OK, I will test your zip asap.

I see in your code:
Code: Pascal  [Select][+][-]
  1.  uos_AddIntoFileFromMem(PlayerIndex1, Pchar(edit3.Text));
  2.     //// add Output into wav file (save record) from TMemoryStream  with default parameters
Why did you add this?
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

Fred vS

  • Hero Member
  • *****
  • Posts: 3410
    • StrumPract is the musicians best friend
Re: Capture sound from line in or microphone
« Reply #34 on: June 10, 2024, 05:40:13 pm »
Hi CM630

Indeed, trying your code there is something strange from sample 36865, with some 0 or Nan value.
It is like the buffer cannot be filled sometime.
That needs, I fear, a white night and some cups coffee, I cannot see why at the moment.

Maybe you could try using a uos_AddIntoMemoryStream(), you could have better result?

You may also get the array of samples live, with a uos_LoopProcIn() with a uos_InputGetBuffer().
And then add at each loop the buffer to the main buffer by you self.

Code: Pascal  [Select][+][-]
  1. // before play
  2.  uos_LoopProcIn(PlayerIndex1, InputIndex1, @AddBuffer);
  3. ...
  4. // the loop proc:
  5. procedure Tform1.AddBuffer;
  6. var
  7. mybuffertemp :array of single;
  8. begin
  9. mybuffertemp :=  uos_InputGetBuffer(PlayerIndex1, InputIndex1);
  10. // do what you want with the buffer
  11. end;

Quote
Could you share your working project?
I just use the SimpleRecorder project with your code.
All my projects use live DSP with uos_LoopProcIn() and so with little buffer of max 4096 frames-samples.
« Last Edit: June 10, 2024, 06:37:10 pm by Fred vS »
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

Fred vS

  • Hero Member
  • *****
  • Posts: 3410
    • StrumPract is the musicians best friend
Re: Capture sound from line in or microphone
« Reply #35 on: June 10, 2024, 07:07:22 pm »
Re-re Hello CM630.

I think I get it.  ;D
Could you try with this (change the FrameCount to 65536 ):

Code: Pascal  [Select][+][-]
  1.     SetLength(thebuffer, 0);
  2.     uos_AddIntoMemoryBuffer(PlayerIndex1, @thebuffer, -1, 0, -1, 65536);
 

With this, here both array are the same without Nan.
The default FrameCount (1024) is too little, need to be updated.

EDIT: After some test, it seems that 4096 minimum FrameCount is OK, maybe could be the default.

I used that code:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button2Click(Sender: TObject); // The Start button
  2. var
  3.    outformatst : string;
  4.    outformat :integer;
  5. begin
  6.   if (checkbox1.Checked = True) or (checkbox2.Checked = True) then
  7.   begin
  8.  
  9.     PlayerIndex1 := 0;
  10.     // PlayerIndex : from 0 to what your computer can do ! (depends of ram, cpu, ...)
  11.     // If PlayerIndex exists already, it will be overwritten...
  12.  
  13.     uos_CreatePlayer(PlayerIndex1);
  14.     //// Create the player.
  15.     //// PlayerIndex : from 0 to what your computer can do !
  16.     //// If PlayerIndex exists already, it will be overwriten...
  17.  
  18.    if bwav.checked then
  19.      begin
  20.     outformatst := '.wav';
  21.     outformat := 0;
  22.     end else
  23.     begin
  24.     outformatst := '.ogg';
  25.     outformat := 3;
  26.     end;
  27.  
  28.      edit3.Text := 'rec_' +  UTF8Decode(formatdatetime('YY_MM_DD_HH_mm_ss', now)) + outformatst ;
  29.  
  30.     // saving in a file using a File-Stream:
  31.  
  32.      uos_AddIntoFile(PlayerIndex1, PChar(edit3.Text), -1, -1, -1, 4096, outformat);
  33.  
  34. //   function uos_AddIntoFile(PlayerIndex: cint32; Filename: PChar; SampleRate: cint32;
  35. // Channels: cint32; SampleFormat: cint32 ; FramesCount: cint32 ; FileFormat: cint32): cint32;
  36. // Add a Output into audio wav file with custom parameters from TFileStream
  37. // PlayerIndex : Index of a existing Player
  38. // FileName : filename of saved audio wav file
  39. // SampleRate : delault : -1 (44100)
  40. // Channels : delault : -1 (2:stereo) (1:mono, 2:stereo, ...)
  41. // SampleFormat : default : -1 (2:Int16) (1:Int32, 2:Int16)
  42. // FramesCount : default : -1 (= 65536)
  43. // FileFormat : default : -1 (wav) (0:wav, 1:pcm, 2:custom, 3:ogg);
  44.  
  45. // saving in a Memory-Buffer:  
  46.     SetLength(thebuffer, 0);
  47.     uos_AddIntoMemoryBuffer(PlayerIndex1, @thebuffer, -1, 0, -1, 65536);
  48.    
  49.     {$if defined(cpuarm) or defined(cpuaarch64)}  // need a lower latency
  50.     out1index := uos_AddIntoDevOut(PlayerIndex1, -1, 0.8, -1, -1, -1, -1, -1) ;
  51.      {$else}
  52.     out1index := uos_AddIntoDevOut(PlayerIndex1);
  53.      {$endif}
  54.  
  55.     uos_outputsetenable(PlayerIndex1, out1index, checkbox1.Checked);
  56.  
  57.      // uos_AddIntoDevOut(PlayerIndex1, -1, -1, 8000, -1, -1,65536, -1);   //// add a Output into device with custom parameters
  58.     //////////// PlayerIndex : Index of a existing Player
  59.     //////////// Device ( -1 is default Output device )
  60.     //////////// Latency  ( -1 is latency suggested ) )
  61.     //////////// SampleRate : delault : -1 (44100)
  62.     //////////// Channels : delault : -1 (2:stereo) (0: no channels, 1:mono, 2:stereo, ...)
  63.     //////////// SampleFormat : -1 default : Int16 : (0: Float32, 1:Int32, 2:Int16)
  64.     //////////// FramesCount : -1 default : 65536
  65.     // ChunkCount : default : -1 (= 512)
  66.  
  67.    In1Index := uos_AddFromDevIn(PlayerIndex1);
  68.  
  69.     //// add input from mic with custom parameters
  70.     //////////// PlayerIndex : Index of a existing Player
  71.     //////////// Device ( -1 is default Input device )
  72.     //////////// Latency  ( -1 is latency suggested ) )
  73.     //////////// SampleRate : delault : -1 (44100)
  74.     //////////// OutputIndex : OutputIndex of existing Output // -1 : all output, -2: no output, other integer : existing output)
  75.     //////////// SampleFormat : -1 default : Int16 : (0: Float32, 1:Int32, 2:Int16)
  76.     //////////// FramesCount : -1 default : 4096   ( > = safer, < =  better latency )
  77.  
  78.    uos_InputAddDSPVolume(PlayerIndex1, In1Index, 1, 1);
  79.     ///// DSP Volume changer
  80.     //////////// PlayerIndex : Index of a existing Player
  81.     ////////// In1Index : InputIndex of a existing input
  82.     ////////// VolLeft : Left volume
  83.     ////////// VolRight : Right volume
  84.  
  85.     uos_InputSetDSPVolume(PlayerIndex1, In1Index, TrackBar1.position / 100,
  86.     TrackBar3.position / 100, True); /// Set volume
  87.  
  88.     /////// procedure to execute when stream is terminated
  89.     //   uos_EndProc(PlayerIndex1, @ClosePlayer1);
  90.     ///// Assign the procedure of object to execute at end
  91.     //////////// PlayerIndex : Index of a existing Player
  92.     //////////// ClosePlayer1 : procedure of object to execute inside the loop
  93.  
  94.     uos_Play(PlayerIndex1);  /////// everything is ready to play...
  95.  
  96.     Button2.Enabled   := False;
  97.     Button3.Enabled   := True;
  98.     Button4.Enabled   := False;
  99.     //CheckBox1.Enabled := False;
  100.     CheckBox2.Enabled := False;
  101.   end;
  102. end;

and this:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button3Click(Sender: TObject); // The Stop button
  2. var
  3.    myarray : array of single;
  4.    i : integer;
  5.    BufferInfo : Tuos_BufferInfos;
  6. begin
  7.   uos_Stop(PlayerIndex1);
  8.   ClosePlayer1;
  9.   myarray := uos_File2Buffer(pchar(Edit3.Text), 0 , BufferInfo, -1, -1); // your workaround
  10.  
  11.   StringGrid1.RowCount := max (Length(thebuffer), length(myarray));
  12.  
  13.     for i:= 0 to high(thebuffer) do
  14.   begin
  15.     StringGrid1.Cells[0,i] := IntToStr(i);
  16.     StringGrid1.Cells[1,i] := FloatToStr(thebuffer[i]);
  17.   end;
  18.  
  19.   for i:= 0 to high(myarray) do
  20.   StringGrid1.Cells[2,i] := FloatToStr(myarray[i]);
  21.  
  22.   StringGrid1.SaveToCSVFile('lazsound.txt',#9);
  23.  
  24. end;

Quote
nchan =0;
Hum, other mystery, here I get nchan = 2 and in uos code it can be only 1 or 2, not 0.  :-\
 
Code: Pascal  [Select][+][-]
  1. if  ((Pa_GetDeviceInfo(StreamIn[x].PAParam.device)^.
  2.      maxInputChannels)) > 1 then
  3.     StreamIn[x].PAParam.channelCount := CInt32(2)
  4.   else
  5.     StreamIn[x].PAParam.channelCount := CInt32(1) ;
  6.  
  7.   StreamIn[x].data.channels := StreamIn[x].PAParam.channelCount;

In attachment, my lazsound.txt zipped.
« Last Edit: June 11, 2024, 03:28:24 am by Fred vS »
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

TRon

  • Hero Member
  • *****
  • Posts: 3623
Re: Capture sound from line in or microphone
« Reply #36 on: June 11, 2024, 09:20:48 am »
@Fred vS (but also CM630 if you can do it yourself)

Initially when I got across the audio topic I did not grasp some of the concepts either. What helped me tremendously is writing buffers out to a readable format on disk and use an
already established audio program/wav editor to see how the audio data actually looks like and try to replicate what such program did. Something like audacity comes to mind but it can be any of your favourite audio/wav file editor.

If there is anything wrong with the recording then other audio software is able to let you hear that. So imho it is much better to start out with an already existing and known to be good audio file, preferably opened in/streamed to a buffer that has the same format as the recording buffer used in your own program and then let existing code do it's magic.

It is the most quickest and easiest verification of existing code though having said that I do not have any experience with UOS libraries so no idea if that would be difficult to accomplish (I take it it is not too difficult else why bother writing an audio library in the first place :) ).

All the guessing and trying to fix code based on some descriptions is imho taking the long route.
This tagline is powered by AI (AI advertisement: Free Pascal the only programming language that matters)

CM630

  • Hero Member
  • *****
  • Posts: 1191
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: Capture sound from line in or microphone
« Reply #37 on: June 11, 2024, 12:12:04 pm »
THANKS!
Lazarus on the left, audacity on the right.
 :D


It worked with

Code: Pascal  [Select][+][-]
  1.     SetLength(thebuffer, 0);
  2.     uos_AddIntoMemoryBuffer(PlayerIndex1, @thebuffer, -1, 0, -1, 65536); // save to buffer

@Fred vS, may I propose to add some check in UOS/function Tuos_Player.AddIntoDevOut(Device: cint32; Latency: CDouble;...
It crashes when Device = -1, it should better do nothing?
Device becomes -1 when my headphones are not switched on. Maybe this happens when they are switched off after initializing the app - I have another sound devices, so I suppose Device shall not become -1.
Лазар 4,0RC1 32 bit (sometimes 64 bit); FPC3,2,2

Fred vS

  • Hero Member
  • *****
  • Posts: 3410
    • StrumPract is the musicians best friend
Re: Capture sound from line in or microphone
« Reply #38 on: June 11, 2024, 01:56:45 pm »
Hello CM630

Yep, I am very happy that it finally works for you.  ;D

Quote
may I propose to add some check in UOS/function Tuos_Player.AddIntoDevOut(Device: cint32; Latency: CDouble;...
It crashes when Device = -1, it should better do nothing?

I'll happily add verification but I'm afraid I don't understand what you want.

Tuos_Player.AddIntoDevOut(Device: cint32;...) ---> if you assign Device := -1, it will use your system's default device.
Could you explain exactly when the crash appears, when AddIntoDevOut() is called or later when trying to connect the output?

But have you tried using uos_UpdateDevice(),  before calling uos_AddIntoDevOut()?

EDIT Ok, I see maybe what you mean, if using AddIntoDevOut(Device,...) with Device := -1, check with Pa_GetDefaultOutputDevice() dont give -1 as result, if it is the case, exit with result of function = -1.
The same for AddFromDevIn(Device,...).
Ok, good idea, I will do it, thanks for the proposition.

TRon: Many thanks for your precious advices.
Quote
Something like audacity comes to mind but it can be any of your favourite audio/wav file editor.
It is my favourite too. And audacity uses the same open-source dependencies as uos (Portaudio, Sndfile, mpg123, Soundtouch,..).
« Last Edit: June 11, 2024, 03:32:21 pm by Fred vS »
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

CM630

  • Hero Member
  • *****
  • Posts: 1191
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: Capture sound from line in or microphone
« Reply #39 on: June 11, 2024, 04:43:58 pm »
Quote
may I propose to add some check in UOS/function Tuos_Player.AddIntoDevOut(Device: cint32; Latency: CDouble;...
It crashes when Device = -1, it should better do nothing?


I'll happily add verification but I'm afraid I don't understand what you want.

Tuos_Player.AddIntoDevOut(Device: cint32;...) ---> if you assign Device := -1, it will use your system's default device.
Could you explain exactly when the crash appears, when AddIntoDevOut() is called or later when trying to connect the output?
...

For some reason it does not crash anymore.
But there is another issue:


This is the working code:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button2Click(Sender: TObject);
  2. begin
  3.     lastsample := 0;
  4.     Timer1.Enabled := True;
  5.     PlayerIndex1 := 0;
  6.  
  7.  
  8.     uos_CreatePlayer(PlayerIndex1);
  9.  
  10.  
  11.     // saving in a Memory-Buffer:  
  12.     SetLength(thebuffer, 0);
  13.     uos_AddIntoMemoryBuffer(PlayerIndex1, @thebuffer, -1, 0, -1, 65536); // save to buffer
  14.  
  15.  
  16.       {$if defined(cpuarm) or defined(cpuaarch64)}  // need a lower latency
  17.     out1index := uos_AddIntoDevOut(PlayerIndex1, -1, 0.8, -1, -1, -1, -1) ;
  18.        {$else}
  19.     out1index := uos_AddIntoDevOut(PlayerIndex1);
  20.        {$endif}
  21.  
  22.  
  23.     uos_outputsetenable(PlayerIndex1, out1index, false{checkbox1.Checked});
  24.  
  25.  
  26.    In1Index := uos_AddFromDevIn(PlayerIndex1);
  27.  
  28.  
  29.    uos_InputAddDSPVolume(PlayerIndex1, In1Index, 1, 1);
  30.  
  31.  
  32.     uos_InputSetDSPVolume(PlayerIndex1, In1Index, TrackBar1.position / 100,
  33.     TrackBar3.position / 100, True); /// Set volume
  34.  
  35.  
  36.  
  37.  
  38.     uos_Play(PlayerIndex1);  /////// everything is ready to play...
  39.  
  40.  
  41.     Button2.Enabled   := False;
  42.     Button3.Enabled   := True;
  43.     Button4.Enabled   := False;
  44.     //CheckBox1.Enabled := False;
  45.     CheckBox2.Enabled := False;
  46. end;  
  47.  


And I have added a timer, running once a second. It contains

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Timer1Timer(Sender: TObject);
  2. begin
  3.   Label3.Caption := IntToStr(Length(thebuffer));
  4. end;  
  5.  

When this code is executed, after some time I get exception class 'External: SIGSEGV'

The number of the last sample cont shown before the exception varies, but it is not some huge number:
7 708 672
12 836 864
12 951 552
2 727 936
8 462 336
...

So I commented Label3.Caption := IntToStr(Length(thebuffer)); in the timer and added it in the Stop button procedure.
I run the app again, nothing crashed. But 10 minutes after pressing the Stop, I had Label3 showing 6 012 928.
6 012 928/(2·44100) = 68,173786848 second <<< 10 minutes.

Also, when I closed the app it showed “SIGSEG...” at some address in

Code: Pascal  [Select][+][-]
  1. procedure tuosthread.execute
  2. ...
  3.  
  4.   {$IF DEFINED(portaudio)}
  5.                       1: // for Input from device
  6.                          ReadDevice(x);
  7.   {$endif}            
« Last Edit: June 11, 2024, 04:49:39 pm by CM630 »
Лазар 4,0RC1 32 bit (sometimes 64 bit); FPC3,2,2

Fred vS

  • Hero Member
  • *****
  • Posts: 3410
    • StrumPract is the musicians best friend
Re: Capture sound from line in or microphone
« Reply #40 on: June 11, 2024, 05:17:58 pm »
Quote
>And I have added a timer, running once a second. It contains

Not sure it's a good idea to annoy the buffer using a timer (same for the whole looping process).
The buffer is filled each time in the main loop, with a timer the call to Length(thebuffer) may occur just as the buffer is updated.
You will also have problems syncing the Tlabel.

Much better is to use uos_LoopProcIn() like showed in previous post:
https://forum.lazarus.freepascal.org/index.php/topic,67510.msg520006.html#msg520006

In this case you will call length(thebuffer) when it is ready and sync the Tlabel there (sync is done by uos_LoopProcIn()).
And if you don't want it added to every loop, in the loop-proc of uos_LoopProcIn() use a TTime or an index to decide when to check-write to Tlabel.

Quote
So I commented Label3.Caption := IntToStr(Length(thebuffer)); in the timer and added it in the Stop button procedure.
You may use uos_EndProc() for this. It will execute the end-procedure at end of loop.

Someting like this:
Code: Pascal  [Select][+][-]
  1. var // global variable
  2. indexcheck : integer = 0;
  3.  
  4. .........................................
  5.  
  6. TForm1 = class(TForm)  
  7. ...
  8. procedure CheckBuffer;   // add this
  9. procedure CheckBufferEnd;   // add this
  10. .......................................
  11.  
  12. procedure TForm1.Button2Click(Sender: TObject);
  13. ...
  14.  // before play
  15.  uos_LoopProcIn(PlayerIndex1, In1Index, @CheckBuffer);  // The procedure to run at each loop
  16.  indexcheck := 0;
  17.  
  18.  uos_EndProc(PlayerIndex1, @CheckBufferEnd); // the procedure to do at end of the thread when the buffer is full.
  19. ...
  20. ..........................................................
  21.  
  22.  // the loop-proc:
  23.  procedure Tform1.CheckBuffer;
  24.   begin
  25.    inc(indexcheck);
  26.    if indexcheck > 10 then // choose your interval but it should work at each loop
  27.      begin
  28.      indexcheck := 0; // reinitialize the interval
  29.      Label3.Caption := IntToStr(Length(thebuffer)); // Synchronization
  30.      end;
  31.   end;
  32.  
  33. // the end-proc:
  34.  procedure Tform1.CheckBufferEnd;
  35.   begin
  36.       Label3.Caption := 'Full buffer ' +  lntToStr(Length(thebuffer)); // Synchronization at end of loop
  37.   end;
« Last Edit: June 11, 2024, 11:08:21 pm by Fred vS »
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

TRon

  • Hero Member
  • *****
  • Posts: 3623
Re: Capture sound from line in or microphone
« Reply #41 on: June 12, 2024, 09:46:25 am »
Good to see that you got things moving now CM630 and thank you Fred vS for the help.

You are both more than welcome and it shows that some things can be fixed quite easily (such as understanding wave layout in memory) with some visualization and audible output for verification.

This tagline is powered by AI (AI advertisement: Free Pascal the only programming language that matters)

CM630

  • Hero Member
  • *****
  • Posts: 1191
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: Capture sound from line in or microphone
« Reply #42 on: June 14, 2024, 11:40:05 am »

Good to see that you got things moving now...
Motion is slow and I am not sure if it is forward or backward.


...
You may also get the array of samples live, with a uos_LoopProcIn() with a uos_InputGetBuffer().
And then add at each loop the buffer to the main buffer by you self.


Code: Pascal  [Select][+][-]
  1. // before play
  2.  uos_LoopProcIn(PlayerIndex1, InputIndex1, @AddBuffer);
  3. ...
  4. // the loop proc:
  5. procedure Tform1.AddBuffer;
  6. var
  7. mybuffertemp :array of single;
  8. begin
  9. mybuffertemp :=  uos_InputGetBuffer(PlayerIndex1, InputIndex1);
  10. // do what you want with the buffer
  11. end;
...
What shall I do with InputIndex1? Shall I set it to zero?




This shows nothing reeasonable, and after a few seconds AddBuffer stops being triggered.
Code: Pascal  [Select][+][-]
  1.  
  2. // the loop proc:
  3. procedure Tform1.AddBuffer;
  4. var
  5.   mybuffertemp :array of single;
  6.   i: integer;
  7. begin
  8.    mybuffertemp :=  uos_InputGetBuffer(PlayerIndex1, InputIndex1);
  9.    Label3.Caption := IntToStr((GetTickCount64 - starttime) div 1000) + ' s ; ' + IntToStr  (Length(thebuffer)) + ';  ' + IntToStr(length(mybuffertemp)) + '; '  + IntToStr(InputIndex1);
  10.    for i:=0 to high(mybuffertemp) do
  11.    begin
  12.      Chart1LineSeriesLeft.AddXY(SampleIndex/2*44100,double(mybuffertemp[i]));
  13.      inc(SampleIndex);
  14.    end;
  15. // do what you want with the buffer
  16. end;
  17.  
  18. procedure TForm1.Button2Click(Sender: TObject);
  19. begin
  20.     lastsample := 0;
  21.     PlayerIndex1 := 0;
  22.     SampleIndex :=0;
  23.  
  24.  
  25.     uos_CreatePlayer(PlayerIndex1);
  26.  
  27.  
  28.     // saving in a Memory-Buffer:
  29.     {
  30.     SetLength(thebuffer, 0);
  31.     uos_AddIntoMemoryBuffer(PlayerIndex1, @thebuffer, -1, 0, -1, 65536); // save to buffer
  32.     }
  33.  
  34.  
  35.       {$if defined(cpuarm) or defined(cpuaarch64)}  // need a lower latency
  36.     out1index := uos_AddIntoDevOut(PlayerIndex1, -1, 0.8, -1, -1, -1, -1) ;
  37.        {$else}
  38.     out1index := uos_AddIntoDevOut(PlayerIndex1);
  39.        {$endif}
  40.  
  41.  
  42.     uos_outputsetenable(PlayerIndex1, out1index, false{checkbox1.Checked});
  43.  
  44.  
  45.    In1Index := uos_AddFromDevIn(PlayerIndex1);
  46.  
  47.  
  48.    uos_InputAddDSPVolume(PlayerIndex1, In1Index, 1, 1);
  49.  
  50.  
  51. //    uos_InputSetDSPVolume(PlayerIndex1, In1Index, TrackBar1.position / 100, TrackBar3.position / 100, True); /// Set volume
  52.  
  53.  
  54.     uos_LoopProcIn(PlayerIndex1, InputIndex1, @AddBuffer);
  55.     starttime := GetTickCount64;
  56.     Chart1LineSeriesLeft.Clear;
  57.     uos_Play(PlayerIndex1);  /////// everything is ready to play...
  58.  
  59.  
  60.     Button2.Enabled   := False;
  61.     Button3.Enabled   := True;
  62.     Button4.Enabled   := False;
  63.     CheckBox2.Enabled := False;
  64. end;
  65.  
« Last Edit: June 14, 2024, 11:46:23 am by CM630 »
Лазар 4,0RC1 32 bit (sometimes 64 bit); FPC3,2,2

Fred vS

  • Hero Member
  • *****
  • Posts: 3410
    • StrumPract is the musicians best friend
Re: Capture sound from line in or microphone
« Reply #43 on: June 14, 2024, 01:33:17 pm »
Hello CM630.

I cannot test your code at the moment, but after first look, can you change this:
Code: Pascal  [Select][+][-]
  1.   uos_LoopProcIn(PlayerIndex1, InputIndex1, @AddBuffer);
with this:
Code: Pascal  [Select][+][-]
  1.   uos_LoopProcIn(PlayerIndex1, In1Index, @AddBuffer);

And change this:
Code: Pascal  [Select][+][-]
  1. mybuffertemp :=  uos_InputGetBuffer(PlayerIndex1, InputIndex1);
with this
Code: Pascal  [Select][+][-]
  1. mybuffertemp :=  uos_InputGetBuffer(PlayerIndex1, In1Index);

Tip: Please read the example in my previous post.
« Last Edit: June 14, 2024, 02:00:29 pm by Fred vS »
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

CM630

  • Hero Member
  • *****
  • Posts: 1191
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: Capture sound from line in or microphone
« Reply #44 on: June 14, 2024, 02:03:25 pm »
I replaced InputIndex1 with In1Index. No difference.


Code: Pascal  [Select][+][-]
  1.  
  2. procedure Tform1.AddBuffer;
  3. ...   Label3.Caption := IntToStr((GetTickCount64 - starttime) div 1000) + ' s ; ' + IntToStr  (Length(thebuffer)) + ';  ' + IntToStr(length(mybuffertemp)) + '; '  + IntToStr(In1Index);
  4.  

Retruns “xxx s; 0; 8192; 0”.

Should not the size of the buffer be higher? And should In1Index always be 0?

Code: Pascal  [Select][+][-]
  1. Tip: Please read my example in my previous post.
I will try it and give feedback...

Лазар 4,0RC1 32 bit (sometimes 64 bit); FPC3,2,2

 

TinyPortal © 2005-2018