com.hviper.codec.uodecode.PeakSignalNoiseRatioTest.java Source code

Java tutorial

Introduction

Here is the source code for com.hviper.codec.uodecode.PeakSignalNoiseRatioTest.java

Source

// Copyright (c) 2015 hornedviper.com.
// Implementation distributed under the MIT licence; see LICENSE.
package com.hviper.codec.uodecode;

import org.apache.commons.io.IOUtils;
import org.junit.Test;

import javax.xml.bind.DatatypeConverter;

import static org.junit.Assert.*;

import java.io.ByteArrayOutputStream;
import java.text.DecimalFormat;
import java.util.Arrays;

/**
 * Confidence check: verify the peak signal-to-noise ratio of decoded audio vs the output of the
 * reference decoder is within reasonable limits.
 */
public class PeakSignalNoiseRatioTest {
    private double computePsnrPcm16(String uoResource, String referenceResource) throws Exception {
        // Read the UO file and the reference encoder's decode output into byte arrays
        byte uoFile[] = IOUtils.toByteArray(this.getClass().getResourceAsStream(uoResource));
        byte referenceWavFile[] = IOUtils.toByteArray(this.getClass().getResourceAsStream(referenceResource));

        // Decode the UO file ourselves into a byte array
        ByteArrayOutputStream decodedWavOutputStream = new ByteArrayOutputStream(referenceWavFile.length);
        UODecode.uoToPcm16Wav(uoFile, decodedWavOutputStream);
        byte decodedWavFile[] = decodedWavOutputStream.toByteArray();

        // Find the start of the sample data; this will be after a 'data' header and four bytes of
        // content length.
        int dataStart = -1;
        for (int i = 0; i < decodedWavFile.length - 4; ++i) {
            if ((decodedWavFile[i] == 'd') && (decodedWavFile[i + 1] == 'a') && (decodedWavFile[i + 2] == 't')
                    && (decodedWavFile[i + 3] == 'a')) {
                dataStart = i + 8; // 8 = length of header + chunk length
                break;
            }
        }
        assertFalse("No 'data' header in decoded output", dataStart < 0);

        // Headers must be equal. Compare as hex strings for better assert failures here.
        String refHeaders = DatatypeConverter.printHexBinary(Arrays.copyOfRange(referenceWavFile, 0, dataStart));
        String ourHeaders = DatatypeConverter.printHexBinary(Arrays.copyOfRange(decodedWavFile, 0, dataStart));
        assertEquals("WAV headers do not match", refHeaders, ourHeaders);
        assertEquals("File lengths do not match", referenceWavFile.length, decodedWavFile.length);

        // Compute total squared error
        int cursor = dataStart;
        long totalSqError = 0;
        int sampleCount = 0;
        int worstSoFar = 0;
        for (; (cursor + 1) < referenceWavFile.length; cursor += 2) {
            short refSample = (short) ((referenceWavFile[cursor] & 0xff)
                    | ((referenceWavFile[cursor + 1] & 0xff) << 8));
            short ourSample = (short) ((decodedWavFile[cursor] & 0xff)
                    | ((decodedWavFile[cursor + 1] & 0xff) << 8));
            int absDiff = Math.abs(ourSample - refSample);

            long sqError = ((long) absDiff) * ((long) absDiff);
            totalSqError += sqError;
            ++sampleCount;
        }
        assertNotEquals("No samples read!", 0, sampleCount);

        // Compute the PSNR in decibels; higher the better
        double psnr;
        if (totalSqError > 0) {
            double sqrtMeanSquaredError = Math.sqrt((double) (totalSqError) / (double) (sampleCount));
            double maxValue = 65535.0;
            psnr = 20.0 * Math.log10(maxValue / sqrtMeanSquaredError);
        } else {
            // Identical! Pick a large PSNR result
            psnr = 1000.0;
        }

        return psnr;
    }

    @Test
    public void OpusSpeechDecodeVsReferenceDecoding() throws Exception {
        double psnr = computePsnrPcm16("/opus-speech.uo", "/opus-speech-reference.wav");
        System.out.println(
                "PSNR of decoded audio vs reference decoding = " + new DecimalFormat("#.#").format(psnr) + " db");
        assertTrue("PSNR against a reference decoding should be 65 db or greater", psnr >= 65.0);
    }
}