[WIP] Updates AudioPackets to be ReusableAudioPackets.

This commit is contained in:
Denny
2018-06-29 06:53:03 -04:00
parent a621a31aa9
commit df1bc1031e
26 changed files with 451 additions and 341 deletions

View File

@@ -24,6 +24,7 @@ import io.github.dsheirer.audio.output.StereoAudioOutput;
import io.github.dsheirer.properties.SystemProperties;
import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import io.github.dsheirer.source.mixer.MixerChannel;
import io.github.dsheirer.source.mixer.MixerChannelConfiguration;
import io.github.dsheirer.source.mixer.MixerManager;
@@ -43,7 +44,7 @@ import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class AudioManager implements Listener<AudioPacket>, IAudioController
public class AudioManager implements Listener<ReusableAudioPacket>, IAudioController
{
private static final Logger mLog = LoggerFactory.getLogger(AudioManager.class);
@@ -58,7 +59,7 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
public static final AudioEvent CONFIGURATION_CHANGE_COMPLETE =
new AudioEvent(AudioEvent.Type.AUDIO_CONFIGURATION_CHANGE_COMPLETE, null);
private LinkedTransferQueue<AudioPacket> mAudioPacketQueue = new LinkedTransferQueue<>();
private LinkedTransferQueue<ReusableAudioPacket> mAudioPacketQueue = new LinkedTransferQueue<>();
private Map<Integer,AudioOutputConnection> mChannelConnectionMap = new HashMap<>();
private List<AudioOutputConnection> mAudioOutputConnections = new ArrayList<>();
private AudioOutputConnection mLowestPriorityConnection;
@@ -170,7 +171,7 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
* Primary ingest point for audio produced by all decoding channels, for distribution to audio playback devices.
*/
@Override
public synchronized void receive(AudioPacket packet)
public synchronized void receive(ReusableAudioPacket packet)
{
mAudioPacketQueue.add(packet);
}
@@ -362,7 +363,7 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
* @param audioPacket from a decoding channel source
* @return an audio output connection or null
*/
private AudioOutputConnection getConnection(AudioPacket audioPacket)
private AudioOutputConnection getConnection(ReusableAudioPacket audioPacket)
{
int channelMetadataID = audioPacket.getMetadata().getMetadataID();
@@ -410,6 +411,8 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
public class AudioPacketProcessor implements Runnable
{
private List<ReusableAudioPacket> mPackets = new ArrayList<>();
@Override
public void run()
{
@@ -419,14 +422,12 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
if(mAudioPacketQueue != null)
{
List<AudioPacket> packets = new ArrayList<AudioPacket>();
mAudioPacketQueue.drainTo(mPackets);
mAudioPacketQueue.drainTo(packets);
for(AudioPacket packet : packets)
for(ReusableAudioPacket packet : mPackets)
{
/* Don't process any packet's marked as do not monitor */
if(!packet.getMetadata().isDoNotMonitor() && packet.getType() == AudioPacket.Type.AUDIO)
if(!packet.getMetadata().isDoNotMonitor() && packet.getType() == ReusableAudioPacket.Type.AUDIO)
{
AudioOutputConnection connection = getConnection(packet);
@@ -434,13 +435,29 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
{
connection.receive(packet);
}
else
{
packet.decrementUserCount();
}
}
else
{
packet.decrementUserCount();
}
}
mPackets.clear();
}
}
catch(Exception e)
{
mLog.error("Encountered error while processing audio packets", e);
while(mPackets.size() > 0)
{
ReusableAudioPacket audioPacket = mPackets.remove(0);
audioPacket.decrementUserCount();
}
}
}
}
@@ -463,7 +480,7 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
mAudioOutput = audioOutput;
}
public void receive(AudioPacket packet)
public void receive(ReusableAudioPacket packet)
{
if(packet.hasMetadata() && packet.getMetadata().getMetadataID() == mChannelMetadataID)
{

View File

@@ -25,6 +25,8 @@ import io.github.dsheirer.dsp.filter.fir.remez.RemezFIRFilterDesigner;
import io.github.dsheirer.module.Module;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.IReusableBufferListener;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import io.github.dsheirer.sample.buffer.ReusableAudioPacketQueue;
import io.github.dsheirer.sample.buffer.ReusableBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,6 +43,7 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab
ISquelchStateListener, Listener<ReusableBuffer>
{
protected static final Logger mLog = LoggerFactory.getLogger(AudioModule.class);
private ReusableAudioPacketQueue mAudioPacketQueue = new ReusableAudioPacketQueue("AudioModule");
private static float[] sHighPassFilterCoefficients;
@@ -73,11 +76,12 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab
private RealFIRFilter2 mHighPassFilter = new RealFIRFilter2(sHighPassFilterCoefficients);
private SquelchStateListener mSquelchStateListener = new SquelchStateListener();
private SquelchState mSquelchState = SquelchState.SQUELCH;
private Listener<AudioPacket> mAudioPacketListener;
private Listener<ReusableAudioPacket> mAudioPacketListener;
private Metadata mMetadata;
/**
* Creates an Audio Module.
*
* @param metadata to use for audio packets produced by this audio module.
*/
public AudioModule(Metadata metadata)
@@ -109,12 +113,15 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab
/* Issue an end-audio packet in case a recorder is still rolling */
if(mAudioPacketListener != null)
{
mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf()));
ReusableAudioPacket endAudioPacket = mAudioPacketQueue.getEndAudioBuffer();
endAudioPacket.setMetadata(mMetadata.copyOf());
endAudioPacket.incrementUserCount();
mAudioPacketListener.receive(endAudioPacket);
}
}
@Override
public void setAudioPacketListener(Listener<AudioPacket> listener)
public void setAudioPacketListener(Listener<ReusableAudioPacket> listener)
{
mAudioPacketListener = listener;
}
@@ -137,8 +144,14 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab
if(mAudioPacketListener != null && mSquelchState == SquelchState.UNSQUELCH)
{
ReusableBuffer highPassFiltered = mHighPassFilter.filter(reusableBuffer);
AudioPacket packet = new AudioPacket(highPassFiltered.getSamplesCopy(), mMetadata.copyOf());
mAudioPacketListener.receive(packet);
ReusableAudioPacket audioPacket = mAudioPacketQueue.getBuffer(highPassFiltered.getSampleCount());
audioPacket.loadAudioFrom(highPassFiltered);
audioPacket.setMetadata(mMetadata.copyOf());
audioPacket.incrementUserCount();
mAudioPacketListener.receive(audioPacket);
highPassFiltered.decrementUserCount();
}
else
@@ -164,7 +177,9 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab
{
if(state == SquelchState.SQUELCH && mAudioPacketListener != null)
{
mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf()));
ReusableAudioPacket endAudioPacket = mAudioPacketQueue.getEndAudioBuffer();
endAudioPacket.setMetadata(mMetadata.copyOf());
mAudioPacketListener.receive(endAudioPacket);
}
mSquelchState = state;

View File

@@ -1,78 +0,0 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 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.audio;
import io.github.dsheirer.channel.metadata.Metadata;
import io.github.dsheirer.sample.real.RealBuffer;
public class AudioPacket
{
private Type mType;
private RealBuffer mAudioData;
private Metadata mMetadata;
public AudioPacket(Type type, Metadata metadata)
{
mType = type;
mMetadata = metadata;
}
public AudioPacket(float[] audio, Metadata metadata)
{
this(Type.AUDIO, metadata);
mAudioData = new RealBuffer(audio);
}
public boolean hasMetadata()
{
return mMetadata != null;
}
public Metadata getMetadata()
{
return mMetadata;
}
public void setMetadata(Metadata metadata)
{
mMetadata = metadata;
}
public Type getType()
{
return mType;
}
public RealBuffer getAudioBuffer()
{
return mAudioData;
}
public boolean hasAudioBuffer()
{
return mAudioData != null;
}
public enum Type
{
AUDIO,
END;
}
}

View File

@@ -1,55 +0,0 @@
package io.github.dsheirer.audio;
import io.github.dsheirer.sample.Listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AudioPacketRateCounter implements Listener<AudioPacket>
{
private final static Logger mLog =
LoggerFactory.getLogger( AudioPacketRateCounter.class );
private Listener<AudioPacket> mListener;
private long mCount;
private long mStartTime = -1;
private String mName;
public AudioPacketRateCounter()
{
mName = "UNNAMED";
}
public AudioPacketRateCounter( String name )
{
mName = name;
}
@Override
public void receive( AudioPacket packet )
{
if( mStartTime == -1 )
{
mStartTime = System.currentTimeMillis();
}
mCount += packet.getAudioBuffer().getSamples().length;
long elapsed = System.currentTimeMillis() - mStartTime;
if( elapsed != 0 )
{
mLog.debug( "[" + mName + "] Audio Rate: " + ( mCount / elapsed ) + " kHz" );
}
if( mListener != null )
{
mListener.receive( packet );
}
}
public void setListener( Listener<AudioPacket> listener )
{
mListener = listener;
}
}

View File

@@ -1,24 +1,22 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2016 Dennis Sheirer
* 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 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.
* 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/>
* 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.audio;
import io.github.dsheirer.sample.ConversionUtils;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -34,17 +32,18 @@ public class AudioUtils
/**
* Converts the audio packets into a byte array of 16-bit, little-endian audio samples
*/
public static byte[] convertTo16BitSamples(List<AudioPacket> audioPackets)
public static byte[] convertTo16BitSamples(List<ReusableAudioPacket> audioPackets)
{
ByteArrayOutputStream stream = new ByteArrayOutputStream();
try
{
for(AudioPacket packet: audioPackets)
for(ReusableAudioPacket audioPacket: audioPackets)
{
//Converting from 32-bit floats to signed 16-bit samples
ByteBuffer buffer = ConversionUtils.convertToSigned16BitSamples(packet.getAudioBuffer().getSamples());
ByteBuffer buffer = ConversionUtils.convertToSigned16BitSamples(audioPacket.getAudioSamples());
stream.write(buffer.array());
audioPacket.decrementUserCount();
}
}
catch(IOException e)

View File

@@ -1,8 +1,9 @@
package io.github.dsheirer.audio;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
public interface IAudioPacketListener
{
public Listener<AudioPacket> getAudioPacketListener();
public Listener<ReusableAudioPacket> getAudioPacketListener();
}

View File

@@ -1,9 +1,10 @@
package io.github.dsheirer.audio;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
public interface IAudioPacketProvider
{
public void setAudioPacketListener( Listener<AudioPacket> listener );
public void setAudioPacketListener( Listener<ReusableAudioPacket> listener );
public void removeAudioPacketListener();
}

View File

@@ -19,12 +19,12 @@
package io.github.dsheirer.audio.broadcast;
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
import io.github.dsheirer.audio.AudioPacket;
import io.github.dsheirer.channel.metadata.Metadata;
import io.github.dsheirer.icon.IconManager;
import io.github.dsheirer.properties.SystemProperties;
import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import io.github.dsheirer.util.ThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,7 +46,7 @@ import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class BroadcastModel extends AbstractTableModel implements Listener<AudioPacket>
public class BroadcastModel extends AbstractTableModel implements Listener<ReusableAudioPacket>
{
private final static Logger mLog = LoggerFactory.getLogger(BroadcastModel.class);
@@ -276,7 +276,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
}
@Override
public void receive(AudioPacket audioPacket)
public void receive(ReusableAudioPacket audioPacket)
{
if(audioPacket.hasMetadata() && audioPacket.getMetadata().isStreamable())
{
@@ -284,11 +284,14 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
{
if(mBroadcasterMap.containsKey(channel.getChannelName()))
{
audioPacket.incrementUserCount();
mStreamManager.receive(audioPacket);
return;
}
}
}
audioPacket.decrementUserCount();
}
/**

View File

@@ -18,9 +18,9 @@
******************************************************************************/
package io.github.dsheirer.audio.broadcast;
import io.github.dsheirer.audio.AudioPacket;
import io.github.dsheirer.record.AudioRecorder;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import io.github.dsheirer.util.ThreadPool;
import io.github.dsheirer.util.TimeStamp;
import org.slf4j.Logger;
@@ -37,7 +37,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class StreamManager implements Listener<AudioPacket>
public class StreamManager implements Listener<ReusableAudioPacket>
{
private final static Logger mLog = LoggerFactory.getLogger(StreamManager.class);
private static final long MAXIMUM_RECORDER_LIFESPAN_MILLIS = 30000; //30 seconds
@@ -115,7 +115,7 @@ public class StreamManager implements Listener<AudioPacket>
}
@Override
public void receive(AudioPacket audioPacket)
public void receive(ReusableAudioPacket audioPacket)
{
if(mRunning.get() && audioPacket.hasMetadata())
{
@@ -123,9 +123,9 @@ public class StreamManager implements Listener<AudioPacket>
{
int channelMetadataID = audioPacket.getMetadata().getMetadataID();
AudioPacket.Type type = audioPacket.getType();
ReusableAudioPacket.Type type = audioPacket.getType();
if(type == AudioPacket.Type.AUDIO)
if(type == ReusableAudioPacket.Type.AUDIO)
{
if(mStreamRecorders.containsKey(channelMetadataID))
{
@@ -133,6 +133,7 @@ public class StreamManager implements Listener<AudioPacket>
if(recorder != null)
{
audioPacket.incrementUserCount();
recorder.receive(audioPacket);
}
}
@@ -141,11 +142,12 @@ public class StreamManager implements Listener<AudioPacket>
AudioRecorder recorder = BroadcastFactory.getAudioRecorder(getTemporaryRecordingPath(),
mBroadcastFormat);
recorder.start();
audioPacket.incrementUserCount();
recorder.receive(audioPacket);
mStreamRecorders.put(channelMetadataID, recorder);
}
}
else if(type == AudioPacket.Type.END)
else if(type == ReusableAudioPacket.Type.END)
{
removeRecorder(channelMetadataID);
}
@@ -155,6 +157,8 @@ public class StreamManager implements Listener<AudioPacket>
}
}
}
audioPacket.decrementUserCount();
}
/**

View File

@@ -18,7 +18,7 @@
******************************************************************************/
package io.github.dsheirer.audio.convert;
import io.github.dsheirer.audio.AudioPacket;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import java.util.List;
@@ -27,7 +27,7 @@ public interface IAudioConverter
/**
* Converts the PCM audio packets to converted audio format. May produce partial audio frame data.
*/
public byte[] convert(List<AudioPacket> audioPackets);
public byte[] convert(List<ReusableAudioPacket> audioPackets);
/**
* Finalizes audio conversion by fully converting any partial frames left in the buffer and returning the

View File

@@ -19,8 +19,8 @@
package io.github.dsheirer.audio.convert;
import io.github.dsheirer.audio.AudioFormats;
import io.github.dsheirer.audio.AudioPacket;
import io.github.dsheirer.audio.AudioUtils;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import net.sourceforge.lame.lowlevel.LameEncoder;
import net.sourceforge.lame.mp3.Lame;
import net.sourceforge.lame.mp3.MPEGMode;
@@ -54,7 +54,7 @@ public class MP3AudioConverter implements IAudioConverter
}
@Override
public byte[] convert(List<AudioPacket> audioPackets)
public byte[] convert(List<ReusableAudioPacket> audioPackets)
{
mMP3Stream.reset();

View File

@@ -18,8 +18,9 @@
******************************************************************************/
package io.github.dsheirer.audio.convert;
import io.github.dsheirer.audio.AudioPacket;
import io.github.dsheirer.record.mp3.MP3Recorder;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import io.github.dsheirer.sample.buffer.ReusableAudioPacketQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,6 +34,7 @@ public class MP3SilenceGenerator implements ISilenceGenerator
private MP3AudioConverter mGenerator = new MP3AudioConverter(MP3Recorder.MP3_BIT_RATE, MP3Recorder.CONSTANT_BIT_RATE);
private byte[] mPreviousPartialFrameData;
private ReusableAudioPacketQueue mAudioPacketQueue = new ReusableAudioPacketQueue("MP3 Silence Generator");
/**
* Generates MP3 audio silence frames
@@ -44,9 +46,11 @@ public class MP3SilenceGenerator implements ISilenceGenerator
public byte[] generate(long duration)
{
int length = (int)(duration * 8); //8000 Hz sample rate
float[] silence = new float[length];
AudioPacket silencePacket = new AudioPacket(silence, null);
List<AudioPacket> silencePackets = new ArrayList<>();
ReusableAudioPacket silencePacket = mAudioPacketQueue.getBuffer(length);
Arrays.fill(silencePacket.getAudioSamples(), 0.0f);
silencePacket.incrementUserCount();
List<ReusableAudioPacket> silencePackets = new ArrayList<>();
silencePackets.add(silencePacket);
byte[] frameData = mGenerator.convert(silencePackets);

View File

@@ -19,10 +19,10 @@
package io.github.dsheirer.audio.output;
import io.github.dsheirer.audio.AudioEvent;
import io.github.dsheirer.audio.AudioPacket;
import io.github.dsheirer.channel.metadata.Metadata;
import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import io.github.dsheirer.source.mixer.MixerChannel;
import io.github.dsheirer.util.ThreadPool;
import org.slf4j.Logger;
@@ -46,11 +46,11 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
public abstract class AudioOutput implements Listener<ReusableAudioPacket>, LineListener
{
private final static Logger mLog = LoggerFactory.getLogger(AudioOutput.class);
private LinkedTransferQueue<AudioPacket> mBuffer = new LinkedTransferQueue<>();
private LinkedTransferQueue<ReusableAudioPacket> mBuffer = new LinkedTransferQueue<>();
private int mBufferStartThreshold;
private int mBufferStopThreshold;
@@ -190,7 +190,7 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
* Converts the audio packet data into a byte buffer format appropriate for
* the underlying source data line.
*/
protected abstract ByteBuffer convert(AudioPacket packet);
protected abstract ByteBuffer convert(ReusableAudioPacket packet);
/**
* Audio output channel name
@@ -271,7 +271,7 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
}
@Override
public void receive(AudioPacket packet)
public void receive(ReusableAudioPacket packet)
{
if(mCanProcessAudio)
{
@@ -281,11 +281,17 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
mBuffer.add(packet);
}
else
{
packet.decrementUserCount();
}
}
public class BufferProcessor implements Runnable
{
private AtomicBoolean mProcessing = new AtomicBoolean();
private List<ReusableAudioPacket> mAudioPackets = new ArrayList<ReusableAudioPacket>();
public BufferProcessor()
{
@@ -300,13 +306,11 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
* processor can run at any given time */
if(mProcessing.compareAndSet(false, true))
{
List<AudioPacket> packets = new ArrayList<AudioPacket>();
mBuffer.drainTo(mAudioPackets);
mBuffer.drainTo(packets);
for(AudioPacket packet : packets)
for(ReusableAudioPacket packet : mAudioPackets)
{
if(packet.getType() == AudioPacket.Type.AUDIO)
if(packet.getType() == ReusableAudioPacket.Type.AUDIO)
{
broadcast(packet.getMetadata());
@@ -338,8 +342,14 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
updateTimestamp();
}
else
{
packet.decrementUserCount();
}
}
mAudioPackets.clear();
checkStop();
mProcessing.set(false);

View File

@@ -19,7 +19,7 @@
package io.github.dsheirer.audio.output;
import io.github.dsheirer.audio.AudioFormats;
import io.github.dsheirer.audio.AudioPacket;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import io.github.dsheirer.source.mixer.MixerChannel;
import javax.sound.sampled.Mixer;
@@ -43,14 +43,16 @@ public class MonoAudioOutput extends AudioOutput
/**
* Converts the audio packet data into mono audio frames.
*/
protected ByteBuffer convert(AudioPacket packet)
protected ByteBuffer convert(ReusableAudioPacket packet)
{
if(packet.hasAudioBuffer())
ByteBuffer buffer = null;
if(packet.hasAudioSamples())
{
float[] samples = packet.getAudioBuffer().getSamples();
float[] samples = packet.getAudioSamples();
/* Little-endian byte buffer */
ByteBuffer buffer = ByteBuffer.allocate(samples.length * 2).order(ByteOrder.LITTLE_ENDIAN);
buffer = ByteBuffer.allocate(samples.length * 2).order(ByteOrder.LITTLE_ENDIAN);
ShortBuffer shortBuffer = buffer.asShortBuffer();
@@ -58,10 +60,10 @@ public class MonoAudioOutput extends AudioOutput
{
shortBuffer.put((short) (sample * Short.MAX_VALUE));
}
return buffer;
}
return null;
packet.decrementUserCount();
return buffer;
}
}

View File

@@ -19,7 +19,7 @@
package io.github.dsheirer.audio.output;
import io.github.dsheirer.audio.AudioFormats;
import io.github.dsheirer.audio.AudioPacket;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import io.github.dsheirer.source.mixer.MixerChannel;
import javax.sound.sampled.Mixer;
@@ -45,15 +45,16 @@ public class StereoAudioOutput extends AudioOutput
* channel containing the audio and the other channel containing zero
* valued (silent) samples.
*/
protected ByteBuffer convert(AudioPacket packet)
protected ByteBuffer convert(ReusableAudioPacket packet)
{
if(packet.hasAudioBuffer())
ByteBuffer buffer = null;
if(packet.hasAudioSamples())
{
float[] samples = packet.getAudioBuffer().getSamples();
float[] samples = packet.getAudioSamples();
/* Little-endian byte buffer */
ByteBuffer buffer = ByteBuffer.allocate(samples.length * 4)
.order(ByteOrder.LITTLE_ENDIAN);
buffer = ByteBuffer.allocate(samples.length * 4).order(ByteOrder.LITTLE_ENDIAN);
ShortBuffer shortBuffer = buffer.asShortBuffer();
@@ -73,10 +74,10 @@ public class StereoAudioOutput extends AudioOutput
shortBuffer.put((short) (sample * Short.MAX_VALUE));
}
}
return buffer;
}
return null;
packet.decrementUserCount();
return buffer;
}
}

View File

@@ -19,7 +19,6 @@
package io.github.dsheirer.controller.channel;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.audio.AudioPacket;
import io.github.dsheirer.channel.metadata.Attribute;
import io.github.dsheirer.channel.metadata.AttributeChangeRequest;
import io.github.dsheirer.channel.metadata.ChannelMetadataModel;
@@ -35,6 +34,7 @@ import io.github.dsheirer.module.log.EventLogManager;
import io.github.dsheirer.record.RecorderManager;
import io.github.dsheirer.record.RecorderType;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import io.github.dsheirer.source.Source;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.SourceManager;
@@ -54,7 +54,7 @@ public class ChannelProcessingManager implements ChannelEventListener
private Map<Integer,ProcessingChain> mProcessingChains = new HashMap<>();
private List<Listener<AudioPacket>> mAudioPacketListeners = new CopyOnWriteArrayList<>();
private List<Listener<ReusableAudioPacket>> mAudioPacketListeners = new CopyOnWriteArrayList<>();
private List<Listener<Message>> mMessageListeners = new CopyOnWriteArrayList<>();
private ChannelModel mChannelModel;
@@ -232,7 +232,7 @@ public class ChannelProcessingManager implements ChannelEventListener
processingChain = new ProcessingChain(channel.getChannelType());
/* Register global listeners */
for(Listener<AudioPacket> listener : mAudioPacketListeners)
for(Listener<ReusableAudioPacket> listener : mAudioPacketListeners)
{
processingChain.addAudioPacketListener(listener);
}
@@ -367,7 +367,7 @@ public class ChannelProcessingManager implements ChannelEventListener
* Adds a message listener that will be added to all channels to receive
* any messages.
*/
public void addAudioPacketListener(Listener<AudioPacket> listener)
public void addAudioPacketListener(Listener<ReusableAudioPacket> listener)
{
mAudioPacketListeners.add(listener);
}
@@ -375,7 +375,7 @@ public class ChannelProcessingManager implements ChannelEventListener
/**
* Removes a message listener.
*/
public void removeAudioPacketListener(Listener<AudioPacket> listener)
public void removeAudioPacketListener(Listener<ReusableAudioPacket> listener)
{
mAudioPacketListeners.remove(listener);
}

View File

@@ -18,7 +18,6 @@
******************************************************************************/
package io.github.dsheirer.module;
import io.github.dsheirer.audio.AudioPacket;
import io.github.dsheirer.audio.IAudioPacketListener;
import io.github.dsheirer.audio.IAudioPacketProvider;
import io.github.dsheirer.audio.squelch.ISquelchStateListener;
@@ -52,6 +51,7 @@ import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.IReusableBufferListener;
import io.github.dsheirer.sample.buffer.IReusableBufferProvider;
import io.github.dsheirer.sample.buffer.IReusableComplexBufferListener;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import io.github.dsheirer.sample.buffer.ReusableBuffer;
import io.github.dsheirer.sample.buffer.ReusableBufferBroadcaster;
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
@@ -99,7 +99,7 @@ public class ProcessingChain implements IChannelEventListener
private final static Logger mLog = LoggerFactory.getLogger(ProcessingChain.class);
private Broadcaster<AttributeChangeRequest> mAttributeChangeRequestBroadcaster = new Broadcaster<>();
private Broadcaster<AudioPacket> mAudioPacketBroadcaster = new Broadcaster<>();
private ReusableBufferBroadcaster<ReusableAudioPacket> mAudioPacketBroadcaster = new ReusableBufferBroadcaster<>();
private Broadcaster<CallEvent> mCallEventBroadcaster = new Broadcaster<>();
private Broadcaster<ChannelEvent> mChannelEventBroadcaster = new Broadcaster<>();
private ReusableBufferBroadcaster<ReusableComplexBuffer> mComplexBufferBroadcaster = new ReusableBufferBroadcaster();
@@ -724,12 +724,12 @@ public class ProcessingChain implements IChannelEventListener
/**
* Adds the listener to receive audio packets from all modules.
*/
public void addAudioPacketListener(Listener<AudioPacket> listener)
public void addAudioPacketListener(Listener<ReusableAudioPacket> listener)
{
mAudioPacketBroadcaster.addListener(listener);
}
public void removeAudioPacketListener(Listener<AudioPacket> listener)
public void removeAudioPacketListener(Listener<ReusableAudioPacket> listener)
{
mAudioPacketBroadcaster.removeListener(listener);
}

View File

@@ -1,7 +1,6 @@
package io.github.dsheirer.module.decode.p25.audio;
import io.github.dsheirer.audio.AudioFormats;
import io.github.dsheirer.audio.AudioPacket;
import io.github.dsheirer.audio.IAudioPacketProvider;
import io.github.dsheirer.audio.squelch.ISquelchStateListener;
import io.github.dsheirer.audio.squelch.SquelchState;
@@ -15,6 +14,8 @@ import io.github.dsheirer.module.decode.p25.message.ldu.LDU1Message;
import io.github.dsheirer.module.decode.p25.message.ldu.LDU2Message;
import io.github.dsheirer.module.decode.p25.message.ldu.LDUMessage;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import io.github.dsheirer.sample.buffer.ReusableAudioPacketQueue;
import jmbe.iface.AudioConversionLibrary;
import jmbe.iface.AudioConverter;
import org.slf4j.Logger;
@@ -33,11 +34,12 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
private boolean mEncryptedCallStateEstablished = false;
private AudioConverter mAudioConverter;
private Listener<AudioPacket> mAudioPacketListener;
private Listener<ReusableAudioPacket> mAudioPacketListener;
private SquelchStateListener mSquelchStateListener = new SquelchStateListener();
private NonClippingGain mGain = new NonClippingGain(5.0f, 0.95f);
private Metadata mMetadata;
private LDU1Message mCachedLDU1Message = null;
private ReusableAudioPacketQueue mAudioPacketQueue = new ReusableAudioPacketQueue("P25AudioModule");
public P25AudioModule(Metadata metadata)
{
@@ -142,7 +144,12 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
audio = mGain.apply(audio);
mAudioPacketListener.receive(new AudioPacket(audio, mMetadata.copyOf()));
ReusableAudioPacket audioPacket = mAudioPacketQueue.getBuffer(audio.length);
audioPacket.setMetadata(mMetadata.copyOf());
audioPacket.loadAudioFrom(audio);
audioPacket.incrementUserCount();
mAudioPacketListener.receive(audioPacket);
}
}
else
@@ -230,7 +237,7 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
}
@Override
public void setAudioPacketListener(Listener<AudioPacket> listener)
public void setAudioPacketListener(Listener<ReusableAudioPacket> listener)
{
mAudioPacketListener = listener;
}
@@ -255,7 +262,10 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
{
if(mAudioPacketListener != null)
{
mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf()));
ReusableAudioPacket endAudioPacket = mAudioPacketQueue.getEndAudioBuffer();
endAudioPacket.setMetadata(mMetadata.copyOf());
endAudioPacket.incrementUserCount();
mAudioPacketListener.receive(endAudioPacket);
}
mEncryptedCallStateEstablished = false;

View File

@@ -18,11 +18,11 @@
******************************************************************************/
package io.github.dsheirer.record;
import io.github.dsheirer.audio.AudioPacket;
import io.github.dsheirer.audio.IAudioPacketListener;
import io.github.dsheirer.channel.metadata.Metadata;
import io.github.dsheirer.module.Module;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import io.github.dsheirer.util.ThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,12 +38,12 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public abstract class AudioRecorder extends Module implements Listener<AudioPacket>, IAudioPacketListener
public abstract class AudioRecorder extends Module implements Listener<ReusableAudioPacket>, IAudioPacketListener
{
private final static Logger mLog = LoggerFactory.getLogger(AudioRecorder.class);
private LinkedBlockingQueue<AudioPacket> mAudioPacketQueue = new LinkedBlockingQueue<>(500);
private List<AudioPacket> mPacketsToProcess = new ArrayList<>();
private LinkedBlockingQueue<ReusableAudioPacket> mAudioPacketQueue = new LinkedBlockingQueue<>(500);
private List<ReusableAudioPacket> mPacketsToProcess = new ArrayList<>();
private FileOutputStream mFileOutputStream;
private AtomicBoolean mRunning = new AtomicBoolean();
@@ -108,12 +108,12 @@ public abstract class AudioRecorder extends Module implements Listener<AudioPack
}
/**
* Implements the IAudioPacketListener interface and simply redirects to the Listener<AudioPacket> interface.
* Implements the IAudioPacketListener interface and simply redirects to the Listener<ReusableAudioPacket> interface.
* This is necessary since you can't have multiple methods with the same erasure (ie Listener<xxx>) in the
* parent module class.
*/
@Override
public Listener<AudioPacket> getAudioPacketListener()
public Listener<ReusableAudioPacket> getAudioPacketListener()
{
return this;
}
@@ -122,7 +122,7 @@ public abstract class AudioRecorder extends Module implements Listener<AudioPack
* Processes the audio packet and captures the latest Metadata for the recording for easy access.
*/
@Override
public void receive(AudioPacket audioPacket)
public void receive(ReusableAudioPacket audioPacket)
{
if(mRunning.get())
{
@@ -140,8 +140,13 @@ public abstract class AudioRecorder extends Module implements Listener<AudioPack
{
mLog.error("recorder buffer overflow - stopping recorder [" + getPath().toString() + "]");
stop();
audioPacket.decrementUserCount();
}
}
else
{
audioPacket.decrementUserCount();
}
}
/**
@@ -189,7 +194,7 @@ public abstract class AudioRecorder extends Module implements Listener<AudioPack
/**
* Records the list of audio packets in the sub-class specific audio format.
*/
protected abstract void record(List<AudioPacket> audioPackets) throws IOException;
protected abstract void record(List<ReusableAudioPacket> audioPackets) throws IOException;
/**
* Starts this recorder as a scheduled thread running under the executor argument
@@ -234,12 +239,14 @@ public abstract class AudioRecorder extends Module implements Listener<AudioPack
{
record(mPacketsToProcess);
for(AudioPacket packet : mPacketsToProcess)
for(ReusableAudioPacket packet : mPacketsToProcess)
{
if(packet.getType() == AudioPacket.Type.AUDIO)
if(packet.getType() == ReusableAudioPacket.Type.AUDIO)
{
mSampleCount += packet.getAudioBuffer().getSamples().length;
mSampleCount += packet.getAudioSamples().length;
}
packet.decrementUserCount();
}
}
catch(IOException ioe)

View File

@@ -16,13 +16,13 @@
*/
package io.github.dsheirer.record;
import io.github.dsheirer.audio.AudioPacket;
import io.github.dsheirer.channel.metadata.Metadata;
import io.github.dsheirer.properties.SystemProperties;
import io.github.dsheirer.record.wave.AudioPacketWaveRecorder;
import io.github.dsheirer.record.wave.ComplexBufferWaveRecorder;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.OverflowableTransferQueue;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import io.github.dsheirer.sample.real.IOverflowListener;
import io.github.dsheirer.util.ThreadPool;
import org.slf4j.Logger;
@@ -37,7 +37,7 @@ import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class RecorderManager implements Listener<AudioPacket>
public class RecorderManager implements Listener<ReusableAudioPacket>
{
private static final Logger mLog = LoggerFactory.getLogger(RecorderManager.class);
@@ -45,7 +45,8 @@ public class RecorderManager implements Listener<AudioPacket>
public static final long IDLE_RECORDER_REMOVAL_THRESHOLD = 6000; //6 seconds
private Map<String,AudioPacketWaveRecorder> mRecorders = new HashMap<>();
private OverflowableTransferQueue<AudioPacket> mAudioPacketQueue = new OverflowableTransferQueue<>(1000, 100);
private OverflowableTransferQueue<ReusableAudioPacket> mAudioPacketQueue = new OverflowableTransferQueue<>(1000, 100);
private List<ReusableAudioPacket> mAudioPackets = new ArrayList<>();
private ScheduledFuture<?> mBufferProcessorFuture;
private boolean mCanStartNewRecorders = true;
@@ -96,12 +97,16 @@ public class RecorderManager implements Listener<AudioPacket>
* @param audioPacket to process
*/
@Override
public void receive(AudioPacket audioPacket)
public void receive(ReusableAudioPacket audioPacket)
{
if(audioPacket.hasMetadata() && audioPacket.getMetadata().isRecordable())
{
mAudioPacketQueue.offer(audioPacket);
}
else
{
audioPacket.decrementUserCount();
}
}
/**
@@ -109,13 +114,11 @@ public class RecorderManager implements Listener<AudioPacket>
*/
private void processBuffers()
{
List<AudioPacket> audioPackets = new ArrayList<>();
mAudioPacketQueue.drainTo(mAudioPackets, 50);
mAudioPacketQueue.drainTo(audioPackets, 50);
while(!audioPackets.isEmpty())
while(!mAudioPackets.isEmpty())
{
for(AudioPacket audioPacket : audioPackets)
for(ReusableAudioPacket audioPacket : mAudioPackets)
{
String identifier = audioPacket.getMetadata().getUniqueIdentifier();
@@ -123,17 +126,18 @@ public class RecorderManager implements Listener<AudioPacket>
{
AudioPacketWaveRecorder recorder = mRecorders.get(identifier);
if(audioPacket.getType() == AudioPacket.Type.AUDIO)
if(audioPacket.getType() == ReusableAudioPacket.Type.AUDIO)
{
audioPacket.incrementUserCount();
recorder.receive(audioPacket);
}
else if(audioPacket.getType() == AudioPacket.Type.END)
else if(audioPacket.getType() == ReusableAudioPacket.Type.END)
{
AudioPacketWaveRecorder finished = mRecorders.remove(identifier);
finished.stop();
}
}
else if(audioPacket.getType() == AudioPacket.Type.AUDIO)
else if(audioPacket.getType() == ReusableAudioPacket.Type.AUDIO)
{
if(mCanStartNewRecorders)
{
@@ -147,6 +151,7 @@ public class RecorderManager implements Listener<AudioPacket>
recorder.start();
audioPacket.incrementUserCount();
recorder.receive(audioPacket);
mRecorders.put(identifier, recorder);
}
@@ -164,10 +169,12 @@ public class RecorderManager implements Listener<AudioPacket>
}
}
}
audioPacket.decrementUserCount();
}
audioPackets.clear();
mAudioPacketQueue.drainTo(audioPackets, 50);
mAudioPackets.clear();
mAudioPacketQueue.drainTo(mAudioPackets, 50);
}
}
@@ -194,7 +201,7 @@ public class RecorderManager implements Listener<AudioPacket>
/**
* Constructs a file name and path for an audio recording
*/
private String getFilePrefix(AudioPacket packet)
private String getFilePrefix(ReusableAudioPacket packet)
{
StringBuilder sb = new StringBuilder();

View File

@@ -18,9 +18,9 @@
******************************************************************************/
package io.github.dsheirer.record.mp3;
import io.github.dsheirer.audio.AudioPacket;
import io.github.dsheirer.audio.convert.MP3AudioConverter;
import io.github.dsheirer.record.AudioRecorder;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -55,7 +55,7 @@ public class MP3Recorder extends AudioRecorder
}
@Override
protected void record(List<AudioPacket> audioPackets) throws IOException
protected void record(List<ReusableAudioPacket> audioPackets) throws IOException
{
OutputStream outputStream = getOutputStream();
@@ -92,7 +92,7 @@ public class MP3Recorder extends AudioRecorder
* writes the ID3 tags to the output stream.
* @param audioPackets
*/
private void processMetadata(List<AudioPacket> audioPackets)
private void processMetadata(List<ReusableAudioPacket> audioPackets)
{
//TODO: detect metadata changes and write out ID3 tags to the MP3 stream
}

View File

@@ -1,69 +0,0 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 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.record.wave;
import io.github.dsheirer.audio.AudioPacket;
import io.github.dsheirer.audio.IAudioPacketListener;
import io.github.dsheirer.channel.metadata.Metadata;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.real.RealBuffer;
import java.io.IOException;
import java.nio.file.Path;
public class AudioPacketMonoWaveReader extends MonoWaveReader implements Listener<RealBuffer>
{
private Metadata mMetadata = new Metadata();
private IAudioPacketListener mListener;
/**
* Reads wav files and converts the contents to AudioPackets.
*
* @param path of file to read containing 8kHz, 16-bit signed litte endian PCM audio
* @param realTime causes the read() method to read and dispatch audio packets in real time. Note: when using
* real time playback, the thread calling the read() method will sleep as needed to effect real-time playback,
* therefore you should invoke read() on a separate thread if this will impact the calling thread.
* @throws IOException on any file IO errors
*/
public AudioPacketMonoWaveReader(Path path, boolean realTime) throws IOException
{
super(path, realTime);
setListener(this);
}
public void setMetadata(Metadata metadata)
{
mMetadata = metadata;
}
public void setListener(IAudioPacketListener listener)
{
mListener = listener;
}
@Override
public void receive(RealBuffer realBuffer)
{
if(mListener != null)
{
mListener.getAudioPacketListener().receive(new AudioPacket(realBuffer.getSamples(), mMetadata));
}
}
}

View File

@@ -17,11 +17,12 @@
package io.github.dsheirer.record.wave;
import io.github.dsheirer.audio.AudioFormats;
import io.github.dsheirer.audio.AudioPacket;
import io.github.dsheirer.channel.metadata.Metadata;
import io.github.dsheirer.module.Module;
import io.github.dsheirer.sample.ConversionUtils;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.OverflowableReusableBufferTransferQueue;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import io.github.dsheirer.util.ThreadPool;
import io.github.dsheirer.util.TimeStamp;
import org.slf4j.Logger;
@@ -31,7 +32,6 @@ import javax.sound.sampled.AudioFormat;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -39,7 +39,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
/**
* WAVE audio recorder module for recording audio buffers to a wave file
*/
public class AudioPacketWaveRecorder extends Module implements Listener<AudioPacket>
public class AudioPacketWaveRecorder extends Module implements Listener<ReusableAudioPacket>
{
private final static Logger mLog = LoggerFactory.getLogger(AudioPacketWaveRecorder.class);
@@ -48,9 +48,10 @@ public class AudioPacketWaveRecorder extends Module implements Listener<AudioPac
private Path mFile;
private AudioFormat mAudioFormat;
private OverflowableReusableBufferTransferQueue<ReusableAudioPacket> mTransferQueue =
new OverflowableReusableBufferTransferQueue<>(500, 100);
private BufferProcessor mBufferProcessor;
private ScheduledFuture<?> mProcessorHandle;
private LinkedBlockingQueue<AudioPacket> mAudioPackets = new LinkedBlockingQueue<>(500);
private Metadata mMetadata;
private long mLastBufferReceived;
@@ -153,20 +154,17 @@ public class AudioPacketWaveRecorder extends Module implements Listener<AudioPac
* Primary input method for receiving audio packets to enqueue for later writing to the wav file.
*/
@Override
public void receive(AudioPacket audioPacket)
public void receive(ReusableAudioPacket audioPacket)
{
if(mRunning.get())
{
boolean success = mAudioPackets.offer(audioPacket);
if(!success)
{
mLog.error("recorder buffer overflow - purging [" + mFile.toFile().getAbsolutePath() + "]");
mAudioPackets.clear();
}
mTransferQueue.offer(audioPacket);
mLastBufferReceived = System.currentTimeMillis();
}
else
{
audioPacket.decrementUserCount();
}
}
@Override
@@ -188,17 +186,23 @@ public class AudioPacketWaveRecorder extends Module implements Listener<AudioPac
*/
private void write() throws IOException
{
AudioPacket audioPacket = mAudioPackets.poll();
ReusableAudioPacket audioPacket = mTransferQueue.poll();
while(audioPacket != null && audioPacket.getType() == AudioPacket.Type.AUDIO)
while(audioPacket != null && audioPacket.getType() == ReusableAudioPacket.Type.AUDIO)
{
if(audioPacket.hasMetadata())
{
mMetadata = audioPacket.getMetadata();
}
mWriter.writeData(ConversionUtils.convertToSigned16BitSamples(audioPacket.getAudioBuffer()));
audioPacket = mAudioPackets.poll();
mWriter.writeData(ConversionUtils.convertToSigned16BitSamples(audioPacket.getAudioSamples()));
audioPacket.decrementUserCount();
audioPacket = mTransferQueue.poll();
}
if(audioPacket != null)
{
audioPacket.decrementUserCount();
}
}
@@ -216,7 +220,7 @@ public class AudioPacketWaveRecorder extends Module implements Listener<AudioPac
catch(IOException ioe)
{
/* Stop this module if/when we get an IO exception */
mAudioPackets.clear();
mTransferQueue.clear();
stop();
mLog.error("IO Exception while trying to write to the wave writer", ioe);

View File

@@ -0,0 +1,151 @@
/*******************************************************************************
* 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.channel.metadata.Metadata;
/**
* Reusable audio packet that carries audio sample data, associated metadata and a type to indicate if this
* packet contains audio data or is an end-audio packet.
*/
public class ReusableAudioPacket extends AbstractReusableBuffer
{
private Type mType = Type.AUDIO;
private Metadata mMetadata;
private float[] mAudioSamples;
/**
* Constructs a reusable audio packet. This constructor is package private and should only be used by
* a reusable audio packet queue.
*
* @param bufferDisposedListener to be notified when all users have released the packet
* @param length of the sample buffer
*/
ReusableAudioPacket(IReusableBufferDisposedListener bufferDisposedListener, int length)
{
super(bufferDisposedListener);
resize(length);
}
/**
* Indicates if this packet has associated metadata
*/
public boolean hasMetadata()
{
return mMetadata != null;
}
/**
* Associated audio metadata
*/
public Metadata getMetadata()
{
return mMetadata;
}
/**
* Sets the metadata associated with this audio packet.
*/
public void setMetadata(Metadata metadata)
{
mMetadata = metadata;
}
/**
* Indicates the type of audio packet
*/
public Type getType()
{
return mType;
}
/**
* Sets the type of audio packet.
*
* This method is package private and is used by the reusable audio packet queue.
*/
void setType(Type type)
{
mType = type;
}
/**
* PCM 8 kHz audio samples
*/
public float[] getAudioSamples()
{
return mAudioSamples;
}
/**
* Indicates if this audio packet contains audio samples.
*/
public boolean hasAudioSamples()
{
return mType != null && mType == Type.AUDIO && mAudioSamples != null;
}
/**
* Resizes the internal audio sample buffer.
*/
public void resize(int length)
{
if(mAudioSamples == null || mAudioSamples.length != length)
{
mAudioSamples = new float[length];
}
}
/**
* Loads a copy of the float sample data from the reusable buffer into this audio packet, resizing the
* internal audio sample buffer as necessary.
*
* @param reusableBuffer to load audio sample data from
*/
public void loadAudioFrom(ReusableBuffer reusableBuffer)
{
if(reusableBuffer.getSamples().length != mAudioSamples.length)
{
resize(reusableBuffer.getSamples().length);
}
System.arraycopy(reusableBuffer.getSamples(), 0, mAudioSamples, 0, reusableBuffer.getSamples().length);
}
/**
* Loads the audio sample array into this buffer.
*
* Note: this method is used for compatibility with legacy audio converters that have not been updated to
* use reusable audio packets and is therefore deprecated and will be removed once converters (ie JMBE) have
* been updated.
*
* @param audio to load
*/
@Deprecated
public void loadAudioFrom(float[] audio)
{
mAudioSamples = audio;
}
/**
* Audio Packet Type
*/
public enum Type
{
AUDIO,
END;
}
}

View File

@@ -0,0 +1,76 @@
/*******************************************************************************
* 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;
/**
* Queue for creating and reusing/managing reusable audio packets.
*/
public class ReusableAudioPacketQueue extends AbstractReusableBufferQueue<ReusableAudioPacket>
{
private final static Logger mLog = LoggerFactory.getLogger(ReusableAudioPacketQueue.class);
public ReusableAudioPacketQueue(String debugName)
{
super(debugName);
}
/**
* Provides 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 ReusableAudioPacket getBuffer(int size)
{
ReusableAudioPacket buffer = getRecycledBuffer();
if(buffer == null)
{
buffer = new ReusableAudioPacket(this, size);
incrementBufferCount();
buffer.setDebugName("Owner:" + getDebugName());
mLog.debug("Buffer Created - Count:" + getBufferCount() + (getDebugName() != null ? " [" + getDebugName() + "]" : ""));
}
buffer.setType(ReusableAudioPacket.Type.AUDIO);
buffer.resize(size);
return buffer;
}
public ReusableAudioPacket getEndAudioBuffer()
{
ReusableAudioPacket buffer = getRecycledBuffer();
if(buffer == null)
{
buffer = new ReusableAudioPacket(this, 0);
incrementBufferCount();
buffer.setDebugName("Owner:" + getDebugName());
mLog.debug("Buffer Created - Count:" + getBufferCount() + (getDebugName() != null ? " [" + getDebugName() + "]" : ""));
}
buffer.setType(ReusableAudioPacket.Type.END);
return buffer;
}
}

View File

@@ -20,7 +20,7 @@ import io.github.dsheirer.sample.Listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ReusableBufferBroadcaster<T extends ReusableBuffer> extends Broadcaster<T>
public class ReusableBufferBroadcaster<T extends AbstractReusableBuffer> extends Broadcaster<T>
{
private final static Logger mLog = LoggerFactory.getLogger(ReusableBufferBroadcaster.class);