-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #268 from membraneframework/livebooks-1.0
Livebooks from membrane guide
- Loading branch information
Showing
20 changed files
with
1,000 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# Livebook examples | ||
|
||
This folder contains interactive livebook examples. To launch them you need to install livebook first. | ||
|
||
## Installation | ||
|
||
It is recommended to install Livebook via command line ([see official installation guide](https://github.com/livebook-dev/livebook#escript)). | ||
|
||
If livebook was installed directly from the official page, one should add `$PATH` variable to the Livebook environment: | ||
 |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Membrane audio mixer | ||
|
||
This livebook shows how to mix a beep sound into background music over a period of time. | ||
|
||
To run the demo, [install Livebook](https://github.com/livebook-dev/livebook#escript) and open the `audio_mixer.livemd` file there. | ||
|
||
## Copyright and License | ||
|
||
Copyright 2024, [Software Mansion](https://swmansion.com/?utm_source=git&utm_medium=readme&utm_campaign=membrane) | ||
|
||
[](https://swmansion.com/?utm_source=git&utm_medium=readme&utm_campaign=membrane) | ||
|
||
Licensed under the [Apache License, Version 2.0](LICENSE) |
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
# Mixing audio files | ||
|
||
```elixir | ||
File.cd(__DIR__) | ||
Logger.configure(level: :error) | ||
|
||
Mix.install([ | ||
{:membrane_core, "~> 1.0"}, | ||
{:membrane_audio_mix_plugin, "~> 0.16.0"}, | ||
{:membrane_file_plugin, "~> 0.16.0"}, | ||
{:membrane_mp3_mad_plugin, "~> 0.18.0"}, | ||
{:membrane_ffmpeg_swresample_plugin, "~> 0.19.0"}, | ||
{:membrane_aac_fdk_plugin, "~> 0.18.0"}, | ||
{:membrane_kino_plugin, github: "membraneframework-labs/membrane_kino_plugin", tag: "v0.3.1"}, | ||
{:membrane_tee_plugin, "~> 0.12.0"} | ||
]) | ||
``` | ||
|
||
## Installation | ||
|
||
To run this demo one needs to install native dependencies: | ||
|
||
1. [MP3 MAD](https://github.com/membraneframework/membrane_mp3_mad_plugin/tree/v0.14.0#installation) | ||
2. [AAC FDK](https://github.com/membraneframework/membrane_aac_fdk_plugin#installation) | ||
3. [SWResample FFmpeg](https://github.com/membraneframework/membrane_ffmpeg_swresample_plugin#installation) | ||
|
||
## Description | ||
|
||
This is an example of mixing multiple short "beep" sound into background music, one by one, every second. | ||
|
||
## Pipeline definition | ||
|
||
Define all constants. | ||
|
||
```elixir | ||
n_beeps = 30 | ||
beep_filepath = "./assets/beep.aac" | ||
background_filepath = "./assets/sample_music_short.mp3" | ||
:ok | ||
``` | ||
|
||
The file's "beep" sound input is decoded from `AAC` and split into separate inputs using the `Tee` element. These inputs are then filled into the mixer with corresponding time offsets. | ||
|
||
```elixir | ||
import Membrane.ChildrenSpec | ||
|
||
alias Membrane.{File, AAC, Tee, Time} | ||
|
||
beep_audio_input = | ||
child({:file_source, :beep}, %File.Source{location: beep_filepath}) | ||
|> child({:decoder_aac, :beep}, AAC.FDK.Decoder) | ||
|> child(:beeps, Tee.PushOutput) | ||
|
||
beeps_split = | ||
for i <- 1..n_beeps do | ||
get_child(:beeps) | ||
|> via_in(:input, options: [offset: Time.seconds(i)]) | ||
|> get_child(:mixer) | ||
end | ||
|
||
:ok | ||
``` | ||
|
||
The background music is loaded from a file and then decoded from the `MP3` format to the appropriate `Raw Audio` format. | ||
|
||
All mixer inputs must be of the same format. | ||
|
||
```elixir | ||
import Membrane.ChildrenSpec | ||
|
||
alias Membrane.{File, RawAudio, MP3} | ||
alias Membrane.FFmpeg.SWResample.Converter | ||
|
||
background_audio_input = | ||
child(:file_source, %File.Source{location: background_filepath}) | ||
|> child(:decoder_mp3, MP3.MAD.Decoder) | ||
|> child(:converter, %Converter{ | ||
input_stream_format: %RawAudio{channels: 2, sample_format: :s24le, sample_rate: 44_100}, | ||
output_stream_format: %RawAudio{channels: 1, sample_format: :s16le, sample_rate: 44_100} | ||
}) | ||
|> get_child(:mixer) | ||
|
||
:ok | ||
``` | ||
|
||
Mixer is created and directly connected to audio input of the player. | ||
|
||
```elixir | ||
import Membrane.ChildrenSpec | ||
|
||
alias Membrane.{AudioMixer, AAC, Kino} | ||
|
||
kino = Membrane.Kino.Player.new(audio: true) | ||
|
||
mixer_output = | ||
child(:mixer, Membrane.AudioMixer) | ||
|> child(:encoder_aac, AAC.FDK.Encoder) | ||
|> via_in(:audio) | ||
|> child(:player, %Kino.Player.Sink{kino: kino}) | ||
|
||
:ok | ||
``` | ||
|
||
Whole pipeline structure. | ||
|
||
```elixir | ||
spec = beeps_split ++ [beep_audio_input, background_audio_input, mixer_output] | ||
:ok | ||
``` | ||
|
||
## Playing audio | ||
|
||
```elixir | ||
alias Membrane.RCPipeline, as: RC | ||
|
||
pipeline = RC.start!() | ||
RC.exec_actions(pipeline, spec: spec) | ||
|
||
kino | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Membrane messages source and sink | ||
|
||
This livebook shows how to setup a simple pipeline and send messages through it. | ||
|
||
To run the demo, [install Livebook](https://github.com/livebook-dev/livebook#escript) and open the `messages_source_and_sink.livemd` file there. | ||
|
||
## Copyright and License | ||
|
||
Copyright 2024, [Software Mansion](https://swmansion.com/?utm_source=git&utm_medium=readme&utm_campaign=membrane) | ||
|
||
[](https://swmansion.com/?utm_source=git&utm_medium=readme&utm_campaign=membrane) | ||
|
||
Licensed under the [Apache License, Version 2.0](LICENSE) |
140 changes: 140 additions & 0 deletions
140
livebooks/messages_source_and_sink/messages_source_and_sink.livemd
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
# Messages source and sink | ||
|
||
```elixir | ||
File.cd(__DIR__) | ||
Logger.configure(level: :error) | ||
|
||
Mix.install([ | ||
{:membrane_core, "~> 1.0"} | ||
]) | ||
``` | ||
|
||
## Erlang messages driven source | ||
|
||
```elixir | ||
defmodule MessageSource do | ||
use Membrane.Source | ||
|
||
require Membrane.Logger | ||
|
||
def_output_pad :output, | ||
flow_control: :push, | ||
accepted_format: _any | ||
|
||
def_options register_name: [ | ||
description: "The name under which the element's process will be registered", | ||
spec: atom() | ||
] | ||
|
||
|
||
@impl true | ||
def handle_init(_ctx, opts) do | ||
Process.register(self(), opts.register_name) | ||
{[], %{buffered: []}} | ||
end | ||
|
||
@impl true | ||
def handle_playing(_ctx, state) do | ||
{actions, state} = send_buffers(state) | ||
{[stream_format: {:output, %Membrane.RemoteStream{type: :bytestream}}] ++ actions, state} | ||
end | ||
|
||
@impl true | ||
def handle_info({:message, message}, ctx, state) do | ||
state = %{state | buffered: state.buffered ++ [message]} | ||
|
||
if ctx.playback == :playing do | ||
send_buffers(state) | ||
else | ||
{[], state} | ||
end | ||
end | ||
|
||
@impl true | ||
def handle_info(msg, _ctx, state) do | ||
Membrane.Logger.warning("Unknown message received: #{inspect(msg)}") | ||
{[], state} | ||
end | ||
|
||
defp send_buffers(state) do | ||
actions = | ||
Enum.map(state.buffered, fn message -> | ||
{:buffer, {:output, %Membrane.Buffer{payload: message}}} | ||
end) | ||
|
||
{actions, %{state | buffered: []}} | ||
end | ||
end | ||
``` | ||
|
||
## Erlang messages driven sink | ||
|
||
```elixir | ||
defmodule MessageSink do | ||
use Membrane.Sink | ||
|
||
def_input_pad :input, | ||
flow_control: :push, | ||
accepted_format: _any | ||
|
||
def_options receiver: [ | ||
description: "PID of the process that will receive messages from the sink", | ||
spec: pid() | ||
] | ||
|
||
@impl true | ||
def handle_init(_ctx, opts) do | ||
{[], %{receiver: opts.receiver}} | ||
end | ||
|
||
@impl true | ||
def handle_buffer(:input, buffer, _ctx, state) do | ||
send(state.receiver, {:message, self(), buffer.payload}) | ||
{[], state} | ||
end | ||
end | ||
``` | ||
|
||
## Pipeline definition and startup | ||
|
||
```elixir | ||
alias Membrane.RCPipeline | ||
import Membrane.ChildrenSpec | ||
|
||
defmodule MyPipeline do | ||
use Membrane.Pipeline | ||
|
||
@impl true | ||
def handle_init(_ctx, opts) do | ||
spec = | ||
child(:source, %MessageSource{register_name: :messages_source}) | ||
|> child(:sink, %MessageSink{receiver: Keyword.get(opts, :receiver)}) | ||
|
||
{[spec: spec], nil} | ||
end | ||
end | ||
|
||
{:ok, _supervisor, pipeline} = Membrane.Pipeline.start(MyPipeline, receiver: self()) | ||
payloads = 1..10 | ||
|
||
Task.async(fn -> | ||
Enum.each( | ||
payloads, | ||
&send(:messages_source, {:message, &1}) | ||
) | ||
end) | ||
|
||
:ok | ||
``` | ||
|
||
## Printing of the messages received and pipeline termination | ||
|
||
```elixir | ||
for _i <- 1..10 do | ||
receive do | ||
{:message, _pid, _value} = msg -> IO.inspect(msg) | ||
end | ||
end | ||
|
||
RCPipeline.terminate(pipeline) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Membrane playing mp3 file demo | ||
|
||
This livebook shows how to load `MP3` audio from the file, transcode it to the `AAC` codec, and play it. | ||
|
||
To run the demo, [install Livebook](https://github.com/livebook-dev/livebook#escript) and open the `playing_mp3_file.livemd` file there. | ||
|
||
## Copyright and License | ||
|
||
Copyright 2024, [Software Mansion](https://swmansion.com/?utm_source=git&utm_medium=readme&utm_campaign=membrane) | ||
|
||
[](https://swmansion.com/?utm_source=git&utm_medium=readme&utm_campaign=membrane) | ||
|
||
Licensed under the [Apache License, Version 2.0](LICENSE) |
Binary file not shown.
Oops, something went wrong.