From 7749fd10f18853b37542dbcf02fe881c2f513758 Mon Sep 17 00:00:00 2001 From: Denny Sheirer Date: Wed, 16 Mar 2022 05:00:07 -0400 Subject: [PATCH] #1170 resolves issue with MP3 encoded audio raspy. Adds auto-clipping of PCM audio prior to encoding and adds 4x new 32-bit input sample rate selections to choose from, in addition to original 4x 16-bit input formats. (#1171) Co-authored-by: Dennis Sheirer --- .../alias/action/beep/BeepAction.java | 4 +- .../github/dsheirer/audio/AudioFormats.java | 161 +++++++++--------- .../broadcast/AudioStreamingBroadcaster.java | 6 +- .../audio/broadcast/BroadcastFactory.java | 24 +-- .../BroadcastifyCallBroadcaster.java | 4 +- .../icecast/IcecastAudioBroadcaster.java | 6 +- .../icecast/IcecastHTTPAudioBroadcaster.java | 6 +- .../icecast/IcecastTCPAudioBroadcaster.java | 6 +- .../v1/ShoutcastV1AudioBroadcaster.java | 6 +- .../ShoutcastV2AudioStreamingBroadcaster.java | 6 +- ...oSampleRate.java => InputAudioFormat.java} | 23 ++- .../dsheirer/audio/convert/LameFactory.java | 10 +- .../audio/convert/MP3AudioConverter.java | 35 +++- .../audio/convert/MP3FrameInspector.java | 2 +- .../audio/convert/MP3SilenceGenerator.java | 12 +- .../audio/playback/MonoAudioOutput.java | 2 +- .../audio/playback/StereoAudioOutput.java | 12 +- .../preference/mp3/MP3PreferenceEditor.java | 8 +- .../playback/PlaybackPreferenceEditor.java | 4 +- .../preference/mp3/MP3Preference.java | 24 +-- .../dsheirer/record/AudioSegmentRecorder.java | 8 +- .../dsheirer/sample/ConversionUtils.java | 43 ++++- .../dsheirer/source/mixer/MixerManager.java | 16 +- 23 files changed, 245 insertions(+), 183 deletions(-) rename src/main/java/io/github/dsheirer/audio/convert/{AudioSampleRate.java => InputAudioFormat.java} (65%) diff --git a/src/main/java/io/github/dsheirer/alias/action/beep/BeepAction.java b/src/main/java/io/github/dsheirer/alias/action/beep/BeepAction.java index f6b6457b..c18fa9b9 100644 --- a/src/main/java/io/github/dsheirer/alias/action/beep/BeepAction.java +++ b/src/main/java/io/github/dsheirer/alias/action/beep/BeepAction.java @@ -71,7 +71,7 @@ public class BeepAction extends RecurringAction @Override public void performAction(Alias alias, IMessage message ) { - DataLine.Info info = new DataLine.Info(Clip.class, AudioFormats.PCM_SIGNED_8_KHZ_16BITS_MONO); + DataLine.Info info = new DataLine.Info(Clip.class, AudioFormats.PCM_SIGNED_8000_HZ_16_BIT_MONO); if(!AudioSystem.isLineSupported(info)) { @@ -82,7 +82,7 @@ public class BeepAction extends RecurringAction try { Clip clip = (Clip)AudioSystem.getLine(info); - clip.open(AudioFormats.PCM_SIGNED_8_KHZ_16BITS_MONO, mToneBytes, 0, mToneBytes.length); + clip.open(AudioFormats.PCM_SIGNED_8000_HZ_16_BIT_MONO, mToneBytes, 0, mToneBytes.length); clip.start(); } catch(Exception e) diff --git a/src/main/java/io/github/dsheirer/audio/AudioFormats.java b/src/main/java/io/github/dsheirer/audio/AudioFormats.java index ea901d46..a0653bc3 100644 --- a/src/main/java/io/github/dsheirer/audio/AudioFormats.java +++ b/src/main/java/io/github/dsheirer/audio/AudioFormats.java @@ -24,112 +24,107 @@ import javax.sound.sampled.DataLine; import javax.sound.sampled.Line; import javax.sound.sampled.SourceDataLine; +/** + * Audio formats + */ public class AudioFormats { - public static final AudioFormat.Encoding IMBE_ENCODING = - new AudioFormat.Encoding( "IMBE" ); - public static final boolean LITTLE_ENDIAN = false; - public static final boolean BIG_ENDIAN = true; - - public static final float IMBE_FRAME_RATE = 50; - public static final float IMBE_SAMPLE_RATE = 50; public static final float PCM_8_KHZ_RATE = 8000; public static final float PCM_16_KHZ_RATE = 16000; public static final float PCM_22050_HZ_RATE = 22050; public static final float PCM_44100_HZ_RATE = 44100; - public static final float PCM_48_KHZ_RATE = 48000; - - public static final int IMBE_FRAME_SIZE_BYTES = 18; - public static final int IMBE_SAMPLE_SIZE_BITS = 144; + public static final int ONE_CHANNEL = 1; public static final int TWO_CHANNELS = 2; - public static final int PCM_SAMPLE_SIZE_BITS = 16; - public static final int PCM_FRAME_SIZE_BYTES_MONO = 2; - public static final int PCM_FRAME_SIZE_BYTES_STEREO = 4; - - public static AudioFormat IMBE_AUDIO_FORMAT = - new AudioFormat( IMBE_ENCODING, - IMBE_SAMPLE_RATE, - IMBE_SAMPLE_SIZE_BITS, - ONE_CHANNEL, - IMBE_FRAME_SIZE_BYTES, - IMBE_FRAME_RATE, - LITTLE_ENDIAN ); - - public static AudioFormat PCM_SIGNED_8_KHZ_16BITS_MONO = - new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, - PCM_8_KHZ_RATE, - PCM_SAMPLE_SIZE_BITS, - ONE_CHANNEL, - PCM_FRAME_SIZE_BYTES_MONO, - PCM_8_KHZ_RATE, - LITTLE_ENDIAN ); + public static final int PCM_SAMPLE_SIZE_16_BITS = 16; + public static final int PCM_SAMPLE_SIZE_32_BITS = 32; + public static final int PCM_FRAME_SIZE_BYTES_16_BIT_MONO = 2; + public static final int PCM_FRAME_SIZE_BYTES_32_BIT_MONO = 4; + public static final int PCM_FRAME_SIZE_BYTES_16_BIT_STEREO = 4; - public static AudioFormat PCM_SIGNED_16_KHZ_16BITS_MONO = - new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, - PCM_16_KHZ_RATE, - PCM_SAMPLE_SIZE_BITS, - ONE_CHANNEL, - PCM_FRAME_SIZE_BYTES_MONO, - PCM_16_KHZ_RATE, - LITTLE_ENDIAN ); - - public static AudioFormat PCM_SIGNED_22050_HZ_16BITS_MONO = - new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, - PCM_22050_HZ_RATE, - PCM_SAMPLE_SIZE_BITS, - ONE_CHANNEL, - PCM_FRAME_SIZE_BYTES_MONO, - PCM_22050_HZ_RATE, - LITTLE_ENDIAN ); - - public static AudioFormat PCM_SIGNED_44100_HZ_16BITS_MONO = - new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, - PCM_44100_HZ_RATE, - PCM_SAMPLE_SIZE_BITS, - ONE_CHANNEL, - PCM_FRAME_SIZE_BYTES_MONO, - PCM_44100_HZ_RATE, - LITTLE_ENDIAN ); - - public static AudioFormat PCM_SIGNED_8KHZ_16BITS_STEREO = - new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, + public static AudioFormat PCM_SIGNED_8000_HZ_32_BIT_MONO = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, PCM_8_KHZ_RATE, - PCM_SAMPLE_SIZE_BITS, + PCM_SAMPLE_SIZE_32_BITS, + ONE_CHANNEL, + PCM_FRAME_SIZE_BYTES_32_BIT_MONO, + PCM_8_KHZ_RATE, + LITTLE_ENDIAN ); + + public static AudioFormat PCM_SIGNED_8000_HZ_16_BIT_MONO = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, + PCM_8_KHZ_RATE, + PCM_SAMPLE_SIZE_16_BITS, + ONE_CHANNEL, + PCM_FRAME_SIZE_BYTES_16_BIT_MONO, + PCM_8_KHZ_RATE, + LITTLE_ENDIAN ); + + public static AudioFormat PCM_SIGNED_16000_HZ_16_BIT_MONO = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, + PCM_16_KHZ_RATE, + PCM_SAMPLE_SIZE_16_BITS, + ONE_CHANNEL, + PCM_FRAME_SIZE_BYTES_16_BIT_MONO, + PCM_16_KHZ_RATE, + LITTLE_ENDIAN ); + + public static AudioFormat PCM_SIGNED_16000_HZ_32_BIT_MONO = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, + PCM_16_KHZ_RATE, + PCM_SAMPLE_SIZE_32_BITS, + ONE_CHANNEL, + PCM_FRAME_SIZE_BYTES_32_BIT_MONO, + PCM_16_KHZ_RATE, + LITTLE_ENDIAN ); + + public static AudioFormat PCM_SIGNED_22050_HZ_16_BIT_MONO = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, + PCM_22050_HZ_RATE, + PCM_SAMPLE_SIZE_16_BITS, + ONE_CHANNEL, + PCM_FRAME_SIZE_BYTES_16_BIT_MONO, + PCM_22050_HZ_RATE, + LITTLE_ENDIAN ); + + public static AudioFormat PCM_SIGNED_22050_HZ_32_BIT_MONO = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, + PCM_22050_HZ_RATE, + PCM_SAMPLE_SIZE_32_BITS, + ONE_CHANNEL, + PCM_FRAME_SIZE_BYTES_32_BIT_MONO, + PCM_22050_HZ_RATE, + LITTLE_ENDIAN ); + + public static AudioFormat PCM_SIGNED_44100_HZ_16_BIT_MONO = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, + PCM_44100_HZ_RATE, + PCM_SAMPLE_SIZE_16_BITS, + ONE_CHANNEL, + PCM_FRAME_SIZE_BYTES_16_BIT_MONO, + PCM_44100_HZ_RATE, + LITTLE_ENDIAN ); + + public static AudioFormat PCM_SIGNED_44100_HZ_32_BIT_MONO = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, + PCM_44100_HZ_RATE, + PCM_SAMPLE_SIZE_32_BITS, + ONE_CHANNEL, + PCM_FRAME_SIZE_BYTES_32_BIT_MONO, + PCM_44100_HZ_RATE, + LITTLE_ENDIAN ); + + public static AudioFormat PCM_SIGNED_8000_HZ_16BITS_STEREO = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, + PCM_8_KHZ_RATE, + PCM_SAMPLE_SIZE_16_BITS, TWO_CHANNELS, - PCM_FRAME_SIZE_BYTES_STEREO, + PCM_FRAME_SIZE_BYTES_16_BIT_STEREO, PCM_8_KHZ_RATE, LITTLE_ENDIAN ); - public static AudioFormat PCM_SIGNED_48KHZ_16BITS_MONO = - new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, - PCM_48_KHZ_RATE, - PCM_SAMPLE_SIZE_BITS, - ONE_CHANNEL, - PCM_FRAME_SIZE_BYTES_MONO, - PCM_48_KHZ_RATE, - LITTLE_ENDIAN ); - - public static AudioFormat PCM_SIGNED_48KHZ_16BITS_STEREO = - new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, - PCM_48_KHZ_RATE, - PCM_SAMPLE_SIZE_BITS, - TWO_CHANNELS, - PCM_FRAME_SIZE_BYTES_STEREO, - PCM_48_KHZ_RATE, - LITTLE_ENDIAN ); - /** * Source Data Line Info for a 48 kHz, 16-bits signed PCM one channel */ public static final Line.Info MONO_SOURCE_DATALINE_INFO = - new DataLine.Info( SourceDataLine.class, PCM_SIGNED_8_KHZ_16BITS_MONO); + new DataLine.Info( SourceDataLine.class, PCM_SIGNED_8000_HZ_16_BIT_MONO); /** * Source Data Line Info for a 48 kHz, 16-bits signed PCM two channels */ public static final Line.Info STEREO_SOURCE_DATALINE_INFO = - new DataLine.Info( SourceDataLine.class, PCM_SIGNED_8KHZ_16BITS_STEREO); + new DataLine.Info( SourceDataLine.class, PCM_SIGNED_8000_HZ_16BITS_STEREO); } diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/AudioStreamingBroadcaster.java b/src/main/java/io/github/dsheirer/audio/broadcast/AudioStreamingBroadcaster.java index 289d22e3..1b2dc456 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/AudioStreamingBroadcaster.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/AudioStreamingBroadcaster.java @@ -18,8 +18,8 @@ */ package io.github.dsheirer.audio.broadcast; -import io.github.dsheirer.audio.convert.AudioSampleRate; import io.github.dsheirer.audio.convert.ISilenceGenerator; +import io.github.dsheirer.audio.convert.InputAudioFormat; import io.github.dsheirer.audio.convert.MP3Setting; import io.github.dsheirer.identifier.IdentifierCollection; import io.github.dsheirer.util.ThreadPool; @@ -73,13 +73,13 @@ public abstract class AudioStreamingBroadcaster new RealResampler(8000, 16000, 4096, 512); - case SR_22050 -> new RealResampler(8000, 22050, 4096, 512); - case SR_44100 -> new RealResampler(8000, 44100, 4096, 512); + case SR_16000, SR_32_16000 -> new RealResampler(8000, 16000, 4096, 512); + case SR_22050, SR_32_22050 -> new RealResampler(8000, 22050, 4096, 512); + case SR_44100, SR_32_44100 -> new RealResampler(8000, 44100, 4096, 512); default -> throw new IllegalArgumentException("Unrecognized sample rate for resampling: " + sampleRate); }; } diff --git a/src/main/java/io/github/dsheirer/audio/convert/MP3AudioConverter.java b/src/main/java/io/github/dsheirer/audio/convert/MP3AudioConverter.java index 52f1c267..75a5416b 100644 --- a/src/main/java/io/github/dsheirer/audio/convert/MP3AudioConverter.java +++ b/src/main/java/io/github/dsheirer/audio/convert/MP3AudioConverter.java @@ -32,6 +32,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +/** + * Converts PCM audio packets to Mono, MP3 encoded audio + */ public class MP3AudioConverter implements IAudioConverter { private final static Logger mLog = LoggerFactory.getLogger( MP3AudioConverter.class ); @@ -40,25 +43,33 @@ public class MP3AudioConverter implements IAudioConverter private RealResampler mResampler; private ByteArrayOutputStream mMP3EncodedFramesStream = new ByteArrayOutputStream(); private byte[] mOutputFramesBuffer; + private InputAudioFormat mInputAudioFormat; /** - * Converts PCM 16-bit Little Endian audio packets to Mono, MP3 compressed audio. + * Constructs an instance. * - * @param audioSampleRate for the desired input sample rate (resampled from default 8 kHz as needed) + * @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 */ - public MP3AudioConverter(AudioSampleRate audioSampleRate, MP3Setting setting) + public MP3AudioConverter(InputAudioFormat inputAudioFormat, MP3Setting setting) { - mEncoder = LameFactory.getLameEncoder(audioSampleRate, setting); + mInputAudioFormat = inputAudioFormat; + mEncoder = LameFactory.getLameEncoder(inputAudioFormat, setting); - if(audioSampleRate != AudioSampleRate.SR_8000) + //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) { - mResampler = LameFactory.getResampler(audioSampleRate); + mResampler = LameFactory.getResampler(inputAudioFormat); } mOutputFramesBuffer = new byte[mEncoder.getPCMBufferSize()]; } + /** + * Converts the list of PCM audio packets to MP3 encoded. + * @param audioPackets of PCM audio sampled at 8 kHz + * @return encoded MP3 audio + */ public List convert(List audioPackets) { List converted = new ArrayList<>(); @@ -70,7 +81,17 @@ public class MP3AudioConverter implements IAudioConverter for(int x = 0; x < audioPackets.size(); x++) { - byte[] bytesToEncode = ConversionUtils.convertToSigned16BitSamples(audioPackets.get(x)).array(); + byte[] bytesToEncode = null; + + if(mInputAudioFormat.getAudioFormat().getSampleSizeInBits() == 16) + { + bytesToEncode = ConversionUtils.convertToSigned16BitSamples(audioPackets.get(x)).array(); + } + else + { + bytesToEncode = ConversionUtils.convertToSigned32BitSamples(audioPackets.get(x)).array(); + } + int bytesToEncodePointer = 0; int inputChunkSize = FastMath.min(mOutputFramesBuffer.length, bytesToEncode.length); diff --git a/src/main/java/io/github/dsheirer/audio/convert/MP3FrameInspector.java b/src/main/java/io/github/dsheirer/audio/convert/MP3FrameInspector.java index b3e397ac..d6054f64 100644 --- a/src/main/java/io/github/dsheirer/audio/convert/MP3FrameInspector.java +++ b/src/main/java/io/github/dsheirer/audio/convert/MP3FrameInspector.java @@ -58,7 +58,7 @@ public class MP3FrameInspector public static void main(String[] args) { mLog.info("Starting ..."); - MP3SilenceGenerator gen = new MP3SilenceGenerator(AudioSampleRate.SR_8000, MP3Setting.getDefault()); + MP3SilenceGenerator gen = new MP3SilenceGenerator(InputAudioFormat.SR_8000, MP3Setting.getDefault()); List audio = gen.generate(173); diff --git a/src/main/java/io/github/dsheirer/audio/convert/MP3SilenceGenerator.java b/src/main/java/io/github/dsheirer/audio/convert/MP3SilenceGenerator.java index 12ff3df5..ff1f5b85 100644 --- a/src/main/java/io/github/dsheirer/audio/convert/MP3SilenceGenerator.java +++ b/src/main/java/io/github/dsheirer/audio/convert/MP3SilenceGenerator.java @@ -31,16 +31,16 @@ public class MP3SilenceGenerator implements ISilenceGenerator public static final boolean CONSTANT_BIT_RATE = false; private MP3AudioConverter mMP3AudioConverter; - private AudioSampleRate mAudioSampleRate; + private InputAudioFormat mInputAudioFormat; private byte[] mPreviousPartialFrameData; /** * Generates MP3 audio silence frames */ - public MP3SilenceGenerator(AudioSampleRate audioSampleRate, MP3Setting setting) + public MP3SilenceGenerator(InputAudioFormat inputAudioFormat, MP3Setting setting) { - mAudioSampleRate = audioSampleRate; - mMP3AudioConverter = new MP3AudioConverter(audioSampleRate, setting); + mInputAudioFormat = inputAudioFormat; + mMP3AudioConverter = new MP3AudioConverter(inputAudioFormat, setting); } /** @@ -53,7 +53,7 @@ public class MP3SilenceGenerator implements ISilenceGenerator double duration_secs = (double)duration_ms / 1000.0; //We generate silence at 8000 kHz, because it gets resampled by the silence generator to the target rate - int length = (int)(duration_secs * AudioSampleRate.SR_8000.getSampleRate()); + int length = (int)(duration_secs * InputAudioFormat.SR_8000.getSampleRate()); List silenceBuffers = new ArrayList<>(); @@ -94,7 +94,7 @@ public class MP3SilenceGenerator implements ISilenceGenerator { mLog.debug("Starting ..."); - MP3SilenceGenerator generator = new MP3SilenceGenerator(AudioSampleRate.SR_44100, MP3Setting.VBR_7); + MP3SilenceGenerator generator = new MP3SilenceGenerator(InputAudioFormat.SR_44100, MP3Setting.VBR_7); int x = 100; diff --git a/src/main/java/io/github/dsheirer/audio/playback/MonoAudioOutput.java b/src/main/java/io/github/dsheirer/audio/playback/MonoAudioOutput.java index a5ad453d..5222f5e4 100644 --- a/src/main/java/io/github/dsheirer/audio/playback/MonoAudioOutput.java +++ b/src/main/java/io/github/dsheirer/audio/playback/MonoAudioOutput.java @@ -36,7 +36,7 @@ public class MonoAudioOutput extends AudioOutput public MonoAudioOutput(Mixer mixer, UserPreferences userPreferences) { - super(mixer, MixerChannel.MONO, AudioFormats.PCM_SIGNED_8_KHZ_16BITS_MONO, + super(mixer, MixerChannel.MONO, AudioFormats.PCM_SIGNED_8000_HZ_16_BIT_MONO, AudioFormats.MONO_SOURCE_DATALINE_INFO, BUFFER_SIZE, userPreferences); } diff --git a/src/main/java/io/github/dsheirer/audio/playback/StereoAudioOutput.java b/src/main/java/io/github/dsheirer/audio/playback/StereoAudioOutput.java index 70f77305..e3fffebe 100644 --- a/src/main/java/io/github/dsheirer/audio/playback/StereoAudioOutput.java +++ b/src/main/java/io/github/dsheirer/audio/playback/StereoAudioOutput.java @@ -1,6 +1,6 @@ -/******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2022 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 @@ -14,8 +14,8 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see - * - ******************************************************************************/ + * **************************************************************************** + */ package io.github.dsheirer.audio.playback; import io.github.dsheirer.audio.AudioFormats; @@ -36,7 +36,7 @@ public class StereoAudioOutput extends AudioOutput public StereoAudioOutput(Mixer mixer, MixerChannel channel, UserPreferences userPreferences) { - super(mixer, channel, AudioFormats.PCM_SIGNED_8KHZ_16BITS_STEREO, AudioFormats.STEREO_SOURCE_DATALINE_INFO, + super(mixer, channel, AudioFormats.PCM_SIGNED_8000_HZ_16BITS_STEREO, AudioFormats.STEREO_SOURCE_DATALINE_INFO, BUFFER_SIZE, userPreferences); } diff --git a/src/main/java/io/github/dsheirer/gui/preference/mp3/MP3PreferenceEditor.java b/src/main/java/io/github/dsheirer/gui/preference/mp3/MP3PreferenceEditor.java index 21d9aef8..66f6b74e 100644 --- a/src/main/java/io/github/dsheirer/gui/preference/mp3/MP3PreferenceEditor.java +++ b/src/main/java/io/github/dsheirer/gui/preference/mp3/MP3PreferenceEditor.java @@ -19,7 +19,7 @@ package io.github.dsheirer.gui.preference.mp3; -import io.github.dsheirer.audio.convert.AudioSampleRate; +import io.github.dsheirer.audio.convert.InputAudioFormat; import io.github.dsheirer.audio.convert.MP3Setting; import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.preference.mp3.MP3Preference; @@ -43,7 +43,7 @@ public class MP3PreferenceEditor extends HBox private MP3Preference mMP3Preference; private GridPane mEditorPane; private ComboBox mMP3SettingComboBox; - private ComboBox mAudioSampleRateComboBox; + private ComboBox mAudioSampleRateComboBox; public MP3PreferenceEditor(UserPreferences userPreferences) { @@ -96,12 +96,12 @@ public class MP3PreferenceEditor extends HBox return mMP3SettingComboBox; } - private ComboBox getAudioSampleRateComboBox() + private ComboBox getAudioSampleRateComboBox() { if(mAudioSampleRateComboBox == null) { mAudioSampleRateComboBox = new ComboBox<>(); - mAudioSampleRateComboBox.getItems().addAll(AudioSampleRate.values()); + mAudioSampleRateComboBox.getItems().addAll(InputAudioFormat.values()); mAudioSampleRateComboBox.getSelectionModel().select(mMP3Preference.getAudioSampleRate()); mAudioSampleRateComboBox.getSelectionModel().selectedItemProperty() .addListener((observable, oldValue, newValue) -> mMP3Preference.setAudioSampleRate(newValue)); diff --git a/src/main/java/io/github/dsheirer/gui/preference/playback/PlaybackPreferenceEditor.java b/src/main/java/io/github/dsheirer/gui/preference/playback/PlaybackPreferenceEditor.java index 91c09ac2..c13051d1 100644 --- a/src/main/java/io/github/dsheirer/gui/preference/playback/PlaybackPreferenceEditor.java +++ b/src/main/java/io/github/dsheirer/gui/preference/playback/PlaybackPreferenceEditor.java @@ -407,7 +407,7 @@ public class PlaybackPreferenceEditor extends HBox byte[] bytes = buffer.array(); - DataLine.Info info = new DataLine.Info(Clip.class, AudioFormats.PCM_SIGNED_8_KHZ_16BITS_MONO); + DataLine.Info info = new DataLine.Info(Clip.class, AudioFormats.PCM_SIGNED_8000_HZ_16_BIT_MONO); if(!AudioSystem.isLineSupported(info)) { @@ -418,7 +418,7 @@ public class PlaybackPreferenceEditor extends HBox try { Clip clip = (Clip)AudioSystem.getLine(info); - clip.open(AudioFormats.PCM_SIGNED_8_KHZ_16BITS_MONO, bytes, 0, bytes.length); + clip.open(AudioFormats.PCM_SIGNED_8000_HZ_16_BIT_MONO, bytes, 0, bytes.length); clip.start(); } catch(Exception e) diff --git a/src/main/java/io/github/dsheirer/preference/mp3/MP3Preference.java b/src/main/java/io/github/dsheirer/preference/mp3/MP3Preference.java index 355350a3..4b6788a2 100644 --- a/src/main/java/io/github/dsheirer/preference/mp3/MP3Preference.java +++ b/src/main/java/io/github/dsheirer/preference/mp3/MP3Preference.java @@ -19,7 +19,7 @@ package io.github.dsheirer.preference.mp3; -import io.github.dsheirer.audio.convert.AudioSampleRate; +import io.github.dsheirer.audio.convert.InputAudioFormat; import io.github.dsheirer.audio.convert.MP3Setting; import io.github.dsheirer.preference.Preference; import io.github.dsheirer.preference.PreferenceType; @@ -38,7 +38,7 @@ public class MP3Preference extends Preference private static final String PREFERENCE_KEY_AUDIO_MP3_SAMPLE_RATE = "audio.mp3.sample.rate"; private final static Logger mLog = LoggerFactory.getLogger(MP3Preference.class); private Preferences mPreferences = Preferences.userNodeForPackage(MP3Preference.class); - private AudioSampleRate mAudioSampleRate; + private InputAudioFormat mInputAudioFormat; private MP3Setting mMP3Setting; /** @@ -96,36 +96,36 @@ public class MP3Preference extends Preference /** * Preferred audio sample rate for input to the LAME MP3 encoder */ - public AudioSampleRate getAudioSampleRate() + public InputAudioFormat getAudioSampleRate() { - if(mAudioSampleRate == null) + if(mInputAudioFormat == null) { try { - String rate = mPreferences.get(PREFERENCE_KEY_AUDIO_MP3_SAMPLE_RATE, AudioSampleRate.getDefault().name()); - mAudioSampleRate = AudioSampleRate.valueOf(rate); + String rate = mPreferences.get(PREFERENCE_KEY_AUDIO_MP3_SAMPLE_RATE, InputAudioFormat.getDefault().name()); + mInputAudioFormat = InputAudioFormat.valueOf(rate); } catch(Exception e) { mLog.error("Error parsing mp3 setting preference", e); } - if(mAudioSampleRate == null) + if(mInputAudioFormat == null) { - mAudioSampleRate = AudioSampleRate.getDefault(); + mInputAudioFormat = InputAudioFormat.getDefault(); } } - return mAudioSampleRate; + return mInputAudioFormat; } /** * Sets the input Audio Sample Rate preference */ - public void setAudioSampleRate(AudioSampleRate audioSampleRate) + public void setAudioSampleRate(InputAudioFormat inputAudioFormat) { - mAudioSampleRate = audioSampleRate; - mPreferences.put(PREFERENCE_KEY_AUDIO_MP3_SAMPLE_RATE, mAudioSampleRate.name()); + mInputAudioFormat = inputAudioFormat; + mPreferences.put(PREFERENCE_KEY_AUDIO_MP3_SAMPLE_RATE, mInputAudioFormat.name()); notifyPreferenceUpdated(); } } diff --git a/src/main/java/io/github/dsheirer/record/AudioSegmentRecorder.java b/src/main/java/io/github/dsheirer/record/AudioSegmentRecorder.java index bbbce446..fd611d99 100644 --- a/src/main/java/io/github/dsheirer/record/AudioSegmentRecorder.java +++ b/src/main/java/io/github/dsheirer/record/AudioSegmentRecorder.java @@ -21,7 +21,7 @@ package io.github.dsheirer.record; import io.github.dsheirer.audio.AudioFormats; import io.github.dsheirer.audio.AudioSegment; -import io.github.dsheirer.audio.convert.AudioSampleRate; +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.preference.UserPreferences; @@ -93,10 +93,10 @@ public class AudioSegmentRecorder outputStream.write(id3Bytes); //Convert audio to MP3 and write to file - AudioSampleRate audioSampleRate = userPreferences.getMP3Preference().getAudioSampleRate(); + InputAudioFormat inputAudioFormat = userPreferences.getMP3Preference().getAudioSampleRate(); MP3Setting mp3Setting = userPreferences.getMP3Preference().getMP3Setting(); - MP3AudioConverter converter = new MP3AudioConverter(audioSampleRate, mp3Setting); + MP3AudioConverter converter = new MP3AudioConverter(inputAudioFormat, mp3Setting); List mp3Frames = converter.convert(audioSegment.getAudioBuffers()); for(byte[] mp3Frame: mp3Frames) { @@ -128,7 +128,7 @@ public class AudioSegmentRecorder { if(audioSegment.hasAudio()) { - WaveWriter writer = new WaveWriter(AudioFormats.PCM_SIGNED_8_KHZ_16BITS_MONO, path); + WaveWriter writer = new WaveWriter(AudioFormats.PCM_SIGNED_8000_HZ_16_BIT_MONO, path); for(float[] audioBuffer: audioSegment.getAudioBuffers()) { diff --git a/src/main/java/io/github/dsheirer/sample/ConversionUtils.java b/src/main/java/io/github/dsheirer/sample/ConversionUtils.java index cc2b91b5..df7700df 100644 --- a/src/main/java/io/github/dsheirer/sample/ConversionUtils.java +++ b/src/main/java/io/github/dsheirer/sample/ConversionUtils.java @@ -66,7 +66,48 @@ public class ConversionUtils for(float sample : samples) { - converted.putShort((short)(sample * Short.MAX_VALUE)); + if(sample > 1.0f) + { + converted.putShort(Short.MAX_VALUE); + } + else if(sample < -1.0f) + { + converted.putShort((short)-Short.MAX_VALUE); + } + else + { + converted.putShort((short)(sample * Short.MAX_VALUE)); + } + } + + return converted; + } + + /** + * Converts the float samples into a little-endian 32-bit sample byte buffer. + * + * @param samples - float array of sample data + * @return - little-endian 32-bit sample byte buffer + */ + public static ByteBuffer convertToSigned32BitSamples(float[] samples) + { + ByteBuffer converted = ByteBuffer.allocate(samples.length * 4); + converted.order(ByteOrder.LITTLE_ENDIAN); + + for(float sample : samples) + { + if(sample > 1.0f) + { + converted.putInt(Integer.MAX_VALUE); + } + else if(sample < -1.0f) + { + converted.putInt(-Integer.MAX_VALUE); + } + else + { + converted.putInt((int)(sample * Integer.MAX_VALUE)); + } } return converted; diff --git a/src/main/java/io/github/dsheirer/source/mixer/MixerManager.java b/src/main/java/io/github/dsheirer/source/mixer/MixerManager.java index 7de642ee..c9102dd9 100644 --- a/src/main/java/io/github/dsheirer/source/mixer/MixerManager.java +++ b/src/main/java/io/github/dsheirer/source/mixer/MixerManager.java @@ -76,7 +76,7 @@ public class MixerManager if(channel == MixerChannel.MONO) { DataLine.Info info = new DataLine.Info(TargetDataLine.class, - AudioFormats.PCM_SIGNED_8_KHZ_16BITS_MONO); + AudioFormats.PCM_SIGNED_8000_HZ_16_BIT_MONO); TargetDataLine dataLine; @@ -86,7 +86,7 @@ public class MixerManager if(dataLine != null) { - return new RealMixerSource(dataLine, AudioFormats.PCM_SIGNED_8_KHZ_16BITS_MONO, + return new RealMixerSource(dataLine, AudioFormats.PCM_SIGNED_8000_HZ_16_BIT_MONO, new RealShortAdapter()); } } @@ -100,7 +100,7 @@ public class MixerManager else //STEREO { DataLine.Info info = new DataLine.Info(TargetDataLine.class, - AudioFormats.PCM_SIGNED_8KHZ_16BITS_STEREO); + AudioFormats.PCM_SIGNED_8000_HZ_16BITS_STEREO); TargetDataLine dataLine; @@ -110,7 +110,7 @@ public class MixerManager if(dataLine != null) { - return new RealMixerSource(dataLine, AudioFormats.PCM_SIGNED_8KHZ_16BITS_STEREO, + return new RealMixerSource(dataLine, AudioFormats.PCM_SIGNED_8000_HZ_16BITS_STEREO, new RealChannelShortAdapter(mixerConfig.getChannel())); } } @@ -302,12 +302,12 @@ public class MixerManager private static EnumSet getSupportedTargetChannels(Mixer mixer) { DataLine.Info stereoInfo = new DataLine.Info(TargetDataLine.class, - AudioFormats.PCM_SIGNED_8KHZ_16BITS_STEREO); + AudioFormats.PCM_SIGNED_8000_HZ_16BITS_STEREO); boolean stereoSupported = mixer.isLineSupported(stereoInfo); DataLine.Info monoInfo = new DataLine.Info(TargetDataLine.class, - AudioFormats.PCM_SIGNED_8_KHZ_16BITS_MONO); + AudioFormats.PCM_SIGNED_8000_HZ_16_BIT_MONO); boolean monoSupported = mixer.isLineSupported(monoInfo); @@ -337,11 +337,11 @@ public class MixerManager List channels = new ArrayList<>(); DataLine.Info stereoInfo = new DataLine.Info(SourceDataLine.class, - AudioFormats.PCM_SIGNED_8KHZ_16BITS_STEREO); + AudioFormats.PCM_SIGNED_8000_HZ_16BITS_STEREO); boolean stereoSupported = mixer.isLineSupported(stereoInfo); - DataLine.Info monoInfo = new DataLine.Info(SourceDataLine.class, AudioFormats.PCM_SIGNED_8_KHZ_16BITS_MONO); + DataLine.Info monoInfo = new DataLine.Info(SourceDataLine.class, AudioFormats.PCM_SIGNED_8000_HZ_16_BIT_MONO); boolean monoSupported = mixer.isLineSupported(monoInfo);