savonet / liquidsoap

Liquidsoap is a statically typed scripting general-purpose language with dedicated operators and backend for all thing media, streaming, file generation, automation, HTTP backend and more.
http://liquidsoap.info
GNU General Public License v2.0
1.45k stars 131 forks source link

Document crossfading with multiple type of sources #1695

Closed LunarLoony closed 3 years ago

LunarLoony commented 3 years ago

My Liquidsoap is configured to play jingles, ads, and songs in rotation. I've set the songs to crossfade, which technically works, but it never rotates back to news unless I get rid of crossfade.

My code:

music = crossfade(fade_in=3., fade_out=2., duration=5., audio_to_stereo(playlist(reload_mode="watch", "~/radio/music")))

ads = audio_to_stereo(playlist("~/radio/ads"))
jingles = audio_to_stereo(playlist("~/radio/jingles"))

radio = rotate(weights = [1,3,1,1,3],[jingles,music,jingles,ads,music])

Side note: if I crossfade the final rotation (i.e. crossfade(radio) right at the end), it has no effect.

toots commented 3 years ago

Hi!

This is not really a bug. The crossfade mixes track so there is no real track limit to switch on.. Or else, you would start the news while the two tracks are being mixed.

The recommendation if you want to have finer control is to build your own fading function.

If you want to use the latest main, this can be written quite easily as:

# A function to add a source_tag metadata to a source:
def source_tag(s,tag) =
  def f(_)
    [("source_tag",tag)]
  end
  map_metadata(id=tag,insert_missing=true,f,s)
end

# No need for audio_to_stereo anymore!
music = source_tag(playlist(reload_mode="watch", "~/radio/music"), "music")
ads = source_tag(playlist("~/radio/ads"), "ads")
jingles = source_tag(playlist("~/radio/jingles"), "jingles")

radio = rotate(weights = [1,3,1,1,3],[jingles,music,jingles,ads,music])

# Now a custom crossfade function:
def transition(a,b)
  # If old or new source is not music, no fade
  if a.metadata["source_tag"] != "music" or a.metadata["source_tag"] != "music" then
    sequence([a.source, b.source])
  else
    # Else, apply the standard fade
    cross.smart(fade_in=3., fade_out=2., a, b)
  end
end

radio = cross(duration=5., transition, radio)

This will ensure that transitions between music tracks are crossfaded while transitions to other tracks are not.

toots commented 3 years ago

I'm closing this since I believe there isn't much more we can do. Feel free to follow-up here or on slack!

smimram commented 3 years ago

Since this issue comes from times to time, maybe could we add an option to add a track boundary in the middle of transition?

toots commented 3 years ago

Since this issue comes from times to time, maybe could we add an option to add a track boundary in the middle of transition?

I think the effect would not be what people intent, it would switch to the new source while two tracks are being played at once.

I'm gonna try to add this to the documentation somewhere, maybe that'll be a good start?

smimram commented 3 years ago

Yes, documenting things is always a good start :)

LunarLoony commented 3 years ago

Thanks for the response, that's definitely cleared it up. I think it does need documenting, because I got the impression that crossfade could be used in this instance (probably me just misinterpreting something, but there you go...)

toots commented 3 years ago

Okay. I'm working on the online doc right now ahead of the second beta. Will add something in the cookbook.

LunarLoony commented 3 years ago

Hi!

This is not really a bug. The crossfade mixes track so there is no real track limit to switch on.. Or else, you would start the news while the two tracks are being mixed.

The recommendation if you want to have finer control is to build your own fading function.

If you want to use the latest main, this can be written quite easily as:

# A function to add a source_tag metadata to a source:
def source_tag(s,tag) =
  def f(_)
    [("source_tag",tag)]
  end
  map_metadata(id=tag,insert_missing=true,f,s)
end

# No need for audio_to_stereo anymore!
music = source_tag(playlist(reload_mode="watch", "~/radio/music"), "music")
ads = source_tag(playlist("~/radio/ads"), "ads")
jingles = source_tag(playlist("~/radio/jingles"), "jingles")

radio = rotate(weights = [1,3,1,1,3],[jingles,music,jingles,ads,music])

# Now a custom crossfade function:
def transition(a,b)
  # If old or new source is not music, no fade
  if a.metadata["source_tag"] != "music" or a.metadata["source_tag"] != "music" then
    sequence([a.source, b.source])
  else
    # Else, apply the standard fade
    cross.smart(fade_in=3., fade_out=2., a, b)
  end
end

radio = cross(duration=5., transition, radio)

This will ensure that transitions between music tracks are crossfaded while transitions to other tracks are not.

I thought it worth mentioning that I tried this code and ran into a whole mess of errors. Again, probably something I've done wrong or mistyped. If I can reproduce this, would you prefer me to open a new issue or reply here with the errors?

toots commented 3 years ago

A function to add a source_tag metadata to a source:

def sourcetag(s,tag) = def f() [("source_tag",tag)] end map_metadata(id=tag,insert_missing=true,f,s) end

No need for audio_to_stereo anymore!

music = source_tag(playlist(reload_mode="watch", "~/radio/music"), "music") ads = source_tag(playlist("~/radio/ads"), "ads") jingles = source_tag(playlist("~/radio/jingles"), "jingles")

radio = rotate(weights = [1,3,1,1,3],[jingles,music,jingles,ads,music])

Now a custom crossfade function:

def transition(a,b)

If old or new source is not music, no fade

if a.metadata["source_tag"] != "music" or a.metadata["source_tag"] != "music" then sequence([a.source, b.source]) else

Else, apply the standard fade

cross.smart(fade_in=3., fade_out=2., a, b)

end end

radio = cross(duration=5., transition, radio)

Yeah feel free to open a new issue. You would need the latest main. Also, source_tag was missing a bit, here it is fixed:

def source_tag(s,tag) =
  def f(_)
    [("source_tag",(tag:string))]
  end
  map_metadata(id=tag,insert_missing=true,f,s)
end
918483 commented 3 months ago

Hello, how can I use this same idea in the current version? I need the fade to be done only in the playlist and not rotate jingles?