# Crossfade multiple movies

**URL:** <https://discourse.processing.org/t/crossfade-multiple-movies/25160>\
**Category:** Libraries\
**Created:** [November 3, 2020, 5:56pm UTC](https://discourse.processing.org/t/crossfade-multiple-movies/25160 "2020-11-03T17:56:57Z")\
**Posts on this page:** 1\
**Showing post:** 2

<div class="post-metadata">

**Author:** ![josephh](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/josephh/32/210_2.png) [@josephh](https://discourse.processing.org/u/josephh)\
**Post date:** [November 7, 2020, 12:27am UTC](https://discourse.processing.org/t/crossfade-multiple-movies/25160/2 "2020-11-07T00:27:09Z")

</div>

Hi,

Thank you for asking this question because it was a nice coding challenge! 🙂

Here is my solution :

- Let’s suppose you have a list of movies, it’s a `Sequence`

- Each `Movie` has it’s own `duration`

- If you were to draw a timeline of the sequence with every movie, you would have this :  

- Now we want to have linear crossfade which mean we can represent it like this (centered around the cut) :  

- When we want to display the sequence at a certain time, we need to first loop through the list of movies and find the one we are currently in, we can do this with this code :

```auto
// We start at 0
float previousCut = 0;

// Loop through movies
for (Movie movie : movies) {
  // The next cut
  float nextCut = previousCut + movie.duration;

  if (currentTime < nextCut) {
    // We display the movie
    // Then break the loop
    break;
  }

  // It's not this movie, increase the cut
  previousCut += movie.duration
}

```

- Then we need to handle two cases, when the timeline cursor is at the end of the clip and we fade out, we need to crossfade is this condition is `true` :  

- We do the same for when the cursor is at the beginning of a clip and we fade in :  

- We then simply linearly interpolate (with [`map()`](https://processing.org/reference/map_.html)) the alpha value for both movies (the current one and the previous/next one for fade in/out). Be careful to switch the order by which you display both movies because it’s going to glitch as soon as the cursor pass after a cut.

These two methods compute the alpha value by passing the distance from the beginning of the fade :

```auto
// Compute the alpha value when fading out
float computeFadeOut(float distFromCut) {
  return map(distFromCut, 0, fadeDuration, 255, 0);
}

// Same for fade in but reverse
float computeFadeIn(float distFromCut) {
  return map(distFromCut, 0, fadeDuration, 0, 255);
}

```

Here you have a complete sketch with the above principle :

 ![multi_cross_fade](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/2X/e/ed39dea7115ff8b36ff2f98a77628cbaeb6abdcc.png)

```auto
Sequence sequence;

void setup() {
  size(1000, 600);

  // Create the sequence
  sequence = new Sequence(5000);

  // Initialize random clips in milliseconds
  sequence.initializeRandom(5, 5000, 10000);
}

void draw() {
  // Reset the background
  background(0);

  // Display the sequence output
  sequence.displayPlayback();

  // Display the timeline
  sequence.displayTimeline(width / 2, height - 50, width - 50, 50);

  // Stop when finished
  if (millis() > sequence.getTotalDuration()) noLoop();
}

/*
* The Movie class with a duration and a color
 */
class Movie {
  float duration;
  color col;

  Movie(float duration, color col) {
    this.duration = duration;
    this.col = col;
  }

  // Initialize the movie with a black background
  Movie(float duration) {
    this(duration, color(0));
  }

  void display(float x, float y, float sizeX, float sizeY, float alpha) {
    fill(col, alpha);
    rect(x, y, sizeX, sizeY);
  }

  int getDurationSeconds() {
    return floor(duration / 1000);
  }

  void randomizeColor() {
    col = color(random(255), random(255), random(255));
  }

  // Display the movie on the whole window
  void fullScreen(float alpha) {
    display(0, 0, width, height, alpha);
  }
}

/*
* The Sequence class is holding a list of movies and
 * a fade duration for crossfading between movies
 */
class Sequence {
  ArrayList<Movie> movies;
  float fadeDuration;

  Sequence(float fadeDuration) {
    this.movies = new ArrayList<Movie>();
    this.fadeDuration = fadeDuration;
  }

  // Randomly create movies with different durations
  void initializeRandom(int n, float minDuration, float maxDuration) {
    for (int i = 0; i < n; i++) {
      Movie movie = new Movie(random(minDuration, maxDuration));
      movie.randomizeColor();
      movies.add(movie);
    }
  }

  // Return the total duration of the sequence
  float getTotalDuration() {
    float total = 0;
    for (Movie movie : movies) total += movie.duration;
    return total;
  }

  // Compute the alpha value when fading out
  float computeFadeOut(float distFromCut) {
    return map(distFromCut, 0, fadeDuration, 255, 0);
  }

  // Same for fade in but reverse
  float computeFadeIn(float distFromCut) {
    return map(distFromCut, 0, fadeDuration, 0, 255);
  }

  // The display method where we do the crossfade
  void displayPlayback() {
    noStroke();

    float previousCut = 0;
    float currentTime = millis();

    for (int i = 0; i < movies.size(); i++) {
      Movie movie = movies.get(i);

      // The timestamp of the next movie
      float nextCut = previousCut + movie.duration;

      // If true this is the current movie
      if (currentTime < nextCut) {
        // Test if we need to crossfade
        if (nextCut - currentTime < fadeDuration / 2) {
          float distFromCut = currentTime - (nextCut - fadeDuration / 2);

          movie.fullScreen(computeFadeOut(distFromCut));

          if (i < movies.size() - 1) {
            movies.get(i + 1).fullScreen(computeFadeIn(distFromCut));
          }
        } else if (currentTime - previousCut < fadeDuration / 2) { // After the previous clip
          float distFromCut = currentTime - (previousCut - fadeDuration / 2);

          if (i > 0) {
            movies.get(i - 1).fullScreen(computeFadeOut(distFromCut));
          }

          movie.fullScreen(computeFadeIn(distFromCut));
        } else {
          // We are not crossfading
          movie.fullScreen(255);
        }

        // Display time stamp
        fill(0);
        textAlign(CENTER, CENTER);
        textSize(45);
        text((previousCut + movie.duration - millis()) / 1000, width / 2, height / 2);

        // Be break to stop the loop
        break;
      }

      // Each time we add the movie length
      previousCut += floor(movie.duration);
    }
  }

  void displayTimeline(float centerX, float centerY, float hSize, float vSize) {
    float totalDuration = getTotalDuration();
    float leftX = centerX - (hSize / 2);
    float incrX = leftX;
    float topY = centerY - vSize / 2;
    float downY = centerY + vSize / 2;
    float fadeSize = map(fadeDuration, 0, totalDuration, 0, hSize);

    for (int i = 0; i < movies.size(); i++) {
      float clipWidth = map(movies.get(i).duration, 0, totalDuration, 0, hSize);

      // Display clip
      stroke(0);
      strokeWeight(3);
      movies.get(i).display(incrX, topY, clipWidth, vSize, 200);

      // Display cross fade
      stroke(255, 0, 0);
      strokeWeight(2);
      noFill();

      if (i > 0) {
        // Fade in
        line(incrX - fadeSize / 2, downY, incrX + fadeSize / 2, topY);

        // Fade out
        line(incrX - fadeSize / 2, topY, incrX + fadeSize / 2, downY);
      } else {
        // Secial case for the first fade in
        line(incrX, centerY, incrX + fadeSize / 2, topY);
      }

      // Also for the last one
      if (i == movies.size() - 1) {
        line(incrX + clipWidth - fadeSize / 2, topY, incrX + clipWidth, centerY);
      }

      // Display duration
      fill(0);
      noStroke();
      textSize(20);
      text(movies.get(i).getDurationSeconds(), incrX + clipWidth / 2, topY - 20);

      // Add the size of the drawn clip
      incrX += clipWidth;
    }

    // Display the time cursor
    float cursorX = map(millis(), 0, totalDuration, leftX, leftX + hSize);
    stroke(255, 0, 0);
    strokeWeight(4);
    line(cursorX, topY, cursorX, downY);

    // Draw triangle on top
    fill(255, 0, 0);
    triangle(cursorX, topY + 5, cursorX - 5, topY - 5, cursorX + 5, topY - 5);
  }
}

```

_Note : I put comments to explain it and don’t feel lost, it’s just that I made the whole drawing part for the timeline and it’s not the real core of the problem, you should look at the `void displayPlayback()` method first.  
I also didn’t use the Processing movie library but rather simulate a movie with a solid color background. It should be easy to adapt 😉_

Hope it was clear!

---

_[View the full topic](https://discourse.processing.org/t/crossfade-multiple-movies/25160)._
