mirror of
https://github.com/clangen/musikcube.git
synced 2025-02-06 03:39:50 +00:00
Added the ability to modify the playback transport from the android
client.
This commit is contained in:
parent
7bc5928e5a
commit
27c74b1d3b
@ -36,7 +36,9 @@ class Messages {
|
||||
SetDefaultOutputDriver("set_default_output_driver"),
|
||||
GetGainSettings("get_gain_settings"),
|
||||
UpdateGainSettings("update_gain_settings"),
|
||||
RunIndexer("run_indexer");
|
||||
RunIndexer("run_indexer"),
|
||||
GetTransportType("get_transport_type"),
|
||||
SetTransportType("set_transport_type");
|
||||
|
||||
override fun toString(): String = rawValue
|
||||
fun matches(name: String): Boolean = (rawValue == name)
|
||||
|
@ -52,10 +52,13 @@ interface IDataProvider {
|
||||
fun setDefaultOutputDriver(driverName: String, deviceId: String = "default"): Observable<Boolean>
|
||||
|
||||
fun getGainSettings(): Observable<IGainSettings>
|
||||
fun updateGainSettings(replayGainMode: IGainSettings.ReplayGainMode, preampGain: Float): Observable<Boolean>
|
||||
fun updateGainSettings(replayGainMode: ReplayGainMode, preampGain: Float): Observable<Boolean>
|
||||
|
||||
fun reindexMetadata(): Observable<Boolean>
|
||||
fun rebuildMetadata(): Observable<Boolean>
|
||||
|
||||
fun getTransportType(): Observable<TransportType>
|
||||
fun setTransportType(type: TransportType): Observable<Boolean>
|
||||
|
||||
val state: State
|
||||
}
|
||||
|
@ -1,22 +1,6 @@
|
||||
package io.casey.musikcube.remote.service.websocket.model
|
||||
|
||||
interface IGainSettings {
|
||||
enum class ReplayGainMode(val rawValue: String) {
|
||||
Disabled("disabled"),
|
||||
Album("album"),
|
||||
Track("track");
|
||||
|
||||
companion object {
|
||||
fun find(raw: String): ReplayGainMode {
|
||||
values().forEach {
|
||||
if (it.rawValue == raw) {
|
||||
return it
|
||||
}
|
||||
}
|
||||
return Disabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val replayGainMode: ReplayGainMode
|
||||
val preampGain: Float
|
||||
|
@ -0,0 +1,18 @@
|
||||
package io.casey.musikcube.remote.service.websocket.model
|
||||
|
||||
enum class ReplayGainMode(val rawValue: String) {
|
||||
Disabled("disabled"),
|
||||
Album("album"),
|
||||
Track("track");
|
||||
|
||||
companion object {
|
||||
fun find(raw: String): ReplayGainMode {
|
||||
values().forEach {
|
||||
if (it.rawValue == raw) {
|
||||
return it
|
||||
}
|
||||
}
|
||||
return Disabled
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package io.casey.musikcube.remote.service.websocket.model
|
||||
|
||||
enum class TransportType(val rawValue: String) {
|
||||
Gapless("gapless"),
|
||||
Crossfade("crossfade");
|
||||
|
||||
companion object {
|
||||
fun find(raw: String): TransportType {
|
||||
values().forEach {
|
||||
if (it.rawValue == raw) {
|
||||
return it
|
||||
}
|
||||
}
|
||||
return Gapless
|
||||
}
|
||||
}
|
||||
}
|
@ -435,7 +435,7 @@ class RemoteDataProvider(private val service: WebSocketService) : IDataProvider
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
override fun updateGainSettings(replayGainMode: IGainSettings.ReplayGainMode, preampGain: Float): Observable<Boolean> {
|
||||
override fun updateGainSettings(replayGainMode: ReplayGainMode, preampGain: Float): Observable<Boolean> {
|
||||
val message = SocketMessage.Builder
|
||||
.request(Messages.Request.UpdateGainSettings)
|
||||
.addOption(Messages.Key.REPLAYGAIN_MODE, replayGainMode.rawValue)
|
||||
@ -455,6 +455,31 @@ class RemoteDataProvider(private val service: WebSocketService) : IDataProvider
|
||||
return runIndexer(Messages.Value.REBUILD)
|
||||
}
|
||||
|
||||
override fun getTransportType(): Observable<TransportType> {
|
||||
val message = SocketMessage.Builder
|
||||
.request(Messages.Request.GetTransportType)
|
||||
.build()
|
||||
|
||||
return service.observe(message, client)
|
||||
.flatMap<TransportType> { socketMessage ->
|
||||
Observable.just(TransportType.find(
|
||||
socketMessage.getStringOption(Messages.Key.TYPE,
|
||||
TransportType.Gapless.rawValue)))
|
||||
}
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
override fun setTransportType(type: TransportType): Observable<Boolean> {
|
||||
val message = SocketMessage.Builder
|
||||
.request(Messages.Request.SetTransportType)
|
||||
.addOption(Messages.Key.TYPE, type.rawValue)
|
||||
.build()
|
||||
|
||||
return service.observe(message, client)
|
||||
.flatMap<Boolean> { socketMessage -> isSuccessful(socketMessage) }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
override fun observeState(): Observable<Pair<IDataProvider.State, IDataProvider.State>> =
|
||||
connectionStatePublisher.observeOn(AndroidSchedulers.mainThread())
|
||||
|
||||
|
@ -2,11 +2,12 @@ package io.casey.musikcube.remote.service.websocket.model.impl.remote
|
||||
|
||||
import io.casey.musikcube.remote.service.websocket.Messages
|
||||
import io.casey.musikcube.remote.service.websocket.model.IGainSettings
|
||||
import io.casey.musikcube.remote.service.websocket.model.ReplayGainMode
|
||||
import org.json.JSONObject
|
||||
|
||||
class RemoteGainSettings(val json: JSONObject): IGainSettings {
|
||||
override val replayGainMode: IGainSettings.ReplayGainMode
|
||||
get() = IGainSettings.ReplayGainMode.find(json.optString(Messages.Key.REPLAYGAIN_MODE, "disabled"))
|
||||
override val replayGainMode: ReplayGainMode
|
||||
get() = ReplayGainMode.find(json.optString(Messages.Key.REPLAYGAIN_MODE, "disabled"))
|
||||
override val preampGain: Float
|
||||
get() = json.optDouble(Messages.Key.PREAMP_GAIN, 0.0).toFloat()
|
||||
}
|
@ -8,8 +8,9 @@ import android.widget.*
|
||||
import io.casey.musikcube.remote.R
|
||||
import io.casey.musikcube.remote.framework.ViewModel
|
||||
import io.casey.musikcube.remote.service.websocket.model.IDevice
|
||||
import io.casey.musikcube.remote.service.websocket.model.IGainSettings
|
||||
import io.casey.musikcube.remote.service.websocket.model.IOutput
|
||||
import io.casey.musikcube.remote.service.websocket.model.ReplayGainMode
|
||||
import io.casey.musikcube.remote.service.websocket.model.TransportType
|
||||
import io.casey.musikcube.remote.ui.settings.viewmodel.RemoteSettingsViewModel
|
||||
import io.casey.musikcube.remote.ui.shared.activity.BaseActivity
|
||||
import io.casey.musikcube.remote.ui.shared.extension.slideThisDown
|
||||
@ -25,6 +26,7 @@ class RemoteSettingsActivity: BaseActivity() {
|
||||
private lateinit var loadingOverlay: View
|
||||
private lateinit var driverSpinner: Spinner
|
||||
private lateinit var deviceSpinner: Spinner
|
||||
private lateinit var transportSpinner: Spinner
|
||||
private lateinit var replayGainSpinner: Spinner
|
||||
private lateinit var preampSeekbar: SeekBar
|
||||
private lateinit var preampTextView: TextView
|
||||
@ -45,6 +47,7 @@ class RemoteSettingsActivity: BaseActivity() {
|
||||
loadingOverlay = findViewById(R.id.loading_overlay)
|
||||
driverSpinner = findViewById(R.id.output_driver_spinner)
|
||||
deviceSpinner = findViewById(R.id.output_device_spinner)
|
||||
transportSpinner = findViewById(R.id.transport_spinner)
|
||||
replayGainSpinner = findViewById(R.id.replaygain_spinner)
|
||||
preampSeekbar = findViewById(R.id.gain_seekbar)
|
||||
preampTextView = findViewById(R.id.gain_textview)
|
||||
@ -91,6 +94,7 @@ class RemoteSettingsActivity: BaseActivity() {
|
||||
driverSpinner.onItemSelectedListener = driverChangeListener
|
||||
deviceSpinner.adapter = DevicesAdapter(viewModel.devicesAt(viewModel.selectedDriverIndex))
|
||||
deviceSpinner.setSelection(viewModel.selectedDeviceIndex)
|
||||
transportSpinner.setSelection(TRANSPORT_TYPE_TO_INDEX[viewModel.transportType]!!)
|
||||
replayGainSpinner.setSelection(REPLAYGAIN_MODE_TO_INDEX[viewModel.replayGainMode]!!)
|
||||
initialized = true
|
||||
}
|
||||
@ -119,7 +123,9 @@ class RemoteSettingsActivity: BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.save(replayGainMode, preampGain, driverName, deviceId)
|
||||
val transport = indexToTransportType(transportSpinner.selectedItemPosition)
|
||||
|
||||
viewModel.save(replayGainMode, preampGain, transport, driverName, deviceId)
|
||||
}
|
||||
|
||||
private fun initListeners() {
|
||||
@ -145,10 +151,10 @@ class RemoteSettingsActivity: BaseActivity() {
|
||||
|
||||
/* replaygain / preamp */
|
||||
val replayGainModes = ArrayAdapter.createFromResource(
|
||||
this, R.array.replaygain_mode_array, android.R.layout.simple_spinner_dropdown_item)
|
||||
this, R.array.replaygain_mode_array,
|
||||
android.R.layout.simple_spinner_dropdown_item)
|
||||
|
||||
replayGainModes.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
|
||||
replayGainSpinner.adapter = replayGainModes
|
||||
|
||||
preampSeekbar.setOnSeekBarChangeListener(object:SeekBar.OnSeekBarChangeListener {
|
||||
@ -166,6 +172,14 @@ class RemoteSettingsActivity: BaseActivity() {
|
||||
})
|
||||
|
||||
preampSeekbar.progress = 2000
|
||||
|
||||
/* transport */
|
||||
val transportModes = ArrayAdapter.createFromResource(
|
||||
this, R.array.transport_type_array,
|
||||
android.R.layout.simple_spinner_dropdown_item)
|
||||
|
||||
transportModes.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
transportSpinner.adapter = transportModes
|
||||
}
|
||||
|
||||
private fun initObservers() {
|
||||
@ -238,17 +252,30 @@ class RemoteSettingsActivity: BaseActivity() {
|
||||
|
||||
companion object {
|
||||
val REPLAYGAIN_MODE_TO_INDEX = mapOf(
|
||||
IGainSettings.ReplayGainMode.Disabled to 0,
|
||||
IGainSettings.ReplayGainMode.Track to 1,
|
||||
IGainSettings.ReplayGainMode.Album to 2)
|
||||
ReplayGainMode.Disabled to 0,
|
||||
ReplayGainMode.Track to 1,
|
||||
ReplayGainMode.Album to 2)
|
||||
|
||||
fun indexToReplayGain(index: Int): IGainSettings.ReplayGainMode {
|
||||
val TRANSPORT_TYPE_TO_INDEX = mapOf(
|
||||
TransportType.Gapless to 0,
|
||||
TransportType.Crossfade to 1)
|
||||
|
||||
fun indexToReplayGain(index: Int): ReplayGainMode {
|
||||
REPLAYGAIN_MODE_TO_INDEX.forEach {
|
||||
if (it.value == index) {
|
||||
return it.key
|
||||
}
|
||||
}
|
||||
return IGainSettings.ReplayGainMode.Disabled
|
||||
return ReplayGainMode.Disabled
|
||||
}
|
||||
|
||||
fun indexToTransportType(index: Int): TransportType {
|
||||
TRANSPORT_TYPE_TO_INDEX.forEach {
|
||||
if (it.value == index) {
|
||||
return it.key
|
||||
}
|
||||
}
|
||||
return TransportType.Gapless
|
||||
}
|
||||
|
||||
fun getStartIntent(context: Context):Intent =
|
||||
|
@ -7,6 +7,7 @@ import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.functions.BiFunction
|
||||
import io.reactivex.functions.Function3
|
||||
import io.reactivex.rxkotlin.subscribeBy
|
||||
|
||||
class RemoteSettingsViewModel: ViewModel<RemoteSettingsViewModel.State>() {
|
||||
@ -33,6 +34,11 @@ class RemoteSettingsViewModel: ViewModel<RemoteSettingsViewModel.State>() {
|
||||
}
|
||||
}
|
||||
|
||||
var transportType: TransportType = TransportType.Gapless
|
||||
private set(value) {
|
||||
field = value
|
||||
}
|
||||
|
||||
val driverName: String
|
||||
get() {
|
||||
outputs?.let {
|
||||
@ -70,12 +76,12 @@ class RemoteSettingsViewModel: ViewModel<RemoteSettingsViewModel.State>() {
|
||||
return Math.max(0, deviceIndex)
|
||||
}
|
||||
|
||||
val replayGainMode: IGainSettings.ReplayGainMode
|
||||
val replayGainMode: ReplayGainMode
|
||||
get() {
|
||||
gain?.let {
|
||||
return it.replayGainMode
|
||||
}
|
||||
return IGainSettings.ReplayGainMode.Disabled
|
||||
return ReplayGainMode.Disabled
|
||||
}
|
||||
|
||||
val preampGain: Float
|
||||
@ -103,12 +109,19 @@ class RemoteSettingsViewModel: ViewModel<RemoteSettingsViewModel.State>() {
|
||||
return listOf()
|
||||
}
|
||||
|
||||
private fun save(replayGainMode: IGainSettings.ReplayGainMode, preampGain: Float)
|
||||
private fun save(replayGainMode: ReplayGainMode,
|
||||
preampGain: Float,
|
||||
transport: TransportType)
|
||||
{
|
||||
provider?.let {
|
||||
val oldState = state
|
||||
state = State.Saving
|
||||
it.updateGainSettings(replayGainMode, preampGain)
|
||||
val gainQuery = it.updateGainSettings(replayGainMode, preampGain)
|
||||
val transportQuery = it.setTransportType(transport)
|
||||
Observable.zip<Boolean, Boolean, Boolean>(
|
||||
gainQuery,
|
||||
transportQuery,
|
||||
BiFunction { b1, b2 -> b1 && b2 })
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeBy(
|
||||
onNext = {
|
||||
@ -119,13 +132,14 @@ class RemoteSettingsViewModel: ViewModel<RemoteSettingsViewModel.State>() {
|
||||
}
|
||||
}
|
||||
|
||||
fun save(replayGainMode: IGainSettings.ReplayGainMode,
|
||||
fun save(replayGainMode: ReplayGainMode,
|
||||
preampGain: Float,
|
||||
transport: TransportType,
|
||||
outputDriver: String,
|
||||
outputDeviceId: String)
|
||||
{
|
||||
if (Strings.empty(outputDriver)) {
|
||||
save(replayGainMode, preampGain)
|
||||
save(replayGainMode, preampGain, transport)
|
||||
}
|
||||
else {
|
||||
provider?.let {
|
||||
@ -133,7 +147,12 @@ class RemoteSettingsViewModel: ViewModel<RemoteSettingsViewModel.State>() {
|
||||
state = State.Saving
|
||||
val gainQuery = it.updateGainSettings(replayGainMode, preampGain)
|
||||
val outputQuery = it.setDefaultOutputDriver(outputDriver, outputDeviceId)
|
||||
Observable.zip<Boolean, Boolean, Boolean>(gainQuery, outputQuery, BiFunction { b1, b2 -> b1 && b2 })
|
||||
val transportQuery = it.setTransportType(transport)
|
||||
Observable.zip<Boolean, Boolean, Boolean, Boolean>(
|
||||
gainQuery,
|
||||
outputQuery,
|
||||
transportQuery,
|
||||
Function3 { b1, b2, b3 -> b1 && b2 && b3 })
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeBy(
|
||||
onNext = {
|
||||
@ -162,22 +181,21 @@ class RemoteSettingsViewModel: ViewModel<RemoteSettingsViewModel.State>() {
|
||||
state = State.Loading
|
||||
val gainQuery = it.getGainSettings()
|
||||
val outputsQuery = it.listOutputDrivers()
|
||||
Observable.zip<IGainSettings, IOutputs, Pair<IGainSettings, IOutputs>>(
|
||||
val transportQuery = it.getTransportType()
|
||||
Observable.zip<IGainSettings, IOutputs, TransportType, Boolean>(
|
||||
gainQuery,
|
||||
outputsQuery,
|
||||
BiFunction { gainSettings, outputs ->
|
||||
Pair(gainSettings, outputs)
|
||||
transportQuery,
|
||||
Function3 { gainSettings, outputs, transportType ->
|
||||
this.gain = gainSettings
|
||||
this.outputs = outputs
|
||||
this.transportType = transportType
|
||||
true
|
||||
})
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeBy(
|
||||
onNext = {
|
||||
gain = it.first
|
||||
outputs = it.second
|
||||
state = State.Ready
|
||||
},
|
||||
onError = {
|
||||
state = State.Disconnected
|
||||
})
|
||||
onNext = { state = State.Ready },
|
||||
onError = { state = State.Disconnected })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,24 @@
|
||||
|
||||
<android.support.v4.widget.Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="32dp"/>
|
||||
android:layout_height="24dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginRight="8dp"
|
||||
android:text="@string/remote_settings_transport_type"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/transport_spinner"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="24dp"/>
|
||||
|
||||
<android.support.v4.widget.Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="24dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
@ -97,7 +114,7 @@
|
||||
|
||||
<android.support.v4.widget.Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="32dp"/>
|
||||
android:layout_height="24dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
@ -138,6 +155,7 @@
|
||||
android:id="@+id/loading_overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
android:background="@color/theme_button_background_transparent">
|
||||
<ProgressBar
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -127,6 +127,9 @@
|
||||
<string name="remote_settings_metadata_actions">metadata library</string>
|
||||
<string name="remote_settings_reindex_button">rescan</string>
|
||||
<string name="remote_settings_rebuild_button">rebuild</string>
|
||||
<string name="remote_settings_transport_type">playback transport</string>
|
||||
<string name="transport_type_gapless">gapless</string>
|
||||
<string name="transport_type_crossfade">crossfade</string>
|
||||
<string name="replaygain_mode_disabled">disabled</string>
|
||||
<string name="replaygain_mode_track">track</string>
|
||||
<string name="replaygain_mode_album">album</string>
|
||||
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="transport_type_array">
|
||||
<item>@string/transport_type_gapless</item>
|
||||
<item>@string/transport_type_crossfade</item>
|
||||
</string-array>
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user