mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
39 [WIP] cleans up the reusable buffers and creates both real and
complex variants.
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.sample.buffer;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
|
||||
public abstract class AbstractReusableBufferQueue<T extends ReusableBuffer> implements IReusableBufferDisposedListener<T>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(AbstractReusableBufferQueue.class);
|
||||
|
||||
private Queue<T> mReusableBufferQueue = new LinkedTransferQueue<>();
|
||||
private int mBufferCount = 0;
|
||||
private String mDebugName;
|
||||
private int mBufferUserCounter;
|
||||
|
||||
public AbstractReusableBufferQueue(String debugName)
|
||||
{
|
||||
mDebugName = debugName;
|
||||
}
|
||||
|
||||
public AbstractReusableBufferQueue()
|
||||
{
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mReusableBufferQueue.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements buffer disposed listener interface. Disposed (ie user count = 0) buffers will automatically callback
|
||||
* to this method to indicate when they are disposed.
|
||||
*
|
||||
* @param reusableBuffer that has been disposed
|
||||
*/
|
||||
@Override
|
||||
public void disposed(T reusableBuffer)
|
||||
{
|
||||
mReusableBufferQueue.offer(reusableBuffer);
|
||||
|
||||
// if(mDebugName != null && mDebugName.contentEquals("NativeBufferConverter"))
|
||||
// {
|
||||
// mLog.debug("Reclaimed: " + reusableBuffer.name());
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reusable buffer from an internal recycling queue, or creates a new buffer if there are currently no
|
||||
* buffers available for reuse.
|
||||
*
|
||||
* @param size of the samples that will be loaded into the buffer
|
||||
* @return a reusable buffer
|
||||
*/
|
||||
public T getBuffer(int size)
|
||||
{
|
||||
T buffer = mReusableBufferQueue.poll();
|
||||
|
||||
if(buffer == null)
|
||||
{
|
||||
buffer = createBuffer(size);
|
||||
mBufferCount++;
|
||||
buffer.setDebugName("Owner:" + mDebugName + " Number:" + mBufferUserCounter++ + " NEW BUFFER");
|
||||
|
||||
// if(mDebugName != null && mDebugName.contentEquals("NativeBufferConverter"))
|
||||
// {
|
||||
// mLog.debug("Buffer Created - Count:" + mBufferCount + (mDebugName != null ? " [" + mDebugName + "]" : ""));
|
||||
// }
|
||||
}
|
||||
else
|
||||
{
|
||||
String previous = buffer.name();
|
||||
buffer.setDebugName("Owner:" + mDebugName + " Number:" + mBufferUserCounter++);
|
||||
|
||||
// if(mDebugName != null && mDebugName.contentEquals("NativeBufferConverter"))
|
||||
// {
|
||||
// mLog.debug("Buffer Reused: " + previous + " New Name:" + buffer.name());
|
||||
// }
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new buffer of the specified size
|
||||
*/
|
||||
protected abstract T createBuffer(int size);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.sample.buffer;
|
||||
|
||||
public interface IReusableBufferDisposedListener<T extends ReusableBuffer>
|
||||
{
|
||||
/**
|
||||
* Notifies the listener that the timestamped buffer has been disposed and can now be reused.
|
||||
* @param t reusableBuffer that has been disposed
|
||||
*/
|
||||
void disposed(T t);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.sample.buffer;
|
||||
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
|
||||
public interface IReusableBufferProvider<T extends ReusableBuffer>
|
||||
{
|
||||
/**
|
||||
* Adds the listener to receive complex buffer samples
|
||||
*/
|
||||
void addBufferListener(Listener<T> listener);
|
||||
|
||||
/**
|
||||
* Removes the listener from receiving complex buffer samples
|
||||
*/
|
||||
void removeBufferListener(Listener<T> listener);
|
||||
|
||||
/**
|
||||
* Indicates if there are any complex buffer listeners registered
|
||||
*/
|
||||
boolean hasBufferListeners();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.sample.buffer;
|
||||
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
|
||||
public interface IReusableComplexBufferListener<T extends ReusableBuffer>
|
||||
{
|
||||
Listener<T> getReusableComplexBufferListener();
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.sample.buffer;
|
||||
|
||||
import io.github.dsheirer.sample.OverflowableTransferQueue;
|
||||
|
||||
public class OverflowableReusableBufferTransferQueue<T extends ReusableBuffer> extends OverflowableTransferQueue<T>
|
||||
{
|
||||
/**
|
||||
* Concurrent transfer queue that couples a higher-throughput linked transfer queue with an atomic integer for
|
||||
* monitoring queue size. When the queue size exceeds maximum size (overflow), all inbound elements are ignored
|
||||
* until the queue size is reduced to or below the reset threshold.
|
||||
*
|
||||
* This implementation includes special handling for reusable complex buffers.
|
||||
*
|
||||
* @param maximumSize of the queue. Overflow state will occur once queue size exceeds this value.
|
||||
* @param resetThreshold for resetting overflow state to normal, once queue size is at or below this value.
|
||||
*/
|
||||
public OverflowableReusableBufferTransferQueue(int maximumSize, int resetThreshold)
|
||||
{
|
||||
super(maximumSize, resetThreshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the overflow method to decrement the user count on any buffers that are being discarded when the queue
|
||||
* is in an overflow state.
|
||||
*
|
||||
* @param t reusableBuffer that will be discarded
|
||||
*/
|
||||
@Override
|
||||
protected void overflow(T t)
|
||||
{
|
||||
t.decrementUserCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the buffer clear method to decrement the user count on each buffer that is being cleared from the queue.
|
||||
*/
|
||||
@Override
|
||||
public void clear()
|
||||
{
|
||||
synchronized(mQueue)
|
||||
{
|
||||
T buffer = mQueue.poll();
|
||||
|
||||
while(buffer != null)
|
||||
{
|
||||
buffer.decrementUserCount();
|
||||
buffer = mQueue.poll();
|
||||
}
|
||||
|
||||
mCounter.set(0);
|
||||
mOverflow.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.sample.buffer;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class ReusableBuffer
|
||||
{
|
||||
private float[] mSamples;
|
||||
private IReusableBufferDisposedListener mBufferDisposedListener;
|
||||
private long mTimestamp;
|
||||
private AtomicInteger mUserCount = new AtomicInteger();
|
||||
private String mDebugName;
|
||||
|
||||
/**
|
||||
* Creates a reusable, timestamped complex buffer using the specified time in milliseconds.
|
||||
*
|
||||
* NOTE: reusability of this buffer requires strict user count tracking. Each component that receives this
|
||||
* buffer should not modify the buffer contents. Each component should also increment the user count before
|
||||
* sending this buffer to another component and should decrement the user count when finished using this buffer.
|
||||
*
|
||||
* @param bufferDisposedListener to be notified when all consumers/users are finished using the buffer
|
||||
* @param samples of data
|
||||
* @param timestamp in millis for the buffer
|
||||
*/
|
||||
ReusableBuffer(IReusableBufferDisposedListener bufferDisposedListener, float[] samples, long timestamp)
|
||||
{
|
||||
mSamples = samples;
|
||||
|
||||
Validate.notNull(bufferDisposedListener, "Reusable Buffer Listener cannot be null");
|
||||
mBufferDisposedListener = bufferDisposedListener;
|
||||
mTimestamp = timestamp;
|
||||
}
|
||||
|
||||
public void setDebugName(String debugName)
|
||||
{
|
||||
mDebugName = debugName;
|
||||
}
|
||||
|
||||
public String name()
|
||||
{
|
||||
return (mDebugName != null ? mDebugName : "(null)") + " User Count:" + getUserCount();
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Samples for this buffer
|
||||
*/
|
||||
public float[] getSamples()
|
||||
{
|
||||
return mSamples;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the samples from this buffer
|
||||
*/
|
||||
public float[] getSamplesCopy()
|
||||
{
|
||||
float[] samples = getSamples();
|
||||
float[] copy = new float[samples.length];
|
||||
System.arraycopy(samples, 0, copy, 0, samples.length);
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of samples contained in this buffer
|
||||
*/
|
||||
public int getSampleCount()
|
||||
{
|
||||
return getSamples().length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a timestamped complex buffer using the current system time in milliseconds.
|
||||
*
|
||||
* NOTE: reusability of this buffer requires strict user count tracking. Each component that receives this
|
||||
* buffer should not modify the buffer contents. Each component should also increment the user count before
|
||||
* sending this buffer to another component and should decrement the user count when finished using this buffer.
|
||||
*
|
||||
* @param bufferDisposedListener to be notified when all consumers are finished using the buffer
|
||||
* @param samples of data
|
||||
*/
|
||||
public ReusableBuffer(IReusableBufferDisposedListener bufferDisposedListener, float[] samples)
|
||||
{
|
||||
this(bufferDisposedListener, samples, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reference timestamp for this complex buffer. Timestamp is relative to the first sample in the buffer.
|
||||
* @return timestamp as milliseconds since epoch
|
||||
*/
|
||||
public long getTimestamp()
|
||||
{
|
||||
return mTimestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(long timestamp)
|
||||
{
|
||||
mTimestamp = timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads this buffer by copying the sample array into this buffer. Internal buffer length will be automatically
|
||||
* resized to fit the new sample array length.
|
||||
*
|
||||
* This is convenient if the source of the sample array originates via a native buffer.
|
||||
* @param samples to copy into this buffer
|
||||
* @param timestamp for the samples in millis
|
||||
*/
|
||||
public void reloadFrom(float[] samples, long timestamp)
|
||||
{
|
||||
if(mUserCount.get() > 0)
|
||||
{
|
||||
throw new IllegalStateException("New data cannot be loaded into this reusable buffer while the user count " +
|
||||
"is above zero - user count:" + mUserCount.get());
|
||||
}
|
||||
|
||||
mUserCount.set(0);
|
||||
|
||||
if(mSamples.length != samples.length)
|
||||
{
|
||||
mSamples = new float[samples.length];
|
||||
}
|
||||
|
||||
System.arraycopy(samples, 0, mSamples, 0, samples.length);
|
||||
|
||||
mTimestamp = timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads this buffer by filling this buffer from the float buffer (ie copying the buffer contents). Internal
|
||||
* buffer length will be automatically resized to accommodate the float buffer's capacity/size.
|
||||
*
|
||||
* @param floatBuffer to copy into this buffer
|
||||
* @param timestamp for the samples in millis
|
||||
*/
|
||||
public void reloadFrom(FloatBuffer floatBuffer, long timestamp)
|
||||
{
|
||||
if(mUserCount.get() > 0)
|
||||
{
|
||||
throw new IllegalStateException("New data cannot be loaded into this reusable buffer while the user count " +
|
||||
"is above zero - user count:" + mUserCount.get());
|
||||
}
|
||||
|
||||
mUserCount.set(0);
|
||||
|
||||
if(mSamples.length != floatBuffer.capacity())
|
||||
{
|
||||
mSamples = new float[floatBuffer.capacity()];
|
||||
}
|
||||
|
||||
floatBuffer.rewind();
|
||||
floatBuffer.get(mSamples);
|
||||
|
||||
mTimestamp = timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of users that will receive this buffer. Each user is expected to invoke the
|
||||
* decrementUserCount() method to indicate that they are finished using this buffer so that this buffer can be
|
||||
* reused once the user count is zero.
|
||||
*
|
||||
* @param userCount that will receive this reusable buffer
|
||||
*/
|
||||
public void setUserCount(int userCount)
|
||||
{
|
||||
mUserCount.set(userCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements the user count for this buffer to indicate that the user/receiver of this buffer is finished using
|
||||
* the buffer.
|
||||
*
|
||||
* User count is established prior to sending this buffer to each listener and each listener invokes this method to
|
||||
* indicate that they have finished processing the buffer so that when the user count reaches zero, this buffer
|
||||
* can be reused.
|
||||
*
|
||||
* This method is thread-safe.
|
||||
*/
|
||||
public void decrementUserCount()
|
||||
{
|
||||
int currentCount = mUserCount.decrementAndGet();
|
||||
|
||||
if(currentCount == 0)
|
||||
{
|
||||
mBufferDisposedListener.disposed(this);
|
||||
}
|
||||
else if(currentCount < 0)
|
||||
{
|
||||
throw new IllegalStateException("User count is below zero. This indicates that this buffer's decrement" +
|
||||
" user count was invoked by more than the expected user count");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the user count to indicate that this buffer will be sent to another user.
|
||||
*
|
||||
* Users/receivers of this buffer that wish to forward the buffer to additional users should increment the expected
|
||||
* user count prior to invoking the decrementUserCount() to indicate that the original receiver of the buffer is
|
||||
* finished to avoid the user count reaching zero and the buffer being flagged as ready for reuse.
|
||||
*
|
||||
* This method is thread-safe.
|
||||
*/
|
||||
public void incrementUserCount()
|
||||
{
|
||||
mUserCount.incrementAndGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the user count by the specified count to indicate that this buffer will be sent to additional users.
|
||||
*
|
||||
* Users/receivers of this buffer that wish to forward the buffer to additional users should increment the expected
|
||||
* user count prior to invoking the decrementUserCount() to indicate that the original receiver of the buffer is
|
||||
* finished to avoid the user count reaching zero and the buffer being flagged as ready for reuse.
|
||||
*
|
||||
* This method is thread-safe.
|
||||
*
|
||||
* @param additionalUserCount to add to the current user count
|
||||
*/
|
||||
public void incrementUserCount(int additionalUserCount)
|
||||
{
|
||||
mUserCount.addAndGet(additionalUserCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of users currently registered for this buffer
|
||||
*/
|
||||
public int getUserCount()
|
||||
{
|
||||
return mUserCount.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.sample.buffer;
|
||||
|
||||
import io.github.dsheirer.dsp.filter.channelizer.SampleTimestampManager;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
/**
|
||||
* Assembles complex float samples into a ComplexBuffer containing an array of floats. Monitors incoming sample count
|
||||
* to accurately assign a relative timestamp to each assembled buffer.
|
||||
*/
|
||||
public class ReusableBufferAssembler
|
||||
{
|
||||
private ReusableComplexBufferQueue mReusableComplexBufferQueue = new ReusableComplexBufferQueue("ReusableBufferAssembler");
|
||||
private SampleTimestampManager mTimestampManager;
|
||||
private FloatBuffer mBuffer;
|
||||
private int mBufferSize;
|
||||
private long mCurrentBufferTimestamp;
|
||||
private Listener<ReusableComplexBuffer> mListener;
|
||||
|
||||
public ReusableBufferAssembler(int bufferSize, double sampleRate)
|
||||
{
|
||||
Validate.isTrue(bufferSize % 2 == 0);
|
||||
mBufferSize = bufferSize;
|
||||
mBuffer = FloatBuffer.allocate(mBufferSize);
|
||||
mTimestampManager = new SampleTimestampManager(sampleRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the reference timestamp to use for all subsequent samples that are assembled. This timestamp should
|
||||
* identify the relative timestamp of the first/next sample that is received by this assembler.
|
||||
* @param timestamp to use relative to the incoming sample arrays.
|
||||
*/
|
||||
public void updateTimestamp(long timestamp)
|
||||
{
|
||||
mTimestampManager.setReferenceTimestamp(timestamp);
|
||||
|
||||
if(mBuffer.position() == 0)
|
||||
{
|
||||
mCurrentBufferTimestamp = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mListener = null;
|
||||
mBuffer = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the samples to this assembler. As each buffer is assembled, it will be dispatched to the registered
|
||||
* listener.
|
||||
*
|
||||
* @param samples to assemble
|
||||
*/
|
||||
public void receive(float[] samples)
|
||||
{
|
||||
if(mBuffer.remaining() >= samples.length)
|
||||
{
|
||||
mBuffer.put(samples);
|
||||
mTimestampManager.increment(samples.length / 2);
|
||||
|
||||
if(!mBuffer.hasRemaining())
|
||||
{
|
||||
flush();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int offset = 0;
|
||||
|
||||
while(offset < samples.length)
|
||||
{
|
||||
int toCopy = mBuffer.remaining();
|
||||
|
||||
if((samples.length - offset) < toCopy)
|
||||
{
|
||||
toCopy = samples.length - offset;
|
||||
}
|
||||
|
||||
mBuffer.put(samples, offset, toCopy);
|
||||
|
||||
offset += toCopy;
|
||||
mTimestampManager.increment(toCopy / 2);
|
||||
|
||||
if(!mBuffer.hasRemaining())
|
||||
{
|
||||
flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the current buffer contents to the registered listener
|
||||
*/
|
||||
public void flush()
|
||||
{
|
||||
if(mListener != null && mBuffer.position() > 0)
|
||||
{
|
||||
mBuffer.rewind();
|
||||
ReusableComplexBuffer reusableComplexBuffer = mReusableComplexBufferQueue.getBuffer(mBufferSize);
|
||||
reusableComplexBuffer.reloadFrom(mBuffer, mCurrentBufferTimestamp);
|
||||
reusableComplexBuffer.setUserCount(1);
|
||||
mListener.receive(reusableComplexBuffer);
|
||||
}
|
||||
|
||||
mBuffer.rewind();
|
||||
mCurrentBufferTimestamp = mTimestampManager.getCurrentTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the listener to receive the output from this buffer assembler. Note: the listener is responsible for
|
||||
* managing the user count for any buffers received from this buffer assembler.
|
||||
*/
|
||||
public void setListener(Listener<ReusableComplexBuffer> listener)
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.sample.buffer;
|
||||
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ReusableBufferBroadcaster extends Broadcaster<ReusableComplexBuffer>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ReusableBufferBroadcaster.class);
|
||||
|
||||
/**
|
||||
* Increments the user count for the reusable complex buffer and then broadcasts the buffer to all registered
|
||||
* listeners.
|
||||
*
|
||||
* The total user count is established and applied to the buffer prior to dispatching. If we were to simply
|
||||
* increment the user count prior to sending to each consumer, there is a possibility that the consumer could
|
||||
* immediately decrement the user count and prematurely signal that the buffer is ready for disposal before we
|
||||
* send the buffer to all consumers.
|
||||
*/
|
||||
@Override
|
||||
public void broadcast(ReusableComplexBuffer reusableComplexBuffer)
|
||||
{
|
||||
for(Listener<ReusableComplexBuffer> listener : mListeners)
|
||||
{
|
||||
reusableComplexBuffer.incrementUserCount();
|
||||
listener.receive(reusableComplexBuffer);
|
||||
}
|
||||
|
||||
//Decrement user counter for this broadcaster
|
||||
reusableComplexBuffer.decrementUserCount();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.sample.buffer;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ReusableBufferQueue extends AbstractReusableBufferQueue<ReusableBuffer>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ReusableBufferQueue.class);
|
||||
|
||||
public ReusableBufferQueue(String debugName)
|
||||
{
|
||||
super(debugName);
|
||||
}
|
||||
|
||||
public ReusableBufferQueue()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReusableBuffer createBuffer(int size)
|
||||
{
|
||||
return new ReusableBuffer(this, new float[size]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.sample.buffer;
|
||||
|
||||
public class ReusableComplexBuffer extends ReusableBuffer
|
||||
{
|
||||
/**
|
||||
* Creates a reusable, timestamped complex buffer using the specified time in milliseconds.
|
||||
*
|
||||
* NOTE: reusability of this buffer requires strict user count tracking. Each component that receives this
|
||||
* buffer should not modify the buffer contents. Each component should also increment the user count before
|
||||
* sending this buffer to another component and should decrement the user count when finished using this buffer.
|
||||
*
|
||||
* @param disposalListener to be notified when all consumers/users are finished using the buffer
|
||||
* @param samples of data
|
||||
* @param timestamp in millis for the buffer
|
||||
*/
|
||||
ReusableComplexBuffer(IReusableBufferDisposedListener disposalListener, float[] samples, long timestamp)
|
||||
{
|
||||
super(disposalListener, samples, timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a timestamped complex buffer using the current system time in milliseconds.
|
||||
*
|
||||
* NOTE: reusability of this buffer requires strict user count tracking. Each component that receives this
|
||||
* buffer should not modify the buffer contents. Each component should also increment the user count before
|
||||
* sending this buffer to another component and should decrement the user count when finished using this buffer.
|
||||
*
|
||||
* @param disposalListener to be notified when all consumers are finished using the buffer
|
||||
* @param samples of data
|
||||
*/
|
||||
protected ReusableComplexBuffer(IReusableBufferDisposedListener disposalListener, float[] samples)
|
||||
{
|
||||
this(disposalListener, samples, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of complex samples contained in this buffer
|
||||
*/
|
||||
@Override
|
||||
public int getSampleCount()
|
||||
{
|
||||
return getSamples().length / 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.sample.buffer;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ReusableComplexBufferQueue extends AbstractReusableBufferQueue<ReusableComplexBuffer>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ReusableComplexBufferQueue.class);
|
||||
|
||||
public ReusableComplexBufferQueue(String debugName)
|
||||
{
|
||||
super(debugName);
|
||||
}
|
||||
|
||||
public ReusableComplexBufferQueue()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReusableComplexBuffer createBuffer(int size)
|
||||
{
|
||||
return new ReusableComplexBuffer(this, new float[size]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user