mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
Adds de-emphasis filter to P25 decoded IMBE audio output.
This commit is contained in:
@@ -95,7 +95,8 @@ public class StereoAudioOutput extends AudioOutput
|
||||
catch ( LineUnavailableException e )
|
||||
{
|
||||
mLog.error( "Couldn't obtain source data line for 48kHz PCM stereo "
|
||||
+ "audio output" );
|
||||
+ "audio output - mixer [" + mixer.getMixerInfo().getName() +
|
||||
"] channel [" + mMixerChannel.name() + "]" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,11 @@ public class FloatCircularBuffer
|
||||
Arrays.fill( mBuffer, (float)0.0 );
|
||||
}
|
||||
|
||||
public int getSize()
|
||||
{
|
||||
return mBuffer.length;
|
||||
}
|
||||
|
||||
public float[] getBuffer()
|
||||
{
|
||||
return mBuffer;
|
||||
|
||||
60
src/dsp/filter/iir/DeemphasisFilter.java
Normal file
60
src/dsp/filter/iir/DeemphasisFilter.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package dsp.filter.iir;
|
||||
|
||||
import sample.real.RealBuffer;
|
||||
|
||||
public class DeemphasisFilter
|
||||
{
|
||||
private static final float MAX_SAMPLE_VALUE = 0.95f;
|
||||
private float mAlpha;
|
||||
private float mGain;
|
||||
private float mPrevious = 0.0f;
|
||||
|
||||
public DeemphasisFilter( float sampleRate, float cutoff, float gain )
|
||||
{
|
||||
mAlpha = (float)Math.exp( -2.0 * Math.PI * cutoff * ( 1.0 / sampleRate ) );
|
||||
mGain = gain;
|
||||
}
|
||||
|
||||
public float filter( float sample )
|
||||
{
|
||||
mPrevious = sample + ( mAlpha * mPrevious );
|
||||
|
||||
return declip( mPrevious * mGain );
|
||||
}
|
||||
|
||||
private float declip( float value )
|
||||
{
|
||||
if( value > MAX_SAMPLE_VALUE )
|
||||
{
|
||||
return MAX_SAMPLE_VALUE;
|
||||
}
|
||||
else if( value < -MAX_SAMPLE_VALUE )
|
||||
{
|
||||
return -MAX_SAMPLE_VALUE;
|
||||
}
|
||||
|
||||
else return value;
|
||||
}
|
||||
|
||||
public float[] filter( float[] samples )
|
||||
{
|
||||
for( int x = 0; x < samples.length; x++ )
|
||||
{
|
||||
samples[ x ] = filter( samples[ x ] );
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
public RealBuffer filter( RealBuffer buffer )
|
||||
{
|
||||
float[] samples = buffer.getSamples();
|
||||
|
||||
for( int x = 0; x < samples.length; x++ )
|
||||
{
|
||||
samples[ x ] = filter( samples[ x ] );
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
@@ -149,7 +149,7 @@ public class P25DecoderPanel extends DecoderPanel
|
||||
{
|
||||
mFromAlias.setText( from.getName() );
|
||||
|
||||
String icon = from.getIconName();
|
||||
final String icon = from.getIconName();
|
||||
|
||||
if( icon != null )
|
||||
{
|
||||
@@ -180,7 +180,7 @@ public class P25DecoderPanel extends DecoderPanel
|
||||
{
|
||||
mToAlias.setText( to.getName() );
|
||||
|
||||
String icon = to.getIconName();
|
||||
final String icon = to.getIconName();
|
||||
|
||||
if( icon != null )
|
||||
{
|
||||
|
||||
@@ -6,8 +6,6 @@ import message.IMessageListener;
|
||||
import message.Message;
|
||||
import module.Module;
|
||||
import module.decode.p25.message.ldu.LDUMessage;
|
||||
import module.decode.p25.message.tdu.TDUMessage;
|
||||
import module.decode.p25.message.tdu.lc.TDULinkControlMessage;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -23,6 +21,8 @@ import audio.squelch.ISquelchStateListener;
|
||||
import audio.squelch.SquelchState;
|
||||
import controller.channel.ChannelEvent;
|
||||
import controller.channel.IChannelEventListener;
|
||||
import dsp.audio.P25AGC;
|
||||
import dsp.filter.iir.DeemphasisFilter;
|
||||
|
||||
public class P25AudioModule extends Module implements Listener<Message>,
|
||||
IAudioPacketProvider, IMessageListener, IChannelEventListener,
|
||||
@@ -43,6 +43,8 @@ public class P25AudioModule extends Module implements Listener<Message>,
|
||||
private AudioMetadata mAudioMetadata;
|
||||
private ChannelEventListener mChannelEventListener = new ChannelEventListener();
|
||||
private SquelchStateListener mSquelchStateListener = new SquelchStateListener();
|
||||
// private P25AGC mAGC = new P25AGC( 2.0, 10.0 );
|
||||
private DeemphasisFilter mDeemphasis = new DeemphasisFilter( 48000.0f, 300.0f, 0.6f );
|
||||
|
||||
public P25AudioModule( boolean record )
|
||||
{
|
||||
@@ -89,9 +91,10 @@ public class P25AudioModule extends Module implements Listener<Message>,
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary inject point for p25 imbe audio frame messages. Each LDU audio
|
||||
* message contains 9 imbe voice frames. If the audio is not encrypted,
|
||||
* we insert each audio frame asynchronously into the audio converter.
|
||||
* Primary processing method for p25 imbe audio frame messages. Each LDU
|
||||
* audio message contains 9 imbe voice frames. Each frame is converted to
|
||||
* audio when the JMBE library is loaded, processed by auto-gain, and
|
||||
* broadcast to the audio packet listener.
|
||||
*/
|
||||
public void receive( Message message )
|
||||
{
|
||||
@@ -101,15 +104,15 @@ public class P25AudioModule extends Module implements Listener<Message>,
|
||||
{
|
||||
LDUMessage ldu = (LDUMessage)message;
|
||||
|
||||
/* If we detect a valid LDU message with the encryption flag
|
||||
* set to true, we toggle the encrypted audio state until we
|
||||
* receive the first TDU or TDULC message, then reset it. */
|
||||
/* Only process unencrypted audio frames */
|
||||
if( !( ldu.isValid() && ldu.isEncrypted() ) )
|
||||
{
|
||||
for( byte[] frame: ((LDUMessage)message).getIMBEFrames() )
|
||||
for( byte[] frame: ldu.getIMBEFrames() )
|
||||
{
|
||||
float[] audio = mAudioConverter.decode( frame );
|
||||
|
||||
audio = mDeemphasis.filter( audio );
|
||||
|
||||
mAudioPacketListener.receive(
|
||||
new AudioPacket( audio, mAudioMetadata.copyOf() ) );
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ import source.IControllableFileSource;
|
||||
import source.IFrameLocationListener;
|
||||
import source.RealSource;
|
||||
|
||||
public class RealWaveSource extends RealSource implements IControllableFileSource
|
||||
public class RealWaveSource extends RealSource
|
||||
implements IControllableFileSource, AutoCloseable
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( RealWaveSource.class );
|
||||
@@ -168,6 +169,11 @@ public class RealWaveSource extends RealSource implements IControllableFileSourc
|
||||
{
|
||||
if( samplesRead < buffer.length )
|
||||
{
|
||||
if( samplesRead == -1 )
|
||||
{
|
||||
throw new IOException( "End of recording" );
|
||||
}
|
||||
|
||||
buffer = Arrays.copyOf( buffer, samplesRead );
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user