-2

I want to code an easy audio filter app using the App Designer in Matlab. One should be able to load an audio file, press play and change parameters like input gain, cutoff frequency etc. while the file is being played.

I just cannot wrap my head around how to make it possible to change the parameters in realtime and update the corresponding variables so that one can hear how the filter is changing.

This is the code I have written by now:

classdef EulerFilter < matlab.apps.AppBase

% Properties that correspond to app components
properties (Access = public)
    UIFigure         matlab.ui.Figure
    CutoffKnobLabel  matlab.ui.control.Label
    CutoffKnob       matlab.ui.control.Knob
    PlayButton       matlab.ui.control.StateButton
end


properties (Access = public)
    inputGain % input Gain
    CutoffHz % cutoff frequency in Hz
end

methods (Access = public)

    function play(app)
        % setup file stream
        frameLength = 256;
        fileReader = dsp.AudioFileReader(...
            'Sun Behind CloudsDry.wav',...
            'SamplesPerFrame',frameLength);
        deviceWriter = audioDeviceWriter(...
            'SampleRate',fileReader.SampleRate);

        % code snippet

        % porcessing of frames
        while ~isDone(fileReader)
            % code snippet
        end

        release(fileReader);
        release(deviceWriter);
    end

end


methods (Access = private)

    % Code that executes after component creation
    function startupFcn(app)
        app.inputGain = 1;
        app.CutoffHz = 22000;
    end

    % Value changed function: PlayButton
    function PlayButtonValueChanged(app, event)
        value = app.PlayButton.Value;
        play(app);
    end

    % Value changing function: CutoffKnob
    function CutoffKnobValueChanging(app, event)
        %display(event)
        changingValue = event.Value;
        app.CutoffHz = changingValue;
    end
end

% App initialization and construction
methods (Access = private)

    % Create UIFigure and components
    function createComponents(app)

        % Create UIFigure
        app.UIFigure = uifigure;
        app.UIFigure.Position = [100 100 640 480];
        app.UIFigure.Name = 'UI Figure';

        % Create CutoffKnobLabel
        app.CutoffKnobLabel = uilabel(app.UIFigure);
        app.CutoffKnobLabel.HorizontalAlignment = 'center';
        app.CutoffKnobLabel.Position = [159 322 37 22];
        app.CutoffKnobLabel.Text = 'Cutoff';

        % Create CutoffKnob
        app.CutoffKnob = uiknob(app.UIFigure, 'continuous');
        app.CutoffKnob.Limits = [10 22000];
        app.CutoffKnob.MajorTicks = [10 1000 5000 22000];
        app.CutoffKnob.ValueChangingFcn = createCallbackFcn(app, @CutoffKnobValueChanging, true);
        app.CutoffKnob.Position = [155 367 45 45];
        app.CutoffKnob.Value = 22000;

        % Create PlayButton
        app.PlayButton = uibutton(app.UIFigure, 'state');
        app.PlayButton.ValueChangedFcn = createCallbackFcn(app, @PlayButtonValueChanged, true);
        app.PlayButton.Text = 'Play';
        app.PlayButton.Position = [60 40 100 22];
    end
end

methods (Access = public)

    % Construct app
    function app = EulerFilter

        % Create and configure components
        createComponents(app)

        % Register the app with App Designer
        registerApp(app, app.UIFigure)

        % Execute the startup function
        runStartupFcn(app, @startupFcn)

        if nargout == 0
            clear app
        end
    end

    % Code that executes before app deletion
    function delete(app)

        % Delete UIFigure when app is deleted
        delete(app.UIFigure)
    end
end
end

It is mostly the functions Matlab has generated for the GUI. I have added some properties which hold the values for input gain, cutoff etc. as well as the play() function which performs the signal processing. I can run the app, press the play button and hear the audio file being played, but when I change the cutoff frequency for example, nothing changes. I guess this is because I execute the play() function inside the callback function when the play button was pressed and thus the callback functions when the cutoff knob is turned cannot be executed before the other one has finished.

When I first change the parameters and then press play, everything is correct except that I cannot change the parameters while the file is playing.

I have tried the following without success:

  • calling the callback function inside the while loop in the play() function, but I don't know what argument I have to pass for event (Matlab always tells me that it doesn't know the command or arguments are missing) or if this is even useful
  • execute the play() function inside the runStartupFcn() but this function is executed before the GUI is shown which is useless of course
  • I cannot add functions elsewhere as far as I can tell

So now the question is: Can I make the app work in realtime?

I am looking forward to your answers!

user1662035
  • 363
  • 4
  • 13
  • You could try using a timer to repeat your audio processing code, instead of a loop. That creates the opportunity for Matlab to process (and run callbacks for) other GUI events in between timer callbacks. – Ben Voigt Jul 24 '18 at 19:40

3 Answers3

0

I think that the solution to your problem is creating a second thread. In Your main thread you have access to your handles and variables and your second thread runs the sound. My idea would have been including the changes that you do in thread A to thread B. Unaffortunately you can just use multithreading with the MatLab Parallel Processing Toolbox. I hope there is another way though.

Cheers, Pablo

Pablo Jeken Rico
  • 569
  • 5
  • 22
0

There seem to be a lot of solutions/examples for Matlab's guide but not yet for the app designer.

It looks like your best bet is to call a pause function in the while loop to give your program time to get the updated values. Experiment with different pause times to make sure your program has enough time to update. Matlab can pause the current executing thread by using :

pause(0.001) % 0.001 sec

Or do a direct call to Java for more accuracy

java.lang.Thread.sleep(duration)  % duration in msec

I'm pretty sure this will give your program the time to access the variables and update. This way you could check every 10/20/50/1000 loop cycles and update the parameters as often as you'd like to minimize any audible artifacts.

% Init counter to see how many loops have passed
counter = 0;

% processing of frames
while ~isDone(fileReader)

    % Do your playback process stuff

    if(counter > 10) % Updates every 10 loops or so
        pause(0.001);
        counter = 0;
    end

    counter = counter + 1;

end

Note: Code not tested, please let me know

Otherwise maybe have a look at a callback approach solution.

This is why GUIs in Matlab aren't always such a good idea :-) I understand why you might be doing this for a learning purpose but otherwise I would maybe investigate more integration with Java into your Matlab GUI's to handle all of the threading (or even the GUI design with Java itself). To get started...

WoodyDev
  • 1,386
  • 1
  • 9
  • 19
0

May be too late to the thread to be of help, but the problem you're running into is when CALLBACK1 is invoked, it calls your PLAY() function, which does not run to completion until your file reading WHILE loop is complete. In other words, CALLBACK1 never finishes running until your file reading is complete.

If you change the cutoff frequency while CALLBACK1 is still reading the file, it (I assume) is calling its own callback, I'll refer to as CALLBACK2. Since MATLAB is single threaded, CALLBACK2 can't run until CALLBACK1 finishes running. Hence why you run into the issue you have.

The way to handle this is for CALLBACK1 to start a MATLAB TIMER object, and configure the timer object (which runs in a separate thread) to read the file at some frequency. This way, CALLBACK1 finishes running pretty quickly, while the timer object does your playing. This allows CALLBACK2 to execute and do its thing.

The complication you may still run into is whether you can change the cutoff frequency "on the fly" for the "playing" to reflect it. That's more a question whether the AudioFileReader object allows that.

Hope this helps.