Skip to content

Commit 85373db

Browse files
authored
feat: add sample rate preference (#33) (#92)
* Added setting sampling rate (#33) * Fixed PR comments
1 parent 85dd157 commit 85373db

9 files changed

Lines changed: 171 additions & 23 deletions

File tree

app/src/main/kotlin/org/fossify/voicerecorder/activities/SettingsActivity.kt

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,13 @@ import org.fossify.voicerecorder.extensions.getAllRecordings
3535
import org.fossify.voicerecorder.extensions.hasRecordings
3636
import org.fossify.voicerecorder.extensions.launchFolderPicker
3737
import org.fossify.voicerecorder.helpers.BITRATES
38+
import org.fossify.voicerecorder.helpers.DEFAULT_BITRATE
39+
import org.fossify.voicerecorder.helpers.DEFAULT_SAMPLING_RATE
3840
import org.fossify.voicerecorder.helpers.EXTENSION_M4A
3941
import org.fossify.voicerecorder.helpers.EXTENSION_MP3
4042
import org.fossify.voicerecorder.helpers.EXTENSION_OGG
43+
import org.fossify.voicerecorder.helpers.SAMPLING_RATES
44+
import org.fossify.voicerecorder.helpers.SAMPLING_RATE_BITRATE_LIMITS
4145
import org.fossify.voicerecorder.models.Events
4246
import org.greenrobot.eventbus.EventBus
4347
import java.util.Locale
@@ -76,6 +80,7 @@ class SettingsActivity : SimpleActivity() {
7680
setupSaveRecordingsFolder()
7781
setupExtension()
7882
setupBitrate()
83+
setupSamplingRate()
7984
setupAudioSource()
8085
setupRecordAfterLaunch()
8186
setupKeepScreenOn()
@@ -202,18 +207,22 @@ class SettingsActivity : SimpleActivity() {
202207
RadioGroupDialog(this@SettingsActivity, items, config.extension) {
203208
config.extension = it as Int
204209
binding.settingsExtension.text = config.getExtensionText()
210+
adjustBitrate()
211+
adjustSamplingRate()
205212
}
206213
}
207214
}
208215

209216
private fun setupBitrate() {
210217
binding.settingsBitrate.text = getBitrateText(config.bitrate)
211218
binding.settingsBitrateHolder.setOnClickListener {
212-
val items = BITRATES.map { RadioItem(it, getBitrateText(it)) } as ArrayList
219+
val items = BITRATES[config.extension]!!
220+
.map { RadioItem(it, getBitrateText(it)) } as ArrayList
213221

214222
RadioGroupDialog(this@SettingsActivity, items, config.bitrate) {
215223
config.bitrate = it as Int
216224
binding.settingsBitrate.text = getBitrateText(config.bitrate)
225+
adjustSamplingRate()
217226
}
218227
}
219228
}
@@ -222,6 +231,52 @@ class SettingsActivity : SimpleActivity() {
222231
return getString(R.string.bitrate_value).format(value / 1000)
223232
}
224233

234+
private fun adjustBitrate() {
235+
val availableBitrates = BITRATES[config.extension]!!
236+
if (!availableBitrates.contains(config.bitrate)) {
237+
config.bitrate = DEFAULT_BITRATE
238+
binding.settingsBitrate.text = getBitrateText(config.bitrate)
239+
}
240+
}
241+
242+
private fun setupSamplingRate() {
243+
binding.settingsSamplingRate.text = getSamplingRateText(config.samplingRate)
244+
binding.settingsSamplingRateHolder.setOnClickListener {
245+
val items = getSamplingRatesArray()
246+
.map { RadioItem(it, getSamplingRateText(it)) } as ArrayList
247+
248+
RadioGroupDialog(this@SettingsActivity, items, config.samplingRate) {
249+
config.samplingRate = it as Int
250+
binding.settingsSamplingRate.text = getSamplingRateText(config.samplingRate)
251+
}
252+
}
253+
}
254+
255+
private fun getSamplingRateText(value: Int): String {
256+
return getString(R.string.sampling_rate_value).format(value)
257+
}
258+
259+
private fun getSamplingRatesArray(): ArrayList<Int> {
260+
val baseRates = SAMPLING_RATES[config.extension]!!
261+
val limits = SAMPLING_RATE_BITRATE_LIMITS[config.extension]!!
262+
val filteredRates = baseRates.filter {
263+
config.bitrate in limits[it]!![0]..limits[it]!![1]
264+
} as ArrayList
265+
return filteredRates
266+
}
267+
268+
private fun adjustSamplingRate() {
269+
val availableSamplingRates = getSamplingRatesArray()
270+
if (!availableSamplingRates.contains(config.samplingRate)) {
271+
if (availableSamplingRates.contains(DEFAULT_SAMPLING_RATE)) {
272+
config.samplingRate = DEFAULT_SAMPLING_RATE
273+
} else {
274+
config.samplingRate = availableSamplingRates.last()
275+
}
276+
binding.settingsSamplingRate.text = getSamplingRateText(config.samplingRate)
277+
}
278+
}
279+
225280
private fun setupRecordAfterLaunch() {
226281
binding.settingsRecordAfterLaunch.isChecked = config.recordAfterLaunch
227282
binding.settingsRecordAfterLaunchHolder.setOnClickListener {

app/src/main/kotlin/org/fossify/voicerecorder/helpers/Config.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ class Config(context: Context) : BaseConfig(context) {
4545
get() = prefs.getInt(BITRATE, DEFAULT_BITRATE)
4646
set(bitrate) = prefs.edit().putInt(BITRATE, bitrate).apply()
4747

48+
var samplingRate: Int
49+
get() = prefs.getInt(SAMPLING_RATE, DEFAULT_SAMPLING_RATE)
50+
set(samplingRate) = prefs.edit().putInt(SAMPLING_RATE, samplingRate).apply()
51+
4852
var recordAfterLaunch: Boolean
4953
get() = prefs.getBoolean(RECORD_AFTER_LAUNCH, false)
5054
set(recordAfterLaunch) = prefs.edit().putBoolean(RECORD_AFTER_LAUNCH, recordAfterLaunch)

app/src/main/kotlin/org/fossify/voicerecorder/helpers/Constants.kt

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,73 @@ const val EXTENSION_M4A = 0
1414
const val EXTENSION_MP3 = 1
1515
const val EXTENSION_OGG = 2
1616

17-
val BITRATES = arrayListOf(32000, 64000, 96000, 128000, 160000, 192000, 256000, 320000)
18-
const val DEFAULT_BITRATE = 192000
19-
const val SAMPLE_RATE = 48000
17+
val BITRATES_MP3 = arrayListOf(
18+
8000, 16000, 24000, 32000, 64000, 96000, 128000, 160000, 192000, 256000, 320000
19+
)
20+
val BITRATES_M4A = arrayListOf(
21+
8000, 14000, 24000, 28000, 32000, 64000, 96000, 128000, 160000, 192000, 288000
22+
)
23+
val BITRATES_OPUS = arrayListOf(
24+
8000, 16000, 24000, 32000, 64000, 96000, 128000, 160000, 192000, 256000, 320000
25+
)
26+
val BITRATES = mapOf(
27+
EXTENSION_M4A to BITRATES_M4A,
28+
EXTENSION_MP3 to BITRATES_MP3,
29+
EXTENSION_OGG to BITRATES_OPUS
30+
)
31+
const val DEFAULT_BITRATE = 96000
32+
33+
val SAMPLING_RATES_MP3 = arrayListOf(8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000)
34+
val SAMPLING_RATES_M4A = arrayListOf(11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000)
35+
val SAMPLING_RATES_OPUS = arrayListOf(8000, 12000, 16000, 24000, 48000)
36+
val SAMPLING_RATES = mapOf(
37+
EXTENSION_M4A to SAMPLING_RATES_M4A,
38+
EXTENSION_MP3 to SAMPLING_RATES_MP3,
39+
EXTENSION_OGG to SAMPLING_RATES_OPUS
40+
)
41+
const val DEFAULT_SAMPLING_RATE = 48000
42+
43+
// sampling rate -> [min bitrate, max bitrate]
44+
// according to https://redmine.digispot.ru/projects/support-eng/wiki/Recommended_Sampling_Rate_and_Bitrate_Combinations_for_AAC_codec
45+
val SAMPLING_RATE_BITRATE_LIMITS_M4A = mapOf(
46+
11025 to arrayListOf(8000, 15999),
47+
12000 to arrayListOf(8000, 15999),
48+
16000 to arrayListOf(8000, 31999),
49+
22050 to arrayListOf(24000, 31999),
50+
24000 to arrayListOf(24000, 31999),
51+
32000 to arrayListOf(32000, 160000),
52+
44100 to arrayListOf(56000, 160000),
53+
48000 to arrayListOf(56000, 288000)
54+
)
55+
56+
// according to https://svn.code.sf.net/p/lame/svn/trunk/lame/doc/html/detailed.html#b
57+
val SAMPLING_RATE_BITRATE_LIMITS_MP3 = mapOf(
58+
8000 to arrayListOf(8000, 64000),
59+
11025 to arrayListOf(8000, 64000),
60+
12000 to arrayListOf(8000, 64000),
61+
16000 to arrayListOf(8000, 160000),
62+
22050 to arrayListOf(8000, 160000),
63+
24000 to arrayListOf(8000, 160000),
64+
32000 to arrayListOf(32000, 320000),
65+
44100 to arrayListOf(32000, 320000),
66+
48000 to arrayListOf(32000, 320000)
67+
)
68+
69+
// OPUS has only recommendations for bitrate, no limits: https://www.rfc-editor.org/rfc/rfc7587#section-3.1.1
70+
// only minimum value is set according to them
71+
val SAMPLING_RATE_BITRATE_LIMITS_OPUS = mapOf(
72+
8000 to arrayListOf(8000, 320000),
73+
12000 to arrayListOf(16000, 320000),
74+
16000 to arrayListOf(28000, 320000),
75+
24000 to arrayListOf(48000, 320000),
76+
48000 to arrayListOf(64000, 320000)
77+
)
78+
79+
val SAMPLING_RATE_BITRATE_LIMITS = mapOf(
80+
EXTENSION_M4A to SAMPLING_RATE_BITRATE_LIMITS_M4A,
81+
EXTENSION_MP3 to SAMPLING_RATE_BITRATE_LIMITS_MP3,
82+
EXTENSION_OGG to SAMPLING_RATE_BITRATE_LIMITS_OPUS
83+
)
2084

2185
const val RECORDING_RUNNING = 0
2286
const val RECORDING_STOPPED = 1
@@ -31,6 +95,7 @@ const val SAVE_RECORDINGS = "save_recordings"
3195
const val EXTENSION = "extension"
3296
const val AUDIO_SOURCE = "audio_source"
3397
const val BITRATE = "bitrate"
98+
const val SAMPLING_RATE = "sampling_rate"
3499
const val RECORD_AFTER_LAUNCH = "record_after_launch"
35100
const val USE_RECYCLE_BIN = "use_recycle_bin"
36101
const val LAST_RECYCLE_BIN_CHECK = "last_recycle_bin_check"

app/src/main/kotlin/org/fossify/voicerecorder/recorder/MediaRecorderWrapper.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import android.content.Context
55
import android.media.MediaRecorder
66
import android.os.ParcelFileDescriptor
77
import org.fossify.voicerecorder.extensions.config
8-
import org.fossify.voicerecorder.helpers.SAMPLE_RATE
98

109
class MediaRecorderWrapper(val context: Context) : Recorder {
1110

@@ -15,7 +14,7 @@ class MediaRecorderWrapper(val context: Context) : Recorder {
1514
setOutputFormat(context.config.getOutputFormat())
1615
setAudioEncoder(context.config.getAudioEncoder())
1716
setAudioEncodingBitRate(context.config.bitrate)
18-
setAudioSamplingRate(SAMPLE_RATE)
17+
setAudioSamplingRate(context.config.samplingRate)
1918
}
2019

2120
override fun setOutputFile(path: String) {

app/src/main/kotlin/org/fossify/voicerecorder/recorder/Mp3Recorder.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import com.naman14.androidlame.LameBuilder
1010
import org.fossify.commons.extensions.showErrorToast
1111
import org.fossify.commons.helpers.ensureBackgroundThread
1212
import org.fossify.voicerecorder.extensions.config
13-
import org.fossify.voicerecorder.helpers.SAMPLE_RATE
1413
import java.io.File
1514
import java.io.FileNotFoundException
1615
import java.io.FileOutputStream
@@ -29,15 +28,15 @@ class Mp3Recorder(val context: Context) : Recorder {
2928
private var fileDescriptor: ParcelFileDescriptor? = null
3029
private var outputStream: FileOutputStream? = null
3130
private val minBufferSize = AudioRecord.getMinBufferSize(
32-
SAMPLE_RATE,
31+
context.config.samplingRate,
3332
AudioFormat.CHANNEL_IN_MONO,
3433
AudioFormat.ENCODING_PCM_16BIT
3534
)
3635

3736
@SuppressLint("MissingPermission")
3837
private val audioRecord = AudioRecord(
3938
context.config.audioSource,
40-
SAMPLE_RATE,
39+
context.config.samplingRate,
4140
AudioFormat.CHANNEL_IN_MONO,
4241
AudioFormat.ENCODING_PCM_16BIT,
4342
minBufferSize * 2
@@ -65,9 +64,9 @@ class Mp3Recorder(val context: Context) : Recorder {
6564
}
6665

6766
androidLame = LameBuilder()
68-
.setInSampleRate(SAMPLE_RATE)
67+
.setInSampleRate(context.config.samplingRate)
6968
.setOutBitrate(context.config.bitrate / 1000)
70-
.setOutSampleRate(SAMPLE_RATE)
69+
.setOutSampleRate(context.config.samplingRate)
7170
.setOutChannels(1)
7271
.build()
7372

app/src/main/res/layout/activity_settings.xml

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,28 @@
216216

217217
</RelativeLayout>
218218

219+
<RelativeLayout
220+
android:id="@+id/settings_audio_source_holder"
221+
style="@style/SettingsHolderTextViewStyle"
222+
android:layout_width="match_parent"
223+
android:layout_height="wrap_content">
224+
225+
<org.fossify.commons.views.MyTextView
226+
android:id="@+id/settings_audio_source_label"
227+
style="@style/SettingsTextLabelStyle"
228+
android:layout_width="wrap_content"
229+
android:layout_height="wrap_content"
230+
android:text="@string/audio_source" />
231+
232+
<org.fossify.commons.views.MyTextView
233+
android:id="@+id/settings_audio_source"
234+
style="@style/SettingsTextValueStyle"
235+
android:layout_width="match_parent"
236+
android:layout_height="wrap_content"
237+
android:layout_below="@+id/settings_audio_source_label"
238+
tools:text="Camera" />
239+
</RelativeLayout>
240+
219241
<RelativeLayout
220242
android:id="@+id/settings_extension_holder"
221243
style="@style/SettingsHolderTextViewStyle"
@@ -240,47 +262,48 @@
240262
</RelativeLayout>
241263

242264
<RelativeLayout
243-
android:id="@+id/settings_audio_source_holder"
265+
android:id="@+id/settings_bitrate_holder"
244266
style="@style/SettingsHolderTextViewStyle"
245267
android:layout_width="match_parent"
246268
android:layout_height="wrap_content">
247269

248270
<org.fossify.commons.views.MyTextView
249-
android:id="@+id/settings_audio_source_label"
271+
android:id="@+id/settings_bitrate_label"
250272
style="@style/SettingsTextLabelStyle"
251273
android:layout_width="wrap_content"
252274
android:layout_height="wrap_content"
253-
android:text="@string/audio_source" />
275+
android:text="@string/bitrate" />
254276

255277
<org.fossify.commons.views.MyTextView
256-
android:id="@+id/settings_audio_source"
278+
android:id="@+id/settings_bitrate"
257279
style="@style/SettingsTextValueStyle"
258280
android:layout_width="match_parent"
259281
android:layout_height="wrap_content"
260-
android:layout_below="@+id/settings_audio_source_label"
261-
tools:text="128 kbps" />
282+
android:layout_below="@+id/settings_bitrate_label"
283+
tools:text="192 kbps" />
284+
262285
</RelativeLayout>
263286

264287
<RelativeLayout
265-
android:id="@+id/settings_bitrate_holder"
288+
android:id="@+id/settings_sampling_rate_holder"
266289
style="@style/SettingsHolderTextViewStyle"
267290
android:layout_width="match_parent"
268291
android:layout_height="wrap_content">
269292

270293
<org.fossify.commons.views.MyTextView
271-
android:id="@+id/settings_bitrate_label"
294+
android:id="@+id/settings_sampling_rate_label"
272295
style="@style/SettingsTextLabelStyle"
273296
android:layout_width="wrap_content"
274297
android:layout_height="wrap_content"
275-
android:text="@string/bitrate" />
298+
android:text="@string/sample_rate" />
276299

277300
<org.fossify.commons.views.MyTextView
278-
android:id="@+id/settings_bitrate"
301+
android:id="@+id/settings_sampling_rate"
279302
style="@style/SettingsTextValueStyle"
280303
android:layout_width="match_parent"
281304
android:layout_height="wrap_content"
282-
android:layout_below="@+id/settings_bitrate_label"
283-
tools:text="192 kbps" />
305+
android:layout_below="@+id/settings_sampling_rate_label"
306+
tools:text="48000 Hz" />
284307

285308
</RelativeLayout>
286309

app/src/main/res/values-pl/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<string name="bitrate">Przepływność</string>
2222
<string name="record_after_launch">Rozpoczynaj nagrywanie automatycznie przy uruchomieniu aplikacji</string>
2323
<string name="keep_screen_on">Pozostawiaj ekran włączony podczas nagrywania</string>
24+
<string name="sample_rate">Częstotliwość próbkowania</string>
2425
<string name="audio_source_camcorder">Aparat</string>
2526
<string name="audio_source_default">Domyślne systemowe</string>
2627
<string name="audio_source_unprocessed">Nieprzetworzone</string>

app/src/main/res/values/donottranslate.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77
<string name="ogg">ogg</string>
88
<string name="ogg_opus">ogg (Opus)</string>
99
<string name="bitrate_value">%d kbps</string>
10+
<string name="sampling_rate_value">%d Hz</string>
1011
</resources>

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
<string name="bitrate">Bitrate</string>
2929
<string name="record_after_launch">Start recording automatically after launching the app</string>
3030
<string name="keep_screen_on">Keep the screen on during recording</string>
31+
<string name="sample_rate">Sample rate</string>
3132
<!-- Settings Audio source selection -->
3233
<string name="audio_source_camcorder">Camera</string>
3334
<string name="audio_source_default">Android default</string>

0 commit comments

Comments
 (0)