%% Surround Sound Matrix Encoding and Decoding
% This example shows how to generate a stereo signal from a multichannel
% audio signal using matrix encoding, and how to recover the original
% channels from the stereo mix using matrix decoding. This example
% illustrates MATLAB(R) and Simulink(R) implementations. This example also
% shows how performance can be improved by using dataflow execution domain.

% Copyright 2014-2020 The MathWorks, Inc.

%% Introduction
% Matrix decoding is an audio technique that decodes an audio signal with M
% channels into an audio signal with N channels (N > M) for play back on a
% system with N speakers. The original audio signal is usually generated
% using a matrix encoder, which transforms N-channel signals to M-channel
% signals.
%
% Matrix encoding and decoding enables the same audio content to be played
% on different systems. For example, a surround sound multichannel signal
% may be encoded into a stereo signal. The stereo signal may be played back
% on a stereo system to accommodate settings where a surround sound
% receiver does not exist, or it may be decoded and played as surround if
% surround equipment is present [1].
%
% In this example, we showcase a matrix encoder used to encode a
% four-channel signal (left, right, center and surround) to a stereo
% signal. The four original signals are then regenerated using a matrix
% decoder. This example is a simplified version of the encoding and
% decoding scheme used in the Dolby Pro Logic system [2].

%% Simulink Version 
% The *audiomatrixdecoding* model implements the audio matrix
% encoding/decoding example.
model_name = 'audiomatrixdecoding';
open_system(model_name);

%%
% The input to the matrix encoder consists of four separate audio channels
% (center, left, right and surround).
%%
open_system([model_name '/Audio Channels'],'force');

%%
% Double-click the *Audio Channels* subsystem to launch a tuning dialog.
% The dialog enables you to control the relative power between the right
% channel and left channel inputs, as well as the power level of the
% surround channel.
% 
% <<../AudioUpmixControls.png>>
%%

%%
% You can also toggle between listening to any of the original, encoded or
% decoded audio channels by double-clicking the |Audio Player Selector|
% subsystem and selecting the channel of your choice from the dialog drop
% down menu.

%% Matrix Encoder
% The *Matrix Encoder* encodes the four input channels into a stereo
% signal.
%
% Notice that since the input left and right channels only contribute to
% the output left and right channels, respectively, the output stereo
% signal conserves the balance between left and right channels. 
%
% The surround input channel is passed through a Hilbert transformer,
% thereby creating a 180 degree phase differential between the surround
% component feeding the left and right stereo outputs [2].
%
% You may listen to the encoded left and right stereo signals by
% double-clicking the *Audio Player Selector* subsystem and selecting
% either the 'Encoded Total Left' or 'Encoded Total Right' channels.
open_system([model_name '/Dataflow Subsystem/Matrix Encoder'],'force');


%% Matrix Decoder
% The *Matrix Decoder* extracts the four original channels from the encoded
% stereo signal.
%
% The lowpass frequencies are first separated using a Linkwitz-Riley
% cross-over filter. For more information about the implementation of the
% Linkwitz-Riley filter, refer to
% <docid:audio_examples#mw_577ee1a5-96f4-4b37-b05c-1a41293e2319 Multiband
% Dynamic Range Compression>.
%
% The left and right stereo channels are passed through to the left and
% right output channels, respectively. Therefore, there is no loss of
% separation between left and right channels in the output. 
%
% The center output channel is equal to the sum of the stereo input
% signals, thereby cancelling the phase-shifted surround left and right
% components.
% 
% The surround output channel is derived by first taking the difference of
% the stereo signals. Since the original input center signal contributes
% equally to both stereo channels, the center channel does not leak into
% the output surround signal. Moreover, note that the original left and
% right signals contribute to the output surround channel. The surround
% signal is delayed by 10 msec to achieve a precedence effect [3].
%
% You may listen to the decoded surround signal by double-clicking the
% *Audio Player Selector* subsystem and selecting one of the decoded
% signals.
open_system([model_name '/Dataflow Subsystem/Matrix Decoder'],'force');

%% Improve Simulation Performance Using Dataflow Domain
% This example can use dataflow execution domain in Simulink to make use of
% multiple cores on your desktop to improve simulation performance. To
% learn more about dataflow and how to run Simulink models using multiple
% threads, see <docid:dsp_ug#mw_ba342715-0088-4475-add2-80ba2e6fe387
% Dataflow Introduction>.

%% Specify Dataflow Execution Domain
% In Simulink, you specify dataflow as the execution domain for a subsystem
% by setting the Domain parameter to Dataflow using Property Inspector.
% Dataflow domains automatically partition your model and simulate the
% system using multiple threads for better simulation performance. Once you
% set the Domain parameter to Dataflow, you can use Dataflow Simulation
% Assistant to analyze your model to get better performance. You can open
% Dataflow Simulation Assistant by clicking on the *Dataflow assistant*
% button below the *Automatic frame size calculation* parameter in Property
% Inspector.
%
% <<../audiomatrixdecoding_PI.png>>
%
% <<../audiomatrixdecoding_assistant_01.png>>
%

%% Multicore Simulation of Dataflow Domain
% The Dataflow Simulation Assistant suggests changing model settings for
% optimal simulation performance. To accept the proposed model settings,
% next to *Suggested model settings for simulation performance*, click
% *Accept all*. Alternatively, you can expand the section to change the
% settings individually.
% In the Dataflow Simulation Assistant, click the *Analyze* button to start
% the analysis of the dataflow domain for simulation performance. Once the
% analysis is finished, the Dataflow Simulation Assistant shows how many
% threads the dataflow subsystem will use during simulation.
%
% <<../audiomatrixdecoding_assistant_02.png>>
%

%%
% For this model, the assistant shows one thread because the data
% dependency between blocks prevents them from being executed concurrently.
% By pipelining the data dependent blocks, the Dataflow Subsystem can
% increase concurrency for higher data throughput. Dataflow Simulation
% Assistant shows the recommended number of pipeline delays as *Suggested
% Latency*. For this model, the suggested latency is two. Click the *Accept*
% button next to *Suggested Latency* in the Dataflow Simulation Assistant
% to use the recommended latency for Dataflow Subsystem. Dataflow
% Simulation Assistant now shows the number of threads as three, meaning
% that the blocks inside the dataflow subsystem simulate in parallel using
% three threads.
%
% <<../audiomatrixdecoding_assistant_03.png>>
%

%% Compensate for Latency
% When latency is increased in the dataflow execution domain to break data
% dependencies between blocks and create concurrency, that delay needs to
% be accounted for in other parts of the model. For example, signals that
% are compared or combined with the signals at the output ports of the
% Dataflow Subsystem must be delayed to align in time with the signals at
% the output ports of the Dataflow Subsystem. In this example, the audio
% signal from the Audio Channels block that goes to the Audio Player
% Selector must be delayed to align with other signals going into the Audio
% Player Selector block. To compensate for the latency specified on the
% dataflow subsystem, use a delay block to delay this signal by two frames.
% For this signal, the frame length is 1024. A delay value of 2048 is set
% in the delay block to align the signal from the Audio Channels block and
% the signal processed through Dataflow Subsystem.
%
% <<../audiomatrixdecoding_compensating_delay.png>>
%

%% Dataflow Simulation Performance
% To measure performance improvement gained by using dataflow, compare
% execution time of the model with and without dataflow. The Audio Device
% Writer block runs in real time and limits the simulation speed of the
% model to real time. Comment out the Audio Device Writer block when
% measuring execution time. On a Windows desktop computer with Intel(R)
% Xeon(R) CPU W-2133 @ 3.6 GHz 6 Cores 12 Threads processor this model
% using dataflow domain executes 2.6x times faster compared to original
% model.

%% MATLAB Version
% |HelperAudioMatrixDecoderSim| is the MATLAB function containing the audio
% matrix decoder example's implementation. It instantiates, initializes and
% steps through the objects forming the algorithm.
%
% The function |audioMatrixDecoderApp| wraps around
% |HelperAudioMatrixDecoderSim| and iteratively calls it.
%
% Execute |audioMatrixDecoderApp| to run the simulation. Note that the
% simulation runs until you explicitly stop it.
%
% |audioMatrixDecoderApp| launches a UI designed to interact with the
% simulation. Similar to the Simulink version of the example, the UI allows
% you to tune the relative power between the right channel and left channel
% inputs, as well as the power level of the surround channel. You can also
% toggle between listening to any of the original, encoded or decoded audio
% channels by changing the value of the 'Audio Output' drop-down box in the
% UI.
%
% There are also three buttons on the UI - the 'Reset' button will reset
% the simulation internal state to its initial condition and the
% 'Pause Simulation' button will hold the simulation until you press on it
% again. The simulation may be terminated by either closing the UI or by
% clicking on the 'Stop simulation' button. 
%
% MATLAB Coder can be used to generate C code for the function
% |HelperAudioMatrixDecoderSim|. In order to generate a MEX-file for your
% platform, execute the command |HelperMatrixDecodingCodeGeneration|
% from a folder with write permissions. 
%
% By calling the wrapper function |audioMatrixDecoderApp| with |'true'| as
% an argument, the generated MEX-file can be used instead of
% |HelperAudioMatrixDecoderSim| for the simulation. In this scenario, the
% UI is still running inside the MATLAB environment, but the main
% processing algorithm is being performed by a MEX-file. Performance is
% improved in this mode without compromising the ability to tune
% parameters.

%%
close_system('audiomatrixdecoding',0);

%% References
%
% [1] https://en.wikipedia.org/wiki/Matrix_decoder
% 
% [2] Dolby Pro Logic Surround Decoder: Principles of Operation, Roger
% Dressler, Dolby Labs
%
% [3] https://en.wikipedia.org/wiki/Precedence_effect
