%% Determine and Decrease Underrun
% Underrun refers to output signal silence, which occurs when the audio stream loop does not keep pace with the output device. Determine the underrun of an audio stream loop, add artificial computational load to the audio stream loop, and then modify properties of your audioDeviceWriter object to decrease underrun. Your results depend on your computer.
% Create a dsp.AudioFileReader object, and specify the file to read. Use the audioinfo function to return a structure containing information about the audio file.

fileReader = dsp.AudioFileReader('speech_dft.mp3');
fileInfo = audioinfo('speech_dft.mp3');

%
% Create an audioDeviceWriter object. Use the SampleRate of the file reader as the SampleRate of the device writer. Call setup to reduce the computational load of initialization in an audio stream loop.

deviceWriter = audioDeviceWriter('SampleRate',fileReader.SampleRate);
setup(deviceWriter,zeros(fileReader.SamplesPerFrame,fileInfo.NumChannels))

%
% Run your audio stream loop with input from file and output to device. Print the total samples underrun and the underrun in seconds.

totalUnderrun = 0;
while ~isDone(fileReader)
    input = fileReader();
    numUnderrun = deviceWriter(input);
    totalUnderrun = totalUnderrun + numUnderrun;
end
fprintf('Total samples underrun: %d.\n',totalUnderrun)
fprintf('Total seconds underrun: %d.\n',double(totalUnderrun)/double(deviceWriter.SampleRate))

%
% Release your dsp.AudioFileReader and audioDeviceWriter objects and set your counter variable to zero.

release(fileReader)
release(deviceWriter)
totalUnderrun = 0;

%
% Use pause to mimic an algorithm that takes 0.075 seconds to process. The pause causes the audio stream loop to go slower than the device, which results in periods of silence in the output audio signal.

while ~isDone(fileReader)
    input = fileReader();
    numUnderrun = deviceWriter(input);
    totalUnderrun = totalUnderrun + numUnderrun;
    pause(0.075)
end
fprintf('Total samples underrun: %d.\n',totalUnderrun)
fprintf('Total seconds underrun: %d.\n',double(totalUnderrun)/double(deviceWriter.SampleRate))

%
% Release your audioDeviceReader and dsp.AudioFileWriter and set the counter variable to zero.

release(fileReader)
release(deviceWriter)
totalUnderrun = 0;

%
% Set the frame size of your audio stream loop to 2048. Because the SupportVariableSizeInput property of your audioDeviceWriter System object is set to false, the buffer size of your audio device is the same size as the input frame size. Increasing your device buffer size decreases underrun.

fileReader = dsp.AudioFileReader('speech_dft.mp3');
fileReader.SamplesPerFrame = 2048;
fileInfo = audioinfo('speech_dft.mp3');

deviceWriter = audioDeviceWriter('SampleRate',fileReader.SampleRate);
setup(deviceWriter,zeros(fileReader.SamplesPerFrame,fileInfo.NumChannels))

%
% Calculate the total underrun.

while ~isDone(fileReader)
    input = fileReader();
    numUnderrun = deviceWriter(input);
    totalUnderrun = totalUnderrun + numUnderrun;
    pause(0.075)
end
fprintf('Total samples underrun: %d.\n',totalUnderrun)
fprintf('Total seconds underrun: %d.\n',double(totalUnderrun)/double(deviceWriter.SampleRate))

%
% The increased frame size reduces the total underrun of your audio stream loop. However, increasing the frame size also increases latency. Other approaches to reduce underrun include:
% Increasing the buffer size independent of input frame size. To increase buffer size independent of input frame size, you must first set SupportVariableSizeInput to true. This approach also increases latency.
% Decreasing the sample rate. Decreasing the sample rate reduces both latency and underrun at the cost of signal resolution.
% Choosing an optimal driver and device for your system.
%
% Copyright 2012 The MathWorks, Inc.