4 Commits

Author SHA1 Message Date
Michatec 0d0980a1ef refactor(radio): change Reverb indices to size_t 2026-04-05 19:14:57 +02:00
Michachatz ae215691ca Update radio.cpp 2026-04-05 19:09:43 +02:00
Michachatz 52f1a57de3 Update radio.cpp 2026-04-05 19:07:27 +02:00
Michatec 53abe918ca fix(player): improve cast player integration and service lifecycle management 2026-04-05 18:19:00 +02:00
2 changed files with 38 additions and 36 deletions
+24 -8
View File
@@ -92,18 +92,34 @@ public:
*/ */
class Reverb { class Reverb {
public: public:
std::vector<float> delayLine; std::vector<float> d1, d2, d3;
int pos = 0; size_t p1 = 0, p2 = 0, p3 = 0;
float feedback = 0.4f;
float feedback = 0.7f;
float mix = 0.0f; float mix = 0.0f;
Reverb() { delayLine.resize(4410, 0.0f); } // ~100ms Reverb() {
d1.resize(11025, 0.0f); // 250ms
d2.resize(14700, 0.0f); // 333ms
d3.resize(17640, 0.0f); // 400ms
}
float process(float in) { float process(float in) {
float delayed = delayLine[static_cast<size_t>(pos)]; float y1 = d1[p1];
delayLine[static_cast<size_t>(pos)] = in + delayed * feedback; float y2 = d2[p2];
pos = (pos + 1) % static_cast<int>(delayLine.size()); float y3 = d3[p3];
return in + delayed * mix;
d1[p1] = in + y1 * feedback;
d2[p2] = in + y2 * feedback;
d3[p3] = in + y3 * feedback;
p1 = (p1 + 1) % d1.size();
p2 = (p2 + 1) % d2.size();
p3 = (p3 + 1) % d3.size();
float reverb = (y1 + y2 + y3) / 3.0f;
return in * (1.0f - mix) + reverb * mix;
} }
}; };
@@ -156,7 +156,8 @@ class PlayerService : MediaLibraryService(), SharedPreferences.OnSharedPreferenc
castPlayer = CastPlayer.Builder(this).setLocalPlayer(exoPlayer).build() castPlayer = CastPlayer.Builder(this).setLocalPlayer(exoPlayer).build()
// manually add seek to next and seek to previous since headphones issue them, and they are translated to next and previous station // manually add seek to next and seek to previous since headphones issue them, and they are translated to next and previous station
player = object : ForwardingPlayer(exoPlayer) { // IMPORTANT: Use castPlayer here instead of exoPlayer so the session controls both local and remote playback
player = object : ForwardingPlayer(castPlayer) {
override fun getAvailableCommands(): Player.Commands { override fun getAvailableCommands(): Player.Commands {
return super.getAvailableCommands().buildUpon().add(COMMAND_SEEK_TO_NEXT) return super.getAvailableCommands().buildUpon().add(COMMAND_SEEK_TO_NEXT)
.add(COMMAND_SEEK_TO_PREVIOUS).build() .add(COMMAND_SEEK_TO_PREVIOUS).build()
@@ -170,6 +171,7 @@ class PlayerService : MediaLibraryService(), SharedPreferences.OnSharedPreferenc
return C.TIME_UNSET // this will hide progress bar for HLS stations in the notification return C.TIME_UNSET // this will hide progress bar for HLS stations in the notification
} }
} }
player.addListener(playerListener)
} }
@@ -483,35 +485,23 @@ class PlayerService : MediaLibraryService(), SharedPreferences.OnSharedPreferenc
isPlaying isPlaying
) )
if (isPlaying) { if (!isPlaying) {
// playback is active
} else {
// cancel sleep timer // cancel sleep timer
cancelSleepTimer() cancelSleepTimer()
// reset metadata // reset metadata
updateMetadata() updateMetadata()
// playback is not active // Check playback state to decide whether to stop the service
// Not playing because playback is paused, ended, suppressed, or the player
// is buffering, stopped or failed. Check player.getPlayWhenReady,
// player.getPlaybackState, player.getPlaybackSuppressionReason and
// player.getPlaybackError for details.
when (player.playbackState) { when (player.playbackState) {
// player is able to immediately play from its current position Player.STATE_ENDED, Player.STATE_IDLE -> {
stopSelf()
}
Player.STATE_READY -> { Player.STATE_READY -> {
// Playback is paused. For radio, we can stop the service to remove the notification.
stopSelf() stopSelf()
} }
// buffering - data needs to be loaded
Player.STATE_BUFFERING -> { Player.STATE_BUFFERING -> {
stopSelf() // DO NOT stop the service while buffering (especially important for Cast)
}
// player finished playing all media
Player.STATE_ENDED -> {
stopSelf()
}
// initial state or player is stopped or playback failed
Player.STATE_IDLE -> {
stopSelf()
} }
} }
} }
@@ -520,14 +510,10 @@ class PlayerService : MediaLibraryService(), SharedPreferences.OnSharedPreferenc
override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) { override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
super.onPlayWhenReadyChanged(playWhenReady, reason) super.onPlayWhenReadyChanged(playWhenReady, reason)
if (!playWhenReady) { if (!playWhenReady) {
when (reason) { // Only stop if not buffering and not ready to play (i.e. truly stopped/paused)
Player.PLAY_WHEN_READY_CHANGE_REASON_END_OF_MEDIA_ITEM -> { if (player.playbackState != Player.STATE_BUFFERING) {
stopSelf() stopSelf()
} }
else -> {
stopSelf()
}
}
} }
} }