forked from chenxiaolong/BCR
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request chenxiaolong#48 from chenxiaolong/wave-output2
Add support for WAV/PCM output
- Loading branch information
Showing
20 changed files
with
579 additions
and
293 deletions.
There are no files selected for viewing
13 changes: 13 additions & 0 deletions
13
app/src/main/java/com/chiller3/bcr/AudioFormatExtension.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.chiller3.bcr | ||
|
||
import android.media.AudioFormat | ||
import android.os.Build | ||
|
||
val AudioFormat.frameSizeInBytesCompat: Int | ||
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { | ||
frameSizeInBytes | ||
} else{ | ||
// Hardcoded for Android 9 compatibility only | ||
assert(encoding == AudioFormat.ENCODING_PCM_16BIT) | ||
2 * channelCount | ||
} |
137 changes: 0 additions & 137 deletions
137
app/src/main/java/com/chiller3/bcr/CodecBottomSheetFragment.kt
This file was deleted.
Oops, something went wrong.
153 changes: 153 additions & 0 deletions
153
app/src/main/java/com/chiller3/bcr/FormatBottomSheetFragment.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
package com.chiller3.bcr | ||
|
||
import android.os.Bundle | ||
import android.view.LayoutInflater | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import android.widget.LinearLayout | ||
import android.widget.TextView | ||
import androidx.core.view.ViewCompat | ||
import androidx.core.view.isVisible | ||
import com.chiller3.bcr.format.Format | ||
import com.chiller3.bcr.format.FormatParamType | ||
import com.chiller3.bcr.format.Formats | ||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment | ||
import com.google.android.material.button.MaterialButton | ||
import com.google.android.material.button.MaterialButtonToggleGroup | ||
import com.google.android.material.slider.LabelFormatter | ||
import com.google.android.material.slider.Slider | ||
|
||
class FormatBottomSheetFragment : BottomSheetDialogFragment(), | ||
MaterialButtonToggleGroup.OnButtonCheckedListener, LabelFormatter, Slider.OnChangeListener, | ||
View.OnClickListener { | ||
private lateinit var formatParamGroup: LinearLayout | ||
private lateinit var formatParamTitle: TextView | ||
private lateinit var formatParamSlider: Slider | ||
private lateinit var formatReset: MaterialButton | ||
private lateinit var formatNameGroup: MaterialButtonToggleGroup | ||
private val buttonIdToFormat = HashMap<Int, Format>() | ||
private val formatToButtonId = HashMap<Format, Int>() | ||
private lateinit var formatParamType: FormatParamType | ||
|
||
override fun onCreateView( | ||
inflater: LayoutInflater, | ||
container: ViewGroup?, | ||
savedInstanceState: Bundle? | ||
): View? { | ||
val bottomSheet = inflater.inflate(R.layout.format_bottom_sheet, container, false) | ||
|
||
formatParamGroup = bottomSheet.findViewById(R.id.format_param_group) | ||
|
||
formatParamTitle = bottomSheet.findViewById(R.id.format_param_title) | ||
|
||
formatParamSlider = bottomSheet.findViewById(R.id.format_param_slider) | ||
formatParamSlider.setLabelFormatter(this) | ||
formatParamSlider.addOnChangeListener(this) | ||
|
||
formatReset = bottomSheet.findViewById(R.id.format_reset) | ||
formatReset.setOnClickListener(this) | ||
|
||
formatNameGroup = bottomSheet.findViewById(R.id.format_name_group)!! | ||
|
||
for (format in Formats.all) { | ||
if (!format.supported) { | ||
continue | ||
} | ||
|
||
val button = layoutInflater.inflate( | ||
R.layout.format_bottom_sheet_button, formatNameGroup, false) as MaterialButton | ||
val id = ViewCompat.generateViewId() | ||
button.id = id | ||
button.text = format.name | ||
formatNameGroup.addView(button) | ||
buttonIdToFormat[id] = format | ||
formatToButtonId[format] = id | ||
} | ||
|
||
formatNameGroup.addOnButtonCheckedListener(this) | ||
|
||
refreshFormat() | ||
|
||
return bottomSheet | ||
} | ||
|
||
/** | ||
* Update UI based on currently selected format in the preferences. | ||
* | ||
* Calls [refreshParam] via [onButtonChecked]. | ||
*/ | ||
private fun refreshFormat() { | ||
val (format, _) = Formats.fromPreferences(requireContext()) | ||
formatNameGroup.check(formatToButtonId[format]!!) | ||
} | ||
|
||
/** | ||
* Update parameter title and slider to match format parameter specifications. | ||
*/ | ||
private fun refreshParam() { | ||
val (format, param) = Formats.fromPreferences(requireContext()) | ||
formatParamType = format.paramType | ||
|
||
val titleResId = when (format.paramType) { | ||
FormatParamType.CompressionLevel -> R.string.bottom_sheet_compression_level | ||
FormatParamType.Bitrate -> R.string.bottom_sheet_bitrate | ||
} | ||
|
||
formatParamGroup.isVisible = format.paramRange.first != format.paramRange.last | ||
|
||
formatParamTitle.setText(titleResId) | ||
|
||
formatParamSlider.valueFrom = format.paramRange.first.toFloat() | ||
formatParamSlider.valueTo = format.paramRange.last.toFloat() | ||
formatParamSlider.stepSize = format.paramStepSize.toFloat() | ||
formatParamSlider.value = (param ?: format.paramDefault).toFloat() | ||
|
||
// Needed due to a bug in the material3 library where the slider label does not disappear | ||
// when the slider visibility is set to View.GONE | ||
// https://github.com/material-components/material-components-android/issues/2726 | ||
if (format.paramRange.first == format.paramRange.last) { | ||
val ensureLabelsRemoved = formatParamSlider.javaClass.superclass | ||
.getDeclaredMethod("ensureLabelsRemoved") | ||
ensureLabelsRemoved.isAccessible = true | ||
ensureLabelsRemoved.invoke(formatParamSlider) | ||
} | ||
} | ||
|
||
override fun onButtonChecked( | ||
group: MaterialButtonToggleGroup?, | ||
checkedId: Int, | ||
isChecked: Boolean | ||
) { | ||
if (isChecked) { | ||
Preferences.setFormatName(requireContext(), buttonIdToFormat[checkedId]!!.name) | ||
refreshParam() | ||
} | ||
} | ||
|
||
override fun getFormattedValue(value: Float): String = | ||
formatParamType.format(value.toUInt()) | ||
|
||
override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) { | ||
when (slider) { | ||
formatParamSlider -> { | ||
val format = buttonIdToFormat[formatNameGroup.checkedButtonId]!! | ||
Preferences.setFormatParam(requireContext(), format.name, value.toUInt()) | ||
} | ||
} | ||
} | ||
|
||
override fun onClick(v: View?) { | ||
when (v) { | ||
formatReset -> { | ||
Preferences.resetAllFormats(requireContext()) | ||
refreshFormat() | ||
// Need to explicitly refresh the parameter when the default format is already chosen | ||
refreshParam() | ||
} | ||
} | ||
} | ||
|
||
companion object { | ||
val TAG: String = FormatBottomSheetFragment::class.java.simpleName | ||
} | ||
} |
Oops, something went wrong.