views:

133

answers:

4

I have information (20,000 frames of data) about an audio track that I have auto-correlated using:

[r,lags] = xcorr(XX,XX,'biased');

And it looks like this:

alt text

Which hopefully is so far so good. Ideally I would like to be able to take the frame number that corresponds to the highest part of the second peak. I've read around and tried a load of different methods but I just can't seem to get it to retrieve the information for me.

Would anybody be able to shed some light on what I have to do?

Many thanks!


edit1: I have tried using findpeaks, but it doesn't seem to work for me. I'm not sure if that's because I'm using the wrong data or not.

edit2: I'm currently testing a method to use on just this audio track, but soon I want to expand it so that I can perform this method on a whole directory of files, so I kind of need a script that can detect peaks rather than finding the information myself.

edit3: My .M file:

[y, fs, nb] = wavread('Three.wav');                 %# Load the signal into variable y

frameWidth = 441;                                   %# 10ms
numSamples = length(y);                             %# Number of samples in y
numFrames = floor(numSamples/frameWidth);           %# Number of full frames in y
energy = zeros(1,numFrames);                        %# Initialize energy
startSample = zeros(1,numFrames);                   %# Initialize start indices
endSample = zeros(1,numFrames);                     %# Initialize end indices

for frame = 1:numFrames                             %# Loop over frames
  startSample(frame) = (frame-1)*frameWidth+1;      %# Starting index of frame
  endSample(frame) = frame*frameWidth;              %# Ending index of frame
  frameIndex = startSample(frame):endSample(frame); %# Indices of frame samples
  energy(frame) = sum(y(frameIndex).^2);            %# Calculate frame energy
end                                                 %# End loop

XX = filtfilt(ones(1,10)/10, 1, energy);            %# Smooths signal

[r,lags] = xcorr(XX,XX,'biased');                   %# Auto-correlates the data
plot(lags,r), xlabel('lag'), ylabel('xcorr')        %# Plots data
A: 
  1. Smooth the data using a lowpass filter (or average each sample with a number of surrounding samples).
  2. Find the peak in the center by looking for the highest sample value.
  3. find the valley to the right of the peak by searching for the first sample that has a higher value than its predecessor.
  4. Find the peak to the right of the valley by searching for the first sample to a smaller value than its predecessor.
Han
I was hoping that there was a certain command I could use, rather than making a low pass filter. As you can probably tell, I'm not that technically proficient using MATLAB.
Mark Spivey
A: 

If you have the signal processing toolbox, I think the findpeaks function should do the job for you.

Something like:

th = x //(some value that will overlook noise in the data. See the documentation)
[peaks locs] = findpeaks(a,'threshold',th)

http://www.mathworks.com/access/helpdesk/help/toolbox/signal/findpeaks.html

I'm used to using it on image intensity data, which doesn't have quite as much local variance as your data (Those "thick" looking sections of your plot are just many data points going up and down quickly, right?). You might need to smooth the data out a bit first to get it to work.

TG_Matt
Yeah, currently findpeaks doesn't want to output anything whatsoever.
Mark Spivey
Even when you use it without the threshold option? What error/warning does findpeaks return? What format is the data that xcorr returns? Findpeaks only works on Nx1 or 1xN vectors.
TG_Matt
Yeah, i've tried various different options but it just returns a blank matrix. On certain commands it says there are too many output arguments. xcorr returns a 1x40081 double.
Mark Spivey
Could you update your question with examples of what you've tried that hasn't worked?
TG_Matt
@TG_Matt Just updated it.
Mark Spivey
A: 

As a first step, you should use the second output argument of xcorr to get your units on the plot correct:

Fs = length(data)./T; % or replace with whatever your sample frequency is (44.1 kHz?)
[a,lags] = xcorr(data,data,'biased');
plot(lags./Fs,a);

Now you can use your favorite method to get the lag for the second peak; the data explorer button in the figure will do if you just need to do it once. If you need to do it repeatedly, I don't see any way to avoid getting into findpeaks or similar.

Matt Mizumi
I need to use it repeatedly as I want to use it as a batch script in the future. I'm still struggling to use findpeaks though.
Mark Spivey
+3  A: 

EDIT:

%# load the signal
[y, fs, nb] = wavread('Three.wav');
y = mean(y,2);                               %# stereo, take avrg of 2 channels

%# Calculate frame energy
fWidth = round(fs*10e-3);                    %# 10ms
numFrames = floor(length(y)/fWidth);
energy = zeros(1,numFrames);
for f=1:numFrames
  energy(f) = sum( y((f-1)*fWidth+1:f*fWidth).^2 );
end

%# smooth the signal (moving average with window size = 1% * length of data)
WINDOW_SIZE = round(length(energy) * 0.01);  %# 200
XX = filtfilt(ones(1,WINDOW_SIZE)/WINDOW_SIZE, 1, energy);

%# auto-correlation
[r,lags] = xcorr(XX, 'biased');

%# find extrema points
dr = diff(r);
eIdx = find(dr(1:end-1) .* dr(2:end) <= 0) + 1;

[~,loc] = sort(r(eIdx), 'descend');
loc = loc(1:min(3,end));                     %# take the highest 3 values

%# plot
plot(lags,r), hold on
plot(lags(eIdx), r(eIdx), 'g*')
plot(lags(eIdx(loc)), r(eIdx(loc)), 'ro')
hold off, xlabel('lag'), ylabel('xcorr')

alt text

and the lag values corresponding to the marked peaks:

>> lags( eIdx(loc) )
ans =
           0       -6316        6316

Note that we smoothed the signal prior to computing the derivative of the autocorrelation function in order to find the extrema points

Amro
That's similar to what I want, which is good, but of course with your signal, you are looking at a single peak. As I mentioned in my initial post, I'm struggling to identify the values around the second peak to the right - I just don't know the formula!
Mark Spivey
@Mark Spivey: if you increase the moving average window size to say 200 (ie increase the effect of smoothing) `XX = filtfilt(ones(1,200)/200, 1, energy)`, then simply run the same code I posted above and the peaks will be correctly marked: http://img295.imageshack.us/img295/274/greenshot20100817103530.png
Amro
Why does the y axis on your plot have a max xcorr value of 1, whereas mine goes up in increments of 500? That's not right surely?
Mark Spivey
Also I get weird 'loc' data, e.g. 12, 16, 8. So I think i'm doing something wrong again..
Mark Spivey
@Mark Spivey: I updated the code to use your actual signal. In the previous screenshot, I used the normalized auto-correlation with a range of [0,1]). Note that `loc` indexes `eIdx` which contains the extrema points indices inside the `lag` vector
Amro
@Amro Thank you so much, I now have a working prototype of my thumbnailing system! I can't thank you enough!
Mark Spivey
@Amro How would I go about modifying the lag of the autocorellation? I'm trying to find the chorus section of these songs so I want to see what lag value is the most effective in doing this. Thanks!
Mark Spivey
@Mark Spivey: the autocorrelation is estimated at each possible lag, so Im not sure what you mean by modifying the lag ??
Amro
@Amro Sorry I meant to say cross-correlation.
Mark Spivey
@Mark Spivey: I'm still not following ..
Amro
@Amro Never mind that question, it was a stupid thing to ask. I do however have some more sensible questions for you if that's ok? I went through my list of 50 songs. 29 worked, 21 failed to produce a thumbnail. The ones that failed had a very flat slope, so I assumed that the peak picker was struggling to get hold of usable values? Is the likely problem the initial framewidth of the energy samples? I tried adjusting the smoothing strength but it got to a point where it would work, but it would only pick a sample from the first ten seconds - defeating the object of correlation.
Mark Spivey
@Mark Spivey: That's definitely one of the difficulties of doing such automated signal processing, its hard to get values that work well for all your input data..
Amro
@Amro Is there anything I could adjust in that extrema point command possibly?
Mark Spivey
@Amro Say instead of finding the peak, it could find a period where the decline noticeably flattens out? e.g. http://a.imageshack.us/img340/7407/flato.jpg
Mark Spivey