mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-14 17:47:32 -05:00
[WIP] Updates AudioPackets to be ReusableAudioPackets.
This commit is contained in:
@@ -24,6 +24,7 @@ import io.github.dsheirer.audio.output.StereoAudioOutput;
|
|||||||
import io.github.dsheirer.properties.SystemProperties;
|
import io.github.dsheirer.properties.SystemProperties;
|
||||||
import io.github.dsheirer.sample.Broadcaster;
|
import io.github.dsheirer.sample.Broadcaster;
|
||||||
import io.github.dsheirer.sample.Listener;
|
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.MixerChannel;
|
||||||
import io.github.dsheirer.source.mixer.MixerChannelConfiguration;
|
import io.github.dsheirer.source.mixer.MixerChannelConfiguration;
|
||||||
import io.github.dsheirer.source.mixer.MixerManager;
|
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.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
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);
|
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 =
|
public static final AudioEvent CONFIGURATION_CHANGE_COMPLETE =
|
||||||
new AudioEvent(AudioEvent.Type.AUDIO_CONFIGURATION_CHANGE_COMPLETE, null);
|
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 Map<Integer,AudioOutputConnection> mChannelConnectionMap = new HashMap<>();
|
||||||
private List<AudioOutputConnection> mAudioOutputConnections = new ArrayList<>();
|
private List<AudioOutputConnection> mAudioOutputConnections = new ArrayList<>();
|
||||||
private AudioOutputConnection mLowestPriorityConnection;
|
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.
|
* Primary ingest point for audio produced by all decoding channels, for distribution to audio playback devices.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public synchronized void receive(AudioPacket packet)
|
public synchronized void receive(ReusableAudioPacket packet)
|
||||||
{
|
{
|
||||||
mAudioPacketQueue.add(packet);
|
mAudioPacketQueue.add(packet);
|
||||||
}
|
}
|
||||||
@@ -362,7 +363,7 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
|||||||
* @param audioPacket from a decoding channel source
|
* @param audioPacket from a decoding channel source
|
||||||
* @return an audio output connection or null
|
* @return an audio output connection or null
|
||||||
*/
|
*/
|
||||||
private AudioOutputConnection getConnection(AudioPacket audioPacket)
|
private AudioOutputConnection getConnection(ReusableAudioPacket audioPacket)
|
||||||
{
|
{
|
||||||
int channelMetadataID = audioPacket.getMetadata().getMetadataID();
|
int channelMetadataID = audioPacket.getMetadata().getMetadataID();
|
||||||
|
|
||||||
@@ -410,6 +411,8 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
|||||||
|
|
||||||
public class AudioPacketProcessor implements Runnable
|
public class AudioPacketProcessor implements Runnable
|
||||||
{
|
{
|
||||||
|
private List<ReusableAudioPacket> mPackets = new ArrayList<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
@@ -419,14 +422,12 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
|||||||
|
|
||||||
if(mAudioPacketQueue != null)
|
if(mAudioPacketQueue != null)
|
||||||
{
|
{
|
||||||
List<AudioPacket> packets = new ArrayList<AudioPacket>();
|
mAudioPacketQueue.drainTo(mPackets);
|
||||||
|
|
||||||
mAudioPacketQueue.drainTo(packets);
|
for(ReusableAudioPacket packet : mPackets)
|
||||||
|
|
||||||
for(AudioPacket packet : packets)
|
|
||||||
{
|
{
|
||||||
/* Don't process any packet's marked as do not monitor */
|
/* 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);
|
AudioOutputConnection connection = getConnection(packet);
|
||||||
|
|
||||||
@@ -434,13 +435,29 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
|||||||
{
|
{
|
||||||
connection.receive(packet);
|
connection.receive(packet);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
packet.decrementUserCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
packet.decrementUserCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mPackets.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
mLog.error("Encountered error while processing audio packets", 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;
|
mAudioOutput = audioOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void receive(AudioPacket packet)
|
public void receive(ReusableAudioPacket packet)
|
||||||
{
|
{
|
||||||
if(packet.hasMetadata() && packet.getMetadata().getMetadataID() == mChannelMetadataID)
|
if(packet.hasMetadata() && packet.getMetadata().getMetadataID() == mChannelMetadataID)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import io.github.dsheirer.dsp.filter.fir.remez.RemezFIRFilterDesigner;
|
|||||||
import io.github.dsheirer.module.Module;
|
import io.github.dsheirer.module.Module;
|
||||||
import io.github.dsheirer.sample.Listener;
|
import io.github.dsheirer.sample.Listener;
|
||||||
import io.github.dsheirer.sample.buffer.IReusableBufferListener;
|
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 io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -41,6 +43,7 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab
|
|||||||
ISquelchStateListener, Listener<ReusableBuffer>
|
ISquelchStateListener, Listener<ReusableBuffer>
|
||||||
{
|
{
|
||||||
protected static final Logger mLog = LoggerFactory.getLogger(AudioModule.class);
|
protected static final Logger mLog = LoggerFactory.getLogger(AudioModule.class);
|
||||||
|
private ReusableAudioPacketQueue mAudioPacketQueue = new ReusableAudioPacketQueue("AudioModule");
|
||||||
|
|
||||||
private static float[] sHighPassFilterCoefficients;
|
private static float[] sHighPassFilterCoefficients;
|
||||||
|
|
||||||
@@ -73,11 +76,12 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab
|
|||||||
private RealFIRFilter2 mHighPassFilter = new RealFIRFilter2(sHighPassFilterCoefficients);
|
private RealFIRFilter2 mHighPassFilter = new RealFIRFilter2(sHighPassFilterCoefficients);
|
||||||
private SquelchStateListener mSquelchStateListener = new SquelchStateListener();
|
private SquelchStateListener mSquelchStateListener = new SquelchStateListener();
|
||||||
private SquelchState mSquelchState = SquelchState.SQUELCH;
|
private SquelchState mSquelchState = SquelchState.SQUELCH;
|
||||||
private Listener<AudioPacket> mAudioPacketListener;
|
private Listener<ReusableAudioPacket> mAudioPacketListener;
|
||||||
private Metadata mMetadata;
|
private Metadata mMetadata;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an Audio Module.
|
* Creates an Audio Module.
|
||||||
|
*
|
||||||
* @param metadata to use for audio packets produced by this audio module.
|
* @param metadata to use for audio packets produced by this audio module.
|
||||||
*/
|
*/
|
||||||
public AudioModule(Metadata metadata)
|
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 */
|
/* Issue an end-audio packet in case a recorder is still rolling */
|
||||||
if(mAudioPacketListener != null)
|
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
|
@Override
|
||||||
public void setAudioPacketListener(Listener<AudioPacket> listener)
|
public void setAudioPacketListener(Listener<ReusableAudioPacket> listener)
|
||||||
{
|
{
|
||||||
mAudioPacketListener = listener;
|
mAudioPacketListener = listener;
|
||||||
}
|
}
|
||||||
@@ -137,8 +144,14 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab
|
|||||||
if(mAudioPacketListener != null && mSquelchState == SquelchState.UNSQUELCH)
|
if(mAudioPacketListener != null && mSquelchState == SquelchState.UNSQUELCH)
|
||||||
{
|
{
|
||||||
ReusableBuffer highPassFiltered = mHighPassFilter.filter(reusableBuffer);
|
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();
|
highPassFiltered.decrementUserCount();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -164,7 +177,9 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab
|
|||||||
{
|
{
|
||||||
if(state == SquelchState.SQUELCH && mAudioPacketListener != null)
|
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;
|
mSquelchState = state;
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +1,22 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* sdrtrunk
|
* sdr-trunk
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
|
||||||
* it under the terms of the GNU General Public License as published by
|
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* later version.
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
* 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
|
* You should have received a copy of the GNU General Public License along with this program.
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package io.github.dsheirer.audio;
|
package io.github.dsheirer.audio;
|
||||||
|
|
||||||
import io.github.dsheirer.sample.ConversionUtils;
|
import io.github.dsheirer.sample.ConversionUtils;
|
||||||
|
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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
|
* 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();
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
for(AudioPacket packet: audioPackets)
|
for(ReusableAudioPacket audioPacket: audioPackets)
|
||||||
{
|
{
|
||||||
//Converting from 32-bit floats to signed 16-bit samples
|
//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());
|
stream.write(buffer.array());
|
||||||
|
audioPacket.decrementUserCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(IOException e)
|
catch(IOException e)
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package io.github.dsheirer.audio;
|
package io.github.dsheirer.audio;
|
||||||
|
|
||||||
import io.github.dsheirer.sample.Listener;
|
import io.github.dsheirer.sample.Listener;
|
||||||
|
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||||
|
|
||||||
public interface IAudioPacketListener
|
public interface IAudioPacketListener
|
||||||
{
|
{
|
||||||
public Listener<AudioPacket> getAudioPacketListener();
|
public Listener<ReusableAudioPacket> getAudioPacketListener();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
package io.github.dsheirer.audio;
|
package io.github.dsheirer.audio;
|
||||||
|
|
||||||
import io.github.dsheirer.sample.Listener;
|
import io.github.dsheirer.sample.Listener;
|
||||||
|
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||||
|
|
||||||
public interface IAudioPacketProvider
|
public interface IAudioPacketProvider
|
||||||
{
|
{
|
||||||
public void setAudioPacketListener( Listener<AudioPacket> listener );
|
public void setAudioPacketListener( Listener<ReusableAudioPacket> listener );
|
||||||
public void removeAudioPacketListener();
|
public void removeAudioPacketListener();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,12 +19,12 @@
|
|||||||
package io.github.dsheirer.audio.broadcast;
|
package io.github.dsheirer.audio.broadcast;
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
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.channel.metadata.Metadata;
|
||||||
import io.github.dsheirer.icon.IconManager;
|
import io.github.dsheirer.icon.IconManager;
|
||||||
import io.github.dsheirer.properties.SystemProperties;
|
import io.github.dsheirer.properties.SystemProperties;
|
||||||
import io.github.dsheirer.sample.Broadcaster;
|
import io.github.dsheirer.sample.Broadcaster;
|
||||||
import io.github.dsheirer.sample.Listener;
|
import io.github.dsheirer.sample.Listener;
|
||||||
|
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||||
import io.github.dsheirer.util.ThreadPool;
|
import io.github.dsheirer.util.ThreadPool;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -46,7 +46,7 @@ import java.util.function.Consumer;
|
|||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
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);
|
private final static Logger mLog = LoggerFactory.getLogger(BroadcastModel.class);
|
||||||
|
|
||||||
@@ -276,7 +276,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void receive(AudioPacket audioPacket)
|
public void receive(ReusableAudioPacket audioPacket)
|
||||||
{
|
{
|
||||||
if(audioPacket.hasMetadata() && audioPacket.getMetadata().isStreamable())
|
if(audioPacket.hasMetadata() && audioPacket.getMetadata().isStreamable())
|
||||||
{
|
{
|
||||||
@@ -284,11 +284,14 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
{
|
{
|
||||||
if(mBroadcasterMap.containsKey(channel.getChannelName()))
|
if(mBroadcasterMap.containsKey(channel.getChannelName()))
|
||||||
{
|
{
|
||||||
|
audioPacket.incrementUserCount();
|
||||||
mStreamManager.receive(audioPacket);
|
mStreamManager.receive(audioPacket);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
audioPacket.decrementUserCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -18,9 +18,9 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package io.github.dsheirer.audio.broadcast;
|
package io.github.dsheirer.audio.broadcast;
|
||||||
|
|
||||||
import io.github.dsheirer.audio.AudioPacket;
|
|
||||||
import io.github.dsheirer.record.AudioRecorder;
|
import io.github.dsheirer.record.AudioRecorder;
|
||||||
import io.github.dsheirer.sample.Listener;
|
import io.github.dsheirer.sample.Listener;
|
||||||
|
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||||
import io.github.dsheirer.util.ThreadPool;
|
import io.github.dsheirer.util.ThreadPool;
|
||||||
import io.github.dsheirer.util.TimeStamp;
|
import io.github.dsheirer.util.TimeStamp;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -37,7 +37,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
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 final static Logger mLog = LoggerFactory.getLogger(StreamManager.class);
|
||||||
private static final long MAXIMUM_RECORDER_LIFESPAN_MILLIS = 30000; //30 seconds
|
private static final long MAXIMUM_RECORDER_LIFESPAN_MILLIS = 30000; //30 seconds
|
||||||
@@ -115,7 +115,7 @@ public class StreamManager implements Listener<AudioPacket>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void receive(AudioPacket audioPacket)
|
public void receive(ReusableAudioPacket audioPacket)
|
||||||
{
|
{
|
||||||
if(mRunning.get() && audioPacket.hasMetadata())
|
if(mRunning.get() && audioPacket.hasMetadata())
|
||||||
{
|
{
|
||||||
@@ -123,9 +123,9 @@ public class StreamManager implements Listener<AudioPacket>
|
|||||||
{
|
{
|
||||||
int channelMetadataID = audioPacket.getMetadata().getMetadataID();
|
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))
|
if(mStreamRecorders.containsKey(channelMetadataID))
|
||||||
{
|
{
|
||||||
@@ -133,6 +133,7 @@ public class StreamManager implements Listener<AudioPacket>
|
|||||||
|
|
||||||
if(recorder != null)
|
if(recorder != null)
|
||||||
{
|
{
|
||||||
|
audioPacket.incrementUserCount();
|
||||||
recorder.receive(audioPacket);
|
recorder.receive(audioPacket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,11 +142,12 @@ public class StreamManager implements Listener<AudioPacket>
|
|||||||
AudioRecorder recorder = BroadcastFactory.getAudioRecorder(getTemporaryRecordingPath(),
|
AudioRecorder recorder = BroadcastFactory.getAudioRecorder(getTemporaryRecordingPath(),
|
||||||
mBroadcastFormat);
|
mBroadcastFormat);
|
||||||
recorder.start();
|
recorder.start();
|
||||||
|
audioPacket.incrementUserCount();
|
||||||
recorder.receive(audioPacket);
|
recorder.receive(audioPacket);
|
||||||
mStreamRecorders.put(channelMetadataID, recorder);
|
mStreamRecorders.put(channelMetadataID, recorder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(type == AudioPacket.Type.END)
|
else if(type == ReusableAudioPacket.Type.END)
|
||||||
{
|
{
|
||||||
removeRecorder(channelMetadataID);
|
removeRecorder(channelMetadataID);
|
||||||
}
|
}
|
||||||
@@ -155,6 +157,8 @@ public class StreamManager implements Listener<AudioPacket>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
audioPacket.decrementUserCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package io.github.dsheirer.audio.convert;
|
package io.github.dsheirer.audio.convert;
|
||||||
|
|
||||||
import io.github.dsheirer.audio.AudioPacket;
|
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||||
|
|
||||||
import java.util.List;
|
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.
|
* 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
|
* Finalizes audio conversion by fully converting any partial frames left in the buffer and returning the
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
package io.github.dsheirer.audio.convert;
|
package io.github.dsheirer.audio.convert;
|
||||||
|
|
||||||
import io.github.dsheirer.audio.AudioFormats;
|
import io.github.dsheirer.audio.AudioFormats;
|
||||||
import io.github.dsheirer.audio.AudioPacket;
|
|
||||||
import io.github.dsheirer.audio.AudioUtils;
|
import io.github.dsheirer.audio.AudioUtils;
|
||||||
|
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||||
import net.sourceforge.lame.lowlevel.LameEncoder;
|
import net.sourceforge.lame.lowlevel.LameEncoder;
|
||||||
import net.sourceforge.lame.mp3.Lame;
|
import net.sourceforge.lame.mp3.Lame;
|
||||||
import net.sourceforge.lame.mp3.MPEGMode;
|
import net.sourceforge.lame.mp3.MPEGMode;
|
||||||
@@ -54,7 +54,7 @@ public class MP3AudioConverter implements IAudioConverter
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] convert(List<AudioPacket> audioPackets)
|
public byte[] convert(List<ReusableAudioPacket> audioPackets)
|
||||||
{
|
{
|
||||||
mMP3Stream.reset();
|
mMP3Stream.reset();
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,9 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package io.github.dsheirer.audio.convert;
|
package io.github.dsheirer.audio.convert;
|
||||||
|
|
||||||
import io.github.dsheirer.audio.AudioPacket;
|
|
||||||
import io.github.dsheirer.record.mp3.MP3Recorder;
|
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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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 MP3AudioConverter mGenerator = new MP3AudioConverter(MP3Recorder.MP3_BIT_RATE, MP3Recorder.CONSTANT_BIT_RATE);
|
||||||
private byte[] mPreviousPartialFrameData;
|
private byte[] mPreviousPartialFrameData;
|
||||||
|
private ReusableAudioPacketQueue mAudioPacketQueue = new ReusableAudioPacketQueue("MP3 Silence Generator");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates MP3 audio silence frames
|
* Generates MP3 audio silence frames
|
||||||
@@ -44,9 +46,11 @@ public class MP3SilenceGenerator implements ISilenceGenerator
|
|||||||
public byte[] generate(long duration)
|
public byte[] generate(long duration)
|
||||||
{
|
{
|
||||||
int length = (int)(duration * 8); //8000 Hz sample rate
|
int length = (int)(duration * 8); //8000 Hz sample rate
|
||||||
float[] silence = new float[length];
|
ReusableAudioPacket silencePacket = mAudioPacketQueue.getBuffer(length);
|
||||||
AudioPacket silencePacket = new AudioPacket(silence, null);
|
Arrays.fill(silencePacket.getAudioSamples(), 0.0f);
|
||||||
List<AudioPacket> silencePackets = new ArrayList<>();
|
silencePacket.incrementUserCount();
|
||||||
|
|
||||||
|
List<ReusableAudioPacket> silencePackets = new ArrayList<>();
|
||||||
silencePackets.add(silencePacket);
|
silencePackets.add(silencePacket);
|
||||||
|
|
||||||
byte[] frameData = mGenerator.convert(silencePackets);
|
byte[] frameData = mGenerator.convert(silencePackets);
|
||||||
|
|||||||
@@ -19,10 +19,10 @@
|
|||||||
package io.github.dsheirer.audio.output;
|
package io.github.dsheirer.audio.output;
|
||||||
|
|
||||||
import io.github.dsheirer.audio.AudioEvent;
|
import io.github.dsheirer.audio.AudioEvent;
|
||||||
import io.github.dsheirer.audio.AudioPacket;
|
|
||||||
import io.github.dsheirer.channel.metadata.Metadata;
|
import io.github.dsheirer.channel.metadata.Metadata;
|
||||||
import io.github.dsheirer.sample.Broadcaster;
|
import io.github.dsheirer.sample.Broadcaster;
|
||||||
import io.github.dsheirer.sample.Listener;
|
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.MixerChannel;
|
||||||
import io.github.dsheirer.util.ThreadPool;
|
import io.github.dsheirer.util.ThreadPool;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -46,11 +46,11 @@ import java.util.concurrent.ScheduledFuture;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
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 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 mBufferStartThreshold;
|
||||||
private int mBufferStopThreshold;
|
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
|
* Converts the audio packet data into a byte buffer format appropriate for
|
||||||
* the underlying source data line.
|
* the underlying source data line.
|
||||||
*/
|
*/
|
||||||
protected abstract ByteBuffer convert(AudioPacket packet);
|
protected abstract ByteBuffer convert(ReusableAudioPacket packet);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Audio output channel name
|
* Audio output channel name
|
||||||
@@ -271,7 +271,7 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void receive(AudioPacket packet)
|
public void receive(ReusableAudioPacket packet)
|
||||||
{
|
{
|
||||||
if(mCanProcessAudio)
|
if(mCanProcessAudio)
|
||||||
{
|
{
|
||||||
@@ -281,11 +281,17 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
|
|||||||
|
|
||||||
mBuffer.add(packet);
|
mBuffer.add(packet);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
packet.decrementUserCount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BufferProcessor implements Runnable
|
public class BufferProcessor implements Runnable
|
||||||
{
|
{
|
||||||
private AtomicBoolean mProcessing = new AtomicBoolean();
|
private AtomicBoolean mProcessing = new AtomicBoolean();
|
||||||
|
private List<ReusableAudioPacket> mAudioPackets = new ArrayList<ReusableAudioPacket>();
|
||||||
|
|
||||||
|
|
||||||
public BufferProcessor()
|
public BufferProcessor()
|
||||||
{
|
{
|
||||||
@@ -300,13 +306,11 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
|
|||||||
* processor can run at any given time */
|
* processor can run at any given time */
|
||||||
if(mProcessing.compareAndSet(false, true))
|
if(mProcessing.compareAndSet(false, true))
|
||||||
{
|
{
|
||||||
List<AudioPacket> packets = new ArrayList<AudioPacket>();
|
mBuffer.drainTo(mAudioPackets);
|
||||||
|
|
||||||
mBuffer.drainTo(packets);
|
for(ReusableAudioPacket packet : mAudioPackets)
|
||||||
|
|
||||||
for(AudioPacket packet : packets)
|
|
||||||
{
|
{
|
||||||
if(packet.getType() == AudioPacket.Type.AUDIO)
|
if(packet.getType() == ReusableAudioPacket.Type.AUDIO)
|
||||||
{
|
{
|
||||||
broadcast(packet.getMetadata());
|
broadcast(packet.getMetadata());
|
||||||
|
|
||||||
@@ -338,8 +342,14 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
|
|||||||
|
|
||||||
updateTimestamp();
|
updateTimestamp();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
packet.decrementUserCount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mAudioPackets.clear();
|
||||||
|
|
||||||
checkStop();
|
checkStop();
|
||||||
|
|
||||||
mProcessing.set(false);
|
mProcessing.set(false);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
package io.github.dsheirer.audio.output;
|
package io.github.dsheirer.audio.output;
|
||||||
|
|
||||||
import io.github.dsheirer.audio.AudioFormats;
|
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 io.github.dsheirer.source.mixer.MixerChannel;
|
||||||
|
|
||||||
import javax.sound.sampled.Mixer;
|
import javax.sound.sampled.Mixer;
|
||||||
@@ -43,14 +43,16 @@ public class MonoAudioOutput extends AudioOutput
|
|||||||
/**
|
/**
|
||||||
* Converts the audio packet data into mono audio frames.
|
* 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 */
|
/* 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();
|
ShortBuffer shortBuffer = buffer.asShortBuffer();
|
||||||
|
|
||||||
@@ -58,10 +60,10 @@ public class MonoAudioOutput extends AudioOutput
|
|||||||
{
|
{
|
||||||
shortBuffer.put((short) (sample * Short.MAX_VALUE));
|
shortBuffer.put((short) (sample * Short.MAX_VALUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
packet.decrementUserCount();
|
||||||
|
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
package io.github.dsheirer.audio.output;
|
package io.github.dsheirer.audio.output;
|
||||||
|
|
||||||
import io.github.dsheirer.audio.AudioFormats;
|
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 io.github.dsheirer.source.mixer.MixerChannel;
|
||||||
|
|
||||||
import javax.sound.sampled.Mixer;
|
import javax.sound.sampled.Mixer;
|
||||||
@@ -45,15 +45,16 @@ public class StereoAudioOutput extends AudioOutput
|
|||||||
* channel containing the audio and the other channel containing zero
|
* channel containing the audio and the other channel containing zero
|
||||||
* valued (silent) samples.
|
* 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 */
|
/* Little-endian byte buffer */
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(samples.length * 4)
|
buffer = ByteBuffer.allocate(samples.length * 4).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
.order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
|
|
||||||
ShortBuffer shortBuffer = buffer.asShortBuffer();
|
ShortBuffer shortBuffer = buffer.asShortBuffer();
|
||||||
|
|
||||||
@@ -73,10 +74,10 @@ public class StereoAudioOutput extends AudioOutput
|
|||||||
shortBuffer.put((short) (sample * Short.MAX_VALUE));
|
shortBuffer.put((short) (sample * Short.MAX_VALUE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
packet.decrementUserCount();
|
||||||
|
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
package io.github.dsheirer.controller.channel;
|
package io.github.dsheirer.controller.channel;
|
||||||
|
|
||||||
import io.github.dsheirer.alias.AliasModel;
|
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.Attribute;
|
||||||
import io.github.dsheirer.channel.metadata.AttributeChangeRequest;
|
import io.github.dsheirer.channel.metadata.AttributeChangeRequest;
|
||||||
import io.github.dsheirer.channel.metadata.ChannelMetadataModel;
|
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.RecorderManager;
|
||||||
import io.github.dsheirer.record.RecorderType;
|
import io.github.dsheirer.record.RecorderType;
|
||||||
import io.github.dsheirer.sample.Listener;
|
import io.github.dsheirer.sample.Listener;
|
||||||
|
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||||
import io.github.dsheirer.source.Source;
|
import io.github.dsheirer.source.Source;
|
||||||
import io.github.dsheirer.source.SourceException;
|
import io.github.dsheirer.source.SourceException;
|
||||||
import io.github.dsheirer.source.SourceManager;
|
import io.github.dsheirer.source.SourceManager;
|
||||||
@@ -54,7 +54,7 @@ public class ChannelProcessingManager implements ChannelEventListener
|
|||||||
|
|
||||||
private Map<Integer,ProcessingChain> mProcessingChains = new HashMap<>();
|
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 List<Listener<Message>> mMessageListeners = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
private ChannelModel mChannelModel;
|
private ChannelModel mChannelModel;
|
||||||
@@ -232,7 +232,7 @@ public class ChannelProcessingManager implements ChannelEventListener
|
|||||||
processingChain = new ProcessingChain(channel.getChannelType());
|
processingChain = new ProcessingChain(channel.getChannelType());
|
||||||
|
|
||||||
/* Register global listeners */
|
/* Register global listeners */
|
||||||
for(Listener<AudioPacket> listener : mAudioPacketListeners)
|
for(Listener<ReusableAudioPacket> listener : mAudioPacketListeners)
|
||||||
{
|
{
|
||||||
processingChain.addAudioPacketListener(listener);
|
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
|
* Adds a message listener that will be added to all channels to receive
|
||||||
* any messages.
|
* any messages.
|
||||||
*/
|
*/
|
||||||
public void addAudioPacketListener(Listener<AudioPacket> listener)
|
public void addAudioPacketListener(Listener<ReusableAudioPacket> listener)
|
||||||
{
|
{
|
||||||
mAudioPacketListeners.add(listener);
|
mAudioPacketListeners.add(listener);
|
||||||
}
|
}
|
||||||
@@ -375,7 +375,7 @@ public class ChannelProcessingManager implements ChannelEventListener
|
|||||||
/**
|
/**
|
||||||
* Removes a message listener.
|
* Removes a message listener.
|
||||||
*/
|
*/
|
||||||
public void removeAudioPacketListener(Listener<AudioPacket> listener)
|
public void removeAudioPacketListener(Listener<ReusableAudioPacket> listener)
|
||||||
{
|
{
|
||||||
mAudioPacketListeners.remove(listener);
|
mAudioPacketListeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package io.github.dsheirer.module;
|
package io.github.dsheirer.module;
|
||||||
|
|
||||||
import io.github.dsheirer.audio.AudioPacket;
|
|
||||||
import io.github.dsheirer.audio.IAudioPacketListener;
|
import io.github.dsheirer.audio.IAudioPacketListener;
|
||||||
import io.github.dsheirer.audio.IAudioPacketProvider;
|
import io.github.dsheirer.audio.IAudioPacketProvider;
|
||||||
import io.github.dsheirer.audio.squelch.ISquelchStateListener;
|
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.IReusableBufferListener;
|
||||||
import io.github.dsheirer.sample.buffer.IReusableBufferProvider;
|
import io.github.dsheirer.sample.buffer.IReusableBufferProvider;
|
||||||
import io.github.dsheirer.sample.buffer.IReusableComplexBufferListener;
|
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.ReusableBuffer;
|
||||||
import io.github.dsheirer.sample.buffer.ReusableBufferBroadcaster;
|
import io.github.dsheirer.sample.buffer.ReusableBufferBroadcaster;
|
||||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
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 final static Logger mLog = LoggerFactory.getLogger(ProcessingChain.class);
|
||||||
|
|
||||||
private Broadcaster<AttributeChangeRequest> mAttributeChangeRequestBroadcaster = new Broadcaster<>();
|
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<CallEvent> mCallEventBroadcaster = new Broadcaster<>();
|
||||||
private Broadcaster<ChannelEvent> mChannelEventBroadcaster = new Broadcaster<>();
|
private Broadcaster<ChannelEvent> mChannelEventBroadcaster = new Broadcaster<>();
|
||||||
private ReusableBufferBroadcaster<ReusableComplexBuffer> mComplexBufferBroadcaster = new ReusableBufferBroadcaster();
|
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.
|
* 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);
|
mAudioPacketBroadcaster.addListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeAudioPacketListener(Listener<AudioPacket> listener)
|
public void removeAudioPacketListener(Listener<ReusableAudioPacket> listener)
|
||||||
{
|
{
|
||||||
mAudioPacketBroadcaster.removeListener(listener);
|
mAudioPacketBroadcaster.removeListener(listener);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package io.github.dsheirer.module.decode.p25.audio;
|
package io.github.dsheirer.module.decode.p25.audio;
|
||||||
|
|
||||||
import io.github.dsheirer.audio.AudioFormats;
|
import io.github.dsheirer.audio.AudioFormats;
|
||||||
import io.github.dsheirer.audio.AudioPacket;
|
|
||||||
import io.github.dsheirer.audio.IAudioPacketProvider;
|
import io.github.dsheirer.audio.IAudioPacketProvider;
|
||||||
import io.github.dsheirer.audio.squelch.ISquelchStateListener;
|
import io.github.dsheirer.audio.squelch.ISquelchStateListener;
|
||||||
import io.github.dsheirer.audio.squelch.SquelchState;
|
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.LDU2Message;
|
||||||
import io.github.dsheirer.module.decode.p25.message.ldu.LDUMessage;
|
import io.github.dsheirer.module.decode.p25.message.ldu.LDUMessage;
|
||||||
import io.github.dsheirer.sample.Listener;
|
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.AudioConversionLibrary;
|
||||||
import jmbe.iface.AudioConverter;
|
import jmbe.iface.AudioConverter;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -33,11 +34,12 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
|
|||||||
private boolean mEncryptedCallStateEstablished = false;
|
private boolean mEncryptedCallStateEstablished = false;
|
||||||
|
|
||||||
private AudioConverter mAudioConverter;
|
private AudioConverter mAudioConverter;
|
||||||
private Listener<AudioPacket> mAudioPacketListener;
|
private Listener<ReusableAudioPacket> mAudioPacketListener;
|
||||||
private SquelchStateListener mSquelchStateListener = new SquelchStateListener();
|
private SquelchStateListener mSquelchStateListener = new SquelchStateListener();
|
||||||
private NonClippingGain mGain = new NonClippingGain(5.0f, 0.95f);
|
private NonClippingGain mGain = new NonClippingGain(5.0f, 0.95f);
|
||||||
private Metadata mMetadata;
|
private Metadata mMetadata;
|
||||||
private LDU1Message mCachedLDU1Message = null;
|
private LDU1Message mCachedLDU1Message = null;
|
||||||
|
private ReusableAudioPacketQueue mAudioPacketQueue = new ReusableAudioPacketQueue("P25AudioModule");
|
||||||
|
|
||||||
public P25AudioModule(Metadata metadata)
|
public P25AudioModule(Metadata metadata)
|
||||||
{
|
{
|
||||||
@@ -142,7 +144,12 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
|
|||||||
|
|
||||||
audio = mGain.apply(audio);
|
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
|
else
|
||||||
@@ -230,7 +237,7 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAudioPacketListener(Listener<AudioPacket> listener)
|
public void setAudioPacketListener(Listener<ReusableAudioPacket> listener)
|
||||||
{
|
{
|
||||||
mAudioPacketListener = listener;
|
mAudioPacketListener = listener;
|
||||||
}
|
}
|
||||||
@@ -255,7 +262,10 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
|
|||||||
{
|
{
|
||||||
if(mAudioPacketListener != null)
|
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;
|
mEncryptedCallStateEstablished = false;
|
||||||
|
|||||||
@@ -18,11 +18,11 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package io.github.dsheirer.record;
|
package io.github.dsheirer.record;
|
||||||
|
|
||||||
import io.github.dsheirer.audio.AudioPacket;
|
|
||||||
import io.github.dsheirer.audio.IAudioPacketListener;
|
import io.github.dsheirer.audio.IAudioPacketListener;
|
||||||
import io.github.dsheirer.channel.metadata.Metadata;
|
import io.github.dsheirer.channel.metadata.Metadata;
|
||||||
import io.github.dsheirer.module.Module;
|
import io.github.dsheirer.module.Module;
|
||||||
import io.github.dsheirer.sample.Listener;
|
import io.github.dsheirer.sample.Listener;
|
||||||
|
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||||
import io.github.dsheirer.util.ThreadPool;
|
import io.github.dsheirer.util.ThreadPool;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -38,12 +38,12 @@ import java.util.concurrent.ScheduledFuture;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
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 final static Logger mLog = LoggerFactory.getLogger(AudioRecorder.class);
|
||||||
|
|
||||||
private LinkedBlockingQueue<AudioPacket> mAudioPacketQueue = new LinkedBlockingQueue<>(500);
|
private LinkedBlockingQueue<ReusableAudioPacket> mAudioPacketQueue = new LinkedBlockingQueue<>(500);
|
||||||
private List<AudioPacket> mPacketsToProcess = new ArrayList<>();
|
private List<ReusableAudioPacket> mPacketsToProcess = new ArrayList<>();
|
||||||
|
|
||||||
private FileOutputStream mFileOutputStream;
|
private FileOutputStream mFileOutputStream;
|
||||||
private AtomicBoolean mRunning = new AtomicBoolean();
|
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
|
* This is necessary since you can't have multiple methods with the same erasure (ie Listener<xxx>) in the
|
||||||
* parent module class.
|
* parent module class.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Listener<AudioPacket> getAudioPacketListener()
|
public Listener<ReusableAudioPacket> getAudioPacketListener()
|
||||||
{
|
{
|
||||||
return this;
|
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.
|
* Processes the audio packet and captures the latest Metadata for the recording for easy access.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void receive(AudioPacket audioPacket)
|
public void receive(ReusableAudioPacket audioPacket)
|
||||||
{
|
{
|
||||||
if(mRunning.get())
|
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() + "]");
|
mLog.error("recorder buffer overflow - stopping recorder [" + getPath().toString() + "]");
|
||||||
stop();
|
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.
|
* 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
|
* 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);
|
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)
|
catch(IOException ioe)
|
||||||
|
|||||||
@@ -16,13 +16,13 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.dsheirer.record;
|
package io.github.dsheirer.record;
|
||||||
|
|
||||||
import io.github.dsheirer.audio.AudioPacket;
|
|
||||||
import io.github.dsheirer.channel.metadata.Metadata;
|
import io.github.dsheirer.channel.metadata.Metadata;
|
||||||
import io.github.dsheirer.properties.SystemProperties;
|
import io.github.dsheirer.properties.SystemProperties;
|
||||||
import io.github.dsheirer.record.wave.AudioPacketWaveRecorder;
|
import io.github.dsheirer.record.wave.AudioPacketWaveRecorder;
|
||||||
import io.github.dsheirer.record.wave.ComplexBufferWaveRecorder;
|
import io.github.dsheirer.record.wave.ComplexBufferWaveRecorder;
|
||||||
import io.github.dsheirer.sample.Listener;
|
import io.github.dsheirer.sample.Listener;
|
||||||
import io.github.dsheirer.sample.OverflowableTransferQueue;
|
import io.github.dsheirer.sample.OverflowableTransferQueue;
|
||||||
|
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||||
import io.github.dsheirer.sample.real.IOverflowListener;
|
import io.github.dsheirer.sample.real.IOverflowListener;
|
||||||
import io.github.dsheirer.util.ThreadPool;
|
import io.github.dsheirer.util.ThreadPool;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -37,7 +37,7 @@ import java.util.Map;
|
|||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
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);
|
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
|
public static final long IDLE_RECORDER_REMOVAL_THRESHOLD = 6000; //6 seconds
|
||||||
|
|
||||||
private Map<String,AudioPacketWaveRecorder> mRecorders = new HashMap<>();
|
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 ScheduledFuture<?> mBufferProcessorFuture;
|
||||||
|
|
||||||
private boolean mCanStartNewRecorders = true;
|
private boolean mCanStartNewRecorders = true;
|
||||||
@@ -96,12 +97,16 @@ public class RecorderManager implements Listener<AudioPacket>
|
|||||||
* @param audioPacket to process
|
* @param audioPacket to process
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void receive(AudioPacket audioPacket)
|
public void receive(ReusableAudioPacket audioPacket)
|
||||||
{
|
{
|
||||||
if(audioPacket.hasMetadata() && audioPacket.getMetadata().isRecordable())
|
if(audioPacket.hasMetadata() && audioPacket.getMetadata().isRecordable())
|
||||||
{
|
{
|
||||||
mAudioPacketQueue.offer(audioPacket);
|
mAudioPacketQueue.offer(audioPacket);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
audioPacket.decrementUserCount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,13 +114,11 @@ public class RecorderManager implements Listener<AudioPacket>
|
|||||||
*/
|
*/
|
||||||
private void processBuffers()
|
private void processBuffers()
|
||||||
{
|
{
|
||||||
List<AudioPacket> audioPackets = new ArrayList<>();
|
mAudioPacketQueue.drainTo(mAudioPackets, 50);
|
||||||
|
|
||||||
mAudioPacketQueue.drainTo(audioPackets, 50);
|
while(!mAudioPackets.isEmpty())
|
||||||
|
|
||||||
while(!audioPackets.isEmpty())
|
|
||||||
{
|
{
|
||||||
for(AudioPacket audioPacket : audioPackets)
|
for(ReusableAudioPacket audioPacket : mAudioPackets)
|
||||||
{
|
{
|
||||||
String identifier = audioPacket.getMetadata().getUniqueIdentifier();
|
String identifier = audioPacket.getMetadata().getUniqueIdentifier();
|
||||||
|
|
||||||
@@ -123,17 +126,18 @@ public class RecorderManager implements Listener<AudioPacket>
|
|||||||
{
|
{
|
||||||
AudioPacketWaveRecorder recorder = mRecorders.get(identifier);
|
AudioPacketWaveRecorder recorder = mRecorders.get(identifier);
|
||||||
|
|
||||||
if(audioPacket.getType() == AudioPacket.Type.AUDIO)
|
if(audioPacket.getType() == ReusableAudioPacket.Type.AUDIO)
|
||||||
{
|
{
|
||||||
|
audioPacket.incrementUserCount();
|
||||||
recorder.receive(audioPacket);
|
recorder.receive(audioPacket);
|
||||||
}
|
}
|
||||||
else if(audioPacket.getType() == AudioPacket.Type.END)
|
else if(audioPacket.getType() == ReusableAudioPacket.Type.END)
|
||||||
{
|
{
|
||||||
AudioPacketWaveRecorder finished = mRecorders.remove(identifier);
|
AudioPacketWaveRecorder finished = mRecorders.remove(identifier);
|
||||||
finished.stop();
|
finished.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(audioPacket.getType() == AudioPacket.Type.AUDIO)
|
else if(audioPacket.getType() == ReusableAudioPacket.Type.AUDIO)
|
||||||
{
|
{
|
||||||
if(mCanStartNewRecorders)
|
if(mCanStartNewRecorders)
|
||||||
{
|
{
|
||||||
@@ -147,6 +151,7 @@ public class RecorderManager implements Listener<AudioPacket>
|
|||||||
|
|
||||||
recorder.start();
|
recorder.start();
|
||||||
|
|
||||||
|
audioPacket.incrementUserCount();
|
||||||
recorder.receive(audioPacket);
|
recorder.receive(audioPacket);
|
||||||
mRecorders.put(identifier, recorder);
|
mRecorders.put(identifier, recorder);
|
||||||
}
|
}
|
||||||
@@ -164,10 +169,12 @@ public class RecorderManager implements Listener<AudioPacket>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
audioPacket.decrementUserCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
audioPackets.clear();
|
mAudioPackets.clear();
|
||||||
mAudioPacketQueue.drainTo(audioPackets, 50);
|
mAudioPacketQueue.drainTo(mAudioPackets, 50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +201,7 @@ public class RecorderManager implements Listener<AudioPacket>
|
|||||||
/**
|
/**
|
||||||
* Constructs a file name and path for an audio recording
|
* Constructs a file name and path for an audio recording
|
||||||
*/
|
*/
|
||||||
private String getFilePrefix(AudioPacket packet)
|
private String getFilePrefix(ReusableAudioPacket packet)
|
||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
|||||||
@@ -18,9 +18,9 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package io.github.dsheirer.record.mp3;
|
package io.github.dsheirer.record.mp3;
|
||||||
|
|
||||||
import io.github.dsheirer.audio.AudioPacket;
|
|
||||||
import io.github.dsheirer.audio.convert.MP3AudioConverter;
|
import io.github.dsheirer.audio.convert.MP3AudioConverter;
|
||||||
import io.github.dsheirer.record.AudioRecorder;
|
import io.github.dsheirer.record.AudioRecorder;
|
||||||
|
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ public class MP3Recorder extends AudioRecorder
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void record(List<AudioPacket> audioPackets) throws IOException
|
protected void record(List<ReusableAudioPacket> audioPackets) throws IOException
|
||||||
{
|
{
|
||||||
OutputStream outputStream = getOutputStream();
|
OutputStream outputStream = getOutputStream();
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ public class MP3Recorder extends AudioRecorder
|
|||||||
* writes the ID3 tags to the output stream.
|
* writes the ID3 tags to the output stream.
|
||||||
* @param audioPackets
|
* @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
|
//TODO: detect metadata changes and write out ID3 tags to the MP3 stream
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,11 +17,12 @@
|
|||||||
package io.github.dsheirer.record.wave;
|
package io.github.dsheirer.record.wave;
|
||||||
|
|
||||||
import io.github.dsheirer.audio.AudioFormats;
|
import io.github.dsheirer.audio.AudioFormats;
|
||||||
import io.github.dsheirer.audio.AudioPacket;
|
|
||||||
import io.github.dsheirer.channel.metadata.Metadata;
|
import io.github.dsheirer.channel.metadata.Metadata;
|
||||||
import io.github.dsheirer.module.Module;
|
import io.github.dsheirer.module.Module;
|
||||||
import io.github.dsheirer.sample.ConversionUtils;
|
import io.github.dsheirer.sample.ConversionUtils;
|
||||||
import io.github.dsheirer.sample.Listener;
|
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.ThreadPool;
|
||||||
import io.github.dsheirer.util.TimeStamp;
|
import io.github.dsheirer.util.TimeStamp;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -31,7 +32,6 @@ import javax.sound.sampled.AudioFormat;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
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
|
* 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);
|
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 Path mFile;
|
||||||
private AudioFormat mAudioFormat;
|
private AudioFormat mAudioFormat;
|
||||||
|
|
||||||
|
private OverflowableReusableBufferTransferQueue<ReusableAudioPacket> mTransferQueue =
|
||||||
|
new OverflowableReusableBufferTransferQueue<>(500, 100);
|
||||||
private BufferProcessor mBufferProcessor;
|
private BufferProcessor mBufferProcessor;
|
||||||
private ScheduledFuture<?> mProcessorHandle;
|
private ScheduledFuture<?> mProcessorHandle;
|
||||||
private LinkedBlockingQueue<AudioPacket> mAudioPackets = new LinkedBlockingQueue<>(500);
|
|
||||||
private Metadata mMetadata;
|
private Metadata mMetadata;
|
||||||
private long mLastBufferReceived;
|
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.
|
* Primary input method for receiving audio packets to enqueue for later writing to the wav file.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void receive(AudioPacket audioPacket)
|
public void receive(ReusableAudioPacket audioPacket)
|
||||||
{
|
{
|
||||||
if(mRunning.get())
|
if(mRunning.get())
|
||||||
{
|
{
|
||||||
boolean success = mAudioPackets.offer(audioPacket);
|
mTransferQueue.offer(audioPacket);
|
||||||
|
|
||||||
if(!success)
|
|
||||||
{
|
|
||||||
mLog.error("recorder buffer overflow - purging [" + mFile.toFile().getAbsolutePath() + "]");
|
|
||||||
mAudioPackets.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
mLastBufferReceived = System.currentTimeMillis();
|
mLastBufferReceived = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
audioPacket.decrementUserCount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -188,17 +186,23 @@ public class AudioPacketWaveRecorder extends Module implements Listener<AudioPac
|
|||||||
*/
|
*/
|
||||||
private void write() throws IOException
|
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())
|
if(audioPacket.hasMetadata())
|
||||||
{
|
{
|
||||||
mMetadata = audioPacket.getMetadata();
|
mMetadata = audioPacket.getMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
mWriter.writeData(ConversionUtils.convertToSigned16BitSamples(audioPacket.getAudioBuffer()));
|
mWriter.writeData(ConversionUtils.convertToSigned16BitSamples(audioPacket.getAudioSamples()));
|
||||||
audioPacket = mAudioPackets.poll();
|
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)
|
catch(IOException ioe)
|
||||||
{
|
{
|
||||||
/* Stop this module if/when we get an IO exception */
|
/* Stop this module if/when we get an IO exception */
|
||||||
mAudioPackets.clear();
|
mTransferQueue.clear();
|
||||||
stop();
|
stop();
|
||||||
|
|
||||||
mLog.error("IO Exception while trying to write to the wave writer", ioe);
|
mLog.error("IO Exception while trying to write to the wave writer", ioe);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ import io.github.dsheirer.sample.Listener;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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);
|
private final static Logger mLog = LoggerFactory.getLogger(ReusableBufferBroadcaster.class);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user