MATLAB Answers

0

PushButton repeat previously generated audio sample (with other pushbutton)

Asked by Ana Campos on 23 Jun 2019
Latest activity Commented on by Geoff Hayes
on 1 Jul 2019
I will need to use the script below for a listening test. is still not as I need it and the deadline is approaching so I decided to come here and find out if someone can help. (Thanks in advance!)
Information about the script: I am using GUI; among other tools that are not relevant for this specific problem I have included 2 pushbuttons which are causing me some problems. One pushbutton is supposed to play one audio sample (randomly) out of 3 existing audio samples. The other pushbutton is supposed to play the sample again if the listener needs it.
My problems are due to this:
1- Each sample needs to be played randomly but the user needs to be able to listen to it again if he wants. I would like to use the "REPEAT" button for that.
2- I am using a different GUI for each of the three audio samples that will be evaluated by the listener (probably the worst idea ever as the GUIs are exactly the same, but it's how I managed to make it work so far). Each GUI has a "NEXT" button to close the old GUI and open the next one.
When the user changes from the first GUI to the second, the previously evaluated sample, must not be included in the random operation as they can't evaluate the same audio sample twice during the test.
------> I am not being able to solve these two issues and the deadline is almost here. PLEASE I need some help.
Thank you very much!
This is the code I am using for the pushbutton PLAY at the moment:
a = audioread('guitar.wav');
b = audioread('cinematic.wav');
c = audioread('epicorchestra.wav');
numsounds = 3;
sounds = { a; b; c;}
orders=randperm(numsounds,3); % random order
%pick one of the positions in "orders"
location = randi(length(orders));
% extract wave data from line identified by "locations" in column one.
wavData = sounds{location, 1};
% sampling frequency (same for all audio files)
sf = 48000
% play audio sample with that wavdata and corresponding sampling
% frequency
soundsc(wavData, sf);

  2 Comments

Ana - you haven't indicated how you have created your GUIs: with GUIDE, App Designer, or programmatically. This is important because there will be slightly different ways to do what you want...
Sorry! I am using GUIDE:) cheers

Sign in to comment.

1 Answer

Answer by Geoff Hayes
on 24 Jun 2019
 Accepted Answer

Ana - since using GUIDE, you can Store or retrieve UI data with the handles structure (this structure is passed into each callback). First though, you may want to use this structure to manage the names of the wav file. In the GUI's OpeningFcn you could do something like
handles.myWavFiles = {'guitar.wav', 'cinematic.wav', 'epicorchestra.wav'};
guidata(hObject, handles);
The call to guidata is important as it will ensure that the handles object is updated with the array of wav files so that all subsequent callbacks have access to this updated object.
In the pushbutton callback to play one of the random assets, you could do
if ~isempty(handles.myWavFiles)
handles.wavFileIndex = randi(length(handles.myWavFiles),1,1); % a random file to play
guidata(hObject, handles); % save the index of the file that will be played
wavFileToPlay = handles.myWavFiles{handles.wavFileIndex};
reader = audioread(wavFileToPlay);
% etc.
end
Note how we save the index of the file that we are going to play to the handles structure. This way, we have access to file in the repeat button callback.
An alternative to using sound is to use the audioplayer object which offers a little more control over for the audio playback.
In the repeat button callback, you would do
if isfield(handles, 'wavFileIndex')
wavFileToPlay = handles.myWavFiles{handles.wavFileIndex};
reader = audioread(wavFileToPlay);
% play the sound
end
The above code should allow you to randomly choose a wav file (to play) and then repeat the last played file.
As for "moving to the next GUI", I would recommend a different approach. When the user presses the NEXT button, just update your list of played wav files by removing the one that has already been played. That way you don't have to worry about passing data from one GUI to the next. The NEXT pushbutton callback might have code like
if isfield(handles, 'wavFileIndex')
handles.myWavFiles(handles.wavFileIndex) = []; % we are removing this file from the list
guidata(hObject, handles);
end
Then, when the user presses the play button, your code will randomly select a file from that shorter list. (See how we have the if ~isempty(handles.myWavFiles) check in that callback to ensure that we don't try to play something from an empty list.)

  23 Comments

Ana - in your slider callbacks, you need to save the slider value to an array. Something like
function slider7attribute_Callback(hObject, eventdata, handles)
% hObject handle to slider7bassenergy (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Hints: get(hObject,'Value') returns position of slider
% get(hObject,'Min') and get(hObject,'Max') to determine range of slider
handles.subjectSliderStep3(handles.atIteration) = get(hObject, 'Value');
guidata(hObject, handles);
where atIteration indicates which wav file you are considering (1, 2, or 3). You may want to set this in the play callback.
As for the CloseRequest function, I don't know why figure1 is being used. You would need to attach your fig file so that I can see why (it could be that your original GUI was named figure1 then you changed it at some point but there is still a reference to the old name).
For the other attributes, identication and rating, I don't know where they are coming from (the wav file?) so can't comment on how they can be used.
Ok I've tried again and i just can't make it work like that.
Here are my files. If you could explain me how to do it I would appreciate it because I can only make it work if my .txt keeps storing every movement of the sliders and that is not useful at all by the time I'll need to analise the data.
I'll explain what I need again just in case I wasn't very clear last time.
I have X audio samples (in this example only 3) to be rated
Each audio sample will be evaluated using 3 sliders /the sliders will allow the user to rate the samples according to 3 different characteristics
I need to store all of their ratings for each audio sample (3 ratings per sample always) on a .txt or xlsx file (at the moment im using a .txt)
I need to know the sample they were listening to, when they were giving the rating.
Do you know how to do it? Because I'm completely stuck here now.
Cheers
The figure1 may be coming from the tag value that you have assinged to your GUI. I guess if the CloseRequestFcn is firing then you don't need to worry about that.
For the updating of the array of ratings, I would consider initializing the atIteration to zero (in the OpeningFcn)
handles.atIteration = 0;
and then increment it by one when the play button is pressed
function play_Callback(hObject, eventdata, handles)
% hObject handle to play (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
if ~isempty(handles.myWavFiles)
handles.atIteration = handles.atIteration + 1;
handles.wavFileIndex = randi(length(handles.myWavFiles),1,1); % one random file to play
guidata(hObject, handles); % save the index of the file that will be played
wavFileToPlay = handles.myWavFiles{handles.wavFileIndex};
x = audioread(wavFileToPlay);
% NOW PLAY THE AUDIO SAMPLE
sound (x,48000)
end
that way when the ratings slider callbacks fire, you will update the appropriate element in your arrays.
As for knowing which sample the user was listening to when they applied the rating, there are probably several ways to do this. One way might be to record the time at which the user pressed the play button and then the time that the slider callback fired. The difference between the two - in seconds - will give you an idea of how long the audio asset has been playing when the slider callback fired. Knowing this duration and knowing the sample rate of the audio asset, you can estimate the sample
(sliderCallbackFireTimeSec - audioAssetStartTimeSec) * Fs
You can use tic and toc to get the delta
function play_Callback(hObject, eventdata, handles)
% hObject handle to play (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
if ~isempty(handles.myWavFiles)
handles.atIteration = handles.atIteration + 1;
handles.wavFileIndex = randi(length(handles.myWavFiles),1,1); % one random file to play
guidata(hObject, handles); % save the index of the file that will be played
wavFileToPlay = handles.myWavFiles{handles.wavFileIndex};
x = audioread(wavFileToPlay);
% NOW PLAY THE AUDIO SAMPLE
sound (x,48000)
tic % <------- starts the stopwatch timer
end
And then in your slider callbacks, you would call toc
function slider5_Callback(hObject, eventdata, handles)
% hObject handle to slider5 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Hints: get(hObject,'Value') returns position of slider
elapsedTimeSec = toc;
handles.subjectSliderSample1(handles.atIteration) = elapsedTimeSec * 48000;
handles.subjectSliderStep1(handles.atIteration) = get(hObject, 'Value');
guidata(hObject, handles);
Note how there is a new array subjectSliderSample1 for the sample that was being listened to. I'm assuming a sample rate of 48000 since you have hard-coded that elsehwere in your code. This should be the Fs (sampling rate) that is returned when reading the audio asset with audioread.

Sign in to comment.