Noise cancelling radio

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
15 messages Options
Reply | Threaded
Open this post in threaded view
|

Noise cancelling radio

Peter
Hi - I am using Lazarus in a Windows environment to write a program which accepts a stereo audio signal from an external radio and outputs a modified version of the right-hand channel samples to the computer's speakers, all in real-time and at a fixed sample rate. Periodically (say once per second) the program needs to (a) extract a portion of both channel streams, (b) analyse the extracted samples in order to calculate two parameters (actually a gain and a phase). The current values of those parameters are used continuously to modify the left-hand channel samples and add them to the right-hand channel samples in order to produce the modified right-hand channel output which goes to the speakers.

I am hoping that I can use UOSlib for this, and can already do several steps of the program. However, I can't see how to obtain the left-hand and right-hand samples, one by one, and re-place the right-hand samples with modified values so that the modified values are sent to the speakers. Can I do this with UOSlib? - best regards - Peter
Reply | Threaded
Open this post in threaded view
|

Re: Noise cancelling radio

fredvs
Administrator
This post was updated on .
Hello and welcome in uos forum.

> However, I can't see how to obtain the left-hand and right-hand samples, one by one, and re-place
> the right-hand samples with modified values so that the modified values are sent to the speakers.
> Can I do this with UOSlib?

Yes, of course, please take a look at SimplePlayer demo.
There is, for example a DSP "Stereo to Mono".  Also the volume is assigned for left and right channels.

You may also deal that way: in audio-stereo all the odd position of samples are for left channel, all the even position of samples are right channel.

Format of pcm audio data:

0L|0R|1L|1R|2L|2R|3L|3R|.....

Fre;D
Reply | Threaded
Open this post in threaded view
|

Re: Noise cancelling radio

Peter
Hi Fr;d

Please forgive me if I am missing the obvious, but I cannot see how to find the input point where the next sample from the input device will be placed in the memory buffer, and the output point from which the next sample from the memory will be taken and sent to the output device. Here is a snippet of code:

...
     PlayerIndex1 := 0;
     uos_CreatePlayer(PlayerIndex1);
     SetLength(thebuffer,0);
     uos_AddIntoMemoryBuffer(PlayerIndex1,@thebuffer); // {MIC as the default input device}
     In1Index := uos_AddFromDevIn(PlayerIndex1,-1,-1,44100,-1,1,4096,512);
     uos_Play(PlayerIndex1);
...

Off it goes, faithfully inserting from the MIC into memory. Later I use:

...
     PlayerIndex1 := 0;
     uos_CreatePlayer(PlayerIndex1);
     uos_AddIntoDevOut(PlayerIndex1,-1,-1,        {default output device is my PC speaker}
       uos_InputGetSampleRate(PlayerIndex1,In1Index),
       uos_InputGetChannels(PlayerIndex1,In1Index),-1,-1,-1);
    uos_Play(PlayerIndex1);
...

Off it goes again, faithfully reproducing in the speaker what I said during the recording. So that all works fine. But how can I obtain the memory buffer input point during the recording, and the memory buffer output point during playback? - best regards - Peter

Reply | Threaded
Open this post in threaded view
|

Re: Noise cancelling radio

fredvs
Administrator
Hello Peter.

Sorry for that stupid question but what do you mean with ?:

> the input point where the next sample from the input device will be placed in the memory buffer,

What is "input point" ?

Fre;D
Reply | Threaded
Open this post in threaded view
|

Re: Noise cancelling radio

Peter
Hi Fred - thank you very much for your response. I am sorry that I was not clear. By the "input point" I mean the index into the buffer where the next sample will be stored. Thus: sample := thebuffer[input_point-1] returns the value of the last sample stored in the buffer (assuming mono, otherwise perhaps ...[input_point-2]) - best regards - Peter
Reply | Threaded
Open this post in threaded view
|

Re: Noise cancelling radio

fredvs
Administrator
Hello Peter.

>I mean the index into the buffer where the next sample will be stored.

Sorry but I still do not understand.


What do you mean with next sample will be stored ?

When you record samples from a input, the input will give a array of samples (the buffer) at each loop of the thread.
The length of that buffer is assigned when you create the player.

Are you asking how to know, at a precise moment, what index of the buffer the input is busy to fill ?
I am not sure it is possible, the audio-card fill the buffer with input-data then wait for next loop.

IMHO, to get this you could try with the littlest length of buffer to have more precision (we are taking here about mseconds).

But surely I did not understand what you want.

Fre;D
Reply | Threaded
Open this post in threaded view
|

Re: Noise cancelling radio

Peter
Hi Fred - thank you for your reply and patience with my ignorance! Perhaps it would be best to state exactly what I need to do, and then ask you how to do it with UOS. The steps I need are:

1. Take a stereo input from an input device (e.g. a virtual audio cable), and output just the RH channel to an output device (e.g. the computer's speakers).
2. Periodically (say once per second), extract a part of the recorded stream (say 8192 samples) into a separate array for analysis.
3. Manipulate the output stream, replacing the RH samples with modified samples, so that what is played through the output device consists entirely of a stream of modified samples.

I can do 1. already, following your example "simple player" demo. I do not know how to do 2. or 3. and would be very grateful for your help.

With best regards - Peter
Reply | Threaded
Open this post in threaded view
|

Re: Noise cancelling radio

fredvs
Administrator
Hello Peter.

You are welcome!

IMHO, the easier way to fast understand how uos work is to try the demos.

For example, begin with all the "console***.lpi" demos.

This are command-line programs that show the basis how uos work.

For how input works, please take a look at "SimpleRecorder" demo.

Fre;D
Reply | Threaded
Open this post in threaded view
|

Re: Noise cancelling radio

fredvs
Administrator
In reply to this post by Peter
> 2. Periodically (say once per second), extract a part of the recorded stream (say 8192 samples) into a separate array for analysis.

> 3. Manipulate the output stream, replacing the RH samples with modified samples, so that what is played through the output device consists entirely of a stream of modified samples.


You should use uos_InputAddDSP() (see in simpleplayer demo how it works).

Fre;D
Reply | Threaded
Open this post in threaded view
|

Re: Noise cancelling radio

Peter
Thanks Fred - I'll try that - best regards - Peter
Reply | Threaded
Open this post in threaded view
|

Re: Noise cancelling radio

Peter
Hi Fred - thank you for your help and advice. I have adapted a call to uos_InputAddDSP() as you suggested, and it all works. So, I speak into the microphone, modify the samples as required in the function called in the loop, then play back the modified samples through the speakers. However, the memory buffer, in this case, gets longer and longer as time goes on, and so the program will eventually stop working when all the available memory has been used. Can you suggest how to have a short buffer which is re-used so that the program can run indefinitely? - best regards - Peter
Reply | Threaded
Open this post in threaded view
|

Re: Noise cancelling radio

fredvs
Administrator
Hello.

You may use a filestream vs a memorystream.

But the best is to show the code how you do to fill the memory buffer so we can see what to change.

Fre;D
Reply | Threaded
Open this post in threaded view
|

Re: Noise cancelling radio

Peter
Hi Fred - thank you for your continuing patience. Here is the essence of my code ('....' means I have deleted some parts which are not relevant).

unit main;

{$mode objfpc}{$H+}
interface

uses
    forms, dialogs, sysutils, graphics, ctypes, stdctrls, comctrls, extctrls,
    classes, controls, uos, uos_flat, globals;

type
    cfloat = single;
  { TForm1 }
  TForm1 = class(TForm)
....
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
....
    procedure FormCreate(Sender: TObject);
....
    procedure ClosePlayer1;
....
  private
    { private declarations }
  public
    { public declarations }
  end;

  function grab_data(var Data:Tuos_Data;var FFT:Tuos_FFT) : TDArFloat;

....
var

  Form1: TForm1;
  PlayerIndex1: cardinal;
  In1Index, Out1index : integer;
  thebuffer : array of cfloat;
  thebufferinfos : TuosF_BufferInfos;
  thememorystream : Tmemorystream;
  library_loaded : boolean;
 ....

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.ClosePlayer1;
begin
     sleep(500);
     application.ProcessMessages;
....

end;


procedure TForm1.Button2Click(Sender: TObject); // stop recording
begin

{Index of player, overwitten if it already exists}
     PlayerIndex1 := 0;

{Create the player}
     if uos_CreatePlayer(PlayerIndex1) then
     begin

{Saving in a Memory-Buffer}
          SetLength(thebuffer,0);
          in1index := uos_AddIntoMemoryBuffer
            (PlayerIndex1,@thebuffer,44100,2,2,-1);

{Listen to the recording as it is being made}
          out1index := uos_AddIntoDevOut(PlayerIndex1);
          uos_outputsetenable(PlayerIndex1,out1index,true);

{Add input from the mic into IN device with default parameters}
          In1Index := uos_AddFromDevIn(PlayerIndex1);

....

{Specify a procedure to execute when the stream is terminated}
          uos_EndProc(PlayerIndex1,@ClosePlayer1);

....
{Here we specify the function 'grabdata' to be executed in each loop}
          uos_InputAddDSP(PlayerIndex1,In1Index,nil,@grab_data,nil,nil);

{Let's go...}
          uos_Play(PlayerIndex1);
     end;

....

end;

procedure TForm1.Button3Click(Sender: TObject); // stop recording
begin
     uos_Stop(PlayerIndex1);
     ClosePlayer1;
....

end;

procedure TForm1.Button4Click(Sender: TObject); // start play-back

var
   j : cint32;

begin

{Create a player; if is exists it will be overwitten}
     PlayerIndex1 := 0;
     uos_CreatePlayer(PlayerIndex1);

{Add an input from a memory buffer:}
{First describe the memory buffer:}
     j := length(thebuffer);
     uos_CustBufferInfos(thebufferinfos,44100,2,2,Length(thebuffer) div 2);

{Now add in the buffer itself:}
    Out1Index := uos_AddFromMemoryBuffer
       (PlayerIndex1,thebuffer,thebufferinfos,-1,-1);

{add a Output into OUT device with default parameters}
     uos_AddIntoDevOut(PlayerIndex1);

....

{Procedure to execute when the stream has ended}
     uos_EndProc(PlayerIndex1,@button5click);

....

{Let's go...}
     uos_Play(PlayerIndex1);

....

end;

....

procedure TForm1.FormCreate(Sender: TObject);

var
   s1, s2 : string;
   tmr : tmodalresult;
   ii : longint;

begin

{Sound libraries}
     s1 := application.Location+'LibPortaudio-32.dll';
     s2 := application.Location+'LibSndFile-32.dll';

....

{Load the libraries}
     ii := uos_LoadLib(pchar(s1),pchar(s2),nil,nil,nil,nil);
     if (ii <> 0) then
     begin
          library_loaded := false;
          if (ii = -1) then
 tmr := MessageDlg('Can''t find portaudio or sound file',mtWarning,[mbClose],0)
          else
          begin
               if (uosLoadResult.PAloaderror = 1) then
             tmr := MessageDlg(s1+' does not exist',mtWarning,[mbClose],0);
                if (uosLoadResult.PAloaderror = 2) then
             tmr := MessageDlg('Cannot load '+s1,mtWarning,[mbClose],0);
                if (uosLoadResult.SFloaderror = 1) then
             tmr := MessageDlg(s2+' does not exist',mtWarning,[mbClose],0);
                if (uosLoadResult.SFloaderror = 2) then
             tmr := MessageDlg('Cannot load '+s2,mtWarning,[mbClose],0);
          end;
          application.processmessages;
          formdestroy(sender);
          close;
          halt;
     end;
     library_loaded := true;
 
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin

....

     if (library_loaded) then uos_free;
end;

....

function grab_data
 (var Data:Tuos_Data;var FFT:Tuos_FFT) : TDArFloat; {array of float}

var
   i, j, sn : integer;
   ps1, ps2: PDArshort;  {input is in Int16 format; pointer}
   buffer2 : TDArFloat; {array of cfloat, becomes the result}
   x1G, cp, sp, x1d, x2d, rr : double;
   s1, s2 : string;

begin
     setlength(Buffer2,Data.OutFrames); {set the length of the local buffer}
     i := 0;

{We assume int 16 format}
     ps1 := @Data.Buffer; {pointer to the input}
     ps2 := @Buffer2;  {becomes the output}

{One frame is one sample * 2 channels, so data.outframes = 8192}
     while (i < Data.OutFrames-1) do
     begin

.... // my stuff happens here
          ps2^[i] := <result of my stuff>;
          ps2^[i+1] := ps2^[i]; {duplicate into channel 2}
          i := i+2;
     end;
     Result := Buffer2; {This is the new array}
....  

end;


end.
Reply | Threaded
Open this post in threaded view
|

Re: Noise cancelling radio

fredvs
Administrator
Hello Peter.

Maybe, instead of using uos_AddIntoMemoryBuffer,  you may use your custom memory buffer and do what you want with it.

For this use function grab_data:

var
MyBuffer: TDArFloat;  

..

function grab_data;
..

   Result := Buffer2;
 
   MyBuffer := ... // Dos stuff to MyBuffer, like setlength, do MyBuffer + result, etc
end;

And to play MyBuffer use uos_AddFromMemoryBuffer.

Fre;D
Reply | Threaded
Open this post in threaded view
|

Re: Noise cancelling radio

Peter
OK Fred, thanks - I'll try something along those lines - best regards - Peter