/* * ***************************************************************************** * Copyright (C) 2014-2022 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see * **************************************************************************** */ package io.github.dsheirer.buffer; import io.github.dsheirer.sample.complex.ComplexSamples; import io.github.dsheirer.sample.complex.InterleavedComplexSamples; import java.util.Iterator; /** * Native buffer sample array wrapper class that provides access to a stream of either interleaved or * non-interleaved complex sample buffers converted from the raw byte sample array. */ public class ByteNativeBuffer extends AbstractNativeBuffer { private static final int FRAGMENT_SIZE = 2048; private final static float[] LOOKUP_VALUES; private float mAverageDc; //Creates a static lookup table that converts the 8-bit valued range from 0 - 255 into scaled float values //of -1.0 to 0 to 1.0 static { LOOKUP_VALUES = new float[256]; for(int x = 0; x < 256; x++) { LOOKUP_VALUES[x] = ((float)x - 127.5f) / 128.0f; } } private byte[] mSamples; /** * Constructs an instance * @param samples to process * @param timestamp of the samples * @param averageDc measured from sample stream * @param samplesPerMillisecond to calculate derivative timestamps for sub-buffers. */ public ByteNativeBuffer(byte[] samples, long timestamp, float averageDc, float samplesPerMillisecond) { super(timestamp, samplesPerMillisecond); //Ensure we're an even multiple of the fragment size. Typically, this will be 64k or 128k if(samples.length % FRAGMENT_SIZE != 0) { throw new IllegalArgumentException("Samples byte[] length [" + samples.length + "] must be an even multiple of " + FRAGMENT_SIZE); } mSamples = samples; mAverageDc = averageDc; } @Override public int sampleCount() { return mSamples.length / 2; } @Override public Iterator iterator() { return new ComplexSamplesIterator(); } @Override public Iterator iteratorInterleaved() { return new InterleavedComplexSamplesIterator(); } private class ComplexSamplesIterator implements Iterator { private int mSamplesPointer = 0; @Override public boolean hasNext() { return mSamplesPointer < mSamples.length; } @Override public ComplexSamples next() { long timestamp = getFragmentTimestamp(mSamplesPointer); float[] i = new float[FRAGMENT_SIZE]; float[] q = new float[FRAGMENT_SIZE]; int samplesOffset = mSamplesPointer; for(int pointer = 0; pointer < i.length; pointer++) { i[pointer] = LOOKUP_VALUES[(0xFF & mSamples[samplesOffset++])] - mAverageDc; q[pointer] = LOOKUP_VALUES[(0xFF & mSamples[samplesOffset++])] - mAverageDc; } mSamplesPointer = samplesOffset; return new ComplexSamples(i, q, timestamp); } } /** * Iterator of interleaved complex samples over the native byte buffer array. */ private class InterleavedComplexSamplesIterator implements Iterator { private int mSamplesPointer = 0; @Override public boolean hasNext() { return mSamplesPointer < mSamples.length; } @Override public InterleavedComplexSamples next() { long timestamp = getFragmentTimestamp(mSamplesPointer); float[] converted = new float[FRAGMENT_SIZE * 2]; int samplesPointer = mSamplesPointer; for(int pointer = 0; pointer < converted.length; pointer++) { converted[pointer] = LOOKUP_VALUES[(0xFF & mSamples[samplesPointer++])] - mAverageDc; } mSamplesPointer = samplesPointer; return new InterleavedComplexSamples(converted, timestamp); } } }