#660 Updates audio processing subsystem to use audio segments. Updates audio playback, recording and streaming to use audio segments. Adds support to UserPreferences for sound card selection and audio tone insertion during playback. Adds record preference for WAVE or MP3. Resolves issues with metadata in recordings. (#675)

This commit is contained in:
Denny Sheirer
2020-03-19 10:35:05 +00:00
committed by GitHub
parent de249c448b
commit fa16db129b
88 changed files with 4446 additions and 4183 deletions

View File

@@ -22,6 +22,7 @@
package io.github.dsheirer.audio.codec.mbe;
import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.preference.UserPreferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,9 +33,9 @@ public abstract class AmbeAudioModule extends JmbeAudioModule
private static final String AMBE_CODEC = "AMBE 3600 x 2450";
private static boolean sLibraryStatusLogged = false;
public AmbeAudioModule(UserPreferences userPreferences)
public AmbeAudioModule(UserPreferences userPreferences, AliasList aliasList)
{
super(userPreferences);
super(userPreferences, aliasList);
if(!sLibraryStatusLogged)
{

View File

@@ -22,6 +22,7 @@
package io.github.dsheirer.audio.codec.mbe;
import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.preference.UserPreferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,9 +33,9 @@ public abstract class ImbeAudioModule extends JmbeAudioModule
private static final String IMBE_CODEC = "IMBE";
private static boolean sLibraryStatusLogged = false;
public ImbeAudioModule(UserPreferences userPreferences)
public ImbeAudioModule(UserPreferences userPreferences, AliasList aliasList)
{
super(userPreferences);
super(userPreferences, aliasList);
if(!sLibraryStatusLogged)
{

View File

@@ -23,14 +23,15 @@
package io.github.dsheirer.audio.codec.mbe;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.audio.AbstractAudioModule;
import io.github.dsheirer.audio.squelch.ISquelchStateListener;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.IMessageListener;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableAudioPacketQueue;
import jmbe.iface.IAudioCodec;
import jmbe.iface.IAudioCodecLibrary;
import org.slf4j.Logger;
@@ -44,17 +45,18 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
public abstract class JmbeAudioModule extends AbstractAudioModule implements Listener<IMessage>, IMessageListener
public abstract class JmbeAudioModule extends AbstractAudioModule implements Listener<IMessage>, IMessageListener,
ISquelchStateListener
{
private static final Logger mLog = LoggerFactory.getLogger(JmbeAudioModule.class);
private static final String JMBE_AUDIO_LIBRARY = "JMBE";
private static List<String> mLibraryLoadStatusLogged = new ArrayList<>();
private IAudioCodec mAudioCodec;
private UserPreferences mUserPreferences;
private ReusableAudioPacketQueue mAudioPacketQueue = new ReusableAudioPacketQueue("JmbeAudioModule");
public JmbeAudioModule(UserPreferences userPreferences)
public JmbeAudioModule(UserPreferences userPreferences, AliasList aliasList)
{
super(aliasList);
mUserPreferences = userPreferences;
MyEventBus.getEventBus().register(this);
loadConverter();
@@ -73,11 +75,6 @@ public abstract class JmbeAudioModule extends AbstractAudioModule implements Lis
return getAudioCodec() != null;
}
protected ReusableAudioPacketQueue getAudioPacketQueue()
{
return mAudioPacketQueue;
}
@Override
public Listener<IMessage> getMessageListener()
{

View File

@@ -22,21 +22,9 @@
package io.github.dsheirer.audio.codec.mbe;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.dsheirer.audio.convert.thumbdv.ThumbDv;
import io.github.dsheirer.audio.convert.thumbdv.message.response.AmbeResponse;
import io.github.dsheirer.module.decode.p25.audio.VoiceFrame;
import io.github.dsheirer.record.wave.AudioPacketWaveRecorder;
import io.github.dsheirer.record.wave.WaveMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* Utility for converting MBE call sequences (*.mbe) to PCM wave audio format
*/
@@ -44,64 +32,64 @@ public class MBECallSequenceConverter
{
private final static Logger mLog = LoggerFactory.getLogger(MBECallSequenceConverter.class);
public static void convert(Path input, Path output) throws IOException
{
InputStream inputStream = Files.newInputStream(input);
ObjectMapper mapper = new ObjectMapper();
MBECallSequence sequence = mapper.readValue(inputStream, MBECallSequence.class);
convert(sequence, output);
}
public static void convert(MBECallSequence callSequence, Path outputPath)
{
if(callSequence == null || callSequence.isEncrypted())
{
throw new IllegalArgumentException("Cannot decode null or encrypted call sequence");
}
if(callSequence != null && !callSequence.isEncrypted())
{
ThumbDv.AudioProtocol protocol = ThumbDv.AudioProtocol.P25_PHASE2;
AudioPacketWaveRecorder recorder = new AudioPacketWaveRecorder(outputPath);
recorder.start();
long delayMillis = 0;
try(ThumbDv thumbDv = new ThumbDv(protocol, recorder))
{
thumbDv.start();
for(VoiceFrame voiceFrame: callSequence.getVoiceFrames())
{
mLog.debug("Frame [" + voiceFrame.getFrame() + "] + Hex [" + AmbeResponse.toHex(voiceFrame.getFrameBytes()) + "]");
thumbDv.decode(voiceFrame.getFrameBytes());
delayMillis += 30;
}
if(delayMillis > 0)
{
delayMillis += 1000;
try
{
Thread.sleep(delayMillis);
}
catch(InterruptedException ie)
{
}
}
}
catch(IOException ioe)
{
mLog.error("Error", ioe);
}
recorder.stop(Paths.get(outputPath.toString().replace(".tmp", ".wav")), new WaveMetadata());
}
}
public static void main(String[] args)
{
// public static void convert(Path input, Path output) throws IOException
// {
// InputStream inputStream = Files.newInputStream(input);
// ObjectMapper mapper = new ObjectMapper();
// MBECallSequence sequence = mapper.readValue(inputStream, MBECallSequence.class);
// convert(sequence, output);
// }
//
// public static void convert(MBECallSequence callSequence, Path outputPath)
// {
// if(callSequence == null || callSequence.isEncrypted())
// {
// throw new IllegalArgumentException("Cannot decode null or encrypted call sequence");
// }
//
// if(callSequence != null && !callSequence.isEncrypted())
// {
// ThumbDv.AudioProtocol protocol = ThumbDv.AudioProtocol.P25_PHASE2;
//
// AudioPacketWaveRecorder recorder = new AudioPacketWaveRecorder(outputPath);
// recorder.start();
//
// long delayMillis = 0;
//
// try(ThumbDv thumbDv = new ThumbDv(protocol, recorder))
// {
// thumbDv.start();
// for(VoiceFrame voiceFrame: callSequence.getVoiceFrames())
// {
// mLog.debug("Frame [" + voiceFrame.getFrame() + "] + Hex [" + AmbeResponse.toHex(voiceFrame.getFrameBytes()) + "]");
// thumbDv.decode(voiceFrame.getFrameBytes());
// delayMillis += 30;
// }
//
// if(delayMillis > 0)
// {
// delayMillis += 1000;
// try
// {
// Thread.sleep(delayMillis);
// }
// catch(InterruptedException ie)
// {
//
// }
// }
// }
// catch(IOException ioe)
// {
// mLog.error("Error", ioe);
// }
//
// recorder.stop(Paths.get(outputPath.toString().replace(".tmp", ".wav")), new WaveMetadata());
// }
// }
//
// public static void main(String[] args)
// {
// String mbe = "/home/denny/SDRTrunk/recordings/20190331085324_154250000_3_TS0_65084_6591007.mbe";
// String mbe = "/home/denny/SDRTrunk/recordings/20190331085324_154250000_2_TS1_65035.mbe";
//
@@ -118,5 +106,5 @@ public class MBECallSequenceConverter
// {
// mLog.error("Error", ioe);
// }
}
// }
}