#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

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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/>
* * *****************************************************************************
* 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.module.decode;
@@ -135,16 +132,8 @@ public class DecoderFactory
{
List<Module> modules = new ArrayList<Module>();
//Adds an alias action manager when there are alias actions specified
if(channel.getAliasListName() != null && !channel.getAliasListName().isEmpty())
{
AliasList aliasList = aliasModel.getAliasList(channel.getAliasListName());
if(aliasList != null && aliasList.hasAliasActions())
{
modules.add(new AliasActionManager(aliasList));
}
}
AliasList aliasList = aliasModel.getAliasList(channel.getAliasListName());
modules.add(new AliasActionManager(aliasList));
ChannelType channelType = channel.getChannelType();
@@ -157,12 +146,13 @@ public class DecoderFactory
modules.add(new AMDecoder(decodeConfig));
modules.add(new AlwaysUnsquelchedDecoderState(DecoderType.AM, channel.getName()));
AudioModule audioModuleAM = new AudioModule(aliasList);
modules.add(audioModuleAM);
//Check if the user wants all audio recorded ..
if(((DecodeConfigAM)decodeConfig).getRecordAudio())
{
AudioModule audioModuleAM = new AudioModule();
audioModuleAM.setRecordAudio(true);
modules.add(audioModuleAM);
}
if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER)
@@ -173,7 +163,7 @@ public class DecoderFactory
case NBFM:
modules.add(new NBFMDecoder(decodeConfig));
modules.add(new AlwaysUnsquelchedDecoderState(DecoderType.NBFM, channel.getName()));
AudioModule audioModuleFM = new AudioModule();
AudioModule audioModuleFM = new AudioModule(aliasList);
//Check if the user wants all audio recorded ..
if(((DecodeConfigNBFM)decodeConfig).getRecordAudio())
@@ -190,7 +180,7 @@ public class DecoderFactory
MessageDirection direction = ((DecodeConfigLTRStandard)decodeConfig).getMessageDirection();
modules.add(new LTRStandardDecoder(null, direction));
modules.add(new LTRStandardDecoderState());
modules.add(new AudioModule());
modules.add(new AudioModule(aliasList));
if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER)
{
modules.add(new FMDemodulatorModule(FM_CHANNEL_BANDWIDTH, DEMODULATED_AUDIO_SAMPLE_RATE));
@@ -199,7 +189,7 @@ public class DecoderFactory
case LTR_NET:
modules.add(new LTRNetDecoder((DecodeConfigLTRNet)decodeConfig));
modules.add(new LTRNetDecoderState());
modules.add(new AudioModule());
modules.add(new AudioModule(aliasList));
if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER)
{
modules.add(new FMDemodulatorModule(FM_CHANNEL_BANDWIDTH, DEMODULATED_AUDIO_SAMPLE_RATE));
@@ -210,7 +200,7 @@ public class DecoderFactory
ChannelMap channelMap = channelMapModel.getChannelMap(mptConfig.getChannelMapName());
Sync sync = mptConfig.getSync();
modules.add(new MPT1327Decoder(sync));
modules.add(new AudioModule());
modules.add(new AudioModule(aliasList));
SourceType sourceType = channel.getSourceConfiguration().getSourceType();
if(sourceType == SourceType.TUNER || sourceType == SourceType.TUNER_MULTIPLE_FREQUENCIES)
{
@@ -242,7 +232,7 @@ public class DecoderFactory
case PASSPORT:
modules.add(new PassportDecoder(decodeConfig));
modules.add(new PassportDecoderState());
modules.add(new AudioModule());
modules.add(new AudioModule(aliasList));
if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER)
{
modules.add(new FMDemodulatorModule(FM_CHANNEL_BANDWIDTH, DEMODULATED_AUDIO_SAMPLE_RATE));
@@ -275,7 +265,7 @@ public class DecoderFactory
modules.add(new P25P1DecoderState(channel));
}
modules.add(new P25P1AudioModule(userPreferences));
modules.add(new P25P1AudioModule(userPreferences, aliasList));
//Add a channel rotation monitor when we have multiple control channel frequencies specified
if(channel.getSourceConfiguration() instanceof SourceConfigTunerMultipleFrequency &&
@@ -291,8 +281,8 @@ public class DecoderFactory
modules.add(new P25P2DecoderState(channel, 0));
modules.add(new P25P2DecoderState(channel, 1));
modules.add(new P25P2AudioModule(userPreferences, 0));
modules.add(new P25P2AudioModule(userPreferences, 1));
modules.add(new P25P2AudioModule(userPreferences, 0, aliasList));
modules.add(new P25P2AudioModule(userPreferences, 1, aliasList));
break;
default:
throw new IllegalArgumentException("Unknown decoder type [" + decodeConfig.getDecoderType().toString() + "]");

View File

@@ -1,26 +1,24 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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/>
* * *****************************************************************************
* 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.module.decode.p25.audio;
import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.audio.codec.mbe.ImbeAudioModule;
import io.github.dsheirer.audio.squelch.SquelchState;
import io.github.dsheirer.audio.squelch.SquelchStateEvent;
@@ -32,7 +30,6 @@ import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU2Message;
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDUMessage;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
public class P25P1AudioModule extends ImbeAudioModule
{
@@ -43,9 +40,15 @@ public class P25P1AudioModule extends ImbeAudioModule
private NonClippingGain mGain = new NonClippingGain(5.0f, 0.95f);
private LDU1Message mCachedLDU1Message = null;
public P25P1AudioModule(UserPreferences userPreferences)
public P25P1AudioModule(UserPreferences userPreferences, AliasList aliasList)
{
super(userPreferences);
super(userPreferences, aliasList);
}
@Override
protected int getTimeslot()
{
return 0;
}
@Override
@@ -63,21 +66,6 @@ public class P25P1AudioModule extends ImbeAudioModule
@Override
public void start()
{
}
@Override
public void stop()
{
if(hasAudioPacketListener())
{
ReusableAudioPacket endAudioPacket = getAudioPacketQueue().getEndAudioBuffer();
endAudioPacket.resetAttributes();
endAudioPacket.setAudioChannelId(getAudioChannelId());
endAudioPacket.setIdentifierCollection(getIdentifierCollection().copyOf());
endAudioPacket.incrementUserCount();
getAudioPacketListener().receive(endAudioPacket);
}
}
/**
@@ -89,7 +77,7 @@ public class P25P1AudioModule extends ImbeAudioModule
*/
public void receive(IMessage message)
{
if(hasAudioCodec() && hasAudioPacketListener())
if(hasAudioCodec())
{
if(mEncryptedCallStateEstablished)
{
@@ -139,16 +127,8 @@ public class P25P1AudioModule extends ImbeAudioModule
for(byte[] frame : ldu.getIMBEFrames())
{
float[] audio = getAudioCodec().getAudio(frame);
audio = mGain.apply(audio);
ReusableAudioPacket audioPacket = getAudioPacketQueue().getBuffer(audio.length);
audioPacket.resetAttributes();
audioPacket.setAudioChannelId(getAudioChannelId());
audioPacket.setIdentifierCollection(getIdentifierCollection().copyOf());
audioPacket.loadAudioFrom(audio);
getAudioPacketListener().receive(audioPacket);
addAudio(audio);
}
}
else
@@ -169,16 +149,7 @@ public class P25P1AudioModule extends ImbeAudioModule
{
if(event.getSquelchState() == SquelchState.SQUELCH)
{
if(hasAudioPacketListener())
{
ReusableAudioPacket endAudioPacket = getAudioPacketQueue().getEndAudioBuffer();
endAudioPacket.resetAttributes();
endAudioPacket.setAudioChannelId(getAudioChannelId());
endAudioPacket.setIdentifierCollection(getIdentifierCollection().copyOf());
endAudioPacket.incrementUserCount();
getAudioPacketListener().receive(endAudioPacket);
}
closeAudioSegment();
mEncryptedCallStateEstablished = false;
mEncryptedCall = false;
mCachedLDU1Message = null;

View File

@@ -1,27 +1,25 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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/>
* * *****************************************************************************
* 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.module.decode.p25.audio;
import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.audio.codec.mbe.AmbeAudioModule;
import io.github.dsheirer.audio.squelch.SquelchState;
import io.github.dsheirer.audio.squelch.SquelchStateEvent;
@@ -39,7 +37,6 @@ import io.github.dsheirer.module.decode.p25.phase2.message.mac.structure.PushToT
import io.github.dsheirer.module.decode.p25.phase2.timeslot.AbstractVoiceTimeslot;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
import jmbe.iface.IAudioWithMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -70,14 +67,14 @@ public class P25P2AudioModule extends AmbeAudioModule implements IdentifierUpdat
private boolean mEncryptedCallStateEstablished = false;
private boolean mEncryptedCall = false;
public P25P2AudioModule(UserPreferences userPreferences, int timeslot)
public P25P2AudioModule(UserPreferences userPreferences, int timeslot, AliasList aliasList)
{
super(userPreferences);
super(userPreferences, aliasList);
mTimeslot = timeslot;
getIdentifierCollection().setTimeslot(timeslot);
}
private int getTimeslot()
protected int getTimeslot()
{
return mTimeslot;
}
@@ -104,12 +101,6 @@ public class P25P2AudioModule extends AmbeAudioModule implements IdentifierUpdat
reset();
}
@Override
public void stop()
{
}
/**
* Primary message processing method for processing voice timeslots and Push-To-Talk MAC messages
*
@@ -149,7 +140,7 @@ public class P25P2AudioModule extends AmbeAudioModule implements IdentifierUpdat
//There should not be any pending voice timeslots to process since the PTT message is the first in
//the audio call sequence
}
else if(message instanceof EncryptionSynchronizationSequence)
else if(message instanceof EncryptionSynchronizationSequence && message.isValid())
{
mEncryptedCallStateEstablished = true;
mEncryptedCall = ((EncryptionSynchronizationSequence)message).isEncrypted();
@@ -174,7 +165,7 @@ public class P25P2AudioModule extends AmbeAudioModule implements IdentifierUpdat
private void processAudio(List<BinaryMessage> voiceFrames)
{
if(hasAudioCodec() && hasAudioPacketListener())
if(hasAudioCodec())
{
for(BinaryMessage voiceFrame: voiceFrames)
{
@@ -182,13 +173,7 @@ public class P25P2AudioModule extends AmbeAudioModule implements IdentifierUpdat
IAudioWithMetadata audioWithMetadata = getAudioCodec().getAudioWithMetadata(voiceFrameBytes);
processMetadata(audioWithMetadata);
ReusableAudioPacket audioPacket = getAudioPacketQueue().getBuffer(audioWithMetadata.getAudio().length);
audioPacket.resetAttributes();
audioPacket.setAudioChannelId(getAudioChannelId());
audioPacket.setIdentifierCollection(getIdentifierCollection().copyOf());
audioPacket.loadAudioFrom(audioWithMetadata.getAudio());
getAudioPacketListener().receive(audioPacket);
addAudio(audioWithMetadata.getAudio());
}
}
}
@@ -356,19 +341,13 @@ public class P25P2AudioModule extends AmbeAudioModule implements IdentifierUpdat
@Override
public void receive(SquelchStateEvent event)
{
if(event.getTimeslot() == getTimeslot() && event.getSquelchState() == SquelchState.SQUELCH)
if(event.getTimeslot() == getTimeslot())
{
if(hasAudioPacketListener())
if(event.getSquelchState() == SquelchState.SQUELCH)
{
ReusableAudioPacket endAudioPacket = getAudioPacketQueue().getEndAudioBuffer();
endAudioPacket.resetAttributes();
endAudioPacket.setAudioChannelId(getAudioChannelId());
endAudioPacket.setIdentifierCollection(getIdentifierCollection().copyOf());
endAudioPacket.incrementUserCount();
getAudioPacketListener().receive(endAudioPacket);
closeAudioSegment();
reset();
}
reset();
}
}
}

View File

@@ -1,28 +1,24 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2020 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/>
* * *****************************************************************************
* 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.module.decode.p25.phase2;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.audio.AudioPacketManager;
import io.github.dsheirer.audio.playback.AudioPlaybackManager;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.controller.channel.Channel;
@@ -213,13 +209,13 @@ public class P25P2MessageFramer implements Listener<Dibit>
UserPreferences userPreferences = new UserPreferences();
AliasModel aliasModel = new AliasModel();
SourceManager sourceManager = new SourceManager(null, new SettingsManager(new TunerConfigurationModel()), userPreferences);
AudioPlaybackManager audioPlaybackManager = new AudioPlaybackManager(sourceManager.getMixerManager());
AudioPlaybackManager audioPlaybackManager = new AudioPlaybackManager(userPreferences);
//Audio packets are routed through the audio packet manager for metadata enrichment and then
//distributed to the audio packet processors (ie playback, recording, streaming, etc.)
AudioPacketManager audioPacketManager = new AudioPacketManager(aliasModel);
audioPacketManager.addListener(audioPlaybackManager);
audioPacketManager.start();
// AudioPacketManager audioPacketManager = new AudioPacketManager(aliasModel);
// audioPacketManager.addListener(audioPlaybackManager);
// audioPacketManager.start();
Channel channel = new Channel();
DecodeConfigP25Phase2 decodeP2 = new DecodeConfigP25Phase2();
@@ -230,7 +226,7 @@ public class P25P2MessageFramer implements Listener<Dibit>
MessageInjectionModule messageInjectionModule = new MessageInjectionModule();
modules.add(messageInjectionModule);
ProcessingChain processingChain = new ProcessingChain(channel, aliasModel);
processingChain.addAudioPacketListener(audioPacketManager);
// processingChain.addAudioPacketListener(audioPacketManager);
processingChain.addModules(modules);
processingChain.start();