tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

k-rate-oscillator-connections.html (21882B)


      1 <!doctype html>
      2 <html>
      3  <head>
      4    <title>
      5      k-rate AudioParams with inputs for OscillatorNode
      6    </title>
      7    <script src="/resources/testharness.js"></script>
      8    <script src="/resources/testharnessreport.js"></script>
      9    <script src="/webaudio/resources/audit.js"></script>
     10    <script src="/webaudio/resources/audit-util.js"></script>
     11  </head>
     12 
     13  <body>
     14    <script>
     15      let audit = Audit.createTaskRunner();
     16 
     17      // Sample rate must be a power of two to eliminate round-off when
     18      // computing time from frames and vice versa.  Using a non-power of two
     19      // will work, but the thresholds below will not be zero.  They're probably
     20      // closer to 1e-5 or so, but if everything is working correctly, the
     21      // outputs really should be exactly equal.
     22      const sampleRate = 8192;
     23 
     24      // Fairly arbitrary but short duration to limit runtime.
     25      const testFrames = 5 * RENDER_QUANTUM_FRAMES;
     26      const testDuration = testFrames / sampleRate;
     27 
     28      audit.define(
     29          {label: 'Test 1', description: 'k-rate frequency input'},
     30          async (task, should) => {
     31            // Test that an input to the frequency AudioParam set to k-rate
     32            // works.
     33 
     34            // Fairly arbitrary start and end frequencies for the automation.
     35            const freqStart = 100;
     36            const freqEnd = 2000;
     37 
     38            let refSetup = (context) => {
     39              let srcRef = new OscillatorNode(context, {frequency: 0});
     40 
     41              should(
     42                  () => srcRef.frequency.automationRate = 'k-rate',
     43                  `${task.label}: srcRef.frequency.automationRate = 'k-rate'`)
     44                  .notThrow();
     45              should(
     46                  () => srcRef.frequency.setValueAtTime(freqStart, 0),
     47                  `${task.label}: srcRef.frequency.setValueAtTime(${
     48                      freqStart}, 0)`)
     49                  .notThrow();
     50              should(
     51                  () => srcRef.frequency.linearRampToValueAtTime(
     52                      freqEnd, testDuration),
     53                  `${task.label}: srcRef.frequency.linearRampToValueAtTime(${
     54                      freqEnd}, ${testDuration})`)
     55                  .notThrow();
     56 
     57              return srcRef;
     58            };
     59 
     60            let testSetup = (context) => {
     61              let srcTest = new OscillatorNode(context, {frequency: 0});
     62              should(
     63                  () => srcTest.frequency.automationRate = 'k-rate',
     64                  `${task.label}: srcTest.frequency.automationRate = 'k-rate'`)
     65                  .notThrow();
     66 
     67              return srcTest;
     68            };
     69 
     70            let modSetup = (context) => {
     71              let mod = new ConstantSourceNode(context, {offset: 0});
     72 
     73              should(
     74                  () => mod.offset.setValueAtTime(freqStart, 0),
     75                  `${task.label}: modFreq.offset.setValueAtTime(${
     76                      freqStart}, 0)`)
     77                  .notThrow();
     78              should(
     79                  () =>
     80                      mod.offset.linearRampToValueAtTime(freqEnd, testDuration),
     81                  `${task.label}: modFreq.offset.linearRampToValueAtTime(${
     82                      freqEnd}, ${testDuration})`)
     83                  .notThrow();
     84 
     85              // This node is going to be connected to the frequency AudioParam.
     86              return {frequency: mod};
     87            };
     88 
     89            await testParams(should, {
     90              prefix: task.label,
     91              summary: 'k-rate frequency with input',
     92              setupRefOsc: refSetup,
     93              setupTestOsc: testSetup,
     94              setupMod: modSetup
     95            });
     96 
     97            task.done();
     98          });
     99 
    100      audit.define(
    101          {label: 'Test 2', description: 'k-rate detune input'},
    102          async (task, should) => {
    103            // Test that an input to the detune AudioParam set to k-rate works.
    104            // Threshold experimentally determined.  It should be probably not
    105            // be much larger than 5e-5. or something is not right.
    106 
    107            // Fairly arbitrary start and end detune values for automation.
    108            const detuneStart = 0;
    109            const detuneEnd = 2000;
    110 
    111            let refSetup = (context) => {
    112              let srcRef = new OscillatorNode(context, {detune: 0});
    113 
    114              should(
    115                  () => srcRef.detune.automationRate = 'k-rate',
    116                  `${task.label}: srcRef.detune.automationRate = 'k-rate'`)
    117                  .notThrow();
    118 
    119              should(
    120                  () => srcRef.detune.setValueAtTime(detuneStart, 0),
    121                  `${task.label}: srcRef.detune.setValueAtTime(${
    122                      detuneStart}, 0)`)
    123                  .notThrow();
    124              should(
    125                  () => srcRef.detune.linearRampToValueAtTime(
    126                      detuneEnd, testDuration),
    127                  `${task.label}: srcRef.detune.linearRampToValueAtTime(${
    128                      detuneEnd}, ${testDuration})`)
    129                  .notThrow();
    130 
    131              return srcRef;
    132            };
    133 
    134            let testSetup = (context) => {
    135              let srcTest = new OscillatorNode(context, {detune: 0});
    136 
    137              should(
    138                  () => srcTest.detune.automationRate = 'k-rate',
    139                  `${task.label}: srcTest.detune.automationRate = 'k-rate'`)
    140                  .notThrow();
    141 
    142              return srcTest;
    143            };
    144 
    145            let modSetup = (context) => {
    146              let mod = new ConstantSourceNode(context, {offset: 0});
    147 
    148              should(
    149                  () => mod.offset.setValueAtTime(detuneStart, 0),
    150                  `${task.label}: modDetune.offset.setValueAtTime(${
    151                      detuneStart}, 0)`)
    152                  .notThrow();
    153              should(
    154                  () => mod.offset.linearRampToValueAtTime(
    155                      detuneEnd, testDuration),
    156                  `${task.label}: modDetune.offset.linearRampToValueAtTime(${
    157                      detuneEnd}, ${testDuration})`)
    158                  .notThrow();
    159 
    160              return {detune: mod};
    161            };
    162 
    163            await testParams(should, {
    164              prefix: task.label,
    165              summary: 'k-rate detune with input',
    166              setupRefOsc: refSetup,
    167              setupTestOsc: testSetup,
    168              setupMod: modSetup
    169            });
    170 
    171            task.done();
    172          });
    173 
    174      audit.define(
    175          {
    176            label: 'Test 3',
    177            description: 'k-rate frequency input with a-rate detune'
    178          },
    179          async (task, should) => {
    180            // Test OscillatorNode with a k-rate frequency with input and an
    181            // a-rate detune iwth automations.
    182 
    183            // Fairly arbitrary start and end values for the frequency and
    184            // detune automations.
    185            const freqStart = 100;
    186            const freqEnd = 2000;
    187            const detuneStart = 0;
    188            const detuneEnd = -2000;
    189 
    190            let refSetup = (context) => {
    191              let node = new OscillatorNode(context, {frequency: 0});
    192 
    193              // Set up k-rate frequency and a-rate detune
    194              should(
    195                  () => node.frequency.automationRate = 'k-rate',
    196                  `${task.label}: srcRef.frequency.automationRate = 'k-rate'`)
    197                  .notThrow();
    198              should(
    199                  () => node.frequency.setValueAtTime(freqStart, 0),
    200                  `${task.label}: srcRef.frequency.setValueAtTime(${
    201                      freqStart}, 0)`)
    202                  .notThrow();
    203              should(
    204                  () => node.frequency.linearRampToValueAtTime(
    205                      2000, testDuration),
    206                  `${task.label}: srcRef.frequency.linearRampToValueAtTime(${
    207                      freqEnd}, ${testDuration})`)
    208                  .notThrow();
    209              should(
    210                  () => node.detune.setValueAtTime(detuneStart, 0),
    211                  `${task.label}: srcRef.detune.setValueAtTime(${
    212                      detuneStart}, 0)`)
    213                  .notThrow();
    214              should(
    215                  () => node.detune.linearRampToValueAtTime(
    216                      detuneEnd, testDuration),
    217                  `${task.label}: srcRef.detune.linearRampToValueAtTime(${
    218                      detuneEnd}, ${testDuration})`)
    219                  .notThrow();
    220 
    221              return node;
    222            };
    223 
    224            let testSetup = (context) => {
    225              let node = new OscillatorNode(context, {frequency: 0});
    226 
    227              should(
    228                  () => node.frequency.automationRate = 'k-rate',
    229                  `${task.label}: srcTest.frequency.automationRate = 'k-rate'`)
    230                  .notThrow();
    231              should(
    232                  () => node.detune.setValueAtTime(detuneStart, 0),
    233                  `${task.label}: srcTest.detune.setValueAtTime(${
    234                      detuneStart}, 0)`)
    235                  .notThrow();
    236              should(
    237                  () => node.detune.linearRampToValueAtTime(
    238                      detuneEnd, testDuration),
    239                  `${task.label}: srcTest.detune.linearRampToValueAtTime(${
    240                      detuneEnd}, ${testDuration})`)
    241                  .notThrow();
    242 
    243              return node;
    244            };
    245 
    246            let modSetup = (context) => {
    247              let mod = {};
    248              mod['frequency'] = new ConstantSourceNode(context, {offset: 0});
    249 
    250              should(
    251                  () => mod['frequency'].offset.setValueAtTime(freqStart, 0),
    252                  `${task.label}: modFreq.offset.setValueAtTime(${
    253                      freqStart}, 0)`)
    254                  .notThrow();
    255 
    256              should(
    257                  () => mod['frequency'].offset.linearRampToValueAtTime(
    258                      2000, testDuration),
    259                  `${task.label}: modFreq.offset.linearRampToValueAtTime(${
    260                      freqEnd}, ${testDuration})`)
    261                  .notThrow();
    262 
    263              return mod;
    264            };
    265 
    266            await testParams(should, {
    267              prefix: task.label,
    268              summary: 'k-rate frequency input with a-rate detune',
    269              setupRefOsc: refSetup,
    270              setupTestOsc: testSetup,
    271              setupMod: modSetup
    272            });
    273 
    274            task.done();
    275          });
    276 
    277      audit.define(
    278          {
    279            label: 'Test 4',
    280            description: 'a-rate frequency with k-rate detune input'
    281          },
    282          async (task, should) => {
    283            // Test OscillatorNode with an a-rate frequency with automations and
    284            // a k-rate detune with input.
    285 
    286            // Fairly arbitrary start and end values for the frequency and
    287            // detune automations.
    288            const freqStart = 100;
    289            const freqEnd = 2000;
    290            const detuneStart = 0;
    291            const detuneEnd = -2000;
    292 
    293            let refSetup = (context) => {
    294              let node = new OscillatorNode(context, {detune: 0});
    295 
    296              // Set up a-rate frequency and k-rate detune
    297              should(
    298                  () => node.frequency.setValueAtTime(freqStart, 0),
    299                  `${task.label}: srcRef.frequency.setValueAtTime(${
    300                      freqStart}, 0)`)
    301                  .notThrow();
    302              should(
    303                  () => node.frequency.linearRampToValueAtTime(
    304                      2000, testDuration),
    305                  `${task.label}: srcRef.frequency.linearRampToValueAtTime(${
    306                      freqEnd}, ${testDuration})`)
    307                  .notThrow();
    308              should(
    309                  () => node.detune.automationRate = 'k-rate',
    310                  `${task.label}: srcRef.detune.automationRate = 'k-rate'`)
    311                  .notThrow();
    312              should(
    313                  () => node.detune.setValueAtTime(detuneStart, 0),
    314                  `${task.label}: srcRef.detune.setValueAtTime(${
    315                      detuneStart}, 0)`)
    316                  .notThrow();
    317              should(
    318                  () => node.detune.linearRampToValueAtTime(
    319                      detuneEnd, testDuration),
    320                  `${task.label}: srcRef.detune.linearRampToValueAtTime(${
    321                      detuneEnd}, ${testDuration})`)
    322                  .notThrow();
    323 
    324              return node;
    325            };
    326 
    327            let testSetup = (context) => {
    328              let node = new OscillatorNode(context, {detune: 0});
    329 
    330              should(
    331                  () => node.detune.automationRate = 'k-rate',
    332                  `${task.label}: srcTest.detune.automationRate = 'k-rate'`)
    333                  .notThrow();
    334              should(
    335                  () => node.frequency.setValueAtTime(freqStart, 0),
    336                  `${task.label}: srcTest.frequency.setValueAtTime(${
    337                      freqStart}, 0)`)
    338                  .notThrow();
    339              should(
    340                  () => node.frequency.linearRampToValueAtTime(
    341                      freqEnd, testDuration),
    342                  `${task.label}: srcTest.frequency.linearRampToValueAtTime(${
    343                      freqEnd}, ${testDuration})`)
    344                  .notThrow();
    345 
    346              return node;
    347            };
    348 
    349            let modSetup = (context) => {
    350              let mod = {};
    351              const name = 'detune';
    352 
    353              mod['detune'] = new ConstantSourceNode(context, {offset: 0});
    354              should(
    355                  () => mod[name].offset.setValueAtTime(detuneStart, 0),
    356                  `${task.label}: modDetune.offset.setValueAtTime(${
    357                      detuneStart}, 0)`)
    358                  .notThrow();
    359 
    360              should(
    361                  () => mod[name].offset.linearRampToValueAtTime(
    362                      detuneEnd, testDuration),
    363                  `${task.label}: modDetune.offset.linearRampToValueAtTime(${
    364                      detuneEnd}, ${testDuration})`)
    365                  .notThrow();
    366 
    367              return mod;
    368            };
    369 
    370            await testParams(should, {
    371              prefix: task.label,
    372              summary: 'k-rate detune input with a-rate frequency',
    373              setupRefOsc: refSetup,
    374              setupTestOsc: testSetup,
    375              setupMod: modSetup
    376            });
    377 
    378            task.done();
    379          });
    380 
    381      audit.define(
    382          {
    383            label: 'Test 5',
    384            description: 'k-rate inputs for frequency and detune'
    385          },
    386          async (task, should) => {
    387            // Test OscillatorNode with k-rate frequency and detune with inputs
    388            // on both.
    389 
    390            // Fairly arbitrary start and end values for the frequency and
    391            // detune automations.
    392            const freqStart = 100;
    393            const freqEnd = 2000;
    394            const detuneStart = 0;
    395            const detuneEnd = -2000;
    396 
    397            let refSetup = (context) => {
    398              let node = new OscillatorNode(context, {frequency: 0, detune: 0});
    399 
    400              should(
    401                  () => node.frequency.automationRate = 'k-rate',
    402                  `${task.label}: srcRef.frequency.automationRate = 'k-rate'`)
    403                  .notThrow();
    404              should(
    405                  () => node.frequency.setValueAtTime(freqStart, 0),
    406                  `${task.label}: srcRef.setValueAtTime(${freqStart}, 0)`)
    407                  .notThrow();
    408              should(
    409                  () => node.frequency.linearRampToValueAtTime(
    410                      freqEnd, testDuration),
    411                  `${task.label}: srcRef;.frequency.linearRampToValueAtTime(${
    412                      freqEnd}, ${testDuration})`)
    413                  .notThrow();
    414              should(
    415                  () => node.detune.automationRate = 'k-rate',
    416                  `${task.label}: srcRef.detune.automationRate = 'k-rate'`)
    417                  .notThrow();
    418              should(
    419                  () => node.detune.setValueAtTime(detuneStart, 0),
    420                  `${task.label}: srcRef.detune.setValueAtTime(${
    421                      detuneStart}, 0)`)
    422                  .notThrow();
    423              should(
    424                  () => node.detune.linearRampToValueAtTime(
    425                      detuneEnd, testDuration),
    426                  `${task.label}: srcRef.detune.linearRampToValueAtTime(${
    427                      detuneEnd}, ${testDuration})`)
    428                  .notThrow();
    429 
    430              return node;
    431            };
    432 
    433            let testSetup = (context) => {
    434              let node = new OscillatorNode(context, {frequency: 0, detune: 0});
    435 
    436              should(
    437                  () => node.frequency.automationRate = 'k-rate',
    438                  `${task.label}: srcTest.frequency.automationRate = 'k-rate'`)
    439                  .notThrow();
    440              should(
    441                  () => node.detune.automationRate = 'k-rate',
    442                  `${task.label}: srcTest.detune.automationRate = 'k-rate'`)
    443                  .notThrow();
    444 
    445              return node;
    446            };
    447 
    448            let modSetup = (context) => {
    449              let modF = new ConstantSourceNode(context, {offset: 0});
    450 
    451              should(
    452                  () => modF.offset.setValueAtTime(freqStart, 0),
    453                  `${task.label}: modFreq.offset.setValueAtTime(${
    454                      freqStart}, 0)`)
    455                  .notThrow();
    456              should(
    457                  () => modF.offset.linearRampToValueAtTime(
    458                      freqEnd, testDuration),
    459                  `${task.label}: modFreq.offset.linearRampToValueAtTime(${
    460                      freqEnd}, ${testDuration})`)
    461                  .notThrow();
    462 
    463              let modD = new ConstantSourceNode(context, {offset: 0});
    464 
    465              should(
    466                  () => modD.offset.setValueAtTime(detuneStart, 0),
    467                  `${task.label}: modDetune.offset.setValueAtTime(${
    468                      detuneStart}, 0)`)
    469                  .notThrow();
    470              should(
    471                  () => modD.offset.linearRampToValueAtTime(
    472                      detuneEnd, testDuration),
    473                  `${task.label}: modDetune.offset.linearRampToValueAtTime(${
    474                      detuneEnd}, ${testDuration})`)
    475                  .notThrow();
    476 
    477              return {frequency: modF, detune: modD};
    478            };
    479 
    480            await testParams(should, {
    481              prefix: task.label,
    482              summary: 'k-rate inputs for both frequency and detune',
    483              setupRefOsc: refSetup,
    484              setupTestOsc: testSetup,
    485              setupMod: modSetup
    486            });
    487 
    488            task.done();
    489          });
    490 
    491      audit.run();
    492 
    493      async function testParams(should, options) {
    494        // Test a-rate and k-rate AudioParams of an OscillatorNode.
    495        //
    496        // |options| should be a dictionary with these members:
    497        //   prefix       - prefix to use for messages
    498        //   summary      - message to be printed with the final results
    499        //   setupRefOsc  - function returning the reference oscillator
    500        //   setupTestOsc - function returning the test oscillator
    501        //   setupMod     - function returning nodes to be connected to the
    502        //   AudioParams.
    503        //
    504        // |setupRefOsc| and |setupTestOsc| are given the context and each
    505        // method is expected to create an OscillatorNode with the appropriate
    506        // automations for testing.  The constructed OscillatorNode is returned.
    507        //
    508        // The reference oscillator
    509        // should automate the desired AudioParams at the appropriate automation
    510        // rate, and the output is the expected result.
    511        //
    512        // The test oscillator should set up the AudioParams but expect the
    513        // AudioParam(s) have an input that matches the automation for the
    514        // reference oscillator.
    515        //
    516        // |setupMod| must create one or two ConstantSourceNodes with exactly
    517        // the same automations as used for the reference oscillator.  This node
    518        // is used as the input to an AudioParam of the test oscillator.  This
    519        // function returns a dictionary whose members are named 'frequency' and
    520        // 'detune'.  The name indicates which AudioParam the constant source
    521        // node should be connected to.
    522 
    523        // Two channels: 0 = reference signal, 1 = test signal
    524        let context = new OfflineAudioContext({
    525          numberOfChannels: 2,
    526          sampleRate: sampleRate,
    527          length: testDuration * sampleRate
    528        });
    529 
    530        let merger = new ChannelMergerNode(
    531            context, {numberOfInputs: context.destination.channelCount});
    532        merger.connect(context.destination);
    533 
    534        // The reference oscillator.
    535        let srcRef = options.setupRefOsc(context);
    536 
    537        // The test oscillator.
    538        let srcTest = options.setupTestOsc(context);
    539 
    540        // Inputs to AudioParam.
    541        let mod = options.setupMod(context);
    542 
    543        if (mod['frequency']) {
    544          should(
    545              () => mod['frequency'].connect(srcTest.frequency),
    546              `${options.prefix}: modFreq.connect(srcTest.frequency)`)
    547              .notThrow();
    548          mod['frequency'].start()
    549        }
    550 
    551        if (mod['detune']) {
    552          should(
    553              () => mod['detune'].connect(srcTest.detune),
    554              `${options.prefix}: modDetune.connect(srcTest.detune)`)
    555              .notThrow();
    556          mod['detune'].start()
    557        }
    558 
    559        srcRef.connect(merger, 0, 0);
    560        srcTest.connect(merger, 0, 1);
    561 
    562        srcRef.start();
    563        srcTest.start();
    564 
    565        let buffer = await context.startRendering();
    566        let expected = buffer.getChannelData(0);
    567        let actual = buffer.getChannelData(1);
    568 
    569        // The output of the reference and test oscillator should be
    570        // exactly equal because the AudioParam values should be exactly
    571        // equal.
    572        should(actual, options.summary).beCloseToArray(expected, {
    573          absoluteThreshold: 0
    574        });
    575      }
    576    </script>
    577  </body>
    578 </html>