+ considerOrder: true
+ stimuli:
+ - C1: configs/resources/audio/mono_c1.wav
+ C2: configs/resources/audio/mono_c2.wav
+ - C3: configs/resources/audio/mono_c3.wav
+ C2: configs/resources/audio/mono_c2.wav
+
+ - type: finish
+ name: Thank you
+ content: Thank you for attending
+ showResults: true
+ writeResults: true
diff --git a/doc/experimenter.md b/doc/experimenter.md
index 5206b38b..373daf1c 100644
--- a/doc/experimenter.md
+++ b/doc/experimenter.md
@@ -105,6 +105,18 @@ A paired comparison page creates a forced or unforced paired comparison (AB/ABX/
* **reference** Filepath to the reference stimulus (WAV file).
* **stimuli** A map of stimuli representing three conditions. The key is the name of the condition. The value is the filepath to the stimulus (WAV file).
+#### `preference_test` page
+
+A preference test page creates a forced paired comparison between two stimuli. The main difference to `paired_comparison` is that there is no reference or ground truth.
+
+* **type** must be preference_test.
+* **id** Identifier of the page.
+* **name** Name of the page (is shown as title)
+* **content** Content (HTML) of the page. The content is shown on the upper part of the page.
+* **considerOrder** If set to true, a complete set of pairs is generated from the list of stimuli: A set of `{A, B}` would result in two pairs `(A, B)` and `(B, A)`. Otherwise only the pair `(A, B)` would be generated.
+* **mustPlayback** If set to `ended`, the participant must fully play back all stimuli to the end. If set to `processUpdate`, the participant must start playing back all stimuli before responding becomes possible.
+* **stimuli** Either a map of stimuli or an array of maps of stimuli. If it is a map, pairs will be generated from the list and then shuffled. If it is an array of maps, pairs will be generated for each map individually and then all the pairs from all maps are shuffled.
+
#### `likert_multi_stimulus` page
A likert multi stimulus page creates a multi-stimulus likert rating.
@@ -142,6 +154,8 @@ The finish page must be the last page of the experiment.
* **content** Content (HTML) of the page. The content is shown on the upper part of the page.
* **showResults** The results are shown to the participant.
* **writeResults** The results are sent to the remote service (which writes the results into a file).
+* **generateSubjectId** If set to true, a random subject ID is generated and appended to the results.
+* **confirmationCode:** If set, this code is displayed after the results were sent. This can be used to confirm that subjects have completed the experiment and facilitates
## Results
diff --git a/doc/participant.md b/doc/participant.md
index 8b5eb9cf..24316989 100644
--- a/doc/participant.md
+++ b/doc/participant.md
@@ -41,3 +41,8 @@
* SPACE Play/pause the current selection
* a Set begin of loop to current position
* b Set end of loop to current position
+
+### Preference test
+
+* 1 Play item A
+* 2 Play item B
\ No newline at end of file
diff --git a/index.html b/index.html
index dab46eb0..27a000e3 100644
--- a/index.html
+++ b/index.html
@@ -81,6 +81,7 @@
+
@@ -113,6 +114,8 @@
+
+
diff --git a/lib/webmushra/datamodel/PreferenceTestChoice.js b/lib/webmushra/datamodel/PreferenceTestChoice.js
new file mode 100644
index 00000000..8b82b928
--- /dev/null
+++ b/lib/webmushra/datamodel/PreferenceTestChoice.js
@@ -0,0 +1,14 @@
+/*************************************************************************
+ (C) Copyright AudioLabs 2017
+
+This source code is protected by copyright law and international treaties. This source code is made available to You subject to the terms and conditions of the Software License for the webMUSHRA.js Software. Said terms and conditions have been made available to You prior to Your download of this source code. By downloading this source code You agree to be bound by the above mentionend terms and conditions, which can also be found here: https://www.audiolabs-erlangen.de/resources/webMUSHRA. Any unauthorised use of this source code may result in severe civil and criminal penalties, and will be prosecuted to the maximum extent possible under law.
+
+**************************************************************************/
+
+function PreferenceTestChoice() {
+ this.optionA = null;
+ this.optionB = null;
+ this.answer = null;
+ this.comment = null;
+ this.time = null;
+}
diff --git a/lib/webmushra/nls/nls.js b/lib/webmushra/nls/nls.js
index 4eee50a4..64de3208 100644
--- a/lib/webmushra/nls/nls.js
+++ b/lib/webmushra/nls/nls.js
@@ -100,6 +100,10 @@ nls['es']['slightly'] = "Ligeramente molesto";
nls['es']['annoying'] = "Molesto";
nls['es']['very'] = "Muy molesto";
+// captions Preference Test
+nls['en']['pref'] = "Which item do you prefer?";
+nls['de']['pref'] = "Welche Version bevorzugen Sie?";
+nls['fr']['pref'] = "Quel item préférez-vous?";
// captions Paired Comparison AB/ABN
nls['en']['quest'] = "Which item is the reference?";
@@ -116,3 +120,9 @@ nls['en']['attending'] = "Thank you for your participation!";
nls['de']['attending'] = "Vielen Dank für die Teilnahme!";
nls['fr']['attending'] = "Merci pour votre participation!";
nls['es']['attending'] = "Gracias por participar!";
+nls['en']['response'] = "Response";
+nls['de']['response'] = "Antwort";
+nls['fr']['response'] = "Réponse";
+nls['en']['code'] = "Your confirmation code:";
+nls['de']['code'] = "Ihr Bestätigungscode:";
+nls['fr']['code'] = "Votre code de confirmation:";
diff --git a/lib/webmushra/pages/FinishPage.js b/lib/webmushra/pages/FinishPage.js
index fd1e3ce1..d41412c3 100644
--- a/lib/webmushra/pages/FinishPage.js
+++ b/lib/webmushra/pages/FinishPage.js
@@ -32,8 +32,21 @@ FinishPage.prototype.getName = function () {
return this.pageConfig.name;
};
+function getId(){
+ /* Returns an ID based on random number and the current timestampe */
+ return Math.floor(Math.random() * Date.now());
+}
+
+
FinishPage.prototype.storeParticipantData = function() {
var i;
+ if (this.pageConfig.generateSubjectId){
+ this.session.participant.name[this.session.participant.name.length] = "subjectId";
+ this.session.participant.response[this.session.participant.response.length] = getId();
+ } else {
+ this.session.participant.name[this.session.participant.name.length] = "subjectId";
+ this.session.participant.response[this.session.participant.response.length] = null;
+ }
for (i = 0; i < this.pageConfig.questionnaire.length; ++i) {
var element = this.pageConfig.questionnaire[i];
@@ -64,20 +77,33 @@ FinishPage.prototype.render = function (_parent) {
for (i = 0; i < this.pageConfig.questionnaire.length; ++i) {
var element = this.pageConfig.questionnaire[i];
if (element.type === "text") {
- table.append($("
"+ element.label +"
"));
+ table.append($("
"+ element.label +"
"));
} else if (element.type === "number") {
- table.append($("
"+ element.label +"
"));
+ table.append($("
"+ element.label +"
"));
} else if(element.type === "likert") {
this.likert = new LikertScale(element.response, element.name + "_");
var td = $("
");
@@ -287,6 +353,10 @@ FinishPage.prototype.load = function() {
if ($("#" + element.name).val() || element.optional == true) {
++counter;
}
+ } else if(element.type === "dropdown") {
+ if ($("#" + element.name).val() || element.optional == true) {
+ ++counter;
+ }
}
if (counter == this.pageConfig.questionnaire.length) {
$('#send_results').removeAttr('disabled');
diff --git a/lib/webmushra/pages/PreferenceTestPage.js b/lib/webmushra/pages/PreferenceTestPage.js
new file mode 100644
index 00000000..6af7599b
--- /dev/null
+++ b/lib/webmushra/pages/PreferenceTestPage.js
@@ -0,0 +1,184 @@
+/*************************************************************************
+ (C) Copyright AudioLabs 2017
+
+This source code is protected by copyright law and international treaties. This source code is made available to You subject to the terms and conditions of the Software License for the webMUSHRA.js Software. Said terms and conditions have been made available to You prior to Your download of this source code. By downloading this source code You agree to be bound by the above mentionend terms and conditions, which can also be found here: https://www.audiolabs-erlangen.de/resources/webMUSHRA. Any unauthorised use of this source code may result in severe civil and criminal penalties, and will be prosecuted to the maximum extent possible under law.
+
+**************************************************************************/
+
+function PreferenceTestPage(_pageManager, _pageTemplateRenderer, _audioContext, _bufferSize, _audioFileLoader, _stimuli, _session, _pageConfig, _errorHandler, _language) {
+ this.pageManager = _pageManager;
+ this.pageTemplateRenderer = _pageTemplateRenderer;
+ this.audioContext = _audioContext;
+ this.bufferSize = _bufferSize;
+ this.audioFileLoader = _audioFileLoader;
+ this.session = _session;
+ this.pageConfig = _pageConfig;
+ this.errorHandler = _errorHandler;
+ this.language = _language;
+ this.div = null;
+ this.fpc = null;
+
+ this.currentItem = null;
+
+ this.stimuli = _stimuli;
+ this.played = [];
+ for (var i = 0; i < this.stimuli.length; ++i) {
+ this.audioFileLoader.addFile(this.stimuli[i].getFilepath(), (function (_buffer, _stimulus) { _stimulus.setAudioBuffer(_buffer); }), this.stimuli[i]);
+ this.played.push(false);
+ }
+
+ this.filePlayer = null;
+ this.choice = null;
+ this.time = 0;
+ this.startTimeOnPage = null;
+}
+
+
+
+PreferenceTestPage.prototype.getName = function () {
+ return this.pageConfig.name;
+};
+
+PreferenceTestPage.prototype.init = function () {
+ this.filePlayer = new FilePlayer(this.audioContext, this.bufferSize, this.stimuli, this.errorHandler, this.language, this.pageManager.getLocalizer());
+
+ if (typeof this.pageConfig.mustPlayback !== "undefined") {
+ this.filePlayer.genericAudioControl.addEventListener((function (_event) {
+ if (_event.name == this.pageConfig.mustPlayback) {
+ this.played[_event.index] = true;
+ if (this.played.every((element) => element === true)){
+ $('#radio-choice-a').checkboxradio('enable');
+ $('#radio-choice-b').checkboxradio('enable');
+ }
+ }
+ }).bind(this));
+ }
+};
+
+PreferenceTestPage.prototype.render = function (_parent) {
+ var div = $("");
+ _parent.append(div);
+
+ var content;
+ if(this.pageConfig.content === null){
+ content ="";
+ } else {
+ content = this.pageConfig.content;
+ }
+
+ var p = $("
" + content + "
");
+ div.append(p);
+
+ var outerTable = $("
");
+ div.append(outerTable);
+ var trOuter = $("
");
+ outerTable.append(trOuter);
+ var tdLabels = $("
");
+ trOuter.append(tdLabels);
+ var innerTable = $("
");
+ tdLabels.append(innerTable);
+ var trA = $("
");
+ innerTable.append(trA);
+ trA.append($("
A:
"));
+ var trB = $("
");
+ innerTable.append(trB);
+ trB.append($("
B:
"));
+ trA.height(tdLabels.height());
+ trB.height(tdLabels.height());
+ var tdPlayer = $("