diff --git a/src/main/java/io/github/dsheirer/audio/AudioManager.java b/src/main/java/io/github/dsheirer/audio/AudioManager.java index c0f1d877..e48cf36a 100644 --- a/src/main/java/io/github/dsheirer/audio/AudioManager.java +++ b/src/main/java/io/github/dsheirer/audio/AudioManager.java @@ -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, IAudioController +public class AudioManager implements Listener, IAudioController { private static final Logger mLog = LoggerFactory.getLogger(AudioManager.class); @@ -58,7 +59,7 @@ public class AudioManager implements Listener, IAudioController public static final AudioEvent CONFIGURATION_CHANGE_COMPLETE = new AudioEvent(AudioEvent.Type.AUDIO_CONFIGURATION_CHANGE_COMPLETE, null); - private LinkedTransferQueue mAudioPacketQueue = new LinkedTransferQueue<>(); + private LinkedTransferQueue mAudioPacketQueue = new LinkedTransferQueue<>(); private Map mChannelConnectionMap = new HashMap<>(); private List mAudioOutputConnections = new ArrayList<>(); private AudioOutputConnection mLowestPriorityConnection; @@ -170,7 +171,7 @@ public class AudioManager implements Listener, 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, 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, IAudioController public class AudioPacketProcessor implements Runnable { + private List mPackets = new ArrayList<>(); + @Override public void run() { @@ -419,14 +422,12 @@ public class AudioManager implements Listener, IAudioController if(mAudioPacketQueue != null) { - List packets = new ArrayList(); + 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, 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, IAudioController mAudioOutput = audioOutput; } - public void receive(AudioPacket packet) + public void receive(ReusableAudioPacket packet) { if(packet.hasMetadata() && packet.getMetadata().getMetadataID() == mChannelMetadataID) { diff --git a/src/main/java/io/github/dsheirer/audio/AudioModule.java b/src/main/java/io/github/dsheirer/audio/AudioModule.java index 5836d4bc..960ad21e 100644 --- a/src/main/java/io/github/dsheirer/audio/AudioModule.java +++ b/src/main/java/io/github/dsheirer/audio/AudioModule.java @@ -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 { 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 mAudioPacketListener; + private Listener 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 listener) + public void setAudioPacketListener(Listener 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; diff --git a/src/main/java/io/github/dsheirer/audio/AudioPacket.java b/src/main/java/io/github/dsheirer/audio/AudioPacket.java deleted file mode 100644 index a57f2bdb..00000000 --- a/src/main/java/io/github/dsheirer/audio/AudioPacket.java +++ /dev/null @@ -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 - * - ******************************************************************************/ -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; - } -} diff --git a/src/main/java/io/github/dsheirer/audio/AudioPacketRateCounter.java b/src/main/java/io/github/dsheirer/audio/AudioPacketRateCounter.java deleted file mode 100644 index fbc7ea0d..00000000 --- a/src/main/java/io/github/dsheirer/audio/AudioPacketRateCounter.java +++ /dev/null @@ -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 -{ - private final static Logger mLog = - LoggerFactory.getLogger( AudioPacketRateCounter.class ); - - private Listener 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 listener ) - { - mListener = listener; - } -} diff --git a/src/main/java/io/github/dsheirer/audio/AudioUtils.java b/src/main/java/io/github/dsheirer/audio/AudioUtils.java index 08b4d3d8..1eca2361 100644 --- a/src/main/java/io/github/dsheirer/audio/AudioUtils.java +++ b/src/main/java/io/github/dsheirer/audio/AudioUtils.java @@ -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 + * You should have received a copy of the GNU General Public License along with this program. + * If not, see * ******************************************************************************/ 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 audioPackets) + public static byte[] convertTo16BitSamples(List 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) diff --git a/src/main/java/io/github/dsheirer/audio/IAudioPacketListener.java b/src/main/java/io/github/dsheirer/audio/IAudioPacketListener.java index 96833a73..76452704 100644 --- a/src/main/java/io/github/dsheirer/audio/IAudioPacketListener.java +++ b/src/main/java/io/github/dsheirer/audio/IAudioPacketListener.java @@ -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 getAudioPacketListener(); + public Listener getAudioPacketListener(); } diff --git a/src/main/java/io/github/dsheirer/audio/IAudioPacketProvider.java b/src/main/java/io/github/dsheirer/audio/IAudioPacketProvider.java index b6623cee..7fc00a58 100644 --- a/src/main/java/io/github/dsheirer/audio/IAudioPacketProvider.java +++ b/src/main/java/io/github/dsheirer/audio/IAudioPacketProvider.java @@ -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 listener ); + public void setAudioPacketListener( Listener listener ); public void removeAudioPacketListener(); } diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastModel.java b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastModel.java index fc7c9eac..986306fd 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastModel.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/BroadcastModel.java @@ -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 +public class BroadcastModel extends AbstractTableModel implements Listener { private final static Logger mLog = LoggerFactory.getLogger(BroadcastModel.class); @@ -276,7 +276,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener