# Liquidsoap

## audio stream generation

### Smart crossfade

#### Basic operator

Liquidsoap includes an advanced crossfading operator. Using it, you can code which transition you want for your songs, according to the average volume level (in dB) computed on the end of the ending track and the beginning of the new one.

The low level operator is `smart_cross`. With it, you can register a function that returns the transition you like. The arguments passed to this function are:

- volume level for previous track
- volume level for next track
- metadata chunk for previous track
- metadata chunk for next track
- source corresponding to previous track
- source corresponding to next track

You can find its documentation in the language reference.

#### Example

Liquidsoap also includes a ready-to-use operator defined using `smart_cross`, it is called `smart_crossfade` and is defined in the pervasive helper script `utils.liq`. Its code is:

    # Crossfade between tracks,
    # taking the respective volume levels
    # into account in the choice of the
    # transition.
    # @category Source / Track Processing
    # @param ~start_next   Crossing duration, if any.
    # @param ~fade_in      Fade-in duration, if any.
    # @param ~fade_out     Fade-out duration, if any.
    # @param ~width        Width of the volume analysis window.
    # @param ~conservative Always prepare for
    #                      a premature end-of-track.
    # @param s             The input source.
    def smart_crossfade (~start_next=5.,~fade_in=3.,
                         ~fade_out=3., ~width=2.,
    		     ~conservative=false,s)
      high   = -20.
      medium = -32.
      margin = 4.
      fade.out = fade.out(type="sin",duration=fade_out)
      fade.in  = fade.in(type="sin",duration=fade_in)
      add = fun (a,b) -> add(normalize=false,[b,a])
      log = log(label="smart_crossfade")

      def transition(a,b,ma,mb,sa,sb)

        list.iter(fun(x)->
           log(level=4,"Before: #{x}"),ma)
        list.iter(fun(x)->
           log(level=4,"After : #{x}"),mb)

        if
          # If A and B and not too loud and close,
          # fully cross-fade them.
          a <= medium and
          b <= medium and
          abs(a - b) <= margin
        then
          log("Transition: crossed, fade-in, fade-out.")
          add(fade.out(sa),fade.in(sb))

        elsif
          # If B is significantly louder than A,
          # only fade-out A.
          # We don't want to fade almost silent things,
          # ask for >medium.
          b >= a + margin and a >= medium and b <= high
        then
          log("Transition: crossed, fade-out.")
          add(fade.out(sa),sb)

        elsif
          # Do not fade if it's already very low.
          b >= a + margin and a <= medium and b <= high
        then
          log("Transition: crossed, no fade-out.")
          add(sa,sb)

        elsif
          # Opposite as the previous one.
          a >= b + margin and b >= medium and a <= high
        then
          log("Transition: crossed, fade-in.")
          add(sa,fade.in(sb))

        # What to do with a loud end and
        # a quiet beginning ?
        # A good idea is to use a jingle to separate
        # the two tracks, but that's another story.

        else
          # Otherwise, A and B are just too loud
          # to overlap nicely, or the difference
          # between them is too large and
          # overlapping would completely mask one
          # of them.
          log("No transition: just sequencing.")
          sequence([sa, sb])
        end
      end

      smart_cross(width=width, duration=start_next,
                  conservative=conservative,
                  transition,s)
    end

You can use it directly in your script, or use this code to define yours!