Search code examples
matlabaudiocallbackplaybackaudio-player

In Matlab, how can I sync audio with a plot?


I'm using [y, Fs, nbits, opts] = wavread(filename) to read in a wave file. Next I plot(t, y) where t = 0:1/Fs:(length(y)-1)/Fs. I play the wav via sound(y, Fs). My question is, is there a way to have a marker of some sort displayed in the plot that is synced with audio from the wav? I am hoping to listen to the audio while observing where in the plot the corresponding sound is via a marker that moves within the plot.


Solution

  • Below is a solution with good synchronisation. This works well because the audioplayer object is initiating the callback that updates the play head location. Here is the calling script . . .

    fs = 44100;
    durT = 3; %seconds
    durS = fs*durT; %samples
    x = randn(durS, 1);
    
    dt = 1/fs;
    tAxis = dt:dt:durT;
    
    frameRate = 25; %fps
    frameT = 1/frameRate;
    
    mag = 5;
    
    figure;
    plot(tAxis, x);
    ylim([-mag mag])
    xlim([0 durT])
    xlabel('Time [s]')
    
    playHeadLoc = 0;
    hold on; ax = plot([playHeadLoc playHeadLoc], [-mag mag], 'r', 'LineWidth', 2);
    
    player = audioplayer(x, fs);
    myStruct.playHeadLoc = playHeadLoc;
    myStruct.frameT = frameT;
    myStruct.ax = ax;
    
    set(player, 'UserData', myStruct);
    set(player, 'TimerFcn', @apCallback);
    set(player, 'TimerPeriod', frameT);
    play(player);
    

    ...and here is the callback function that you can store in a separate file ...

    function src = apCallback(src, eventdata)
        myStruct = get(src, 'UserData'); %//Unwrap
    
        newPlayHeadLoc = ...
            myStruct.playHeadLoc + ...
            myStruct.frameT;
        set(myStruct.ax, 'Xdata', [newPlayHeadLoc newPlayHeadLoc])
    
        myStruct.playHeadLoc = newPlayHeadLoc;
        set(src, 'UserData', myStruct); %//Rewrap
    end
    

    I have just made the signal a noise sample that you can replace with whatever you like. It is a shame that the audioplayer object has stop and resume methods without any access to a current play head location. Fortunately, there is a user data property that allows you to cram in whatever you like.