Randomly concatenate wav files in Matlab - matlab

I've just started learning to code for a few months, so I'm sorry if I'm not clear on how I ask my question:
I have different .wav files of synthesized syllables. I have to concatenate them randomly, but they can't be next to each other so they can't be immediately repeated one after another.
Right now I'm not even sure on how to do the first step which is reading the files. I used:
audios = dir('*.wav');
But with this, I'm having trouble extracting the syllables because they are saved in a struct array. So I tried:
samples = [1,.25*Fs];
[ba, fs]=audioread ('ba_2.wav',samples);
[bi, fs]=audioread ('bi_2.wav',samples);
[bo, fs]=audioread ('bo_2.wav',samples);
With all the 48 files (one by one), and I don't think this is efficient, but I read that using eval or a loop to assining names to variables is not a good idea. And by this way I can easily concatenate them like this:
p1=[ba;bi;bo];
But after this I'm stuck, because they have to be randomly repeated in a 2 minute loop. I don't know if maybe there's another way to save my syllables so I can better manipulate them, or what I must do.
I really appreciate your help, I haven't advanced in 2 weeks T-T

This may be an implementation. It, unfortunately, uses a loop that may or may not fit your needs. Within the loop, each audio file .wav is read and concatenated to a complete song called in this example Audio_File. The function randperm() is used to create a random array that can be used to index the structure and concatenate the audio files .wav in random order. In this example I use the variable song which can be representative of syllable.
Method 1: Reading and Concatenating as a Single Audio Array
No regard to duration, only concatenates the .wav files randomly.
Audio_Info = dir('*.wav');
Number_Of_Audio_Files = length(Audio_Info);
%Creating a random array used for random indexing%
Random_Array = randperm(Number_Of_Audio_Files);
%Reading in the first song%
Audio_File = audioread(Audio_Info(Random_Array(1)).name);
for Index = 2: Number_Of_Audio_Files
Random_Index = Random_Array(Index);
%Reading in new song from structure%
Song = audioread(Audio_Info(Random_Index).name);
%Concantenating newly read song%
Audio_File = vertcat(Audio_File,Song);
end
%Playing back the audio%
Audio_Properties = audioinfo(Audio_Info(1).name);
Fs = Audio_Properties.SampleRate;
sound(Audio_File,Fs);
Method 2: Reading Files and Playing within Loop for 2 Minutes
Plays .wav files for approximately 2 minutes randomly. Does not trim last .wav file played if it overflows 2 minutes. Any .wav file played will be executed before 2 minutes. This overflow scenario can be accommodated but depending on your length of the .wav files and your application this might not be crucial. The variable Total_Duration shown below is used to keep track of how long the audio has been playing. The function audioinfo() was used to retrieve the sampling frequency that is later used for playback.
Audio_Info = dir('*.wav');
Number_Of_Audio_Files = length(Audio_Info);
%Grabbing audio properties%
Audio_Properties = audioinfo(Audio_Info(1).name);
Fs = Audio_Properties.SampleRate;
Random_Index = 0;
Total_Duration = 0;
Duration_In_Seconds = 120;
while(Total_Duration < Duration_In_Seconds)
Random_Array = randperm(Number_Of_Audio_Files);
while(Random_Array(1) == Random_Index)
Random_Array = randperm(Number_Of_Audio_Files);
end
Random_Index = Random_Array(1);
%Reading in new song from structure%
Song = audioread(Audio_Info(Random_Index).name);
sound(Song,Fs);
Audio_Properties = audioinfo(Audio_Info(Random_Index).name);
Song_Duration = Audio_Properties.Duration;
Total_Duration = Total_Duration + Song_Duration;
pause(Song_Duration);
end
Ran using MATLAB R2019b

Related

Ideas for extracting a portion of a wav file and saving as new wav file in MATLAB

This may be easy but I'm no expert in MatLab. I have a bacth of wav files and I need to extract a 30s sample from each. Example:
1.wav - need 1:34-2:04
2.wav - need 5:15 - 5:45
Ideally I'd like to run a code which will take each wav file and extract the correct time period according to a pre-generated table and save each snippet as a new wav file (e.g., 1_snip.wav would be the 30secs I need to analyze). Any points in the right direction would be great. Thanks!
First you extract raw audio from the recording:
[signal,fs] = audioread(‘file.wav’);
The sampling rate is stored inside fs, representing your samples per second. So, 5 seconds would be 5*fs. It is thus possible to extract chunks with specific time stamps:
sig_chunk = signal(start_in_s*fs+1 : end_in_s*fs);
It’s also possible to take timestamp data from a table and use the for loop to cut audio.
The safest option would be to load them individually and use a mask for each then save. For example:
[x1,fs] = audioread('fileName1.wav');
tinit = 1*60 + 34; % In seconds
tend = 2*60 + 4;
ll = floor(tinit*fs) : floor(tend*fs);
x1 = x1(ll); % apply the mask to the segment of audio you want
audiowrite('fileName1edit.wav',x1,fs,'BitsPerSample',24)
However, if you have a big number of files to deal with, a less reliable but more comfortable solution could be to dump all the wav files in a structure
Files = dir('*.wav');
and load them calling
[x,fs] = audioread(Files(idx).name);
within a for loop of length(Files) inside which you could prompt a box dialog asking for the minute and second to begin and minute and second to end. For example:
for idx = 1 : length(Files)
[x,fs] = audioread(Files(idx).name);
prompt = {'Min start:','Second start:','Min end:','Second end:'};
T = inputdlg(prompt,'Enter the times',[1,20]);
Ninit = round(fs*(str2num(T{1})*60 + str2num(T{2})));
Nend = round(fs*(str2num(T{3})*60 + str2num(T{4})));
ll = Ninit:Nend;
x = x(ll); % or x = x(Ninit:Nend);
audiowrite(Files(idx).name,...);
end
See the documentation for inputdlg() for further examples. If you are not editing the string for the output audio file in audiowrite() with an _edit.mat or similar, make a backup of your files in a folder, for safety.

MATLAB: Combine all videos in directory

I have directories that each have several short (~10 second) .avi videos. Does anybody know how I can concatenate all of the videos in a specific directory in alphabetical order to form one single video?
I would try to use VLC, but I have to do this for over a thousand different directories. I didn't realize this would be so difficult, but not able to find anything on Google.
More specifics:
For each directory I want to perform this action on, all videos are guaranteed to be:
.avi,MJPG,20fps,640x480 resolution,no audio,between less than 1 second to 15 seconds long
I'd like the single video file to play just as if I played the individuals back-to-back.
If there's any other specifics I missed please let me know.
The combined videos are intended to all be put into the same directory and given to another person to perform their own video processing on with Matlab. They'll be doing something with either crosscorrelation or machine learning to try and identify a particular object in the videos.
You can use a combination of the VideoReader and VideoWriter (see doc for more examples). Iterate through your video files in alphabetical order and "stream" them into a new file.
I threw together some (untested) code. I have no idea how fast this is, though:
cd(VIDEO_DIRECTORY);
tmp = dir('*.avi'); % all .avi video clips
videoList = {tmp.name}'; % sort this list if necessary! sort(videoList) might work
% create output in seperate folder (to avoid accidentally using it as input)
mkdir('output');
outputVideo = VideoWriter(fullfile(workingDir,'output/mergedVideo.avi'));
% if all clips are from the same source/have the same specifications
% just initialize with the settings of the first video in videoList
inputVideo_init = VideoReader(videoList{1}); % first video
outputVideo.FrameRate = inputVideo_init.FrameRate;
open(outputVideo) % >> open stream
% iterate over all videos you want to merge (e.g. in videoList)
for i = 1:length(videoList)
% select i-th clip (assumes they are in order in this list!)
inputVideo = VideoReader(videoList{i});
% -- stream your inputVideo into an outputVideo
while hasFrame(inputVideo)
writeVideo(outputVideo, readFrame(inputVideo));
end
end
close(outputVideo) % << close after having iterated through all videos

How do I control which channel sound is played through using MATLAB?

I'm a novice MATLAB user, so apologies if the question is very basic. I need a .wav sound file to be played in a single, specific channel -- let's say the left channel. As it is, my code reads in the sound file, and I add in a column of zeros to nullify the channel I don't want, like so:
currentDir = pwd;
soundFile = [currentDir '\sound1.wav']; % load the file
[y, Fs] = audioread(soundFile); % read the file in
soundData(:,1) = y(:,1); % keeps sound for the left channel
soundData(:,2) = 0; % nullifies the right channel
sound = audioplayer(soundData,Fs);
play(sound);
As it stands, the code currently produces a sound that is full volume in the left speaker, and half volume (but still very much audible) in the right speaker. I have tried this with at least 20 .wav files, with the same result.
In case it's relevant, this happens even when I write in code that explicitly matches the length of the sound variable in 0s, like so:
[y, Fs] = audioread(soundFile);
silentChannel = zeros(size(y));
soundData(:,1) = y(:,1); % keeps sound for the left channel
soundData(:,2) = silentChannel(:,2); % nullifies the right channel
Does anybody know what I'm doing wrong, or have any thoughts?
Your code is definitely correct and it should only play audio in the left channel. I suspect that the problem is caused by a sound-card/driver issues. Allow me suggest the following troubleshooting steps:
Save your output as a wav file using audiowrite('output.wav', soundData, Fs). Play this using a different audio player, such as Audacity. If you still hear output in both channels, it must be a sound-card/driver issue.
Assuming that you are using a Windows PC (going by the syntax in your file path), make sure all sound enhancements are disabled. How to do this depends on the PC. If there is a third-party app controlling the playback settings, you'd have to use that. Otherwise find the settings shown in the picture below in the Control Panel.
In MatLab the expected method for playing sound is the method sound(data,Fs)
To control the channel the sound emits on, you'll want to know how sound() reads data.
data is a matrix with the columns representing channels, and with the rows holding the samples of the waveform for a given sampling fequency Fs
here is a simple implementation.
function treismanwolfe()
close all
clear all
clc
Fs = 40000;
tau = 2*pi();
t = 0:tau/(Fs-1):tau/2;
left = sin(t).*(sin(t*200)+sin(t*1600));
left= left/max(abs(left));
left = left'; %turn column vector into row
right = sin(t).*(sin(t*800)+sin(t*400));
right= right/max(abs(right));
right = right'; %turn column vector into row
data = [left,right*0]; %multiply either by 0 to nullify
sound(data,Fs); %so you can hear it.
end
I hope this works for you. Enjoy!
When I run your code the audio output is only audible in the left channel as you have specified.
#Austin Kootz' version with the sound()-function is just as good and also produces what you're looking for, but with audioplayer-object you have the ability to stop the playback in the middle of the playback (as you probably know)
Have you tried converting your .wav to another format to see if that makes a change?

Average of values from multiple matrices in Matlab

I have 50 matrices contained in one folder, all of dimension 181 x 360. How do I cycle through that folder and take an average of each corresponding data points across all 50 matrices?
If the matrices are contained within Matlab variables stored using save('filename','VariableName') then they can be opened using load('filename.mat').
As such, you can use the result of filesInDirectory = dir; to get a list of all your files, using a search pattern if appropriate, like files = dir('*.mat');
Next you can use your load command, and then whos to see which variables were loaded. You should consider storing these for ease clearing after each iteration of your loop.
Once you have your matrix loaded (one at a time), you can take averages as you need, probably summing a value across multiple loop iterations, then dividing by a total counter you've been measuring (using perhaps count = count + size(MatrixVar, dimension);).
If you need all of the matrices loaded at once, then you can modify the above idea, to load using a loop, then average outside of the loop. In this case, you may need to take care - but 50*181*360 isn't too bad I suspect.
A brief introduction to the load command can be found at this link. It talks mainly about opening one matrix, then plotting the values, but there are some comments about dealing with headers, if needed, and different ways in which you can open data, if load is insufficient. It doesn't talk about binary files, though.
Note on binary files, based on comment to OP's question:
If the file can be opened using
FID = fopen('filename.dat');
fread(FID, 'float');
then you can replace the steps referring to load above, and instead use a loop to find filenames using dir, open the matrices using fopen and fread, then average as needed, finally closing the files and clearing the matrices.
In this case, probably your file identifier is the only part you're likely to need to change during the loop (although your total will increase, if that's how you want to average your data)
Reshaping the matrix, or inverting it, might make the code clearer (which is good!), but might not be necessary depending on what you're trying to average - it may be that selecting only a subsection of the matrix is sufficient.
Possible example code?
Assuming that all of the files in the current directory are to be opened, and that no files are elsewhere, you could try something like:
listOfFiles = dir('*.dat');
for f = 1:size(listOfFiles,1)
FID = fopen(listOfFiles(f).name);
Data = fread(FID, 'float');
% Reshape if needed?
Total = Total + sum(Data(start:end,:)); % This might vary, depending on what you want to average etc.
Counter = Counter + (size(Data,1) * size(Data,2)); % This product will be the 181*360 you had in the matrix, in this case
end
Av = Total/Counter;

add sound at the end of a wave file in matlab

I have a big wave file that I want to add some sounds at the end of it. and because i do this adding in a loop it is not wise to read it every time and add a short part and close it(in this way the algorithm is so SLOW).
is there any more optimized way to just add a sound file at the end of another in matlab(wave file)?
How about keeping it in memory while appending?
big = wavread('big.wav');
flist = dir('*.wav');
for i=1:length(flist)
short = wavread(flist(i).name);
big = [big, short];
end
wavwrite(big, 'bigger.wav');
If you know how many samples you want to add to big vector it's faster if you preallocate the space needed.