实时音频调制

时间:2013-08-26 00:10:51

标签: matlab audio synthesizer

我无法弄清楚如何继续我的项目。简而言之,我正在尝试使用加速度计的数据创建一个FM合成器,以实时更改FM和Pitch参数。

以下是我所依据的一些代码:

% X axis data will be used for fc parameter
% Y axis data will be used for fm parameter

function y = fmSynth(fc, dur, fm)

fs = 48000;
T = 1/fs;      % seconds
t = 0:T:dur;    % time vector

% FM parameters
Imin = 0;
Imax = 20;
I = t.*(Imax - Imin)/dur + Imin;

y = sin(2*pi*fc*t + I.*sin(2*pi*fm*t));
plot(t(1:10000), y(1:10000));
sound(y, fs);

我确实有一个生成波形的函数,但仅限于设定的持续时间(由于内置matlab函数的限制)。我希望它是连续的(直到这样的用户输入才能停止它)以及能够“实时”调制它(稍微有点滞后)。

为此,我找到了一个实时音频处理器代码here

然而,这是用面向对象的编程语言编写的,这是我不熟悉的。

是否有一种简单的方法可以将上述函数实现到此代码(输出rtap),还是必须在子类中编写?如果是这样,怎么样?

对于那些不愿意下载代码的人,这里是主要类:

% RealTimeAudioProcessor
%
% The RealTimeAudioProcessor object allows the user to process consecutive
% frames of audio in real-time (it lags real-time by the frame size plus 
% hardware overhead). The object can easily be extended for any type of
% audio processing by overloading the stepImpl method in a derived class.
%
% Use:
%
% % Create the processor with a number of input channels, number of output
% % channels, sample rate, frame size, and device name.
% rtap = RealTimeAudioProcessor(1, 2, 48000, 256, 'Default');
% 
% % Play. This will start *immediately* and block until all audio output is
% % complete.
% rtap.Play();
% 
% % Release the audio resource for others (be nice).
% rtap.release();
% 
% % The RTAP records some timing values while playing. Show the results.
% rtap.Analyze();
% 
% It is recommended that an ASIO audio driver with appropriate hardware be
% used for the audio interface. (ASIO is an open standard from Steinberg
% Media Technologies GmbH).
%
% Tucker McClure @ The MathWorks
% Copyright 2012 The MathWorks, Inc.

classdef RealTimeAudioProcessor < matlab.system.System

     properties (Nontunable, Access = protected)

        % Settings
        frame_size   = 256;   % Number of samples in the buffer
        sample_rate  = 48000; % Number of samples per second [samples/s]
        end_time     = inf;   % Number of seconds until playback stops [s]
        in_channels  = 2;     % Number of input channels
        out_channels = 2;     % Number of output channels
        device_name  = 'Default';
        draw_rate    = 0.02;  % Rate to update graphics [s]

        % Derived quantities
        time_step;   % Length of frame [s]
        time_window; % Array of time values from [0 time_step) [s]

        % Device interfaces
        ap; % AudioPlayer object to manage output
        ar; % AudioRecorder object to manage input

    end

    properties

        % UI handles
        figure_handle; % Handle of UI figure
        text_handle;   % Handle of text in figure

        samples_until_draw = 0; % Samples left before updating GUI

        % Analysis stuff
        max_in_step_time      = 0;
        max_process_step_time = 0;
        max_out_step_time     = 0;

    end

    methods

        % Constructor; creates the RTAP and its internal dsp.AudioPlayer.
        % After creation, the RTAP is ready to play.
        function rtap = RealTimeAudioProcessor(ins, outs, Fs, w, device)

            fprintf('Initializing a RealTimeAudioProcessor on %s... ', ...
                    device);

            % Set some internals.
            rtap.frame_size   = w;
            rtap.sample_rate  = Fs;
            rtap.in_channels  = ins;
            rtap.out_channels = outs;
            rtap.device_name  = device;

            % Calculate the period.
            rtap.time_step = w/Fs;

            % Create all the time values for a window.
            rtap.time_window = (0:w-1)'/Fs;

            % Ok, we set everything up.
            fprintf('done.\n');

            % Display latency to user.
            fprintf('Minimum latency due to buffering: %5.1fms\n', ...
                    1000 * rtap.time_step);

        end

        % Enter a quasi-real-time loop in which audio is acquired/generated
        % and plugged into the output buffer (if a buffer exists).
        function Play(rtap)

            % If not set up, setup.
            if ~rtap.isLocked

                setup(rtap, ...
                      zeros(rtap.frame_size, 1), ...
                      zeros(rtap.frame_size, rtap.in_channels));

            % Otherwise, if we need a new figure, open one.
            elseif ~ishandle(rtap.figure_handle)

                rtap.GenerateFigure();

            end

            % Keep track of time since 'tic'.
            t_clock = 0;

            % Keep track of how much material we've played since 'tic'.
            % At t_clock, this should reach to t_clock + time_step.
            t_played = 0;

            % Initialize the input.
            in = zeros(rtap.frame_size, rtap.in_channels); %#ok<NASGU>

            % Start a timer.
            tic();

            % Loop until the end time has been reached or the figure has
            % been closed.
            while t_clock < rtap.end_time && ishandle(rtap.figure_handle)

                % Play steps until we're |buffer| into the future.
                if t_played < t_clock + rtap.time_step

                    % Create the times for this frame.
                    time = t_played + rtap.time_window;

                    % Get the input for one frame.
                    if rtap.in_channels > 0
                        step_timer = tic();
                        in = step(rtap.ar);
                        rtap.max_in_step_time = ...
                            max(rtap.max_in_step_time, toc(step_timer));
                    else
                        in = zeros(rtap.frame_size, rtap.in_channels);
                    end

                    % Process one frame.
                    step_timer = tic();
                    out = step(rtap, time, in);
                    rtap.max_process_step_time = ...
                        max(rtap.max_process_step_time, toc(step_timer));

                    % Step the AudioPlayer. Time the step for analysis
                    % purposes.
                    if rtap.out_channels > 0
                        step_timer = tic();
                        step(rtap.ap, out);
                        rtap.max_out_step_time = ...
                            max(rtap.max_out_step_time, toc(step_timer));
                    end

                    % Update the time.
                    t_played = t_played + rtap.time_step;

                end

                % Release focus so that figure callbacks can occur.
                if rtap.samples_until_draw <= 0
                    drawnow();
                    rtap.UpdateGraphics();
                    rtap.samples_until_draw = ...
                        rtap.sample_rate * rtap.draw_rate;
                else
                    rtap.samples_until_draw = ...
                        rtap.samples_until_draw - rtap.frame_size;
                end

                % Update the clock.
                t_clock = toc();

            end

            % Wait for audio to end before exiting. We may have just
            % written out a frame, and there may have already been a frame
            % in the buffer, so chill for 2 frames.
            pause(2*rtap.time_step);

        end

        % Display timing results from last play.
        function Analyze(rtap)
            fprintf(['Results for last play:\n', ...
                     'Maximum input step time:   %5.1fms\n', ...
                     'Maximum process step time: %5.1fms\n', ...
                     'Maximum output step time:  %5.1fms\n'], ...
                    1000*rtap.max_in_step_time, ...  
                    1000*rtap.max_process_step_time, ...  
                    1000*rtap.max_out_step_time);
        end

    end

    methods (Access = protected)

        % Set up the internal System Objects and the figure.
        function setupImpl(rtap, ~, ~)

            % Create the AudioPlayer.
            if rtap.out_channels > 0

                rtap.ap = dsp.AudioPlayer(...
                    'DeviceName',       rtap.device_name, ...
                    'BufferSizeSource', 'Property', ...
                    'BufferSize',       rtap.frame_size, ...
                    'QueueDuration',    0, ...
                    'SampleRate',       rtap.sample_rate);


                % Start with silence. This initializes the AudioPlayer to
                % the window size and number of channels and takes longer
                % than any subsequent call will take.
                step(rtap.ap, zeros(rtap.frame_size, rtap.out_channels));

            end

            % Create the AudioRecorder (if requested).
            if rtap.in_channels > 0

                rtap.ar = dsp.AudioRecorder(...
                    'DeviceName',       'Default', ...
                    'SampleRate',       rtap.sample_rate, ...
                    'BufferSizeSource', 'Property', ...
                    'BufferSize',       rtap.frame_size, ...
                    'SamplesPerFrame',  rtap.frame_size, ...
                    'QueueDuration',    0, ...
                    'OutputDataType',   'double', ...
                    'NumChannels',      rtap.in_channels);

                % Initialize the input.
                step(rtap.ar);

            end

            if ishandle(rtap.figure_handle)
                close(rtap.figure_handle);
            end
            rtap.GenerateFigure();

            % Draw it.
            drawnow();

            % Chill out for a second before rushing forward with sound.
            pause(rtap.time_step);

        end

        % Process one frame of audio, given the inputs corresponding to the
        % frame and the times.
        function out = stepImpl(rtap, time, in) %#ok<INUSD>
            out = zeros(rtap.frame_size, rtap.out_channels);
        end

        % Specify that the step requires 2 inputs.
        function n = getNumInputsImpl(~)
            n = 2;
        end

        % Specify that the step requires 1 output.
        function n = getNumOutputsImpl(~)
            n = 1;
        end

        % Clean up the AudioPlayer.
        function releaseImpl(rtap)

            % Release the dsp.AudioPlayer resource.
            if rtap.out_channels > 0
                release(rtap.ap);
            end

            % Release the dsp.AudioRecorder too.
            if rtap.in_channels > 0
                release(rtap.ar);
            end

            % Close the figure if it's still open.
            if ishandle(rtap.figure_handle)
                set(rtap.text_handle, 'String', 'Closing.');
                close(rtap.figure_handle);
                rtap.figure_handle = [];
            end

        end

        % Generate a figure that stays open with updates for the user.
        % Closing this figure ends playback.
        function GenerateFigure(rtap)

            % Get the screen dimensions for centering the figure.
            screen_dims = get(0, 'ScreenSize');
            figure_width_height = [640 160];
            figure_dims = [floor(0.5*(  screen_dims(3:4) ...
                                      - figure_width_height)) ...
                           figure_width_height];

            % Generate a figure.
            rtap.figure_handle = figure(...
                'Name',          'Real-Time Audio Processor Controller',...
                'NumberTitle',   'off', ...
                'Position',      figure_dims);
            axes('Position', [0 0 1 1], ...
                 'Visible', 'off');
            rtap.text_handle = text(0.5, 0.5, ...
                'Real Time Audio Processor is active.', ...
                'HorizontalAlignment', 'center', ...
                'VerticalAlignment',   'middle', ...
                'Interpreter',         'none');

        end

        function UpdateGraphics(rtap) %#ok<*MANU>
        end

    end

end

包中还有其他文件用作示例,但我无法成功修改它们。另外,我很抱歉没有发布我的代码来读取和过滤加速度计数据(这有点长)。

2 个答案:

答案 0 :(得分:1)

不使用OOP的稍微粗略的解决方案是将声音生成代码放在循环中,例如伪代码:

Fs = ...;
while 1
    % generate your signal here ...
    signal = ....
    % ...
    wavplay(signal,Fs,'sync')   
end

但是,您希望包含一个转义子句,以便以一种很好的方式从循环中断开。你最终会想要一些类似OOP的解决方案,基本上是一个监听器,这样你就可以逃脱循环并可选择地交互式地修改你的信号。监听器可以是非常基本的,例如实现为带按钮的图形。

答案 1 :(得分:1)

以下是一些代码,它们将播放具有不同采样频率gong的matlab的Fs文件。使用鼠标的x位置计算频率:鼠标越靠右,Fs越高。最左边的鼠标位置(0 <x <200像素)将终止脚本。

您可能感兴趣的是有两个wav个连续重复播放的文件,或者通过降低{{audioplayer&amp; play允许它来设置两个声音之间的重叠1}}持续时间,或使用声音合成程序(例如matlab的吉他here)。 pause对象有一些不错的功能:audioplayerstop方法以及pause属性。

timer

- 实际上与@Try Hard提出的算法相同!