private Double sampleAtTargetTime(double lastOutputSample, SampleBuffer outputSampleBuffer, double outputPresentationTimeUs, double outputDurationPerSampleUs, double targetPresentationTimeUs) { double timeDiff = targetPresentationTimeUs - outputPresentationTimeUs; double index = timeDiff / outputDurationPerSampleUs; // the "last known sample" allows an index from -1.0 to 0 // the index cannot exceed the last sample. it must be stored to become the "last known sample" in the next iteration if (index < -1.0 || Math.ceil(index) >= outputSampleBuffer.limit()) { return null; } if (index == -1.0) { // the index points exactly to the last known sample return lastOutputSample; } else if (index % 1 == 0) { // index has no digits. therefore current index points to a known sample return outputSampleBuffer.get((int) index); } else { // the first sample can be the last known one double first = index < 0.0 ? lastOutputSample : outputSampleBuffer.get((int) index); double second = outputSampleBuffer.get((int) Math.ceil(index)); double rel = index % 1; // if the relative is between -1 and 0 if (rel < 0.0) { rel = 1 + rel; } return first + (second - first) * rel; } }
protected SampleBuffer onOutputBufferAvailable(@NonNull ByteBuffer outputBuffer) { double outputPresentationTimeUs = lastOutputPresentationTimeUs; double outputDurationPerSampleUs = 1000000.0 / (double)outputSampleRate; double targetPresentationTimeUs = lastTargetPresentationTimeUs; double targetDurationPerSampleUs = 1000000.0 / (double)targetSampleRate; // wrap the output buffer to make it provide audio samples SampleBuffer outputSampleBuffer = SampleBuffer.wrap(outputBuffer, outputSampleType, outputChannelCount, (long)outputPresentationTimeUs); outputSampleBuffer.position(0); // the buffer size is related to the output and target sample rate // add 2 samples to round up and add an extra sample int sampleSize = outputSampleBuffer.limit() * targetSampleRate / outputSampleRate + 2; SampleBuffer targetSampleBuffer = SampleBuffer.allocate(sampleSize, targetSampleType, ByteOrder.LITTLE_ENDIAN, (long)targetPresentationTimeUs); Double sample; do { sample = sampleAtTargetTime(lastOutputSample, outputSampleBuffer, outputPresentationTimeUs, outputDurationPerSampleUs, targetPresentationTimeUs); if (sample != null) { targetSampleBuffer.put(sample); targetPresentationTimeUs += targetDurationPerSampleUs; } } while (sample != null); lastTargetPresentationTimeUs = targetPresentationTimeUs; lastOutputPresentationTimeUs += outputSampleBuffer.limit() * outputDurationPerSampleUs; lastOutputSample = outputSampleBuffer.get(outputSampleBuffer.limit() - 1); targetSampleBuffer.limit(targetSampleBuffer.position()); targetSampleBuffer.position(0); return targetSampleBuffer; }
@Override public void sendAudio(ByteBuffer data, long presentationTimeUs) { SampleBuffer samples = SampleBuffer.wrap(data, sampleType, presentationTimeUs); double timeUs = presentationTimeUs; double sampleDurationUs = 1000000.0 / sampleRate; for (int i = 0; i < samples.limit(); ++i) { double sample = samples.get(i); double edge = timeUs % 4000.0; if (edge > 2000.0) { // swap sample as it's negative expected sample = sample * -1.0; } edge = edge % 2000.0; // at the edge of a wave the sample can be lower than 0.7 if ((sample > 0.7 && sample < 0.95) || (edge < sampleDurationUs || (2000.0 - sampleDurationUs) < edge)) { testFullAudioManagerDecodeFlowCorrectCounter++; } else { testFullAudioManagerDecodeFlowWrongCounter++; } timeUs += sampleDurationUs; } } };