Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  test_mediaDecoding.html   Sprache: HTML

 
 products/sources/formale Sprachen/C/Firefox/dom/media/webaudio/test/test_mediaDecoding.html


<!DOCTYPE html>
<html>
  <head>
    <title>Test the decodeAudioData API and Resampling</title>
    <script src="/tests/SimpleTest/SimpleTest.js"></script>
    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
  </head>

  <body>
    <pre id="test">
<script src="webaudio.js" type="text/javascript"></script>
<script type="text/javascript">

  // These routines have been copied verbatim from WebKit, and are used in order
  // to convert a memory buffer into a wave buffer.
  function writeString(s, a, offset) {
    for (var i = 0; i < s.length; ++i) {
      a[offset + i] = s.charCodeAt(i);
    }
  }

  function writeInt16(n, a, offset) {
    n = Math.floor(n);

    var b1 = n & 255;
    var b2 = (n >> 8) & 255;

    a[offset + 0] = b1;
    a[offset + 1] = b2;
  }

  function writeInt32(n, a, offset) {
    n = Math.floor(n);
    var b1 = n & 255;
    var b2 = (n >> 8) & 255;
    var b3 = (n >> 16) & 255;
    var b4 = (n >> 24) & 255;

    a[offset + 0] = b1;
    a[offset + 1] = b2;
    a[offset + 2] = b3;
    a[offset + 3] = b4;
  }

  function writeAudioBuffer(audioBuffer, a, offset) {
    var n = audioBuffer.length;
    var channels = audioBuffer.numberOfChannels;

    for (var i = 0; i < n; ++i) {
      for (var k = 0; k < channels; ++k) {
        var buffer = audioBuffer.getChannelData(k);
        var sample = buffer[i] * 32768.0;

        // Clip samples to the limitations of 16-bit.
        // If we don't do this then we'll get nasty wrap-around distortion.
        if (sample < -32768)
          sample = -32768;
        if (sample > 32767)
          sample = 32767;

        writeInt16(sample, a, offset);
        offset += 2;
      }
    }
  }

  function createWaveFileData(audioBuffer) {
    var frameLength = audioBuffer.length;
    var numberOfChannels = audioBuffer.numberOfChannels;
    var sampleRate = audioBuffer.sampleRate;
    var bitsPerSample = 16;
    var byteRate = sampleRate * numberOfChannels * bitsPerSample / 8;
    var blockAlign = numberOfChannels * bitsPerSample / 8;
    var wavDataByteLength = frameLength * numberOfChannels * 2; // 16-bit audio
    var headerByteLength = 44;
    var totalLength = headerByteLength + wavDataByteLength;

    var waveFileData = new Uint8Array(totalLength);

    var subChunk1Size = 16; // for linear PCM
    var subChunk2Size = wavDataByteLength;
    var chunkSize = 4 + (8 + subChunk1Size) + (8 + subChunk2Size);

    writeString("RIFF", waveFileData, 0);
    writeInt32(chunkSize, waveFileData, 4);
    writeString("WAVE", waveFileData, 8);
    writeString("fmt ", waveFileData, 12);

    writeInt32(subChunk1Size, waveFileData, 16);      // SubChunk1Size (4)
    writeInt16(1, waveFileData, 20);                  // AudioFormat (2)
    writeInt16(numberOfChannels, waveFileData, 22);   // NumChannels (2)
    writeInt32(sampleRate, waveFileData, 24);         // SampleRate (4)
    writeInt32(byteRate, waveFileData, 28);           // ByteRate (4)
    writeInt16(blockAlign, waveFileData, 32);         // BlockAlign (2)
    writeInt32(bitsPerSample, waveFileData, 34);      // BitsPerSample (4)

    writeString("data", waveFileData, 36);
    writeInt32(subChunk2Size, waveFileData, 40);      // SubChunk2Size (4)

    // Write actual audio data starting at offset 44.
    writeAudioBuffer(audioBuffer, waveFileData, 44);

    return waveFileData;
  }

</script>
<script class="testbody" type="text/javascript">

  SimpleTest.waitForExplicitFinish();

  // fuzzTolerance and fuzzToleranceMobile are used to determine fuzziness
  // thresholds.  They're needed to make sure that we can deal with neglibible
  // differences in the binary buffer caused as a result of resampling the
  // audio.  fuzzToleranceMobile is typically larger on mobile platforms since
  // we do fixed-point resampling as opposed to floating-point resampling on
  // those platforms.
  // If fuzzMagnitude, is present, is the maximum magnitude difference, per
  // sample, to consider two samples are identical. It is multiplied by the
  // maximum value a sample, in our case INT16_MAX. This allows checking files
  // that should be identical except one has e.g. a higher quantization noise.
  var files = [
    // An ogg file, 44.1khz, mono
    {
      url: "ting-44.1k-1ch.ogg",
      valid: true,
      expectedUrl: "ting-44.1k-1ch.wav",
      numberOfChannels: 1,
      frames: 30592,
      sampleRate: 44100,
      duration: 0.693,
      fuzzTolerance: 5,
      fuzzToleranceMobile: 1284
    },
    // An ogg file, 44.1khz, stereo
    {
      url: "ting-44.1k-2ch.ogg",
      valid: true,
      expectedUrl: "ting-44.1k-2ch.wav",
      numberOfChannels: 2,
      frames: 30592,
      sampleRate: 44100,
      duration: 0.693,
      fuzzTolerance: 6,
      fuzzToleranceMobile: 2544
    },
    // An ogg file, 48khz, mono
    {
      url: "ting-48k-1ch.ogg",
      valid: true,
      expectedUrl: "ting-48k-1ch.wav",
      numberOfChannels: 1,
      frames: 33297,
      sampleRate: 48000,
      duration: 0.693,
      fuzzTolerance: 5,
      fuzzToleranceMobile: 1388
    },
    // An ogg file, 48khz, stereo
    {
      url: "ting-48k-2ch.ogg",
      valid: true,
      expectedUrl: "ting-48k-2ch.wav",
      numberOfChannels: 2,
      frames: 33297,
      sampleRate: 48000,
      duration: 0.693,
      fuzzTolerance: 14,
      fuzzToleranceMobile: 2752
    },
    // Make sure decoding a wave file results in the same buffer (for both the
    // resampling and non-resampling cases)
    {
      url: "ting-44.1k-1ch.wav",
      valid: true,
      expectedUrl: "ting-44.1k-1ch.wav",
      numberOfChannels: 1,
      frames: 30592,
      sampleRate: 44100,
      duration: 0.693,
      fuzzTolerance: 0,
      fuzzToleranceMobile: 0
    },
    {
      url: "ting-48k-1ch.wav",
      valid: true,
      expectedUrl: "ting-48k-1ch.wav",
      numberOfChannels: 1,
      frames: 33297,
      sampleRate: 48000,
      duration: 0.693,
      fuzzTolerance: 0,
      fuzzToleranceMobile: 0
    },
    //  // A wave file
    //  //{ url: "24bit-44khz.wav", valid: true, expectedUrl: "24bit-44khz-expected.wav" },
    // A non-audio file
    { url: "invalid.txt", valid: false, sampleRate: 44100 },
    // A webm file with no audio
    { url: "noaudio.webm", valid: false, sampleRate: 48000 },
    {
      url: "nil-packet.ogg",
      expectedUrl: null,
      valid: true,
      numberOfChannels: 2,
      sampleRate: 48000,
      frames: 18600,
      duration: 0.3874,
    },
    {
      url: "half-a-second-1ch-44100-mulaw.wav",
      // It is expected that mulaw and linear are similar enough at 16-bits
      expectedUrl: "half-a-second-1ch-44100.wav",
      valid: true,
      numberOfChannels: 1,
      sampleRate: 44100,
      frames: 22050,
      duration: 0.5,
      fuzzMagnitude: 0.04,
    },
    {
      url: "half-a-second-1ch-44100-alaw.wav",
      // It is expected that alaw and linear are similar enough at 16-bits
      expectedUrl: "half-a-second-1ch-44100.wav",
      valid: true,
      numberOfChannels: 1,
      sampleRate: 44100,
      frames: 22050,
      duration: 0.5,
      fuzzMagnitude: 0.04,
    },
    {
      url: "waveformatextensible.wav",
      valid: true,
      numberOfChannels: 1,
      sampleRate: 44100,
      frames: 472,
      duration: 0.01
    },
    {
      // A wav file that has 8 channel, but has a channel mask that doesn't
      // match the channel count.
      url: "waveformatextensiblebadmask.wav",
      valid: true,
      numberOfChannels: 8,
      sampleRate: 8000,
      frames: 80,
      duration: 0.01
    },
    {
      // A wav file that has 8 channels, in f32le
      url: "8ch-f32le.wav",
      expectedUrl: "8ch-s16.wav", // similar enough
      valid: true,
      numberOfChannels: 8,
      frames: 4800,
      sampleRate: 48000,
      duration: 0.1,
      // Only compare decoded audio: the headers are different between the two files
      compareDecoded: true
    }
  ];

  // Returns true if the memory buffers are less different that |fuzz| bytes
  function fuzzyMemcmp(buf1, buf2, fuzz) {
    var difference = 0;
    is(buf1.length, buf2.length, "same length");
    for (var i = 0; i < buf1.length; ++i) {
      if (Math.abs(buf1[i] - buf2[i]) > fuzz.magnitude * (2 << 15)) {
        ++difference;
      }
    }
    if (difference > fuzz.count) {
      ok(false, "Expected at most " + fuzz + " bytes difference, found " + difference + " bytes");
    }
    console.log(difference, fuzz.count);
    return difference <= fuzz.count;
  }

  function getFuzzTolerance(test) {
    var kIsMobile =
      navigator.userAgent.includes("Mobile") || // b2g
      navigator.userAgent.includes("Android");  // android
    return {
      magnitude: test.fuzzMagnitude ?? 0,
      count: kIsMobile ? test.fuzzToleranceMobile ?? 0 : test.fuzzTolerance ?? 0
    };
  }

  function bufferIsSilent(buffer) {
    for (var i = 0; i < buffer.length; ++i) {
      if (buffer.getChannelData(0)[i] != 0) {
        return false;
      }
    }
    return true;
  }

  async function checkAudioBuffer(buffer, test) {
    if (buffer.numberOfChannels != test.numberOfChannels) {
      is(buffer.numberOfChannels, test.numberOfChannels, "Correct number of channels");
      return;
    }
    ok(Math.abs(buffer.duration - test.duration) < 1e-3, `Correct duration expected ${test.duration} got ${buffer.duration}`);
    if (Math.abs(buffer.duration - test.duration) >= 1e-3) {
      ok(false, "got: " + buffer.duration + ", expected: " + test.duration);
    }
    is(buffer.sampleRate, test.sampleRate, "Correct sample rate");
    is(buffer.length, test.frames, "Correct length");

    var wave = createWaveFileData(buffer);
    if (test.expectedWaveData && !test.compareDecoded) {
      ok(fuzzyMemcmp(wave, test.expectedWaveData, getFuzzTolerance(test)), "Received expected decoded data for " + test.url);
    } else if (test.compareDecoded) {
      var off = new OfflineAudioContext(1, 1, buffer.sampleRate);
      var decodedReference = await off.decodeAudioData(test.expectedWaveData.buffer);
      compareBuffers(buffer, decodedReference);
    }
  }

  function checkResampledBuffer(buffer, test, callback) {
    if (buffer.numberOfChannels != test.numberOfChannels) {
      is(buffer.numberOfChannels, test.numberOfChannels, "Correct number of channels");
      return;
    }
    ok(Math.abs(buffer.duration - test.duration) < 1e-3, "Correct duration");
    if (Math.abs(buffer.duration - test.duration) >= 1e-3) {
      ok(false, "got: " + buffer.duration + ", expected: " + test.duration);
    }
    // Take into account the resampling when checking the size
    var expectedLength = test.frames * buffer.sampleRate / test.sampleRate;
    SimpleTest.ok(
      Math.abs(buffer.length - expectedLength) < 1.0,
      "Correct length - got " + buffer.length +
      ", expected about " + expectedLength
    );

    // Playback the buffer in the original context, to resample back to the
    // original rate and compare with the decoded buffer without resampling.
    let cx = test.nativeContext;
    var expected = cx.createBufferSource();
    expected.buffer = test.expectedBuffer;
    expected.start();
    var inverse = cx.createGain();
    inverse.gain.value = -1;
    expected.connect(inverse);
    inverse.connect(cx.destination);
    var resampled = cx.createBufferSource();
    resampled.buffer = buffer;
    resampled.start();
    // This stop should do nothing, but it tests for bug 937475
    resampled.stop(test.frames / cx.sampleRate);
    resampled.connect(cx.destination);
    cx.oncomplete = function (e) {
      ok(!bufferIsSilent(e.renderedBuffer), "Expect buffer not silent");
      // Resampling will lose the highest frequency components, so we should
      // pass the difference through a low pass filter.  However, either the
      // input files don't have significant high frequency components or the
      // tolerance in compareBuffers() is too high to detect them.
      compareBuffers(e.renderedBuffer,
        cx.createBuffer(test.numberOfChannels,
          test.frames, test.sampleRate));
      callback();
    }
    cx.startRendering();
  }

  function runResampling(test, response, callback) {
    var sampleRate = test.sampleRate == 44100 ? 48000 : 44100;
    var cx = new OfflineAudioContext(1, 1, sampleRate);
    cx.decodeAudioData(response, function onSuccess(asyncResult) {
      is(asyncResult.sampleRate, sampleRate, "Correct sample rate");

      checkResampledBuffer(asyncResult, test, callback);
    }, function onFailure() {
      ok(false, "Expected successful decode with resample");
      callback();
    });
  }

  function runTest(test, response, callback) {
    // We need to copy the array here, because decodeAudioData will detach the
    // array's buffer.
    var compressedAudio = response.slice(0);
    var expectCallback = false;
    var cx = new OfflineAudioContext(test.numberOfChannels || 1,
      test.frames || 1, test.sampleRate);
    cx.decodeAudioData(response, async function onSuccess(asyncResult) {
      ok(expectCallback, "Success callback should fire asynchronously");
      ok(test.valid, "Did expect success for test " + test.url);

      await checkAudioBuffer(asyncResult, test);

      test.expectedBuffer = asyncResult;
      test.nativeContext = cx;
      runResampling(test, compressedAudio, callback);
    }, function onFailure(e) {
      ok(e instanceof DOMException, "We want to see an exception here");
      is(e.name, "EncodingError""Exception name matches");
      ok(expectCallback, "Failure callback should fire asynchronously");
      ok(!test.valid, "Did expect failure for test " + test.url);
      callback();
    });
    expectCallback = true;
  }

  function loadTest(test, callback) {
    var xhr = new XMLHttpRequest();
    xhr.open("GET", test.url, true);
    xhr.responseType = "arraybuffer";
    xhr.onload = function () {
      if (!test.expectedUrl) {
        runTest(test, xhr.response, callback);
        return;
      }
      var getExpected = new XMLHttpRequest();
      getExpected.open("GET", test.expectedUrl, true);
      getExpected.responseType = "arraybuffer";
      getExpected.onload = function () {
        test.expectedWaveData = new Uint8Array(getExpected.response);
        runTest(test, xhr.response, callback);
      };
      getExpected.send();
    };
    xhr.send();
  }

  function loadNextTest() {
    if (files.length) {
      loadTest(files.shift(), loadNextTest);
    } else {
      SimpleTest.finish();
    }
  }

  loadNextTest();

</script>
</pre>
  </body>
</html>

Messung V0.5
C=94 H=97 G=95

¤ Dauer der Verarbeitung: 0.26 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge