Matlab get amount of clipping - matlab

Hi I am trying to measure the amount of clipped Samples in audio files with matlab. That means i want to get the number of all Samples that are at the Fullscale Value (at min and max). But it should just count them when there are at least two following samples at this Value.
My code so far:
for i = 1 :20
fileName = ['Elektro',num2str(i) '.wav'];
unclipped = audioread(fileName);
summeMax = sum(1 ==unclipped);
summeMin = sum(-1==unclipped);
summe = summeMin +summeMax;
str=sprintf('%d ',summe);
disp(str);
end
As you see its counting every sample and I would need a loop and if statements to detect if there are more than one sample at the fullscale Value.
I tried this it with a loop from j = 1 : length(unclipped) but it takes to long with this Method.
Anyone have suggestions how i could do this?

As #Daniel pointed out in his answer to a previous question of yours, you can find all samples at which the maximum is reached by
maxReached = max(unclipped(:))==unclipped;
To remove all places where the maximum is reached for only one sample, you can use a morphological opening with the structuring element [1,1]:
clippedSamples = imopen(maxReached, [1 1]);
The number of clipped samples is now
sum(clippedSamples);
Of course you'll have to do the same with those samples where the minimum is reached.

Related

How to build a cell-array or matrix of different dimension

I'm using the findpeaks method in Matlab to find peaks. I have a matrix(Peak) of 260x601 where its 260 different trials over 601 time points. I have a separate vector for the actual time (called TimeVec).
I'm using a for loop to loop over the trials.
for i = 1:size(Peak,1)
[pks(i),locs(i)]=findpeaks(Peak(i,:),timeVec,'MinPeakHeight',1);
end
The problem is that each trial could have a different number of peaks therefore it's trying to combine a different number of columns to each iteration. How can I get around this?
This is a short and not fully efficient method:
fp = #(k) findpeaks(Peak(k,:),timeVec,'MinPeakHeight',1);
[pks,locs] = arrayfun(fp,1:size(Peak,1),'UniformOutput',false);
it will be a bit faster with a for loop, but it worth changing this only if you have more data:
[pks,locs] = deal(cell(size(Peak,1),1));
for k = 1:size(Peak,1)
[pks{k},locs{k}] = findpeaks(Peak(k,:),timeVec,'MinPeakHeight',1);
end
for further manipulations on that, use #excaza advice and read the cell array docs.

matlab: running fft on short time intervals in a for-loop for the length of data

I have some EEG data that I would like to break down into 30-second windows and run a fast Fourier transform on each window of data. I've tried to implement a for-loop and increment the index value by the number of samples in the time window. When I run this, I can see that (1) this works for the first window of data, but not the rest of them because (I think) the "number of samples minus one" leads to fewer elements than necessary for data_fft and thus doesn't have the same dimensions as f, which are both being plotted in a figure. (2) I tried to update the index value by adding the number of samples in a window, but after i = 1, it goes to i = 2 in my workspace and not to i = 7681 as I'd hoped. I've spent an embarrassingly long time on trying to figure out how to change this so it works correctly, so any advice is appreciated! Code is below. Let me know if I can clarify anything.
data_ch6 = data(:,6); % looking at just 1 electrode
tmax = 2*60; % total time in sec I want to analyze; just keeping it to 2 minutes for this exercise
tmax_window = 30; %30 sec window
times = tmax/tmax_window; % number of times fft should be run
Nsamps = tmax*hdr.SPR; % total # samples in tmax; sample rate is 256 hz
Nsamps_window = tmax_window*hdr.SPR; % # samples in time window
f = hdr.SPR*(0:((Nsamps_window-1)/2))/Nsamps_window; % frequency for plotting
for i = 1:Nsamps; % need to loop through data in 30 second windows in tmax
data_fft = abs(fft(data_ch6(i:i+Nsamps_window-1))); %run fft on data window
data_fft = data_fft(i:((i+Nsamps_window-1)/2)); %discard half the points
figure
plot(f, data_fft)
i = i+Nsamps_window;
end
Well there are a few things that are wrong in your code. First, let me start be saying that i is a very poor choice for a variable name since in matlab it usually stand for sqrt{-1}.
As for your code, I assume that you intend to perform windowed FFT without overlapping.
1) Your loop goes from 1 to Nsamps with an increment of 1. That means the each time you advance 1 sample. In other words you have Nsamps_window-1 overlap. You can use perhaps i=1:Nsamps_window:Nsamps-Nsamps_window-1 if you are not interested in overlapping.
2) The length of data_fft is Nsamps_window, so I think what you wanted to do is data_fft = data_fft(1:round(Nsamps_window/2));
3) When plotting FFT results, I suggest using dB: plot(20*log10(abs(data_fft)));
4) The line i = i+Nsamps_window; is meaningless since i is your loop variable (it has not effect).

Matlab: variable index size in loop

I've been trying to figure this out myself and also searched here. The fact that I am not quite sure what the problem is isn't helping with this. I have previously found good help in answers to other people here, so I hope that you can help me.
I have a dataset (2000x1100) that consists of 1's and 0's, where each row represents one datapoint over time (columns).
I am trying to find sequences of 0's for each row and exclude those that are below a specific length. I have already found helpful answers here that allowed me to do this (thanks!).
'test' is a reduced test dataset that I am using to test the code.
for j = 1:size(test,1)
testThresh(j,:) = diff([1 test(j,:)]);
startIndex = find(testThresh(j,:) < 0);
if isempty(startIndex)
continue
j = j+1;
end
endIndex = find(testThresh(j,:) > 0)-1;
if numel(endIndex) == 1
for m = 1:size(startIndex,2)
duration = (endIndex(m)-startIndex(m))+1;
threshold = (duration < 10);
startIndexC = startIndex(threshold);
endIndexC = endIndex(threshold);
end
else
duration = (endIndex(j,:)-startIndex(j,:))+1;
threshold = (duration < 10);
startIndexC = startIndex(threshold);
endIndexC = endIndex(threshold);
end
..
I would later replace the below-threshold sequences of 0's with 1's.
The code works fine until I happen to have a row that doesn't contain any 0's. It would skip to the next row as supposed to, but there it will result in an error:
Attempted to access endIndex(2,:); index out of bounds because size(endIndex)=[1,4].
I don't really understand why this causes an error. There isn't any problem with this specific row if it isn't preceded by a no-zero-containing row.
I assume that encountering a row w/o 0 does something to the allowed size of the array. I don't really know how to get around it. I can't pre-allocate because I don't know the size the variable will reach in each iteration (I tried anyway and it didn't help).
I also tried to say that numel(endIndex) >= 1 otherwise skip to next step. That also didn't work. I probably made a really silly mistake, but I am still quite new to Matlab and just can't figure it out.
I hope anyone can help with this!
Any help is greatly appreciated!
Edit:
duration = (endIndex(1,:)-startIndex(1,:))+1;
This is very stupid. I am ashamed. Thanks for pointing this out.
Just for reasons of completion since I wasn't specific enough of what I want to accomplish (sorry about that): I want to determine the start, end and length of stretches of 0's within a lot of 1's. If the length of any of these stretches is below the threshold of 10 (i.e. too short for my purposes to be considered) I want to disregard it.

MATLAB Error:Out of Memory

So I'm trying to perform STFT on a piano recording using matlab, but I get the following error.
Warning: Input arguments must be scalar.
In test3 at 35
??? Error using ==> zeros
Out of memory. Type HELP MEMORY for your options.
Error in ==> test3 at 35
song = cat(1,song,zeros(n_of_padding,1));
The coding I've used is taken from a sample code found on the net.
clc;
clear all;
[song,FS] = wavread('c scale fast.wav');
song = sum(song,2);
song = song/max(abs(song));
wTime = 0.05;
ZP_exp = 1;
P_OL = 50;
% Number of STFT samples per STFT slice
N_window = floor(wTime*FS);
% Number of overlapping points
window_overlap = floor(N_window*(P_OL/100));
wTime = N_window/FS;
%size checking
%make sure there are integer number of windows if not zero pad until they are
L = size(song);
%determine the number of times-1 the overlapping window will fit the song length
N_of_windows = floor(L - N_window/(N_window - window_overlap));
%determine the remainder
N_of_points_left = L - (N_window + N_of_windows*(N_window - window_overlap));
%Calculate the number of points to zero pad
n_of_padding = (N_window - window_overlap) - N_of_points_left;
%append the zeros to the end of the song
song = cat(1,song,zeros(n_of_padding,1));
clear n_of_windows n_of_points_left n_of_padding
n_of_windows = floor((L - N_window)/(N_window - window_overlap))+1;
windowing = hamming(N_window);
N_padding = 2^(nextpow2(N_window)+ZP_exp);
parfor k = 1:N_of_windows
starting = (k-1)*(N_window -window_overlap) +1;
ending = starting+N_window-1;
%Define the Time of the window, i.e., the center of window
times(k) = (starting + ceil(N_window/2))/Fs;
%apply windowing function
frame_sample = music(starting:ending).*windowing;
%take FFT of sample and apply zero padding
F_trans = fft(frame_sample,N_padding);
%store FFT data for later
STFT_out(:,k) = F_trans;
end
Based on some assumptions I would reason that:
- n_of_padding should be smaller than N_window
- N_window is much smaller FS
- Fs is not too high (frequency of your sound, so should not exceed a few thousand?!)
- Your zeros matrix will not be huge
This should mean that the problem is not that you are creating a too large matrix, but that you already filled up the memory before this call.
How to deal with this?
First type dbstop if error
Run your code
When it stops check all variable sizes to see where the space has gone.
If you don't see anything strange (and the big storage is really needed) then you may be able to process your song in parts.
In line 35 you are trying to make an array that exceeds your available memory. Note that a 1 by n array of zeros alone, is n*8 bytes in size. This means if you make such an array, call it x, and check it with whos('x'), like:
x = zeros(10000,1);
whos('x');
You will likely find that x is 80000 bytes. Maybe by adding such an array to your song variable is adding the last bytes that breaks the memory-camel's back. Using and whos('variableName') take whatever the size of song is before line 35, separately add the size of zeros(n_of_padding,1), convert that to MB, and see if it exceeds your maximum possible memory given by help memory.
The most common implication of Out of memory errors on Matlab is that it is unable to allocate memory due to the lack of a contiguous block. This article explains the various reasons that can cause an Out of memory error on MATLAB.
The Out of memory error often points to a faulty implementation of code that expands matrices on the fly (concatenating, out-of-range indexing). In such scenarios, MATLAB creates a copy in memory i.e memory twice the size of the matrix is consumed with each such occurrence.
On Windows this problem can be alleviated to some extent by passing the /3GB /USERVA=3030 switch during boot as explained here. This enables additional virtual memory to be addressed by the application(MATLAB in this case).

Find a Binary Data Sequence in a Signal

Here's my goal:
I'm trying to find a way to search through a data signal and find (index) locations where a known, repeating binary data sequence is located. Then, because the spreading code and demodulation is known, pull out the corresponding chip of data and read it. Currently, I believe xcorr will do the trick.
Here's my problem:
I can't seem to interpret my result from xcorr or xcorr2 to give me what I'm looking for. I'm either having a problem cross-referencing from the vector location of my xcorr function to my time vector, or a problem properly identifying my data sequence with xcorr, or both. Other possibilities may exist.
Where I am at/What I have:
I have created a random BPSK signal that consists of the data sequence of interest and garbage data over a repeating period. I have tried processing it using xcorr, which is where I am stuck.
Here's my code:
%% Clear Variables
clc;
clear all, close all;
%% Create random data
nbits = 2^10;
ngarbage = 3*nbits;
data = randi([0,1],1,nbits);
garbage = randi([0,1],1,ngarbage);
stream = horzcat(data,garbage);
%% Convert from Unipolar to Bipolar Encoding
stream_b = 2*stream - 1;
%% Define Parameters
%%% Variable Parameters
nsamples = 20*nbits;
nseq = 5 %# Iterate stream nseq times
T = 10; %# Number of periods
Ts = 1; %# Symbol Duration
Es = Ts/2; %# Energy per Symbol
fc = 1e9; %# Carrier frequency
%%% Dependent Parameters
A = sqrt(2*Es/Ts); %# Amplitude of Carrier
omega = 2*pi*fc %# Frequency in radians
t = linspace(0,T,nsamples) %# Discrete time from 0 to T periods with nsamples samples
nspb = nsamples/length(stream) %# Number of samples per bit
%% Creating the BPSK Modulation
%# First we have to stretch the stream to fit the time vector. We can quickly do this using _
%# simple matrix manipulation.
% Replicate each bit nspb/nseq times
repStream_b = repmat(stream_b',1,nspb/nseq);
% Tranpose and replicate nseq times to be able to fill to t
modSig_proto = repmat(repStream_b',1,nseq);
% Tranpose column by column, then rearrange into a row vector
modSig = modSig_proto(:)';
%% The Carrier Wave
carrier = A*cos(omega*t);
%% Modulated Signal
sig = modSig.*carrier;
Using XCORR
I use xcorr2() to eliminate the zero padding effect of xcorr on unequal vectors. See comments below for clarification.
corr = abs(xcorr2(data,sig); %# pull the absolute correlation between data and sig
[val,ind] = sort(corr(:),'descend') %# sort the correlation data and assign values and indices
ind_max = ind(1:nseq); %# pull the nseq highest valued indices and send to ind_max
Now, I think this should pull the five highest correlations between data and sig. These should correspond to the end bit of data in the stream for every iteration of stream, because I would think that is where the data would most strongly cross-correlate with sig, but they do not. Sometimes the maxes are not even one stream length apart. So I'm confused here.
Question
In a three part question:
Am I missing a certain step? How do I use xcorr in this case to find where data and sig are most strongly correlated?
Is my entire method wrong? Should I not be looking for the max correlations?
Or should I be attacking this problem from another angle, id est, not use xcorr and maybe use filter or another function?
Your overall method is great and makes a lot of sense. The problem you're having is that you're getting some actual correlation with your garbage data. I noticed that you shifted all of your sream to be zero-centered, but didn't do the same to your data. If you zero-center the data, your correlation peaks will be better defined (at least that worked when I tried it).
data = 2*data -1;
Also, I don't recommend using a simple sort to find your peaks. If you have a wide peak, which is especially possible with a noisy signal, you could have two high points right next to each other. Find a single maximum, and then zero that point and a few neighbors. Then just repeat however many times you like. Alternatively, if you know how long your epoch is, only do a correlation with one epoch's worth of data, and iterate through the signal as it arrives.
With #David K 's and #Patrick Mineault's help I manage to track down where I went wrong. First #Patrick Mineault suggested I flip the signals. The best way to see what you would expect from the result is to slide the small vector along the larger, searched vector. So
corr = xcorr2(sig,data);
Then I like to chop off the end there because it's just extra. I did this with a trim function I made that simply takes the signal you're sliding and trims it's irrelevant pieces off the end of the xcorr result.
trim = #(x,s2) x(1:end - (length(s2) - 1));
trim(corr,data);
Then, as #David K suggests, you need to have the data stream you're looking for encoded the same as your searched signal. So in this case
data = 2*data-1;
Second, if you just have your data at it's original bit length, and not at it's stretched, iterated length, it can be found in the signal but it will be VERY noisy. To reduce the noise, simply stretch the data to match it's stretched length in the iterated signal. So
rdata = repmat(data',1,nspb/nseq);
rdata = repmat(rdata',1,nseq);
data = rdata(:)';
Now finally, we should have crystal clear correlations for this case. And to pull out the maxes that should correspond to those correlations I wrote
[sortedValues sortIndex] = sort(corr(:),'descend');
c = 0 ;
for r = 1 : length(sortedValues)
if sortedValues(r,:) == max(corr)
c = c + 1;
maxIndex(1,c) = sortIndex(r,:);
else break % If you don't do this, you get loop lock
end
end
Now c should end up being nseq for this case and you should have 5 index times where the corrs should be! You can easily pull out the bits with another loop and c or length(maxIndex). I've also made this into a more "real world" toy script, where there is a data stream, doppler, fading, and it's over a time vector in seconds instead of samples.
Thanks for the help!
Try flipping the signal, i.e.:
corr = abs(xcorr2(data,sig(end:-1:1));
Is that any better?