-2

我想使用 Matlab 中的 App Designer 编写一个简单的音频过滤器应用程序。一个人应该能够加载一个音频文件,按下播放键并在文件播放时更改输入增益、截止频率等参数。

我只是不知道如何使实时更改参数和更新相应的变量成为可能,以便人们可以听到过滤器是如何变化的。

这是我现在写的代码:

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

它主要是 Matlab 为 GUI 生成的函数。我添加了一些属性来保存输入增益、截止等的值,以及执行信号处理的 play() 函数。我可以运行应用程序,按下播放按钮并听到正在播放的音频文件,但是当我更改截止频率时,没有任何变化。我猜这是因为我在按下播放按钮时在回调函数中执行了 play() 函数,因此在另一个完成之前无法执行截止旋钮转动时的回调函数。

当我第一次更改参数然后按播放时,一切都正确,除了在文件播放时我无法更改参数。

我尝试了以下方法但没有成功:

  • 在 play() 函数的 while 循环内调用回调函数,但我不知道我必须为事件传递什么参数(Matlab 总是告诉我它不知道命令或参数丢失)或者如果这个甚至有用
  • 在 runStartupFcn() 中执行 play() 函数,但是这个函数是在显示 GUI 之前执行的,这当然没用
  • 据我所知,我无法在其他地方添加功能

所以现在的问题是:我可以让应用程序实时运行吗?

我期待着您的回答!

4

3 回答 3

0

线程可能为时已晚,无法提供帮助,但您遇到的问题是当调用 CALLBACK1 时,它会调用您的 PLAY() 函数,该函数在您的文件读取 WHILE 循环完成之前不会运行完成。换句话说,CALLBACK1 在您的文件读取完成之前永远不会完成运行。

如果您在 CALLBACK1 仍在读取文件时更改截止频率,它(我假设)正在调用自己的回调,我将其称为 CALLBACK2。由于 MATLAB 是单线程的,因此 CALLBACK2 直到 CALLBACK1 完成运行后才能运行。因此,为什么您会遇到问题。

处理这个问题的方法是让 CALLBACK1 启动一个 MATLAB TIMER 对象,并配置定时器对象(它在单独的线程中运行)以某种频率读取文件。这样,CALLBACK1 很快就完成了运行,而计时器对象则在播放。这允许 CALLBACK2 执行并做它的事情。

您可能仍然遇到的复杂情况是您是否可以“即时”更改截止频率以供“播放”以反映它。这更像是 AudioFileReader 对象是否允许这样做的问题。

希望这可以帮助。

于 2018-09-01T13:31:46.367 回答
0

我认为解决您的问题的方法是创建第二个线程。在您的主线程中,您可以访问您的句柄和变量,​​并且您的第二个线程运行声音。我的想法是包括您在线程 A 中对线程 B 所做的更改。不幸的是,您可以将多线程与 MatLab 并行处理工具箱一起使用。我希望还有另一种方法。

干杯,巴勃罗

于 2018-07-24T14:15:08.660 回答
0

Matlab 的指南似乎有很多解决方案/示例,但对于应用程序设计者来说还没有。

看起来您最好的选择是在 while 循环中调用暂停函数,让您的程序有时间获取更新的值。尝试不同pause的时间以确保您的程序有足够的时间进行更新。Matlab 可以使用以下命令暂停当前正在执行的线程:

pause(0.001) % 0.001 sec

或者直接调用 Java 以获得更高的准确性

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

我很确定这将使您的程序有时间访问变量和更新。这样,您可以每 10/20/50/1000 个循环周期检查一次,并尽可能频繁地更新参数,以最大限度地减少任何可听见的伪影。

% 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

注意:代码未经测试,请告诉我

否则也许看看回调方法解决方案

这就是为什么 Matlab 中的 GUI 并不总是一个好主意的原因 :-) 我理解为什么您可能会出于学习目的这样做,但否则我可能会研究更多将 Java 集成到您的 Matlab GUI 中以处理所有线程(甚至是 Java 本身的 GUI 设计)。开始...

于 2018-07-26T13:36:40.437 回答