Asynchronous audio read with multiple channels in Matlab - matlab

Hello wise people of the internet
My final goal is to create a code that shows a real-time spectrogram of 4-channel audio input device.
I have this code that can show spectrogram, but only after recording is complete:
clc; clear;
%~~~~~~~~~~~~~~~~~~~~~~~~
% Parameters
%~~~~~~~~~~~~~~~~~~~~~~~~
FileName2Save='mySpeech.wav';
NumChannels=2; % Number of recording channels
fs=44100; % Sampling Rate
Tw=50; Ts=10; % Parameters of Spectogram Framing: Tw = window Size [msec] | Ts = Windows steps [msec]
SessionTime=10; % Session Recording Time [Seconds] - After SessionTime The code stops sampling and plots spectogram
%~~~~~~~~~~~~~~~~~~~~~~~~
deviceReader=audioDeviceReader(fs, Tw*0.001*fs, "Driver","ASIO", "NumChannels", NumChannels);
setup(deviceReader)
disp('Recording Start')
All=[];
tic
% this bit is from the official documentation, showing how to get audio from 'audioDeviceReader'.
% In my opinion this is a really bad API design for Matlab.
while toc < SessionTime
acquiredAudio = deviceReader();
All=[All; acquiredAudio];
end
disp('Session Complete - Creating Spectogram')
% this bit is showing the spectrogram of the channels in a figure.
for iC=1:NumChannels
subplot(NumChannels,1,iC) % Change the image order as you like
spectrogram(All(:,iC),hann(round(Tw*0.001*fs)),round((Tw-Ts)*0.001*fs),20000, fs, 'yaxis', 'power');
drawnow
end
release(deviceReader)
disp('Recording complete')
I would like to show the spectrogram at real time while recording audio.
I tried couple of approaches:
1. To put the spectrogram command inside of the while.
This did not work because spectrogram command can take time, that make the while to not catch all of the samples.
2. Use parfeval command to run spectrogram in the background.
This did not work because the background thread can not do spectrogram. It also can't do simpler commands like disp.
I also tried afterEach with the parfeval but it makes the code that recieves the audio sample get stuck, and that's the same problem I mentioned earlier: If the code inside the while that receives samples gets stuck even a bit, we're losing samples here.
3. Use parfeval command to get the samples of the audio in the background.
I tried running the code that gets the audio in the background, and then use send command to get the new data and do spectrogram every time.
This did not work (see comment in code):
function GetSamples(q)
All = [];
tic;
while toc < 0.5
newValues = ...;
All = [All, newValues];
send(q, arr); % Goes to next only when spectrogram is complete drawing
end
end
The 'send' command calls the code that prints the spectrogram, and apparently only continues to the next in the while when the spectrogram drawing is complete, which can take a few seconds.
When the next values are queried, were losing a couple of samples here.
4. Use audiorecorder, which is asynchronous by default
Can't, because the requirement here is recording 4 channels at the same time.
If audiorecorder was able to record 4 channels, the problem will get solved because I could do something like:
record(recorder);
tic;
while toc < 5
y = getaudiodata(recorder);
spectogram(...);
drawnow;
end

Related

How can play an audio file with plotting a diagram simultaneously in matlab?

I would like to play an audio file Meanwhile plotting and updating a diagram. However, my audio file is interrupted. I would like to play audio file smoothly in background and update the figure at the same time.
for i=1:10
player = audioplayer(audio, Fs);
play(player);
scatter(x(i),y(i),'r.')
end
Your problem is that play is an asynchronous call: it means the program execution continues immediately after the call to 'play(player)'.
If you intend to play different files at each iteration, try waiting till the current file finishes, you can use something like:
while player.isplaying
pause(0.001)
end
If you meant to play one signal and change the plots, move the play(player) outside of the for loop, and add some delay between each plotting point for example with your code:
player = audioplayer(audio, Fs);
play(player);
for i=1:10
scatter(x(i),y(i),'r.')
pause(0.1)
end
Example for one signal being played and plot being updated:
Build a chirp signal:
Fs = 16e3;
T = 10;
t = 0:1/Fs:T;
f0 = 100;
phi = 2*pi*t.^2*f0;
sig = 0.1*sin(phi);
% Start playing the sound:
player = audioplayer(sig,Fs);
play(player);
% Plotting stuff:
dPhi = gradient(phi)*Fs;
figure;
numPlots = 20;
N = numel(t);
for n = 1 : numPlots
pause(T/numPlots)
ind = 1:N/numPlots*n;
plot(t(ind), dPhi(ind))
end
In general when plotting 'real-time' it is better to use tic-toc to figure current time compared to the time the audio started playing.
Also to improve performance it is better to set the xdata & ydata of the plots instead of re-plotting it every time as this action is much faster (doesn't update all other properties of the axes).
You can look at an old script I once shared to do 'real-time' plotting:
https://www.mathworks.com/matlabcentral/fileexchange/14397-real-time-scope-display--simple-script-

Creating alarm sound in psychtoolbox matlab

I am trying to create an experiment on psychtoolbox and one part of it involves sounding an alarm when the participant fail to respond.
I tried using the beep provided but it does not sound like an alarm at all. Is there any way to achieve that without having to download a external sound?
I have no knowledge of sound or sound waves so please help!
The following code will load a .wav file, and play it through the Psychtoolbox audio system. This allows you to have a timestamp of sound onset, and allows greater control than using sound() or beep. You could alternatively generate a tone using MATLAB itself (it is easy to generate a sine wave of a particular frequency) and use that instead of the .wav data.
%% this block only needs to be performed once, at the start of the experiment
% initialize the Psychtoolbox audio system in low latency mode
InitializePsychSound(1);
% load in a waveform for the warning
[waveform,Fs] = audioread('alarm.wav');
numChannels = size(waveform, 2);
% open the first audio device in low-latency, stereo mode
% if you have more than one device attached, you will need to specify the
% appropriate deviceid
pahandle = PsychPortAudio('Open', 2, [], 1, Fs, numChannels);
%% during the experiment, when you want to play the alarm
PsychPortAudio('FillBuffer', pahandle, waveform' );
startTime = PsychPortAudio('Start', pahandle, 1);
%% at the conclusion of the experiment
PsychPortAudio('Close');
If you'd like to generate your own sound, take a look at the Psychtoolbox function 'MakeBeep', and substitute that in, instead of the waveform, for example, a 1000 Hz tone, lasting 250ms, at a 44.1k sampling rate:
% generate a beep
beepWaveform = MakeBeep(1000,.250,44100);
% make stereo
beepWaveform = repmat(beepWaveform, 2, 1);
% fill buffer, play
PsychPortAudio('FillBuffer', pahandle, beepWaveform );
startTime = PsychPortAudio('Start', pahandle, 1);
To me, beep can do what you want by playing it multiple times in the loop like this:
% Adjust the no. of loop iterations depending on how long you want to play the alarm
for k=1:100
beep; pause(1);
end
Other than that, you can use built-in sounds like this:
load('gong'); % This sound seems suitable to me for alarm. Try others from the list
for k=1:100
sound(y,Fs); pause(1);
end
Here's a list of built-in sounds that you may want to try:
chirp
gong
handel
laughter
splat
train

Use audioread or equivalent to play audio in a specific timestamp

I am using audioread to play audio, now I would like play the track in different timestamps. What I have so far is:
[testSound,Fs] = audioread('test.wav');
sound(testSound,Fs);
Is it possible to somehow specify that the audio-track shall start at for example second 5? To be more specific, my audio sample test.wav is 45 second long, instead of playing the sound from the beginning, I would like to define where it should start playing.
Any help is much appreciated!
You can extract out a portion of the signal so that you're starting at the 5 second mark, then play it. Simply put, you'd start sampling at 5 times the sampling rate as the starting index all the way to the end then play the sound:
[testSound,Fs] = audioread('test.wav'); % From your code
beginSecond = 5; % Define where you want to start playing
beginIndex = floor(beginSecond*Fs); % Find beginning index of where to sample
soundExtract = testSound(beginIndex:end, :); % Extract the signal
sound(soundExtract, Fs); % Play the sound
Alternatively, since you're using audioread, you can actually specify where to start sampling the sound. You'd use the same logic above and specify the beginning and end of where to sample the sound in terms of samples. However, you'd need to know what the sampling rate is first so you'd have to call audioread twice to get the sampling rate, then finally the signal itself:
beginSecond = 5; % Define where you want to start playing
[~, Fs] = audioread('test.wav'); % Get sampling rate
beginIndex = floor(beginSecond*Fs); % Find beginning index of where to sample
[soundExtract, ~] = audioread('test.wav', [beginIndex inf]); % Extract out the signal from the starting point to the end of the file
sound(soundExtract, Fs); % Play the sound

How to play an audio signal in matlab controlling the left and right channel of earphone on Matlab individually?

I am doing audio signal processing in matlab. As a part of my project, I am playing recording audio signal, processing it, and playing it in real-time. Now, the output that I am sending through the two channels, I want that to be processed differently, and want to plot the graphs as well.
Basically, I want the left ear to hear a differently processed signal than the right ear and plot it as well.
Even if it is not in real time i.e. any stored signal (.wav,etc) will be helpful.
Any help will be appreciated (don't have much time :().
If you're using a stored .wav file, you can use wavread to import, which will import the file as a two-column array. If you call this array A, you could manipulate the left channel using A(:, 1) and the right channel using A(:, 2).
If you're using audiorecorder to record the audio, you'll have to change the number of channels from 1 to 2 so that you record in stereo. The default is mono.
EDIT: To plot in realtime, you can work off of the following function I wrote. The function takes the amount of time you want to record as its input runtime. It creates a timer timerID and continuously acquires the audio data from the recorder object and plots it, using drawnow to refresh the figure. If you wanted to do any processing, you could do it in the loop right before the plot commands.
function audioPlot(runtime)
timerID = tic;
recObj = audiorecorder(44100, 24, 2);
record(recObj);
h(1) = subplot(2, 1, 1);
h(2) = subplot(2, 1, 2);
while (toc(timerID) < runtime)
if recObj.TotalSamples > 0
audioData = getaudiodata(recObj);
plot(h(1), audioData(:, 1))
plot(h(2), audioData(:, 2))
xlabel(h(1), 'Left Channel')
xlabel(h(2), 'Right Channel')
drawnow
end
end
stop(recObj);
end
Happy to answer any questions!

Continuous Video Recording in Matlab, Saving/Restarting On a Memory Cap, Ending on a Flag

I would like to record a continuous video in Matlab until some other flag changes, allowing matlab to continue performing other tasks during video acquisition (like deciding whether or not the flag should be set). Since these recordings could last upwards of 3 hours, perhaps closing the recording every hour, writing to a file video_1, then recording for another hour and dumping to video_2, etc for as long as the flag isn't set. However, from what I've seen using Matlab's Image Processing Toolbox, you have to specify some kind of number of frames to capture, or frames per trigger, etc. I'm not really too sure how to proceed.
Some simple code to record video I have is:
% create video obj
video = videoinput('winvideo',1);
% create writer obj
writerObj = VideoWriter('output.avi');
% set video properties
video.LoggingMode = 'disk';
video.DiskLogger = writerObj;
% start recording video
start(video);
% wait
wait(video, inf)
% save video
close(video.DiskLogger);
delete(video);
clear video;
However, the output video is only .3 seconds long. I've followed the following tutorial to get a 30 second recording down to a 3 second video available here but I can't figure out how to make it go on continuously.
Any help would be appreciated!
aviObject = avifile('myVideo.avi'); % Create a new AVI file
for iFrame = 1:100 % Capture 100 frames
% ...
% You would capture a single image I from your webcam here
% ...
F = im2frame(I); % Convert I to a movie frame
aviObject = addframe(aviObject,F); % Add the frame to the AVI file
end
aviObject = close(aviObject); % Close the AVI file
source: How do I record video from a webcam in MATLAB?