3

This is a follow up on another question I already asked here How can I play a single tone or custom wave with Delphi?

To make a long story short, I used MMSystem's waveOutWrite() to create a Discrete Signal, but it seems it's either not working as I expected or I'm not getting it.

I wrote two Discrete Signals to the buffer with fixed spaces, like this Samples[i] := round(vol*sin(2*Pi*AFreq*t));

Where the volume is 1000 for a Signal whereas the "Spaces" is some 0 vol signals.

This is what it looks like while I expected something that would like like |...| enter image description here

Am I getting the Signal stuff all wrong or is it that I'm not using the WinAPI properly? I expected each fixed signal to look the same on the graph

P.S: I'm using Windows' Stereo Mix so there are no interferences P.P.S: Here's the code segment for converting Binary to Signal

function CreateBinaryTone(BinaryString: String): TWaveformSamples;
var
  I: Integer;
  omega,
  dt: double;
  vol: double;
begin
  omega := 2*Pi*AFreq;
  dt := 1/Format.nSamplesPerSec;

  SetLength(Samples, Length(BinaryString));

  for I := 1 to Length(BinaryString) do
  begin
    { Discrete Time }
    Vol := StrToInt(BinaryString[I]) * 1000;
    Samples[I] := vol * sin(omega * dt * I);
  end;

  Result := Samples;
end;
Community
  • 1
  • 1
MitziMeow
  • 635
  • 4
  • 13
  • 1
    Please show your code, the value inside the `sin()` expression should vary with `i` to get a pure frequency with the same amplitude. – LU RD Dec 13 '11 at 14:23
  • Added the code to the question, Thank you. – MitziMeow Dec 13 '11 at 14:37
  • I just tested [Andreas sample](http://stackoverflow.com/questions/7742377/how-can-i-generate-continuous-tones-of-varying-frequencies) which you referred to in your last question. It works fine, have you tested his code as well ? – LU RD Dec 13 '11 at 15:29
  • No I haven't. But I'm going for an Amplitude Modulation, not frequency. Also his is a Continuous Time I want Discrete Time, so it's not the way I need to do it. I might be a little off on the idea, can I have just 0 / any Amplitude? I'm guessing that through microphone's the amplitude would change, so I don't want to rely on some threshold and want it kept as simple as possible. Is this even an option? – MitziMeow Dec 13 '11 at 15:39
  • I'm not sure what you are looking for. You are adding two frequencies and varying the amplitude binary (either 0 or 1000). Have you checked that the Samples vector actually contains zero's where you want them ? – LU RD Dec 13 '11 at 16:03
  • Could it be that using volume of 0 is wrong? the entire sample is 0 when it is. I don't really get the Math part of the vol*sin(...)entirely. But yes I'm trying to use Binary where 0=0 and 1=1000. Also, yes, when vol is 0 and I multiply by it it's a 0. – MitziMeow Dec 13 '11 at 17:37
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/5825/discussion-between-mitzimeow-and-lu-rd) – MitziMeow Dec 13 '11 at 18:07

2 Answers2

2

Your bit time needs to be much longer than the period of your carrier (sine) frequency, and the sample rate also needs to be more than 2X the carrier frequency. So your sample generation loop may need to be much much longer.

hotpaw2
  • 70,107
  • 14
  • 90
  • 153
  • By the look of it, yes much longer. – LU RD Dec 13 '11 at 20:34
  • my sample rate is 2x but what do you mean by bit time? the time 1000 is transmitted? – MitziMeow Dec 14 '11 at 07:23
  • Also, I'm using just a 0/1000 amp wave, thus, I think, I don't have a carrier wave, or does that just mean that my "carrier wave" is always at 0? – MitziMeow Dec 14 '11 at 07:29
  • bit time - How many samples are you generating for each 1 or 0 in your bit vector? Also, the sample rate has to be greater than 2X your AFreq carrier frequency, not just 2X. – hotpaw2 Dec 14 '11 at 16:43
  • 1 sample is less than 1/2 the period of your sine wave. You may need multiple periods of your sine wave for each bit, depending on what you are doing. – hotpaw2 Dec 14 '11 at 19:20
  • I don't really understand... How is the sine wave's length calculated? How could you tell a sample is less than half of it? – MitziMeow Dec 15 '11 at 16:45
  • You can calculated it from the frequency and the samples per second. Then you will be able to tell. – hotpaw2 Dec 15 '11 at 16:56
  • Isn't it Frequency (Hz) = Samples per seconds? – MitziMeow Dec 15 '11 at 22:28
  • You have two different variables in your code, AFreq and nSampsPerSec. – hotpaw2 Dec 15 '11 at 23:12
  • Oh, you mean the Sample Rate right? So the sine wave size is per second? – MitziMeow Dec 16 '11 at 08:40
  • This is what it looks like when I'm using 4 samples for each bit... and each "sequence" of bits still do not look a like http://i41.tinypic.com/vn303n.png – MitziMeow Dec 18 '11 at 08:57
1

I may be late posting on this thread, but, if the point is creating a function that generates discrete, yet continuous sine/cosine signal WITHOUT taking time into consideration (ex. RealTime opperations without predictable time limits) there is an easy, yet effective way to do this!

First of all, maths:

Our friend, Euler has provide us a way to describe a Sine and a Cosine, using complex numbers.

enter image description here

By adding a time variable in this equation, we can make it so it describes a sine and a cosine wave

Euler - Sin/Cos Wave

In this ecuatoin, our cosine wave is represented by the Real part of this complex number and our Cosine wave by its Imaginary part:

Euler - Actual Sin/Cos Presentation

Now, every time we increase this time variable, it's just like we are increasing the rotation by a small angle (let's call it theta or θ). That means our next step, sould have an angle of our previous step PLUS this small θ angle

Keep in mind that we will account ωt as the angle of our last step and θ, as the angle that needs to be added to our last step's angle, in order to generate the new step.

Euler - Next Step (1/2)

If we develop our left side of this equation, we will see something intresting going on there:

Euler - Next Step (2/2)

As we know, if a complex number z is equal with another complex number w, that means that their real and imaginary parts are also equal.

Complex Numbers Equality

Having that in mind, we see a very interesting conclusion: If we have already calculated our last step's Sine and Cosine, we can easily calculate the next step by adding this θ angle, using the equations below for cosine and sine accordingly:

Euler - Next Step Calculation Equations

Finaly, Coding Section

Now, in order to create such function you will need some things:

  1. 2 global, Double variables, presenting last step's sin and cos
  2. An init function that sets the initial "step zero" and its initial phase
  3. A function that calculates and results the next step, when it's being called.

so 1st of all, declarations:

private
    [...]
    sine_last_cos: double;
    sine_last_sin: double;
    cosine_last_cos: double;
    cosine_last_sin: double;
    
    procedure SineInit(starting_phase: double);
    procedure CosineInit(starting_phase: double);
    function SineGen(amplitutde: double; sampling_rate: Integer; Freq: Double): double;
    function CosineGen(amplitutde: double; sampling_rate: Integer; Freq: Double): double;

Next goes implementations:

procedure Tmain.SineInit(starting_phase: double);
begin
    sine_last_sin := sin(starting_phase*pi/180);   //given phase is in deg, not rad
    sine_last_cos := cos(starting_phase*pi/180);
end;

procedure Tmain.CosineInit(starting_phase: double);
begin
    cosine_last_sin := sin(starting_phase*pi/180);
    cosine_last_cos := cos(starting_phase*pi/180);
end;

function Tmain.SineGen(amplitutde: double; sampling_rate: Integer; Freq: Double): double;
var sin_theta: double;
    cos_theta: double;
    new_sin_step, new_cos_step: double;
begin
    sin_theta :=  sin(2*pi*Freq/sampling_rate);    //theta depend on sampling freq
    cos_theta :=  cos(2*pi*Freq/sampling_rate);    //as well as desirable freq
    new_cos_step:= sine_last_cos*cos_theta - sine_last_sin*sin_theta;
    new_sin_step:= sine_last_cos*sin_theta + sine_last_sin*cos_theta;
    sine_last_sin:= new_cos_step;
    sine_last_cos:= new_sin_step;
    result := amplitutde *new_sin_step;
end;

function Tmain.CosineGen(amplitutde: double; sampling_rate: Integer; Freq: Double): double;
var sin_theta: double;
    cos_theta: double;
    new_sin_step, new_cos_step: double;
begin
    sin_theta :=  sin(2*pi*Freq/sampling_rate);    //theta depend on sampling freq
    cos_theta :=  cos(2*pi*Freq/sampling_rate);    //as well as desirable freq
    new_cos_step:= cosine_last_cos*cos_theta - cosine_last_sin*sin_theta;
    new_sin_step:= cosine_last_cos*sin_theta + cosine_last_sin*cos_theta;
    cosine_last_sin:= new_cos_step;
    cosine_last_cos:= new_sin_step;
    result := amplitutde *new_sin_step;
end;

In this example, Init procedures should be called every time the program launches (aka before the first SineGen/CosineGen call) and EVERY time you want to reset your wave.

EDIT: Corrected images + added the forgotten Init Procedures

Andreas M.
  • 182
  • 1
  • 10