Skip to content

Commit

Permalink
#1237 Ensure MP3 input sample rate is supported by LAME encoder setti…
Browse files Browse the repository at this point in the history
…ngs. (#1376)

Co-authored-by: Dennis Sheirer <dsheirer@github.com>
  • Loading branch information
DSheirer and Dennis Sheirer committed Dec 23, 2022
1 parent 70170ff commit 08499ac
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package io.github.dsheirer.audio.convert;

import io.github.dsheirer.audio.AudioFormats;
import java.util.EnumSet;

import javax.sound.sampled.AudioFormat;

Expand All @@ -29,8 +30,8 @@
public enum InputAudioFormat
{
SR_8000(AudioFormats.PCM_SIGNED_8000_HZ_16_BIT_MONO, "16-Bit 8000 Hz (no resample)"),
SR_16000(AudioFormats.PCM_SIGNED_16000_HZ_16_BIT_MONO, "16-Bit 16000 Hz"),
SR_22050(AudioFormats.PCM_SIGNED_22050_HZ_16_BIT_MONO, "16-Bit 22050 Hz (default)"),
SR_16000(AudioFormats.PCM_SIGNED_16000_HZ_16_BIT_MONO, "16-Bit 16000 Hz (default)"),
SR_22050(AudioFormats.PCM_SIGNED_22050_HZ_16_BIT_MONO, "16-Bit 22050 Hz"),
SR_44100(AudioFormats.PCM_SIGNED_44100_HZ_16_BIT_MONO, "16-Bit 44100 Hz"),

SR_32_8000(AudioFormats.PCM_SIGNED_8000_HZ_32_BIT_MONO, "32-Bit 8000 Hz (no resample)"),
Expand All @@ -41,6 +42,17 @@ public enum InputAudioFormat
private AudioFormat mAudioFormat;
private String mLabel;

/**
* Set that includes sample rates of 8 or 16 kHz
*/
public static final EnumSet<InputAudioFormat> SAMPLE_RATES_8_16 = EnumSet.of(SR_8000, SR_32_8000, SR_16000, SR_32_16000);

/**
* Set that includes sample rates of 8, 16, or 22 kHz
*/
public static final EnumSet<InputAudioFormat> SAMPLE_RATES_8_16_22 = EnumSet.of(SR_8000, SR_32_8000, SR_16000,
SR_32_16000, SR_22050, SR_32_22050);

/**
* Constructs an instance
* @param audioFormat for the specified sample rate
Expand All @@ -56,7 +68,7 @@ public enum InputAudioFormat
*/
public static InputAudioFormat getDefault()
{
return SR_22050;
return SR_16000;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,17 @@ public MP3AudioConverter(InputAudioFormat inputAudioFormat, MP3Setting setting,
mEncoder = LameFactory.getLameEncoder(inputAudioFormat, setting);
mNormalizeAudio = normalizeAudio;

//Ensure input audio sample rate is supported for the MP3 setting and update as necessary - should never happen.
if((mInputAudioFormat.getSampleRate() - mEncoder.getEffectiveSampleRate()) > 1.0)
{
mInputAudioFormat = InputAudioFormat.getDefault();
mLog.warn("MP3 setting [" + setting + "] does not support requested input audio sample rate [" +
inputAudioFormat + "] - using default sample rate [" + mInputAudioFormat + "]");
mEncoder = LameFactory.getLameEncoder(inputAudioFormat, setting);
}

//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)
if(mInputAudioFormat != InputAudioFormat.SR_8000 && mInputAudioFormat != InputAudioFormat.SR_32_8000)
{
mResampler = LameFactory.getResampler(inputAudioFormat);
}
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/io/github/dsheirer/audio/convert/MP3Setting.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

package io.github.dsheirer.audio.convert;

import java.util.Arrays;
import java.util.List;
import net.sourceforge.lame.mp3.Lame;

/**
Expand Down Expand Up @@ -79,6 +81,30 @@ public int getSetting()
return mSetting;
}

/**
* Input audio formats that are supported by the MP3Setting value.
* @return list of supported input audio sample rates.
*/
public List<InputAudioFormat> getSupportedSampleRates()
{
switch(this)
{
case CBR_16:
{
return InputAudioFormat.SAMPLE_RATES_8_16.stream().toList();
}
case CBR_32:
case VBR_7:
{
return InputAudioFormat.SAMPLE_RATES_8_16_22.stream().toList();
}
default:
{
return Arrays.stream(InputAudioFormat.values()).toList();
}
}
}

@Override
public String toString()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import io.github.dsheirer.preference.mp3.MP3Preference;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.control.Alert;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
Expand Down Expand Up @@ -99,12 +100,47 @@ private ComboBox<MP3Setting> getMP3SettingComboBox()
mMP3SettingComboBox.getItems().addAll(MP3Setting.values());
mMP3SettingComboBox.getSelectionModel().select(mMP3Preference.getMP3Setting());
mMP3SettingComboBox.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) -> mMP3Preference.setMP3Setting(newValue));
.addListener((observable, oldValue, newValue) -> {
mMP3Preference.setMP3Setting(newValue);
updateAudioSampleRateComboBox();
});
}

return mMP3SettingComboBox;
}

/**
* Updates the input sample rate combo box values and alerts the user whenever we auto-change an input sample rate
* due to a change in the MP3 setting value.
*/
private void updateAudioSampleRateComboBox()
{
InputAudioFormat currentSelection = getAudioSampleRateComboBox().getSelectionModel().getSelectedItem();
MP3Setting setting = getMP3SettingComboBox().getSelectionModel().getSelectedItem();

getAudioSampleRateComboBox().getItems().clear();
getAudioSampleRateComboBox().getItems().addAll(setting.getSupportedSampleRates());

if(currentSelection != null && getAudioSampleRateComboBox().getItems().contains(currentSelection))
{
getAudioSampleRateComboBox().getSelectionModel().select(currentSelection);
}
else
{
getAudioSampleRateComboBox().getSelectionModel().select(InputAudioFormat.getDefault());

Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Sample Rate Updated");
alert.setHeaderText("Input Sample Rate Updated");

Label wrappingLabel = new Label("Previous input sample rate [" + currentSelection +
"] is not supported with encoder setting [" + setting + "]. Sample rate updated to default.");
wrappingLabel.setWrapText(true);
alert.getDialogPane().setContent(wrappingLabel);
alert.show();
}
}

private ComboBox<InputAudioFormat> getAudioSampleRateComboBox()
{
if(mAudioSampleRateComboBox == null)
Expand All @@ -113,7 +149,12 @@ private ComboBox<InputAudioFormat> getAudioSampleRateComboBox()
mAudioSampleRateComboBox.getItems().addAll(InputAudioFormat.values());
mAudioSampleRateComboBox.getSelectionModel().select(mMP3Preference.getAudioSampleRate());
mAudioSampleRateComboBox.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) -> mMP3Preference.setAudioSampleRate(newValue));
.addListener((observable, oldValue, newValue) -> {
if(newValue != null)
{
mMP3Preference.setAudioSampleRate(newValue);
}
});
}

return mAudioSampleRateComboBox;
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/io/github/dsheirer/preference/mp3/MP3Preference.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,24 @@ public InputAudioFormat getAudioSampleRate()
{
mInputAudioFormat = InputAudioFormat.getDefault();
}

//Ensure the input sample rate is supported by the MP3 setting, or change it to the first supported rate.
MP3Setting setting = getMP3Setting();

if(!setting.getSupportedSampleRates().contains(mInputAudioFormat))
{
InputAudioFormat supportedAudioFormat = InputAudioFormat.getDefault();

//Default should always be supported by all mp3 settings, but fall-back to a supported rate if not.
if(!setting.getSupportedSampleRates().contains(supportedAudioFormat))
{
supportedAudioFormat = setting.getSupportedSampleRates().get(0);
}

mLog.warn("MP3 Setting [" + setting + "] does not support input sample rate [" + getAudioSampleRate().name() +
"] - updating to supported sample rate [" + supportedAudioFormat.name() + "]");
setAudioSampleRate(supportedAudioFormat);
}
}

return mInputAudioFormat;
Expand Down

0 comments on commit 08499ac

Please sign in to comment.