mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -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.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||
import io.github.dsheirer.source.mixer.MixerChannel;
|
||||
import io.github.dsheirer.source.mixer.MixerChannelConfiguration;
|
||||
import io.github.dsheirer.source.mixer.MixerManager;
|
||||
@@ -43,7 +44,7 @@ import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class AudioManager implements Listener<AudioPacket>, IAudioController
|
||||
public class AudioManager implements Listener<ReusableAudioPacket>, IAudioController
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(AudioManager.class);
|
||||
|
||||
@@ -58,7 +59,7 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
||||
public static final AudioEvent CONFIGURATION_CHANGE_COMPLETE =
|
||||
new AudioEvent(AudioEvent.Type.AUDIO_CONFIGURATION_CHANGE_COMPLETE, null);
|
||||
|
||||
private LinkedTransferQueue<AudioPacket> mAudioPacketQueue = new LinkedTransferQueue<>();
|
||||
private LinkedTransferQueue<ReusableAudioPacket> mAudioPacketQueue = new LinkedTransferQueue<>();
|
||||
private Map<Integer,AudioOutputConnection> mChannelConnectionMap = new HashMap<>();
|
||||
private List<AudioOutputConnection> mAudioOutputConnections = new ArrayList<>();
|
||||
private AudioOutputConnection mLowestPriorityConnection;
|
||||
@@ -170,7 +171,7 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
||||
* Primary ingest point for audio produced by all decoding channels, for distribution to audio playback devices.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void receive(AudioPacket packet)
|
||||
public synchronized void receive(ReusableAudioPacket packet)
|
||||
{
|
||||
mAudioPacketQueue.add(packet);
|
||||
}
|
||||
@@ -362,7 +363,7 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
||||
* @param audioPacket from a decoding channel source
|
||||
* @return an audio output connection or null
|
||||
*/
|
||||
private AudioOutputConnection getConnection(AudioPacket audioPacket)
|
||||
private AudioOutputConnection getConnection(ReusableAudioPacket audioPacket)
|
||||
{
|
||||
int channelMetadataID = audioPacket.getMetadata().getMetadataID();
|
||||
|
||||
@@ -410,6 +411,8 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
||||
|
||||
public class AudioPacketProcessor implements Runnable
|
||||
{
|
||||
private List<ReusableAudioPacket> mPackets = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
@@ -419,14 +422,12 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
||||
|
||||
if(mAudioPacketQueue != null)
|
||||
{
|
||||
List<AudioPacket> packets = new ArrayList<AudioPacket>();
|
||||
mAudioPacketQueue.drainTo(mPackets);
|
||||
|
||||
mAudioPacketQueue.drainTo(packets);
|
||||
|
||||
for(AudioPacket packet : packets)
|
||||
for(ReusableAudioPacket packet : mPackets)
|
||||
{
|
||||
/* Don't process any packet's marked as do not monitor */
|
||||
if(!packet.getMetadata().isDoNotMonitor() && packet.getType() == AudioPacket.Type.AUDIO)
|
||||
if(!packet.getMetadata().isDoNotMonitor() && packet.getType() == ReusableAudioPacket.Type.AUDIO)
|
||||
{
|
||||
AudioOutputConnection connection = getConnection(packet);
|
||||
|
||||
@@ -434,13 +435,29 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
||||
{
|
||||
connection.receive(packet);
|
||||
}
|
||||
else
|
||||
{
|
||||
packet.decrementUserCount();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
packet.decrementUserCount();
|
||||
}
|
||||
}
|
||||
|
||||
mPackets.clear();
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Encountered error while processing audio packets", e);
|
||||
|
||||
while(mPackets.size() > 0)
|
||||
{
|
||||
ReusableAudioPacket audioPacket = mPackets.remove(0);
|
||||
audioPacket.decrementUserCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,7 +480,7 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
||||
mAudioOutput = audioOutput;
|
||||
}
|
||||
|
||||
public void receive(AudioPacket packet)
|
||||
public void receive(ReusableAudioPacket packet)
|
||||
{
|
||||
if(packet.hasMetadata() && packet.getMetadata().getMetadataID() == mChannelMetadataID)
|
||||
{
|
||||
|
||||
@@ -25,6 +25,8 @@ import io.github.dsheirer.dsp.filter.fir.remez.RemezFIRFilterDesigner;
|
||||
import io.github.dsheirer.module.Module;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacketQueue;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBuffer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -41,6 +43,7 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab
|
||||
ISquelchStateListener, Listener<ReusableBuffer>
|
||||
{
|
||||
protected static final Logger mLog = LoggerFactory.getLogger(AudioModule.class);
|
||||
private ReusableAudioPacketQueue mAudioPacketQueue = new ReusableAudioPacketQueue("AudioModule");
|
||||
|
||||
private static float[] sHighPassFilterCoefficients;
|
||||
|
||||
@@ -73,11 +76,12 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab
|
||||
private RealFIRFilter2 mHighPassFilter = new RealFIRFilter2(sHighPassFilterCoefficients);
|
||||
private SquelchStateListener mSquelchStateListener = new SquelchStateListener();
|
||||
private SquelchState mSquelchState = SquelchState.SQUELCH;
|
||||
private Listener<AudioPacket> mAudioPacketListener;
|
||||
private Listener<ReusableAudioPacket> mAudioPacketListener;
|
||||
private Metadata mMetadata;
|
||||
|
||||
/**
|
||||
* Creates an Audio Module.
|
||||
*
|
||||
* @param metadata to use for audio packets produced by this audio module.
|
||||
*/
|
||||
public AudioModule(Metadata metadata)
|
||||
@@ -109,12 +113,15 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab
|
||||
/* Issue an end-audio packet in case a recorder is still rolling */
|
||||
if(mAudioPacketListener != null)
|
||||
{
|
||||
mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf()));
|
||||
ReusableAudioPacket endAudioPacket = mAudioPacketQueue.getEndAudioBuffer();
|
||||
endAudioPacket.setMetadata(mMetadata.copyOf());
|
||||
endAudioPacket.incrementUserCount();
|
||||
mAudioPacketListener.receive(endAudioPacket);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAudioPacketListener(Listener<AudioPacket> listener)
|
||||
public void setAudioPacketListener(Listener<ReusableAudioPacket> listener)
|
||||
{
|
||||
mAudioPacketListener = listener;
|
||||
}
|
||||
@@ -137,8 +144,14 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab
|
||||
if(mAudioPacketListener != null && mSquelchState == SquelchState.UNSQUELCH)
|
||||
{
|
||||
ReusableBuffer highPassFiltered = mHighPassFilter.filter(reusableBuffer);
|
||||
AudioPacket packet = new AudioPacket(highPassFiltered.getSamplesCopy(), mMetadata.copyOf());
|
||||
mAudioPacketListener.receive(packet);
|
||||
|
||||
ReusableAudioPacket audioPacket = mAudioPacketQueue.getBuffer(highPassFiltered.getSampleCount());
|
||||
audioPacket.loadAudioFrom(highPassFiltered);
|
||||
audioPacket.setMetadata(mMetadata.copyOf());
|
||||
audioPacket.incrementUserCount();
|
||||
|
||||
mAudioPacketListener.receive(audioPacket);
|
||||
|
||||
highPassFiltered.decrementUserCount();
|
||||
}
|
||||
else
|
||||
@@ -164,7 +177,9 @@ public class AudioModule extends Module implements IAudioPacketProvider, IReusab
|
||||
{
|
||||
if(state == SquelchState.SQUELCH && mAudioPacketListener != null)
|
||||
{
|
||||
mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf()));
|
||||
ReusableAudioPacket endAudioPacket = mAudioPacketQueue.getEndAudioBuffer();
|
||||
endAudioPacket.setMetadata(mMetadata.copyOf());
|
||||
mAudioPacketListener.receive(endAudioPacket);
|
||||
}
|
||||
|
||||
mSquelchState = state;
|
||||
|
||||
@@ -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
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* You should have received a copy of the GNU General Public License along with this program.
|
||||
* If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.audio;
|
||||
|
||||
import io.github.dsheirer.sample.ConversionUtils;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -34,17 +32,18 @@ public class AudioUtils
|
||||
/**
|
||||
* Converts the audio packets into a byte array of 16-bit, little-endian audio samples
|
||||
*/
|
||||
public static byte[] convertTo16BitSamples(List<AudioPacket> audioPackets)
|
||||
public static byte[] convertTo16BitSamples(List<ReusableAudioPacket> audioPackets)
|
||||
{
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
|
||||
try
|
||||
{
|
||||
for(AudioPacket packet: audioPackets)
|
||||
for(ReusableAudioPacket audioPacket: audioPackets)
|
||||
{
|
||||
//Converting from 32-bit floats to signed 16-bit samples
|
||||
ByteBuffer buffer = ConversionUtils.convertToSigned16BitSamples(packet.getAudioBuffer().getSamples());
|
||||
ByteBuffer buffer = ConversionUtils.convertToSigned16BitSamples(audioPacket.getAudioSamples());
|
||||
stream.write(buffer.array());
|
||||
audioPacket.decrementUserCount();
|
||||
}
|
||||
}
|
||||
catch(IOException e)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package io.github.dsheirer.audio;
|
||||
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||
|
||||
public interface IAudioPacketListener
|
||||
{
|
||||
public Listener<AudioPacket> getAudioPacketListener();
|
||||
public Listener<ReusableAudioPacket> getAudioPacketListener();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package io.github.dsheirer.audio;
|
||||
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||
|
||||
public interface IAudioPacketProvider
|
||||
{
|
||||
public void setAudioPacketListener( Listener<AudioPacket> listener );
|
||||
public void setAudioPacketListener( Listener<ReusableAudioPacket> listener );
|
||||
public void removeAudioPacketListener();
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
package io.github.dsheirer.audio.broadcast;
|
||||
|
||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||
import io.github.dsheirer.audio.AudioPacket;
|
||||
import io.github.dsheirer.channel.metadata.Metadata;
|
||||
import io.github.dsheirer.icon.IconManager;
|
||||
import io.github.dsheirer.properties.SystemProperties;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -46,7 +46,7 @@ import java.util.function.Consumer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class BroadcastModel extends AbstractTableModel implements Listener<AudioPacket>
|
||||
public class BroadcastModel extends AbstractTableModel implements Listener<ReusableAudioPacket>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(BroadcastModel.class);
|
||||
|
||||
@@ -276,7 +276,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(AudioPacket audioPacket)
|
||||
public void receive(ReusableAudioPacket audioPacket)
|
||||
{
|
||||
if(audioPacket.hasMetadata() && audioPacket.getMetadata().isStreamable())
|
||||
{
|
||||
@@ -284,11 +284,14 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
||||
{
|
||||
if(mBroadcasterMap.containsKey(channel.getChannelName()))
|
||||
{
|
||||
audioPacket.incrementUserCount();
|
||||
mStreamManager.receive(audioPacket);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
audioPacket.decrementUserCount();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.audio.broadcast;
|
||||
|
||||
import io.github.dsheirer.audio.AudioPacket;
|
||||
import io.github.dsheirer.record.AudioRecorder;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import io.github.dsheirer.util.TimeStamp;
|
||||
import org.slf4j.Logger;
|
||||
@@ -37,7 +37,7 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class StreamManager implements Listener<AudioPacket>
|
||||
public class StreamManager implements Listener<ReusableAudioPacket>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(StreamManager.class);
|
||||
private static final long MAXIMUM_RECORDER_LIFESPAN_MILLIS = 30000; //30 seconds
|
||||
@@ -115,7 +115,7 @@ public class StreamManager implements Listener<AudioPacket>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(AudioPacket audioPacket)
|
||||
public void receive(ReusableAudioPacket audioPacket)
|
||||
{
|
||||
if(mRunning.get() && audioPacket.hasMetadata())
|
||||
{
|
||||
@@ -123,9 +123,9 @@ public class StreamManager implements Listener<AudioPacket>
|
||||
{
|
||||
int channelMetadataID = audioPacket.getMetadata().getMetadataID();
|
||||
|
||||
AudioPacket.Type type = audioPacket.getType();
|
||||
ReusableAudioPacket.Type type = audioPacket.getType();
|
||||
|
||||
if(type == AudioPacket.Type.AUDIO)
|
||||
if(type == ReusableAudioPacket.Type.AUDIO)
|
||||
{
|
||||
if(mStreamRecorders.containsKey(channelMetadataID))
|
||||
{
|
||||
@@ -133,6 +133,7 @@ public class StreamManager implements Listener<AudioPacket>
|
||||
|
||||
if(recorder != null)
|
||||
{
|
||||
audioPacket.incrementUserCount();
|
||||
recorder.receive(audioPacket);
|
||||
}
|
||||
}
|
||||
@@ -141,11 +142,12 @@ public class StreamManager implements Listener<AudioPacket>
|
||||
AudioRecorder recorder = BroadcastFactory.getAudioRecorder(getTemporaryRecordingPath(),
|
||||
mBroadcastFormat);
|
||||
recorder.start();
|
||||
audioPacket.incrementUserCount();
|
||||
recorder.receive(audioPacket);
|
||||
mStreamRecorders.put(channelMetadataID, recorder);
|
||||
}
|
||||
}
|
||||
else if(type == AudioPacket.Type.END)
|
||||
else if(type == ReusableAudioPacket.Type.END)
|
||||
{
|
||||
removeRecorder(channelMetadataID);
|
||||
}
|
||||
@@ -155,6 +157,8 @@ public class StreamManager implements Listener<AudioPacket>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
audioPacket.decrementUserCount();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.audio.convert;
|
||||
|
||||
import io.github.dsheirer.audio.AudioPacket;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -27,7 +27,7 @@ public interface IAudioConverter
|
||||
/**
|
||||
* Converts the PCM audio packets to converted audio format. May produce partial audio frame data.
|
||||
*/
|
||||
public byte[] convert(List<AudioPacket> audioPackets);
|
||||
public byte[] convert(List<ReusableAudioPacket> audioPackets);
|
||||
|
||||
/**
|
||||
* Finalizes audio conversion by fully converting any partial frames left in the buffer and returning the
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
package io.github.dsheirer.audio.convert;
|
||||
|
||||
import io.github.dsheirer.audio.AudioFormats;
|
||||
import io.github.dsheirer.audio.AudioPacket;
|
||||
import io.github.dsheirer.audio.AudioUtils;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||
import net.sourceforge.lame.lowlevel.LameEncoder;
|
||||
import net.sourceforge.lame.mp3.Lame;
|
||||
import net.sourceforge.lame.mp3.MPEGMode;
|
||||
@@ -54,7 +54,7 @@ public class MP3AudioConverter implements IAudioConverter
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] convert(List<AudioPacket> audioPackets)
|
||||
public byte[] convert(List<ReusableAudioPacket> audioPackets)
|
||||
{
|
||||
mMP3Stream.reset();
|
||||
|
||||
|
||||
@@ -18,8 +18,9 @@
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.audio.convert;
|
||||
|
||||
import io.github.dsheirer.audio.AudioPacket;
|
||||
import io.github.dsheirer.record.mp3.MP3Recorder;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacketQueue;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -33,6 +34,7 @@ public class MP3SilenceGenerator implements ISilenceGenerator
|
||||
|
||||
private MP3AudioConverter mGenerator = new MP3AudioConverter(MP3Recorder.MP3_BIT_RATE, MP3Recorder.CONSTANT_BIT_RATE);
|
||||
private byte[] mPreviousPartialFrameData;
|
||||
private ReusableAudioPacketQueue mAudioPacketQueue = new ReusableAudioPacketQueue("MP3 Silence Generator");
|
||||
|
||||
/**
|
||||
* Generates MP3 audio silence frames
|
||||
@@ -44,9 +46,11 @@ public class MP3SilenceGenerator implements ISilenceGenerator
|
||||
public byte[] generate(long duration)
|
||||
{
|
||||
int length = (int)(duration * 8); //8000 Hz sample rate
|
||||
float[] silence = new float[length];
|
||||
AudioPacket silencePacket = new AudioPacket(silence, null);
|
||||
List<AudioPacket> silencePackets = new ArrayList<>();
|
||||
ReusableAudioPacket silencePacket = mAudioPacketQueue.getBuffer(length);
|
||||
Arrays.fill(silencePacket.getAudioSamples(), 0.0f);
|
||||
silencePacket.incrementUserCount();
|
||||
|
||||
List<ReusableAudioPacket> silencePackets = new ArrayList<>();
|
||||
silencePackets.add(silencePacket);
|
||||
|
||||
byte[] frameData = mGenerator.convert(silencePackets);
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
package io.github.dsheirer.audio.output;
|
||||
|
||||
import io.github.dsheirer.audio.AudioEvent;
|
||||
import io.github.dsheirer.audio.AudioPacket;
|
||||
import io.github.dsheirer.channel.metadata.Metadata;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||
import io.github.dsheirer.source.mixer.MixerChannel;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
@@ -46,11 +46,11 @@ import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
|
||||
public abstract class AudioOutput implements Listener<ReusableAudioPacket>, LineListener
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(AudioOutput.class);
|
||||
|
||||
private LinkedTransferQueue<AudioPacket> mBuffer = new LinkedTransferQueue<>();
|
||||
private LinkedTransferQueue<ReusableAudioPacket> mBuffer = new LinkedTransferQueue<>();
|
||||
private int mBufferStartThreshold;
|
||||
private int mBufferStopThreshold;
|
||||
|
||||
@@ -190,7 +190,7 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
|
||||
* Converts the audio packet data into a byte buffer format appropriate for
|
||||
* the underlying source data line.
|
||||
*/
|
||||
protected abstract ByteBuffer convert(AudioPacket packet);
|
||||
protected abstract ByteBuffer convert(ReusableAudioPacket packet);
|
||||
|
||||
/**
|
||||
* Audio output channel name
|
||||
@@ -271,7 +271,7 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(AudioPacket packet)
|
||||
public void receive(ReusableAudioPacket packet)
|
||||
{
|
||||
if(mCanProcessAudio)
|
||||
{
|
||||
@@ -281,11 +281,17 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
|
||||
|
||||
mBuffer.add(packet);
|
||||
}
|
||||
else
|
||||
{
|
||||
packet.decrementUserCount();
|
||||
}
|
||||
}
|
||||
|
||||
public class BufferProcessor implements Runnable
|
||||
{
|
||||
private AtomicBoolean mProcessing = new AtomicBoolean();
|
||||
private List<ReusableAudioPacket> mAudioPackets = new ArrayList<ReusableAudioPacket>();
|
||||
|
||||
|
||||
public BufferProcessor()
|
||||
{
|
||||
@@ -300,13 +306,11 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
|
||||
* processor can run at any given time */
|
||||
if(mProcessing.compareAndSet(false, true))
|
||||
{
|
||||
List<AudioPacket> packets = new ArrayList<AudioPacket>();
|
||||
mBuffer.drainTo(mAudioPackets);
|
||||
|
||||
mBuffer.drainTo(packets);
|
||||
|
||||
for(AudioPacket packet : packets)
|
||||
for(ReusableAudioPacket packet : mAudioPackets)
|
||||
{
|
||||
if(packet.getType() == AudioPacket.Type.AUDIO)
|
||||
if(packet.getType() == ReusableAudioPacket.Type.AUDIO)
|
||||
{
|
||||
broadcast(packet.getMetadata());
|
||||
|
||||
@@ -338,8 +342,14 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
|
||||
|
||||
updateTimestamp();
|
||||
}
|
||||
else
|
||||
{
|
||||
packet.decrementUserCount();
|
||||
}
|
||||
}
|
||||
|
||||
mAudioPackets.clear();
|
||||
|
||||
checkStop();
|
||||
|
||||
mProcessing.set(false);
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
package io.github.dsheirer.audio.output;
|
||||
|
||||
import io.github.dsheirer.audio.AudioFormats;
|
||||
import io.github.dsheirer.audio.AudioPacket;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||
import io.github.dsheirer.source.mixer.MixerChannel;
|
||||
|
||||
import javax.sound.sampled.Mixer;
|
||||
@@ -43,14 +43,16 @@ public class MonoAudioOutput extends AudioOutput
|
||||
/**
|
||||
* Converts the audio packet data into mono audio frames.
|
||||
*/
|
||||
protected ByteBuffer convert(AudioPacket packet)
|
||||
protected ByteBuffer convert(ReusableAudioPacket packet)
|
||||
{
|
||||
if(packet.hasAudioBuffer())
|
||||
ByteBuffer buffer = null;
|
||||
|
||||
if(packet.hasAudioSamples())
|
||||
{
|
||||
float[] samples = packet.getAudioBuffer().getSamples();
|
||||
float[] samples = packet.getAudioSamples();
|
||||
|
||||
/* Little-endian byte buffer */
|
||||
ByteBuffer buffer = ByteBuffer.allocate(samples.length * 2).order(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer = ByteBuffer.allocate(samples.length * 2).order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
ShortBuffer shortBuffer = buffer.asShortBuffer();
|
||||
|
||||
@@ -58,10 +60,10 @@ public class MonoAudioOutput extends AudioOutput
|
||||
{
|
||||
shortBuffer.put((short) (sample * Short.MAX_VALUE));
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
return null;
|
||||
packet.decrementUserCount();
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
package io.github.dsheirer.audio.output;
|
||||
|
||||
import io.github.dsheirer.audio.AudioFormats;
|
||||
import io.github.dsheirer.audio.AudioPacket;
|
||||
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
|
||||
import io.github.dsheirer.source.mixer.MixerChannel;
|
||||
|
||||
import javax.sound.sampled.Mixer;
|
||||
@@ -45,15 +45,16 @@ public class StereoAudioOutput extends AudioOutput
|
||||
* channel containing the audio and the other channel containing zero
|
||||
* valued (silent) samples.
|
||||
*/
|
||||
protected ByteBuffer convert(AudioPacket packet)
|
||||
protected ByteBuffer convert(ReusableAudioPacket packet)
|
||||
{
|
||||
if(packet.hasAudioBuffer())
|
||||
ByteBuffer buffer = null;
|
||||
|
||||
if(packet.hasAudioSamples())
|
||||
{
|
||||
float[] samples = packet.getAudioBuffer().getSamples();
|
||||
float[] samples = packet.getAudioSamples();
|
||||
|
||||
/* Little-endian byte buffer */
|
||||
ByteBuffer buffer = ByteBuffer.allocate(samples.length * 4)
|
||||
.order(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer = ByteBuffer.allocate(samples.length * 4).order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
ShortBuffer shortBuffer = buffer.asShortBuffer();
|
||||
|
||||
@@ -73,10 +74,10 @@ public class StereoAudioOutput extends AudioOutput
|
||||
shortBuffer.put((short) (sample * Short.MAX_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
return null;
|
||||
packet.decrementUserCount();
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user