PennController for IBEX › Forums › Support › recording audio production
- This topic has 5 replies, 2 voices, and was last updated 2 years, 3 months ago by Jeremy.
-
AuthorPosts
-
May 24, 2022 at 8:45 am #8205HPIParticipant
Hi Jeremy,
I am implementing a new experiment and recording as data the production of the speakers.I am setting up the skeleton of the experiment and I got to the point in which I present some written stimuli and a button appears to record, they can hear what they recorded, try again, submit, and the experiment closes.
The data file appears on my server.
First questions:
1) can I automatically match from some information stored by Ibex the results of “speaker 1” in the result file, and the audio file of “speaker 1” in the server?
Could I manage to save the audio file assigning a label that corresponds either to an alphanumeric ID assigned at the opening of the experiment, or I don’t know, something like a counter increasing every time someone finishes the experiment?2) Could I act on the type of audio file I save? It would be optimal to store .wav files.
Thank you!
HPI
May 24, 2022 at 11:21 am #8206JeremyKeymasterHi,
1) As long as (all)
UploadRecordings
complete(s) beforeSendResults
(eg. because you insertedUploadRecordings("upload")
beforeSendResults()
— no second argument inUploadRecordings
, because otherwise it would be non-blocking and would therefore likely complete *after*SendResults
) you will have a line for each uploaded zip file in your results file, which you can use to pair zip files with submissions. The recorded audio files are named after the MediaRecorder element that produced them. More details in this post2) Unfortunately that is not currently supported, since all major browsers support only webm/opus when recording via the MediaRecorder API, which PennController uses in its own MediaRecorder element type. The webm files can easily be converted to wav files though, eg. using ffmpeg:
ffmpeg -i "file.webm" -vn "file.wav"
Jeremy
May 27, 2022 at 9:59 am #8213HPIParticipantHi,
so I was using as reference a previous experiment, but I don’t get the difference between two strategies of using the sequence to save audio files.
I have now something like this and it runs.
Sequence(“initiate”,”welcome”, randomize(“experiment”), “Sync”, “send”, “completion_screen”);
InitiateRecorder(“https://ibexfarm.ung.si/uporabniki/ckzj1/Irony/mediarec.php”, “Please adjust your browser settings to allow microphone access, then click the link below.”)
.label(“initiate”)
//UploadRecordings(“sendAsync”, “noblock”)….
UploadRecordings(“Sync”);
SendResults(“send”);What I see in the previous experiment I have is that they write with the strategy to send the audio file within the trial:
so they had written in the sequence SepWith(“Async”, randomize(experiment)), “Sync”.
The idea is that Async sends within the trial, Sync sends the last trial of the experiment.
And they have the command to uploadrecordings written as in //. with the second part “noblock” that is not mentioned anywhere else, and I don’t understand exactly what it does.
I am not getting how to have this correctly running.
Right now it works if I write it the way you see above, so the audio files are sent at the end.
But in this way, I am not sure I understand how to recognize the participant and match him with his data.
—–
Can you explain to me how to use correctly the Uploadrecording command and SepWith function?
thank youHPI
May 27, 2022 at 10:25 am #8214JeremyKeymasterHi,
How
sepWith
works is explained in the Ibex manual. IfUploadRecordings("sendAsync", "noblock")
is commented out with//
, and assuming there is no other trial labeledsendAsync
defined in the code, then any reference tosendAsync
in the sequence will have no effect, since there will be no trial with a matching label to include. Note, however, that you mentionsepWith("Async", randomize(experiment))
(I assume the code hassepWith
, which is the Ibex function to use in a sequence, rather thanSepWith
): the reference here isAsync
, notsendAsync
, so you need to look for a trial labeledAsync
in the code, which does not correspond toUploadRecordings("sendAsync", "noblock")
, so it’s not relevant whether that line is commented out or notRe. how to match the files, here’s a made-up sample of selected lines from a results file:
1612992170,SUBMISSION1,PennController,28,0,experiment,NULL,MediaRecorder,filler1,Filename,filler1.webm,1612991498311,NULL 1612992170,SUBMISSION1,PennController,30,0,experiment,NULL,PennController,UploadRecordings,Filename,12345678-abcd-efgh-ijkl-901234567890.zip,1612991504982,async 1612992170,SUBMISSION1,PennController,29,0,experiment,NULL,MediaRecorder,filler2,Filename,filler2.webm,1612991501417,NULL 1612992170,SUBMISSION1,PennController,31,0,experiment,NULL,PennController,UploadRecordings,Filename,90123456-mnop-qrst-uvwx-789012345678.zip,1612991505176,sync 1612993451,SUBMISSION2,PennController,28,0,experiment,NULL,MediaRecorder,filler1,Filename,filler1.webm,1612991506231,NULL 1612993451,SUBMISSION2,PennController,30,0,experiment,NULL,PennController,UploadRecordings,Filename,abcdefgh-1234-5678-9012-ijklmnopqrst.zip,1612991507023,async 1612993451,SUBMISSION2,PennController,29,0,experiment,NULL,MediaRecorder,filler2,Filename,filler2.webm,1612991507189,NULL 1612993451,SUBMISSION2,PennController,31,0,experiment,NULL,PennController,UploadRecordings,Filename,uvwxyzab-3456-7890-1234-cdefghijklmn.zip,1612991508309,sync
Here, you can see that two zip files correspond to SUBMISSION1:
12345678-abcd-efgh-ijkl-901234567890.zip
and90123456-mnop-qrst-uvwx-789012345678.zip
. The recordingsfiller1.webm
andfiller2.webm
reported in the lines above for SUBMISSION1 will be included in those zip files (they might both be in just one of the two, or one in each, depending on how upload went)Then two zip files correspond to SUBMISSION2:
abcdefgh-1234-5678-9012-ijklmnopqrst.zip
anduvwxyzab-3456-7890-1234-cdefghijklmn.zip
. Likewise, you will find the recordingsfiller1.webm
andfiller2.webm
from SUBMISSION1 in those zip filesJeremy
June 14, 2022 at 4:18 pm #8234HPIParticipantHi, One question on to best use .callback
I have implemented this:
newButton("trigger", "RECORD") .log() .css("font-size", 20) .css("font-family", "Verdana", "sans-serif") .callback( //clear() //, getButton("trigger") .css("font-size", 20) .css("font-family", "Verdana", "sans-serif") .remove() // Print the button now: clicking it will end the trial (=validate the wait command) , getButton("CONTINUE") .css("font-size", 20) .css("font-family", "Verdana", "sans-serif") .remove() // Print the button now: clicking it will end the trial (=validate the wait command) , newMediaRecorder(variable_practice.Output_audio+"_"+id_participant, "audio") .log() .center() .record() , newText("spazio", " ") .center() .print() , newButton("STOP") .css("font-size", 20) .css("font-family", "Verdana", "sans-serif") .print() .wait() .remove() , getMediaRecorder(variable_practice.Output_audio+"_"+id_participant) .stop() .log() , newText("empty"," ") .center() .print() , newButton("PLAY") .css("font-size", 20) .css("font-family", "Verdana", "sans-serif") .log() .print() .wait() .remove() , getMediaRecorder(variable_practice.Output_audio+"_"+id_participant) .play() .wait("playback") .log() , newText("spazio2", " ") .center() .print() , newText("Exit1","If you wish to record again, press RECORD") .css("font-size", 20) .css("font-family", "Verdana", "sans-serif") , newText("Exit2","If the audio seems clear and of good quality, press CONTINUE") .css("font-size", 20) .css("font-family", "Verdana", "sans-serif") , getButton("trigger") .print() // Clicking this will execute the callback sequence again //.wait() //.remove() , getButton("CONTINUE") .css("font-size", 20) .css("font-family", "Verdana", "sans-serif") .print() // Print the button now: clicking it will end the trial (=validate the wait command) ) .print() .wait() .log() .remove() , // This button will only be printed at the end of a test, // So the script won't have a chance to move past the wait command until then newButton("CONTINUE") .css("font-size", 20) .css("font-family", "Verdana", "sans-serif") .css("margin-top","1em") .wait() .log() ,
I would like actually to save two separate audio files if the participant click CALLBACKS.
Can i index the mediarecording like in a for cycle?Like something everytime the participants hits the trigger, a variable + 1 (i + 1), the name of the audio file + “id_participant_” + i
So that if the participant records 10 times the same thing I save 10 version of the file, and I don’t overwrite the same variable?
Thanks!
HPI
June 14, 2022 at 5:21 pm #8235JeremyKeymasterHi,
You cannot do that, because there is one recording per MediaRecorder element, and elements are created before the sequence of trials is run, they cannot be created on the fly
My suggestion is you give unique labels to your recording trials and use
jump
to run them again if needed, eg:Template( row => newTrial( "recorder-"+row.item , newMediaRecorder("mediarecorder-"+row.item, "audio").print().wait().log() , // Jump to this trial (= recorder-ITEM#) newButton("Again").print().callback( jump("recorder-"+row.item) , getButton("Next").click() ) , newButton("Next").print().wait() ) .log("item#", row.item) )
Jeremy
-
AuthorPosts
- You must be logged in to reply to this topic.