Index: HTML/EN/settings/player/audio.html =================================================================== --- HTML/EN/settings/player/audio.html (revision 31875) +++ HTML/EN/settings/player/audio.html (working copy) @@ -123,6 +123,19 @@ [% END %] + [% WRAPPER settingGroup title="SETUP_CROSSFADEONSKIP" desc="SETUP_CROSSFADEONSKIP_DESC" %] + + [% END %] + [% WRAPPER settingGroup title="SETUP_TRANSITIONDURATION" desc="SETUP_TRANSITIONDURATION_DESC" %] [% END %] Index: Slim/Web/Settings/Player/Audio.pm =================================================================== --- Slim/Web/Settings/Player/Audio.pm (revision 31875) +++ Slim/Web/Settings/Player/Audio.pm (working copy) @@ -44,6 +44,10 @@ if ($client->maxTransitionDuration()) { push @prefs,qw(transitionType transitionDuration transitionSmart); } + + if ($client->canImmediateCrossfade()) { + push @prefs,'crossfadeOnSkip'; + } if ($client->hasDigitalOut()) { push @prefs,qw(digitalVolumeControl mp3SilencePrelude); Index: Slim/Player/SqueezePlay.pm =================================================================== --- Slim/Player/SqueezePlay.pm (revision 31875) +++ Slim/Player/SqueezePlay.pm (working copy) @@ -39,6 +39,7 @@ maxSupportedSamplerate accuratePlayPoints firmware + canImmediateCrossfade canDecodeRhapsody canDecodeRtmp hasDigitalOut @@ -60,6 +61,7 @@ maxSupportedSamplerate => 48000, accuratePlayPoints => 0, firmware => 0, + ImmediateCrossfade => 0, canDecodeRhapsody => 0, canDecodeRtmp => 0, hasDigitalOut => 0, @@ -79,6 +81,7 @@ MaxSampleRate => 'maxSupportedSamplerate', AccuratePlayPoints => 'accuratePlayPoints', Firmware => 'firmware', + ImmediateCrossfade => 'canImmediateCrossfade', Rhap => 'canDecodeRhapsody', Rtmp => 'canDecodeRtmp', HasDigitalOut => 'hasDigitalOut', @@ -238,6 +241,13 @@ return $ret; } + +sub forceReady { + my $client = shift; + + $client->readyToStream(1); +} + 1; __END__ Index: Slim/Player/Client.pm =================================================================== --- Slim/Player/Client.pm (revision 31875) +++ Slim/Player/Client.pm (working copy) @@ -1451,6 +1451,8 @@ sub canDecodeRhapsody { 0 }; +sub canImmediateCrossfade { 0 }; + sub hidden { 0 } sub hasScrolling { 0 } Index: Slim/Player/StreamingController.pm =================================================================== --- Slim/Player/StreamingController.pm (revision 31875) +++ Slim/Player/StreamingController.pm (working copy) @@ -62,6 +62,8 @@ songStreamController => undef, nextCheckSyncTime => 0, resumeTime => undef, # elapsed time when paused + crossFade => undef, + fadeIn => undef, # Sync management syncgroupid => undef, @@ -120,7 +122,7 @@ [ [ \&_StopGetNext, \&_BadState, \&_BadState, \&_StopGetNext], # STOPPED [ \&_BadState, \&_StopGetNext, \&_StopGetNext, \&_BadState], # BUFFERING [ \&_BadState, \&_StopGetNext, \&_StopGetNext, \&_BadState], # WAITING_TO_SYNC - [ \&_StopGetNext, \&_StopGetNext, \&_StopGetNext, \&_StopGetNext], # PLAYING + [ \&_FadeNext, \&_FadeNext, \&_FadeNext, \&_FadeNext], # PLAYING [ \&_StopGetNext, \&_StopGetNext, \&_StopGetNext, \&_StopGetNext], # PAUSED ], ContinuePlay => @@ -940,6 +944,68 @@ } } +sub _FadeNext { + my ($self, $event, $params) = @_; + + my $client = $self->master(); + my $clientPrefs = $prefs->client($client); + + my $transitionDuration = $clientPrefs->get('transitionDuration') || 0; + + if (!$self->isPlaying(1) || !$transitionDuration || !$clientPrefs->get("crossfadeOnSkip")) { + _StopGetNext(@_); + return; + } + + # We need to check that all the players in the sync-group can be requested to + # do an immediate crossfade and, by inference, be ready to stream early. + foreach (@{$self->{'players'}}) { + if (!$_->canImmediateCrossfade()) { + _StopGetNext(@_); + return; + } + } + + main::INFOLOG && $log->info('Doing crossfade for ', $event); + + $_->forceReady($transitionDuration) foreach (@{$self->{'players'}}); + $self->{'crossFade'} = $transitionDuration; + + if ( $self->playingSong() && ( $self->isPlaying(1) || $self->isPaused() ) ) { + my $song = $self->playingSong(); + my $handler = $song->currentTrackHandler(); + if ($handler->can('onStop')) { + $handler->onStop($song); + } + } + + closeStream($self); + + _setStreamingState($self, IDLE); + _getNextTrack($self, $params); + + if ($self->{'streamingState'} == IDLE) { # must have failed + _Stop(@_); + } + + elsif ($self->{'streamingState'} == TRACKWAIT) { + + # If we are still waiting after 10s, then give up + my $id = $self->{'nextTrackCallbackId'}; + + Slim::Utils::Timers::setTimer( + $self, + time() + 10, + sub { + if ($self->{'streamingState'} == TRACKWAIT && $self->{'nextTrackCallbackId'} == $id) { + _Stop(@_); + } + }, + undef + ); + } +} + sub _StopGetNext { # stop, getNextTrack -> Stopped, TrackWait my ($self, $event, $params) = @_; _Stop(@_); @@ -958,7 +1024,7 @@ return; } - _StopGetNext(@_); + _FadeNext(@_); } sub _FlushGetNext { # flush -> Idle; IF [moreTracks] THEN getNextTrack -> TrackWait ENDIF @@ -1238,6 +1304,9 @@ my $startedPlayers = 0; my $reportsTrackStart = 0; + my $crossFade = $self->isPlaying('really') ? $self->{'crossFade'} : undef; + $self->{'crossFade'} = undef; + # bug 10438 $self->resetFrameData(); @@ -1270,6 +1339,7 @@ 'replay_gain' => Slim::Player::ReplayGain->fetchGainMode($self->master(), $song), 'seekdata' => $seekdata, 'fadeIn' => $myFadeIn, + 'crossFade' => $crossFade, # we never set the 'loop' parameter } ); Index: Slim/Player/Squeezebox.pm =================================================================== --- Slim/Player/Squeezebox.pm (revision 31875) +++ Slim/Player/Squeezebox.pm (working copy) @@ -553,7 +553,7 @@ # u8_t threshold; // [1] Kb of input buffer data before we autostart or notify the server of buffer fullness # u8_t spdif_enable; // [1] '0' = auto, '1' = on, '2' = off # u8_t transition_period; // [1] seconds over which transition should happen -# u8_t transition_type; // [1] '0' = none, '1' = crossfade, '2' = fade in, '3' = fade out, '4' fade in & fade out +# u8_t transition_type; // [1] '0' = none, '1' = crossfade, '2' = fade in, '3' = fade out, '4' fade in & fade out, '5' = crossfade-immediate # u8_t flags; // [1] 0x80 - loop infinitely # // 0x40 - stream without restarting decoder # // 0x20 - Rtmp (SqueezePlay only) @@ -907,6 +907,9 @@ if ($params->{'fadeIn'}) { $transitionType = 2; $transitionDuration = $params->{'fadeIn'}; + } elsif ($params->{'crossFade'}) { + $transitionType = 5; + $transitionDuration = $params->{'crossFade'}; } else { $transitionType = $prefs->client($master)->get('transitionType') || 0; $transitionDuration = $prefs->client($master)->get('transitionDuration') || 0; Index: Slim/Control/Jive.pm =================================================================== --- Slim/Control/Jive.pm (revision 31875) +++ Slim/Control/Jive.pm (working copy) @@ -1546,6 +1546,38 @@ }; } + # crossfade on skip + if ($client->canImmediateCrossfade()) { + + my $crossfadeOnSkip = $prefs->client($client)->get('crossfadeOnSkip'); + my $currentSetting = $crossfadeOnSkip ? 1 : 0; + my @strings = ('OFF', 'ON',); + my @translated_strings = map { ucfirst($client->string($_)) } @strings; + my @choiceActions; + for my $i (0..$#strings) { + push @choiceActions, + { + player => 0, + cmd => ['playerpref', 'crossfadeOnSkip', "$i"], + }; + } + push @menu, { + text => $client->string("SETUP_CROSSFADEONSKIP"), + id => 'settingsCrossfadeSkip', + node => 'settingsAudio', + weight => 35, + choiceStrings => [ @translated_strings ], + selectedIndex => $currentSetting + 1, # 1 is added to make it count like Lua + actions => { + do => { + choices => [ + @choiceActions + ], + }, + }, + }; + } + # replay gain (aka volume adjustment) if ($client->canDoReplayGain(0)) { push @menu, { Index: strings.txt =================================================================== --- strings.txt (revision 31875) +++ strings.txt (working copy) @@ -5620,6 +5620,12 @@ DE Rechter Kanal EN Right Channel +SETUP_CROSSFADEONSKIP + EN Crossfade on Skip + +SETUP_CROSSFADEONSKIP_DESC + EN Crossfade when jumping within playlist when a track is already playing. + SETUP_TRANSITIONTYPE CS Prolínání DA Blød overgang