1
votes

I have tried to generate a sine sweep in javascript but I am having some problems. The resulting audio wave sounds wrong (breaking up, clicks etc.).

For debugging I plotted out the data and I found a place where the data changes in (it seems) an abnormal way.

I have very little experience with audio programming so I have difficulty debugging and finding the problem.

I try to generate a sine where the frequency sweep from 100 Hz to 200 Hz and back to 100 Hz with 55 cycles per minute.

This is the code:

function SweepFreq(cyclesPerMinuteElement, lowFreqElement, highFreqElement) {
    this.cyclesPerMinute = 55;
    this.cycle_length = 60.0/this.cyclesPerMinute;
    this.lowFreq = 100; 
    this.highFreq = 200;
    this.time = 0.0;
    this.buffer = null;

    this.initSweep = function (element) {
        var sampleRate = 44100.0
        var seconds = 12;
        var length = sampleRate*seconds;
        this.buffer = new Float32Array(length);
        this.generateSineWave(this.buffer, sampleRate, seconds)
    }

    this.generateSineWave = function(buffer, sampleRate, seconds) {
        var deltaTime = 1.0/(sampleRate);

        var old_v = 0;
        var old_frequency = 0;
        var old_waveLength = 0;
        var old_timePos = 0;
        var old_pos = 0;
        var old_val = 0;

        for (var i = 0; i < sampleRate*seconds; i++) {
            var v = this.getProgress(deltaTime);
            var frequency = ((this.highFreq-this.lowFreq)*v)+this.lowFreq;
            var waveLength = 1.0 / frequency;
            var timePos = i / sampleRate;
            var pos = timePos / waveLength;
            var val = Math.sin(pos * 2.0 * Math.PI);
            this.buffer[i] = val;

            // debug stuff
            var diff_v = old_v-v;
            var diff_frequency = old_frequency-frequency;
            var diff_waveLength = old_waveLength - waveLength;
            var diff_timePos = old_timePos - timePos;
            var diff_pos = old_pos - pos;
            var diff_val = old_val - val;
            old_v = v;
            old_frequency = frequency;
            old_waveLength = waveLength;
            old_timePos = timePos;
            old_pos = pos;
            old_val = val;
            if (i > (5*4096+3570) && i < (5*4096+3590)) {
//              console.log(v, frequency, waveLength*100000000.0, diff_v, diff_frequency, diff_waveLength*100000000.0);
                console.log(timePos, pos, val, diff_timePos, diff_pos, diff_val);
            }
            // end of debug stuff
        }
    }

    this.getProgress = function(deltaTime) {
        this.time += deltaTime;
        if (this.time > this.cycle_length)
            this.time -= this.cycle_length;
        var progress = this.time/this.cycle_length;
        if (progress < 0.5) {
            return progress*2.0;
        }
        else {
            return 1.0-((progress-0.5)*2.0)
        }
    }
}

Here is an image of the relevant area of the plot: http://www.shareimage.ro/images/pxf8xb8r18je56drwmy6.png

Here is some output of the values around the area: (Please notice the difference in timePos deltas)

v, frequency, waveLength*100000000.0, diff_v, diff_frequency, diff_waveLength*100000000.0
0.9998941798945973 199.98941798945972 500026.4564261616 -0.0000415721844293504 -0.004157218442941257 10.394362057533634
0.9999357520790266 199.99357520790267 500016.0624962343 -0.0000415721844293504 -0.004157218442941257 10.39392992724722
0.999977324263456 199.99773242634558 500005.6689984104 -0.0000415721844293504 -0.004157218442912836 10.393497823935755
0.9999811035521147 199.99811035521145 500004.7241566063 -0.0000037792886586895946 -0.00037792886587340035 0.9448418041013706
0.9999395313676853 199.99395313676854 500015.1176151494 0.0000415721844293504 0.004157218442912836 -10.393458543117573
0.999897959183256 199.9897959183256 500025.5115057935 0.0000415721844293504 0.004157218442941257 -10.393890644087161
0.9998563869988266 199.98563869988266 500035.90582856524 0.0000415721844293504 0.004157218442941257 -10.394322771771492
0.9998148148143973 199.98148148143974 500046.3005834917 0.0000415721844293504 0.004157218442912836 -10.394754926430771
0.9997732426299679 199.97732426299677 500056.6957706 0.0000415721844293504 0.004157218442969679 -10.39518710832521
0.9997316704455386 199.97316704455386 500067.09138991666 0.0000415721844293504 0.004157218442912836 -10.395619316674182
0.9996900982611092 199.96900982611092 500077.4874414691 0.0000415721844293504 0.004157218442941257 -10.396051552431784
0.9996485260766799 199.96485260766798 500087.883925284 0.0000415721844293504 0.004157218442941257 -10.396483814904128
0.9996069538922505 199.96069538922507 500098.28084138845 0.0000415721844293504 0.004157218442912836 -10.396916104438159
0.9995653817078212 199.9565381707821 500108.67818980943 0.0000415721844293504 0.004157218442969679 -10.39734842094714
0.9995238095233918 199.95238095233918 500119.07597057364 0.0000415721844293504 0.004157218442912836 -10.397780764257597
0.9994822373389625 199.94822373389624 500129.4741837084 0.0000415721844293504 0.004157218442941257 -10.398213134716478
0.9994406651545331 199.9440665154533 500139.87282924046 0.0000415721844293504 0.004157218442941257 -10.398645532063572
0.9993990929701038 199.9399092970104 500150.27190719673 0.0000415721844293504 0.004157218442912836 -10.39907795629888
0.9993575207856744 199.93575207856745 500160.67141760443 0.0000415721844293504 0.004157218442941257 -10.39951040768261 

timePos, pos, val, diff_timePos, diff_pos, diff_val
0.545374149659864 109.06905877697271 0.4204208397546066 -0.000022675736961463855 -0.006802052642697731 -0.03915004637084152
0.5453968253968254 109.07586101815137 0.4588041214830869 -0.000022675736961463855 -0.006802241178661461 -0.03838328172848032
0.5454195011337869 109.08266344786601 0.49635046831417823 -0.000022675736961463855 -0.006802429714639402 -0.037546346831091315
0.5454421768707483 109.08740468218267 0.5219878252653231 -0.000022675736961352833 -0.004741234316654186 -0.02563735695114483
0.5454648526077097 109.08967217018066 0.5340864997440766 -0.000022675736961463855 -0.002267487997997364 -0.012098674478753568
0.5454875283446712 109.09193946964263 0.5460757756696786 -0.000022675736961463855 -0.0022672994619625797 -0.01198927592560195
0.5455102040816326 109.09420658056864 0.5579532472663103 -0.000022675736961463855 -0.0022671109260130606 -0.011877471596631772
0.5455328798185941 109.09647350295867 0.5697165324497411 -0.000022675736961463855 -0.00226692239003512 -0.011763285183430727
0.5455555555555556 109.09874023681267 0.5813632732981516 -0.000022675736961463855 -0.0022667338540003357 -0.011646740848410553
0.545578231292517 109.10100678213077 0.5928911365171865 -0.000022675736961463855 -0.002266545318093449 -0.011527863219034873
0.5456009070294785 109.10327313891281 0.6042978138973617 -0.000022675736961463855 -0.002266356782044454 -0.0114066773801752
0.54562358276644 109.1055393071589 0.615581022770094 -0.000022675736961463855 -0.002266168246094935 -0.011283208872732264
0.5456462585034013 109.10780528686898 0.6267385064542539 -0.000022675736961352833 -0.0022659797100743617 -0.011157483684159919
0.5456689342403628 109.11007107804308 0.637768034700286 -0.000022675736961463855 -0.002265791174096421 -0.011029528246032094
0.5456916099773242 109.11233668068122 0.6486674041261999 -0.000022675736961463855 -0.002265602638146902 -0.010899369425913963
0.5457142857142857 109.11460209478336 0.659434438648618 -0.000022675736961463855 -0.0022654141021405394 -0.010767034522418006
0.5457369614512472 109.11686732034953 0.670066989909156 -0.000022675736961463855 -0.0022652255661625986 -0.010632551260538081
0.5457596371882086 109.11913235737973 0.6805629376928694 -0.000022675736961463855 -0.0022650370301988687 -0.010495947783713322
0.5457823129251701 109.12139720587393 0.6909201903420606 -0.000022675736961463855 -0.002264848494206717 -0.010357252649191295 

The problem as far as I can is is that the delta on timePos suddenly changes from stepping at ~0.006802052642697731 to ~0.0022671109260130606

This happens when progress reaches 0.5 (frequency reaches 200 Hz) and frequency starts to move towards 100 Hz again.

I have been staring at this code for hours and hours and simply cannot see the problem.

This is the code I use to generate waveform with constant frequency:

for (var i=0; i<sampleRate*seconds; i++) {
    var p = (i/sampleRate);
    var v = Math.sin((2*Math.PI) * p * frequency);
    buffer[i] = v;
}
1
Maybe you could put the image on another site (e.g. photobucket) and post a link.Нет войне
I'm finding the code a bit hard to read quickly. what are 'v' and 'val'? What is 'progress'? why do you only have one variable for frequency and wavelength, even though you have two cyclic wave forms going (the audible wave, and its modulator)? could you maybe post your equivalent code for just the unmodulated sine wave (presumably you've got that working?)Нет войне
Here's a link to the image: shareimage.ro/images/pxf8xb8r18je56drwmy6.png Sorry the code is a bit hard to read. v : a value from 0.0-1.0 indicating the "progress" in the current cycle. (55 cycles per minute in this example, each cycle is 1.09 second long). val is the calculated sine value that goes into the audio buffer (PCM).user2624732
@topomorto To generate a waveform with a constant frequency I use this code: for (var i=0; i<sampleRate*seconds; i++) { var p = (i/sampleRate); var v = Math.sin((2*Math.PI) * p * frequency); buffer[i] = v; }user2624732

1 Answers

1
votes

I think the problem is that although you are sweeping the frequency, you are not adding cumulatively to the position of the 'oscillator' based on that frequency - instead, it looks like you are calculating what the position of the oscillator would have been at that moment in time had the calculated frequency always been the frequency.

I tried this algorithm (I've changed the values slightly, to make the output more comfortable to look at in excel!)

function SweepFreq(cyc, lo, hi) {
this.cyclesPerMinute = cyc;
this.cycle_length = 60.0/this.cyclesPerMinute;
this.lowFreq = lo;
this.highFreq = hi;
this.time = 0.0;
this.buffer = null;

window.initSweep = function (element) {
    var sampleRate = 10000.0
    var seconds = 1;
    var length = sampleRate*seconds;
    this.buffer = new Float32Array(length);
    this.generateSineWave(this.buffer, sampleRate, seconds)
}

this.generateSineWave = function(buffer, sampleRate, seconds) {
    var deltaTime = 1.0/(sampleRate);

    var oldCyclePosition=0;

    for (var i = 0; i < sampleRate*seconds; i++) {
        var frequencyFactor = this.getFrequencyFactor(deltaTime);
        var frequency = ((this.highFreq-this.lowFreq)*frequencyFactor)+this.lowFreq;

        var distanceMovedInThisSample  = frequency / sampleRate;
        var currentCyclePosition =  distanceMovedInThisSample + oldCyclePosition;

        var val = Math.sin(currentCyclePosition * 2.0 * Math.PI);
        this.buffer[i] = val;
        oldCyclePosition = currentCyclePosition;
        console.log(val);
    }
}

this.getFrequencyFactor = function(deltaTime) {
    this.time += deltaTime;
    if (this.time > this.cycle_length)
        this.time -= this.cycle_length;
    var progress = this.time/this.cycle_length;
    if (progress < 0.5) {
        return progress*2.0;
    }
    else {
        return 1.0-((progress-0.5)*2.0)
    }
}}

Seemed to give a smooth - looking sweep, anyway!

One more thing is that I have found that waveforms that go to 100% amplitude can sometimes cause clicks and pops through certain drivers and hardware so try normalising the wave to, say 50%.