mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
#1241 Adds user preference to enable/disable audio normalization prior to MP3 encoding/recording. (#1242)
Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
@@ -30,10 +30,8 @@ import io.github.dsheirer.audio.broadcast.shoutcast.v1.ShoutcastV1AudioBroadcast
|
||||
import io.github.dsheirer.audio.broadcast.shoutcast.v1.ShoutcastV1Configuration;
|
||||
import io.github.dsheirer.audio.broadcast.shoutcast.v2.ShoutcastV2AudioStreamingBroadcaster;
|
||||
import io.github.dsheirer.audio.broadcast.shoutcast.v2.ShoutcastV2Configuration;
|
||||
import io.github.dsheirer.audio.convert.IAudioConverter;
|
||||
import io.github.dsheirer.audio.convert.ISilenceGenerator;
|
||||
import io.github.dsheirer.audio.convert.InputAudioFormat;
|
||||
import io.github.dsheirer.audio.convert.MP3AudioConverter;
|
||||
import io.github.dsheirer.audio.convert.MP3Setting;
|
||||
import io.github.dsheirer.audio.convert.MP3SilenceGenerator;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
@@ -88,26 +86,6 @@ public class BroadcastFactory
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an audio convert to convert from 8 kHz PCM audio to the specified format
|
||||
*
|
||||
* @param configuration containing the requested output audio format
|
||||
* @return audio convert or null
|
||||
*/
|
||||
public static IAudioConverter getAudioConverter(BroadcastConfiguration configuration, InputAudioFormat inputAudioFormat,
|
||||
MP3Setting mp3Setting)
|
||||
{
|
||||
switch(configuration.getBroadcastFormat())
|
||||
{
|
||||
case MP3:
|
||||
return new MP3AudioConverter(inputAudioFormat, mp3Setting);
|
||||
default:
|
||||
mLog.info("Unrecognized broadcastAudio format: " + configuration.getBroadcastFormat().name());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a broadcastAudio configuration for the specified server type and format
|
||||
*
|
||||
|
||||
@@ -21,17 +21,16 @@ package io.github.dsheirer.audio.convert;
|
||||
import io.github.dsheirer.audio.AudioUtils;
|
||||
import io.github.dsheirer.dsp.filter.resample.RealResampler;
|
||||
import io.github.dsheirer.sample.ConversionUtils;
|
||||
import net.sourceforge.lame.lowlevel.LameEncoder;
|
||||
import net.sourceforge.lame.mp3.Lame;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import net.sourceforge.lame.lowlevel.LameEncoder;
|
||||
import net.sourceforge.lame.mp3.Lame;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Converts PCM audio packets to Mono, MP3 encoded audio
|
||||
@@ -46,16 +45,20 @@ public class MP3AudioConverter implements IAudioConverter
|
||||
private byte[] mOutputFramesBuffer;
|
||||
private InputAudioFormat mInputAudioFormat;
|
||||
|
||||
private boolean mNormalizeAudio;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param inputAudioFormat for the desired input sample rate and bit size (resampled from default 8 kHz as needed)
|
||||
* @param setting to configure the LAME encoder
|
||||
* @param normalizeAudio to normalize the audio gain prior to encoding
|
||||
*/
|
||||
public MP3AudioConverter(InputAudioFormat inputAudioFormat, MP3Setting setting)
|
||||
public MP3AudioConverter(InputAudioFormat inputAudioFormat, MP3Setting setting, boolean normalizeAudio)
|
||||
{
|
||||
mInputAudioFormat = inputAudioFormat;
|
||||
mEncoder = LameFactory.getLameEncoder(inputAudioFormat, setting);
|
||||
mNormalizeAudio = normalizeAudio;
|
||||
|
||||
//Resampling is only required if desired input sample rate is not system default of 8kHz
|
||||
if(inputAudioFormat != InputAudioFormat.SR_8000 && inputAudioFormat != InputAudioFormat.SR_32_8000)
|
||||
@@ -74,7 +77,11 @@ public class MP3AudioConverter implements IAudioConverter
|
||||
public List<byte[]> convert(List<float[]> audioPackets)
|
||||
{
|
||||
List<byte[]> converted = new ArrayList<>();
|
||||
audioPackets = AudioUtils.normalize(audioPackets);
|
||||
|
||||
if(mNormalizeAudio)
|
||||
{
|
||||
audioPackets = AudioUtils.normalize(audioPackets);
|
||||
}
|
||||
|
||||
if(mResampler != null)
|
||||
{
|
||||
|
||||
@@ -18,11 +18,10 @@
|
||||
*/
|
||||
package io.github.dsheirer.audio.convert;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class MP3SilenceGenerator implements ISilenceGenerator
|
||||
{
|
||||
@@ -40,7 +39,7 @@ public class MP3SilenceGenerator implements ISilenceGenerator
|
||||
public MP3SilenceGenerator(InputAudioFormat inputAudioFormat, MP3Setting setting)
|
||||
{
|
||||
mInputAudioFormat = inputAudioFormat;
|
||||
mMP3AudioConverter = new MP3AudioConverter(inputAudioFormat, setting);
|
||||
mMP3AudioConverter = new MP3AudioConverter(inputAudioFormat, setting, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,6 +25,7 @@ import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.mp3.MP3Preference;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.GridPane;
|
||||
@@ -44,7 +45,12 @@ public class MP3PreferenceEditor extends HBox
|
||||
private GridPane mEditorPane;
|
||||
private ComboBox<MP3Setting> mMP3SettingComboBox;
|
||||
private ComboBox<InputAudioFormat> mAudioSampleRateComboBox;
|
||||
private CheckBox mNormalizeAudioCheckBox;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param userPreferences
|
||||
*/
|
||||
public MP3PreferenceEditor(UserPreferences userPreferences)
|
||||
{
|
||||
mMP3Preference = userPreferences.getMP3Preference();
|
||||
@@ -65,6 +71,9 @@ public class MP3PreferenceEditor extends HBox
|
||||
Label topLabel = new Label("MP3 Encoder Preferences");
|
||||
mEditorPane.add(topLabel, 0, row++, 2, 1);
|
||||
|
||||
|
||||
mEditorPane.add(getNormalizeAudioCheckBox(), 1, row++);
|
||||
|
||||
Label label = new Label("(LAME) Encoder Setting:");
|
||||
GridPane.setHalignment(label, HPos.RIGHT);
|
||||
mEditorPane.add(label, 0, row);
|
||||
@@ -76,7 +85,7 @@ public class MP3PreferenceEditor extends HBox
|
||||
mEditorPane.add(getAudioSampleRateComboBox(), 1, row++);
|
||||
|
||||
Label notice = new Label("Note: sdrtrunk default 8 kHz audio rate is resampled to input sample rate before MP3 encoding");
|
||||
mEditorPane.add(notice, 0, row, 2, 1);
|
||||
mEditorPane.add(notice, 0, row++, 2, 1);
|
||||
}
|
||||
|
||||
return mEditorPane;
|
||||
@@ -109,4 +118,17 @@ public class MP3PreferenceEditor extends HBox
|
||||
|
||||
return mAudioSampleRateComboBox;
|
||||
}
|
||||
|
||||
private CheckBox getNormalizeAudioCheckBox()
|
||||
{
|
||||
if(mNormalizeAudioCheckBox == null)
|
||||
{
|
||||
mNormalizeAudioCheckBox = new CheckBox("Normalize Audio Before Encoding");
|
||||
mNormalizeAudioCheckBox.setSelected(mMP3Preference.isNormalizeAudioBeforeEncode());
|
||||
mNormalizeAudioCheckBox.onActionProperty().set(event ->
|
||||
mMP3Preference.setNormalizeAudioBeforeEncode(getNormalizeAudioCheckBox().isSelected()));
|
||||
}
|
||||
|
||||
return mNormalizeAudioCheckBox;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,11 +24,10 @@ import io.github.dsheirer.audio.convert.MP3Setting;
|
||||
import io.github.dsheirer.preference.Preference;
|
||||
import io.github.dsheirer.preference.PreferenceType;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import java.util.prefs.Preferences;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
/**
|
||||
* User preferences for playlists
|
||||
*/
|
||||
@@ -36,11 +35,14 @@ public class MP3Preference extends Preference
|
||||
{
|
||||
private static final String PREFERENCE_KEY_AUDIO_MP3_SETTING = "audio.mp3.setting";
|
||||
private static final String PREFERENCE_KEY_AUDIO_MP3_SAMPLE_RATE = "audio.mp3.sample.rate";
|
||||
private static final String PREFERENCE_KEY_AUDIO_MP3_NORMALIZE_BEFORE_ENCODE = "audio.mp3.normalize.before.encode";
|
||||
private final static Logger mLog = LoggerFactory.getLogger(MP3Preference.class);
|
||||
private Preferences mPreferences = Preferences.userNodeForPackage(MP3Preference.class);
|
||||
private InputAudioFormat mInputAudioFormat;
|
||||
private MP3Setting mMP3Setting;
|
||||
|
||||
private Boolean mNormalizeAudio;
|
||||
|
||||
/**
|
||||
* Constructs this preference with an update listener
|
||||
* @param updateListener to receive notifications whenever these preferences change
|
||||
@@ -128,4 +130,29 @@ public class MP3Preference extends Preference
|
||||
mPreferences.put(PREFERENCE_KEY_AUDIO_MP3_SAMPLE_RATE, mInputAudioFormat.name());
|
||||
notifyPreferenceUpdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the user prefers to normalize audio before encoding to MP3
|
||||
* @return true to normalize
|
||||
*/
|
||||
public boolean isNormalizeAudioBeforeEncode()
|
||||
{
|
||||
if(mNormalizeAudio == null)
|
||||
{
|
||||
mNormalizeAudio = mPreferences.getBoolean(PREFERENCE_KEY_AUDIO_MP3_NORMALIZE_BEFORE_ENCODE, false);
|
||||
}
|
||||
|
||||
return mNormalizeAudio;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the preferenced to normalize audio before encoding to MP3.
|
||||
* @param normalizeAudio true to normalize
|
||||
*/
|
||||
public void setNormalizeAudioBeforeEncode(boolean normalizeAudio)
|
||||
{
|
||||
mNormalizeAudio = normalizeAudio;
|
||||
mPreferences.putBoolean(PREFERENCE_KEY_AUDIO_MP3_NORMALIZE_BEFORE_ENCODE, normalizeAudio);
|
||||
notifyPreferenceUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,6 @@ import io.github.dsheirer.record.wave.AudioMetadata;
|
||||
import io.github.dsheirer.record.wave.AudioMetadataUtils;
|
||||
import io.github.dsheirer.record.wave.WaveWriter;
|
||||
import io.github.dsheirer.sample.ConversionUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
@@ -39,6 +36,8 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Recording utility for audio segments
|
||||
@@ -96,7 +95,9 @@ public class AudioSegmentRecorder
|
||||
InputAudioFormat inputAudioFormat = userPreferences.getMP3Preference().getAudioSampleRate();
|
||||
MP3Setting mp3Setting = userPreferences.getMP3Preference().getMP3Setting();
|
||||
|
||||
MP3AudioConverter converter = new MP3AudioConverter(inputAudioFormat, mp3Setting);
|
||||
boolean normalizeAudio = userPreferences.getMP3Preference().isNormalizeAudioBeforeEncode();
|
||||
|
||||
MP3AudioConverter converter = new MP3AudioConverter(inputAudioFormat, mp3Setting, normalizeAudio);
|
||||
List<byte[]> mp3Frames = converter.convert(audioSegment.getAudioBuffers());
|
||||
for(byte[] mp3Frame: mp3Frames)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user