Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added setting sampling rate (#33) #92

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ import org.fossify.voicerecorder.extensions.getAllRecordings
import org.fossify.voicerecorder.extensions.hasRecordings
import org.fossify.voicerecorder.extensions.launchFolderPicker
import org.fossify.voicerecorder.helpers.BITRATES
import org.fossify.voicerecorder.helpers.DEFAULT_BITRATE
import org.fossify.voicerecorder.helpers.DEFAULT_SAMPLING_RATE
import org.fossify.voicerecorder.helpers.EXTENSION_M4A
import org.fossify.voicerecorder.helpers.EXTENSION_MP3
import org.fossify.voicerecorder.helpers.EXTENSION_OGG
import org.fossify.voicerecorder.helpers.SAMPLING_RATES
import org.fossify.voicerecorder.helpers.SAMPLING_RATE_BITRATE_LIMITS
import org.fossify.voicerecorder.models.Events
import org.greenrobot.eventbus.EventBus
import java.util.Locale
Expand Down Expand Up @@ -76,6 +80,7 @@ class SettingsActivity : SimpleActivity() {
setupSaveRecordingsFolder()
setupExtension()
setupBitrate()
setupSamplingRate()
setupAudioSource()
setupRecordAfterLaunch()
setupKeepScreenOn()
Expand Down Expand Up @@ -202,18 +207,21 @@ class SettingsActivity : SimpleActivity() {
RadioGroupDialog(this@SettingsActivity, items, config.extension) {
config.extension = it as Int
binding.settingsExtension.text = config.getExtensionText()
adjustBitrate()
adjustSamplingRate()
}
}
}

private fun setupBitrate() {
binding.settingsBitrate.text = getBitrateText(config.bitrate)
binding.settingsBitrateHolder.setOnClickListener {
val items = BITRATES.map { RadioItem(it, getBitrateText(it)) } as ArrayList
val items = BITRATES[config.extension]!!.map { RadioItem(it, getBitrateText(it)) } as ArrayList

RadioGroupDialog(this@SettingsActivity, items, config.bitrate) {
config.bitrate = it as Int
binding.settingsBitrate.text = getBitrateText(config.bitrate)
adjustSamplingRate()
}
}
}
Expand All @@ -222,6 +230,51 @@ class SettingsActivity : SimpleActivity() {
return getString(R.string.bitrate_value).format(value / 1000)
}

private fun adjustBitrate() {
val availableBitrates = BITRATES[config.extension]!!
if (!availableBitrates.contains(config.bitrate)) {
config.bitrate = DEFAULT_BITRATE
binding.settingsBitrate.text = getBitrateText(config.bitrate)
}
}

private fun setupSamplingRate() {
binding.settingsSamplingRate.text = getSamplingRateText(config.samplingRate)
binding.settingsSamplingRateHolder.setOnClickListener {
val items = getSamplingRatesArray().map { RadioItem(it, getSamplingRateText(it)) } as ArrayList

RadioGroupDialog(this@SettingsActivity, items, config.samplingRate) {
config.samplingRate = it as Int
binding.settingsSamplingRate.text = getSamplingRateText(config.samplingRate)
}
}
}

private fun getSamplingRateText(value: Int): String {
return getString(R.string.sampling_rate_value).format(value)
}

private fun getSamplingRatesArray(): ArrayList<Int> {
val baseRates = SAMPLING_RATES[config.extension]!!
val limits = SAMPLING_RATE_BITRATE_LIMITS[config.extension]!!
val filteredRates = baseRates.filter {
config.bitrate in limits[it]!![0]..limits[it]!![1]
} as ArrayList
return filteredRates
}

private fun adjustSamplingRate() {
val availableSamplingRates = getSamplingRatesArray()
if (!availableSamplingRates.contains(config.samplingRate)) {
if (availableSamplingRates.contains(DEFAULT_SAMPLING_RATE)) {
config.samplingRate = DEFAULT_SAMPLING_RATE
} else {
config.samplingRate = availableSamplingRates.last()
}
binding.settingsSamplingRate.text = getSamplingRateText(config.samplingRate)
}
}

private fun setupRecordAfterLaunch() {
binding.settingsRecordAfterLaunch.isChecked = config.recordAfterLaunch
binding.settingsRecordAfterLaunchHolder.setOnClickListener {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getInt(BITRATE, DEFAULT_BITRATE)
set(bitrate) = prefs.edit().putInt(BITRATE, bitrate).apply()

var samplingRate: Int
get() = prefs.getInt(SAMPLING_RATE, DEFAULT_SAMPLING_RATE)
set(samplingRate) = prefs.edit().putInt(SAMPLING_RATE, samplingRate).apply()

var recordAfterLaunch: Boolean
get() = prefs.getBoolean(RECORD_AFTER_LAUNCH, false)
set(recordAfterLaunch) = prefs.edit().putBoolean(RECORD_AFTER_LAUNCH, recordAfterLaunch)
Expand Down
66 changes: 63 additions & 3 deletions app/src/main/kotlin/org/fossify/voicerecorder/helpers/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,68 @@ const val EXTENSION_M4A = 0
const val EXTENSION_MP3 = 1
const val EXTENSION_OGG = 2

val BITRATES = arrayListOf(32000, 64000, 96000, 128000, 160000, 192000, 256000, 320000)
const val DEFAULT_BITRATE = 192000
const val SAMPLE_RATE = 48000
val BITRATES_MP3 = arrayListOf(8000, 16000, 24000, 32000, 64000, 96000, 128000, 160000, 192000, 256000, 320000)
// M4A can't record with bitrate higher than 96000; bug report: https://github.com/FossifyOrg/Voice-Recorder/issues/91
val BITRATES_M4A = arrayListOf(8000, 14000, 24000, 28000, 32000, 64000, 96000)
val BITRATES_OPUS = arrayListOf(8000, 16000, 24000, 32000, 64000, 96000, 128000, 160000, 192000, 256000, 320000)
val BITRATES = mapOf(
EXTENSION_M4A to BITRATES_M4A,
EXTENSION_MP3 to BITRATES_MP3,
EXTENSION_OGG to BITRATES_OPUS
)
const val DEFAULT_BITRATE = 96000

val SAMPLING_RATES_MP3 = arrayListOf(8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000)
val SAMPLING_RATES_M4A = arrayListOf(11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000)
val SAMPLING_RATES_OPUS = arrayListOf(8000, 12000, 16000, 24000, 48000)
val SAMPLING_RATES = mapOf(
EXTENSION_M4A to SAMPLING_RATES_M4A,
EXTENSION_MP3 to SAMPLING_RATES_MP3,
EXTENSION_OGG to SAMPLING_RATES_OPUS
)
const val DEFAULT_SAMPLING_RATE = 48000

// sampling rate -> [min bitrate, max bitrate]
// according to https://redmine.digispot.ru/projects/support-eng/wiki/Recommended_Sampling_Rate_and_Bitrate_Combinations_for_AAC_codec
val SAMPLING_RATE_BITRATE_LIMITS_M4A = mapOf(
11025 to arrayListOf(8000, 15999),
12000 to arrayListOf(8000, 15999),
16000 to arrayListOf(8000, 31999),
22050 to arrayListOf(24000, 31999),
24000 to arrayListOf(24000, 31999),
32000 to arrayListOf(32000, 160000),
44100 to arrayListOf(56000, 160000),
48000 to arrayListOf(56000, 288000)
)

// according to https://svn.code.sf.net/p/lame/svn/trunk/lame/doc/html/detailed.html#b
val SAMPLING_RATE_BITRATE_LIMITS_MP3 = mapOf(
8000 to arrayListOf(8000, 64000),
11025 to arrayListOf(8000, 64000),
12000 to arrayListOf(8000, 64000),
16000 to arrayListOf(8000, 160000),
22050 to arrayListOf(8000, 160000),
24000 to arrayListOf(8000, 160000),
32000 to arrayListOf(32000, 320000),
44100 to arrayListOf(32000, 320000),
48000 to arrayListOf(32000, 320000)
)

// OPUS has only recommendations for bitrate, no limits: https://www.rfc-editor.org/rfc/rfc7587#section-3.1.1
// only minimum value is set according to them
val SAMPLING_RATE_BITRATE_LIMITS_OPUS = mapOf(
8000 to arrayListOf(8000, 320000),
12000 to arrayListOf(16000, 320000),
16000 to arrayListOf(28000, 320000),
24000 to arrayListOf(48000, 320000),
48000 to arrayListOf(64000, 320000)
)

val SAMPLING_RATE_BITRATE_LIMITS = mapOf(
EXTENSION_M4A to SAMPLING_RATE_BITRATE_LIMITS_M4A,
EXTENSION_MP3 to SAMPLING_RATE_BITRATE_LIMITS_MP3,
EXTENSION_OGG to SAMPLING_RATE_BITRATE_LIMITS_OPUS
)

const val RECORDING_RUNNING = 0
const val RECORDING_STOPPED = 1
Expand All @@ -31,6 +90,7 @@ const val SAVE_RECORDINGS = "save_recordings"
const val EXTENSION = "extension"
const val AUDIO_SOURCE = "audio_source"
const val BITRATE = "bitrate"
const val SAMPLING_RATE = "sampling_rate"
const val RECORD_AFTER_LAUNCH = "record_after_launch"
const val USE_RECYCLE_BIN = "use_recycle_bin"
const val LAST_RECYCLE_BIN_CHECK = "last_recycle_bin_check"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import android.content.Context
import android.media.MediaRecorder
import android.os.ParcelFileDescriptor
import org.fossify.voicerecorder.extensions.config
import org.fossify.voicerecorder.helpers.SAMPLE_RATE

class MediaRecorderWrapper(val context: Context) : Recorder {

Expand All @@ -15,7 +14,7 @@ class MediaRecorderWrapper(val context: Context) : Recorder {
setOutputFormat(context.config.getOutputFormat())
setAudioEncoder(context.config.getAudioEncoder())
setAudioEncodingBitRate(context.config.bitrate)
setAudioSamplingRate(SAMPLE_RATE)
setAudioSamplingRate(context.config.samplingRate)
}

override fun setOutputFile(path: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import com.naman14.androidlame.LameBuilder
import org.fossify.commons.extensions.showErrorToast
import org.fossify.commons.helpers.ensureBackgroundThread
import org.fossify.voicerecorder.extensions.config
import org.fossify.voicerecorder.helpers.SAMPLE_RATE
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
Expand All @@ -29,15 +28,15 @@ class Mp3Recorder(val context: Context) : Recorder {
private var fileDescriptor: ParcelFileDescriptor? = null
private var outputStream: FileOutputStream? = null
private val minBufferSize = AudioRecord.getMinBufferSize(
SAMPLE_RATE,
context.config.samplingRate,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT
)

@SuppressLint("MissingPermission")
private val audioRecord = AudioRecord(
context.config.audioSource,
SAMPLE_RATE,
context.config.samplingRate,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
minBufferSize * 2
Expand Down Expand Up @@ -65,9 +64,9 @@ class Mp3Recorder(val context: Context) : Recorder {
}

androidLame = LameBuilder()
.setInSampleRate(SAMPLE_RATE)
.setInSampleRate(context.config.samplingRate)
.setOutBitrate(context.config.bitrate / 1000)
.setOutSampleRate(SAMPLE_RATE)
.setOutSampleRate(context.config.samplingRate)
.setOutChannels(1)
.build()

Expand Down
47 changes: 35 additions & 12 deletions app/src/main/res/layout/activity_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,28 @@

</RelativeLayout>

<RelativeLayout
android:id="@+id/settings_audio_source_holder"
style="@style/SettingsHolderTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<org.fossify.commons.views.MyTextView
android:id="@+id/settings_audio_source_label"
style="@style/SettingsTextLabelStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/audio_source" />

<org.fossify.commons.views.MyTextView
android:id="@+id/settings_audio_source"
style="@style/SettingsTextValueStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/settings_audio_source_label"
tools:text="Camera" />
</RelativeLayout>

<RelativeLayout
android:id="@+id/settings_extension_holder"
style="@style/SettingsHolderTextViewStyle"
Expand All @@ -240,47 +262,48 @@
</RelativeLayout>

<RelativeLayout
android:id="@+id/settings_audio_source_holder"
android:id="@+id/settings_bitrate_holder"
style="@style/SettingsHolderTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<org.fossify.commons.views.MyTextView
android:id="@+id/settings_audio_source_label"
android:id="@+id/settings_bitrate_label"
style="@style/SettingsTextLabelStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/audio_source" />
android:text="@string/bitrate" />

<org.fossify.commons.views.MyTextView
android:id="@+id/settings_audio_source"
android:id="@+id/settings_bitrate"
style="@style/SettingsTextValueStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/settings_audio_source_label"
tools:text="128 kbps" />
android:layout_below="@+id/settings_bitrate_label"
tools:text="192 kbps" />

</RelativeLayout>

<RelativeLayout
android:id="@+id/settings_bitrate_holder"
android:id="@+id/settings_sampling_rate_holder"
style="@style/SettingsHolderTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<org.fossify.commons.views.MyTextView
android:id="@+id/settings_bitrate_label"
android:id="@+id/settings_sampling_rate_label"
style="@style/SettingsTextLabelStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bitrate" />
android:text="@string/sampling_rate" />

<org.fossify.commons.views.MyTextView
android:id="@+id/settings_bitrate"
android:id="@+id/settings_sampling_rate"
style="@style/SettingsTextValueStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/settings_bitrate_label"
tools:text="192 kbps" />
android:layout_below="@+id/settings_sampling_rate_label"
tools:text="48000 Hz" />

</RelativeLayout>

Expand Down
3 changes: 2 additions & 1 deletion app/src/main/res/values-pl/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<string name="bitrate">Przepływność</string>
<string name="record_after_launch">Rozpoczynaj nagrywanie automatycznie przy uruchomieniu aplikacji</string>
<string name="keep_screen_on">Pozostawiaj ekran włączony podczas nagrywania</string>
<string name="sampling_rate">Częstotliwość próbkowania</string>
<string name="audio_source_camcorder">Aparat</string>
<string name="audio_source_default">Domyślne systemowe</string>
<string name="audio_source_unprocessed">Nieprzetworzone</string>
Expand All @@ -34,4 +35,4 @@
<string name="confirm_recording_folder">Musisz potwierdzić folder, w którym chcesz zapisywać swoje nagrania. Naciśnij OK, aby kontynuować.</string>
<string name="move_recordings_to_new_folder_desc">Masz istniejące nagrania. Czy przenieść je do nowego folderu?</string>
<string name="move_recordings">Przenieś nagrania</string>
</resources>
</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values/donottranslate.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
<string name="ogg">ogg</string>
<string name="ogg_opus">ogg (Opus)</string>
<string name="bitrate_value">%d kbps</string>
<string name="sampling_rate_value">%d Hz</string>
</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<string name="bitrate">Bitrate</string>
<string name="record_after_launch">Start recording automatically after launching the app</string>
<string name="keep_screen_on">Keep the screen on during recording</string>
<string name="sampling_rate">Sampling rate</string>
<!-- Settings Audio source selection -->
<string name="audio_source_camcorder">Camera</string>
<string name="audio_source_default">Android default</string>
Expand Down
Loading