+1 vote
in SoSci Survey (dt.) by s098891 (355 points)

In meinem Fragebogen startet die Audioaufnahme (als Antwort auf ein Item) automatisch. Ich wollte verhindern, dass TeilnehmerInnen auf STOP klicken, bevor sie den Satz beenden, und deshalb gesagt, sie brauchen nichts drücken. Jetzt fehlen aber viele die Aufnahmen. Werden die Audios nur gespeichert, wenn man auf STOP drückt? Wenn ja, kann man mit einem Code das automatische STOP-drücken/Speichern der Datei nach abgelaufener Zeit bewirken?

3 Answers

0 votes
by SoSci Survey (320k points)

Im JavaScript-Code der Frage ist die Funktion stopRecording(), welche anschließend onStop() aufruft für die Übermittlung der Inhalte verantwortlich. Sie können diese auch nach einer definierten Zeit einfach aufrufen. Etwa in der "Zeit ist abgelaufen"-Funktion eines Timers.

by s098891 (355 points)
Zumindest wird der stop-Knopf jetzt auch grau, kurz bevor zur nächsten Seite übergegangen wird, jedoch scheint keine Audio-Datei hochgeladen zu werden...
by SoSci Survey (320k points)
Dann blockiert der ausstehende Upload das "Weiter" offenbar nicht korrekt. Ich werde nochmal detailliert in den Quellcode sehe müssen, um das zu klären. Das wird allerdings 2-3 Tage in Anspruch nehmen.
by s098891 (355 points)
Ja, bitte!!!
by s098891 (355 points)
Gibt es schon eine Lösung für das Problem?:)
by SoSci Survey (320k points)
Jetzt schon ... ich habe sie der Leserlichkeit zuliebe oben als neue Antwort gepostet.
0 votes
by SoSci Survey (320k points)

Also ... im ersten Schritt ist es notwendig, dass das reguläre "Weiter" abgefangen wird. Dann muss die Übertragung angestoßen und anschließend das "Weiter" nochmal betätigt werden. Ergänzen Sie dafür unten in der Frage zum Audio-Recorder folgenden JavaScript-Code:

var audioSent = false;
function onSubmit() {
  // Check if already sent
  if (!audioSent) {
    stopRecording();
    return false;
  } else {
    return true;
  }
}
function onSendDone() {
  audioSent = true;
  SoSciTools.submitPage();
}
SoSciTools.questionnaire.attachCheck(onSubmit);

Sie sehen, dass hier eine Funtion onSendDone() verwendet wird. Diese müssen wir in der Funktion onStop() weiter oben im JavaScript-Code noch aufrufen. Ergänzen Sie die existierende Funktion bitte wie folgt:

function onStop(audioURL) {
    var recordedBlob = recordRTC.getBlob();
    // Transfer the data
    %q.id%.sendBLOB(recordedBlob, onSendDone);
}
by s098891 (355 points)
Leider geht es nach Ablauf der Zeit dann nicht weiter zur nächsten Seite, sondern es wird ins Minus gezählt: https://www.soscisurvey.de/speaking123/?act=wQ2anykskG8azkGKfieb7PMu

Der Code sieht so aus:

<!-- Buttons -->
<div style="margin: 2em 0 0.5em">
  <button id="btnStart" type="button" tabindex="50">START</button>
  <button id="btnStop" type="button" tabindex="50">STOP</button>
</div>
<div style="margin: 0.5em 0" id="audioReload">Bitte laden Sie die Seite neu (<a href="javascript:SoSciTools.reloadPage()">Neu Laden</a>) und gestatten Sie der Seite Zugriff auf Ihr Audio-Gerät.</div>

<!-- Audio recorder from https://github.com/muaz-khan/RecordRTC -->
<script src="../plugins/RecordRTC/RecordRTC.min.js"></script>
<script src="../plugins/RecordRTC/gumadapter.js"></script>


<script type="text/javascript">
<!--
"use strict";

if ((webrtcDetectedVersion === null) || (webrtcDetectedVersion < webrtcMinimumVersion)) {
  alert("Ihr Browser unterstützt diese Aufnahme leider nicht.");
  throw new Error("no audio support");
}

var buttonStart = document.getElementById("btnStart");
var buttonStop = document.getElementById("btnStop");
var recordRTC;

function startRecording(button) {
  if (recordRTC) {
    recordRTC.startRecording();
    buttonStart.disabled = true;
    buttonStop.disabled = false;
  }
}

function stopRecording(button) {
  if (recordRTC) {
    recordRTC.stopRecording(onStop);
    // You may want to change this to allow a new try (replacing the old one)
    buttonStart.disabled = false;
    buttonStop.disabled = true;
  }
}

function onStop(audioURL) {
    var recordedBlob = recordRTC.getBlob();
    // Transfer the data
    %q.id%.sendBLOB(recordedBlob, onSendDone);
}

function onStream(stream) {
    var options = {
        mimeType: "audio/ogg",
        audioBitsPerSecond: 128000
    };
    recordRTC = new RecordRTC(stream, options);
    // Enable the start button
    buttonStart.disabled = false;
    // Remove warning
    document.getElementById("audioReload").style.display = "none";
    // Start recording
    startRecording();
}


function init() {
    buttonStart.disabled = true;
    buttonStop.disabled = true;

    var mediaConstraints = {
        video: false,
        audio: true
    };

    function onError(error) {
      alert("Ihr Computer oder Ihr Browser unterstützt derzeit keine Tonaufnahme." + "\n\n" + error);
    }
    navigator.mediaDevices.getUserMedia(mediaConstraints).then(onStream).catch(onError);
};

SoSciTools.attachEvent(window, "load", init);
SoSciTools.attachEvent(buttonStart, "click", startRecording);
SoSciTools.attachEvent(buttonStop, "click", stopRecording);

var audioSent = false;
function onSubmit() {
  // Check if already sent
  if (!audioSent) {
    stopRecording();
    return false;
  } else {
    return true;
  }
}
function onSendDone() {
  audioSent = true;
  SoSciTools.submitPage();
}
SoSciTools.questionnaire.attachCheck(onSubmit);
// -->
</script>
by SoSci Survey (320k points)
In dem Code ist jetzt aber kein Timer ... ich hatte den Code jetzt erstmal auf den "Weiter"-Knopf im Browser optimiert. Eigentlich sollte es auch mit Timer funktionieren, aber womöglich kommt hier noch eine Kleinigkeit dazu.

(a) könnten Sie auch Ihren Timer-Code posten und (b) was sagt denn die JavaScript-Fehlerkonsole?
by s098891 (355 points)
Habe da noch diese zwei Codes auf der Seite:

<script type="text/javascript">
<!--

function weiter() {
  stopRecording();
  SoSciTools.submitPage();
}
// Countdown anzeigen
var date0 = new Date();
var timeout = date0.getTime() + %remain% * 1000;
function updateCountdown() {
  // Zeit berechnen
  var date = new Date();
  var time = Math.ceil((timeout - date.getTime() - 50) / 1000);  // Verbleibende Zeit in Sekunden
  // Zeit anzeigen
  var out = document.getElementById("remain");
  if (!out) {
    return;
  }
  while (out.lastChild) {
    out.removeChild(out.lastChild);
  }
  var minutes = Math.floor(time / 60);
  var seconds = String(time - 60 * minutes);
  if (seconds.length < 2) seconds = "0" + seconds;
  var display = String(minutes) + ":" + seconds;
  var displayNode = document.createTextNode(display);
  out.appendChild(displayNode);
}
// Initialisierung der Weiterleitung
SoSciTools.attachEvent(window, "load", function(evt) {
  // Weiter-Knopf ausblenden (optional)
  SoSciTools.submitButtonsHide();
  // Zusätzlicher Timer für die Aktualisierung des Countdowns
  window.setInterval(updateCountdown, 1000);
  updateCountdown();
  // Timer für automatische Weiterleitung starten
  window.setTimeout(weiter, %remain% * 1000);
});
// -->
</script>

if (!isset($time0)) {
  $time0 = time();
  registerVariable('time0');  // Die Variable $time0 auch nach Ende des PHP-Codes aufbewahren
}
// Prüfung, ob die Zeit schon abgelaufen ist
// (z.B. weil der Teilnehmer die Seite neu geladen hat)
$timer = 8;  // Der Teilnehmer hat 1 Minute (60 Sekunden) Zeit zur Bearbeitung
if (time() >= $time0 + $timer) {
  goToPage('next');
}
// Die verbleibende Zeit muss auch dem JavaScript-Code bekannt gemacht werden
$remain = $time0 + $timer - time();
replace('%remain%', $remain);

spec:   {"video":false,"audio":true}
gumadapter.js:31 chrome: {"video":false,"audio":true}
RecordRTC.min.js:15 started recording audio stream.
RecordRTC.min.js:15 Using recorderType: MediaStreamRecorder
RecordRTC.min.js:15 Passing following config over MediaRecorder API. {mimeType: "audio/ogg", audioBitsPerSecond: 128000, type: "audio", checkForInactiveTracks: false, initCallback: ƒ}
RecordRTC.min.js:15 MediaRecorder API seems unable to record mimeType: audio/ogg
MediaStreamRecorder.record @ RecordRTC.min.js:15
initRecorder @ RecordRTC.min.js:15
startRecording @ RecordRTC.min.js:15
startRecording @ index.php?i=FFR7SZORYXNE&rnd=NGAJ:100
onStream @ index.php?i=FFR7SZORYXNE&rnd=NGAJ:132
Promise.then (async)
init @ index.php?i=FFR7SZORYXNE&rnd=NGAJ:148
load (async)
SoSciTools.attachEvent @ SoSciTools.min.js?i=1285a:60
(anonymous) @ index.php?i=FFR7SZORYXNE&rnd=NGAJ:151
RecordRTC.min.js:15 Recorder state changed: recording
RecordRTC.min.js:15 Initialized recorderType: MediaStreamRecorder for output-type: audio
RecordRTC.min.js:15 Stopped recording audio stream.
RecordRTC.min.js:15 Recorder state changed: stopped
RecordRTC.min.js:15 Recording state should be: "recording", however current state is:  stopped
stopRecording @ RecordRTC.min.js:15
stopRecording @ index.php?i=FFR7SZORYXNE&rnd=NGAJ:108
onSubmit @ index.php?i=FFR7SZORYXNE&rnd=NGAJ:159
f @ SoSciTools.min.js?i=1285a:98
submit @ SoSciTools.min.js?i=1285a:99
SoSciTools.submitPage @ SoSciTools.min.js?i=1285a:128
weiter @ index.php?i=FFR7SZORYXNE&rnd=NGAJ:193
setTimeout (async)
(anonymous) @ index.php?i=FFR7SZORYXNE&rnd=NGAJ:225
load (async)
SoSciTools.attachEvent @ SoSciTools.min.js?i=1285a:60
(anonymous) @ index.php?i=FFR7SZORYXNE&rnd=NGAJ:218
RecordRTC.min.js:15 Stopped recording audio stream.
RecordRTC.min.js:15 Recorder state changed: stopped
RecordRTC.min.js:15 audio/webm -> 144 KB
by SoSci Survey (320k points)
In diesem Code hatten Sie für das Stoppen der Aufnahme ja schon Code ergänzt:

function weiter() {
  stopRecording();
  SoSciTools.submitPage();
}

Das stopRecording(); müsste hier raus - das wurfen wir ja nun automatisiert auf, wenn es weiter gehen soll. Was passiert denn dann? Die Debug-Information sollte zeigen, ob eine Datei übermittelt wurde.
by s098891 (355 points)
Es geht auch ins Minus...
[Information]    Interview number 237 is to be continued
[Information]    Questionnaire base from project speaking123 will be used
[Information]    Retention period on page 7 has been 9 seconds
[Processing]    Reading answers from page 7
[Information]    Answer missing for B111
[Information]    0% of the questions have been answered so far
[Processing]    Create page 8 in questionnaire base
[Content]    Create question B108
[Content]    Show text timer_rec
[Content]    Show text html
[Information]    The following placeholders have been prepared:
%remain% = (text) 8
by SoSci Survey (320k points)
B111 ist der Audio-Recorder? Was zeigt denn die JavaScript-Konsole?
by s098891 (355 points)
Nein, B108 ist der Recorder. Die sagt folgendes, wenns ins Minus geht:

spec:   {"video":false,"audio":true}
gumadapter.js:31 chrome: {"video":false,"audio":true}
RecordRTC.min.js:15 started recording audio stream.
RecordRTC.min.js:15 Using recorderType: MediaStreamRecorder
RecordRTC.min.js:15 Passing following config over MediaRecorder API. {mimeType: "audio/ogg", audioBitsPerSecond: 128000, type: "audio", checkForInactiveTracks: false, initCallback: ƒ}
RecordRTC.min.js:15 MediaRecorder API seems unable to record mimeType: audio/ogg
MediaStreamRecorder.record @ RecordRTC.min.js:15
initRecorder @ RecordRTC.min.js:15
startRecording @ RecordRTC.min.js:15
startRecording @ index.php?i=WWIL5ZCDSMD2&rnd=KGEZ:100
onStream @ index.php?i=WWIL5ZCDSMD2&rnd=KGEZ:132
Promise.then (async)
init @ index.php?i=WWIL5ZCDSMD2&rnd=KGEZ:148
load (async)
SoSciTools.attachEvent @ SoSciTools.min.js?i=1285a:60
(anonymous) @ index.php?i=WWIL5ZCDSMD2&rnd=KGEZ:151
RecordRTC.min.js:15 Recorder state changed: recording
RecordRTC.min.js:15 Initialized recorderType: MediaStreamRecorder for output-type: audio
RecordRTC.min.js:15 Stopped recording audio stream.
RecordRTC.min.js:15 Recorder state changed: stopped
RecordRTC.min.js:15 audio/webm -> 143 KB
by SoSci Survey (320k points)
> RecordRTC.min.js:15 Recorder state changed: stopped
> RecordRTC.min.js:15 audio/webm -> 143 KB

Das spricht dafür, dass die Daten übertragen werden. Geht es danach überhaupt nicht weiter oder zählt der Timer einfach nur kurz ins Minus, bevor es zur nächsten Seite weitergeht?
by s098891 (355 points)
Zuerst wird -2:00 angezeigt und runter gezählt, und sobald eine Minute vergeht, wird interessanterweise nicht -1:00 sondern -3:00 angezeigt und so geht's immer weiter.
by SoSci Survey (320k points)
Nun, das dürften Rechenfehler sein, die durch eine vereinfachte Trennung der Zeit in Minuten und Sekunden passieren. Aber das ist insgesamt natürlich nicht optimal...

Ich muss mir die Verbindung der automatischen Übermittlung in Verbindung mit dem Timer nochmal in Ruhe ansehen. Die beste Lösung wäre vermutlich, die Daten gar nicht über den Fragetyp zur Datenübermittlung, sondern über eine interne Variable zu lösen - eben weil Sie die Daten ja direkt mit dem "Weiter" übermitteln möchten. Allerdings wären dafür einige Modifikationen erforderlich. Deshalb werde ich mir erstmal ansehen, ob man guten Gewissens bei dem Fragetyp bleiben kann...
by s098891 (355 points)
Oje, den Fragetyp aller 50 items zu ändern wäre mir zu riskant, beginne in 10 Tagen mit der Datenerhebung mit 350 TeilnehmerInnen und hätte keine Zeit, den neu zusammen gestellten Test  davor  zu pilotieren...
by SoSci Survey (320k points)
Gut, dann sehe ich zu, dass wir eine Lösung für den bestehenden Fragetyp finden :)
by s098891 (355 points)
Vielen Dank. Bis jetzt hat sich noch nichts aufgetan?
by SoSci Survey (320k points)
Jetzt schon: https://support.soscisurvey.de/?qa=14749/audioaufnahme-wenn-stop-nicht-geklickt-wird&show=15075#a15075

Generell spricht natürlich auch nichts dagegen, selbst ein wenig mit dem JavaScript-Code zu experimentieren :)
0 votes
by SoSci Survey (320k points)

Damit das automatische Übertragen der Aufzeichnung auch mit Timer funktioniert, ist nur eine kleine Modifikation im JavaScript-Codes des Timers erforderlich:

<div id="remain"></div>
<script type="text/javascript">
<!--

var timer1;
var timer2;
function weiter() {
  window.clearTimeout(timer1);
  window.clearTimeout(timer2);
  var out = document.getElementById("remain");
  out.innerHTML = "0:00";
  onSubmit();
}
// Countdown anzeigen
var date0 = new Date();
var timeout = date0.getTime() + %remain% * 1000;
function updateCountdown() {
  // Zeit berechnen
  var date = new Date();
  var time = Math.ceil((timeout - date.getTime() - 50) / 1000);  // Verbleibende Zeit in Sekunden
  // Zeit anzeigen
  var out = document.getElementById("remain");
  if (!out) {
    return;
  }
  while (out.lastChild) {
    out.removeChild(out.lastChild);
  }
  var minutes = Math.floor(time / 60);
  var seconds = String(time - 60 * minutes);
  if (seconds.length < 2) seconds = "0" + seconds;
  var display = String(minutes) + ":" + seconds;
  var displayNode = document.createTextNode(display);
  out.appendChild(displayNode);
}
// Initialisierung der Weiterleitung
SoSciTools.attachEvent(window, "load", function(evt) {
  // Weiter-Knopf ausblenden (optional)
  SoSciTools.submitButtonsHide();
  // Zusätzlicher Timer für die Aktualisierung des Countdowns
  timer1 = window.setInterval(updateCountdown, 1000);
  updateCountdown();
  // Timer für automatische Weiterleitung starten
  timer2 = window.setTimeout(weiter, %remain% * 1000);
});
// -->
</script>

Hier wurde v.a die Funktion weiter() geändert, welche zum einen die Timer stoppt (damit die Anzeige nicht ins negative läuft) und zum anderen die Funktion onSubmit() aufruft, welche wir ja bereits im Audio-Recorder definiert hatten. Diese kümmert sich um Übertragung und Weiterleitung.

Hier der Vollständigkeit halber nochmal der modifizierte Code aus der Audio-Recorder-Frage.

<!-- Buttons -->
<div style="margin: 2em 0 0.5em">
  <button id="btnStart" type="button" tabindex="50">START</button>
  <button id="btnStop" type="button" tabindex="50">STOP</button>
</div>
<div style="margin: 0.5em 0" id="audioReload">Bitte laden Sie die Seite neu (<a href="javascript:SoSciTools.reloadPage()">Neu Laden</a>) und gestatten Sie der Seite Zugriff auf Ihr Audio-Gerät.</div>

<!-- Audio recorder from https://github.com/muaz-khan/RecordRTC -->
<script src="../plugins/RecordRTC/RecordRTC.min.js"></script>
<script src="../plugins/RecordRTC/gumadapter.js"></script>


<script type="text/javascript">
<!--
"use strict";

if ((webrtcDetectedVersion === null) || (webrtcDetectedVersion < webrtcMinimumVersion)) {
  alert("Ihr Browser unterstützt diese Aufnahme leider nicht.");
  throw new Error("no audio support");
}

var buttonStart = document.getElementById("btnStart");
var buttonStop = document.getElementById("btnStop");
var recordRTC;

function startRecording(button) {
  if (recordRTC) {
    recordRTC.startRecording();
    buttonStart.disabled = true;
    buttonStop.disabled = false;
  }
}

function stopRecording(button) {
  if (recordRTC) {
    recordRTC.stopRecording(onStop);
    // You may want to change this to allow a new try (replacing the old one)
    buttonStart.disabled = false;
    buttonStop.disabled = true;
  }
}

function onStop(audioURL) {
    var recordedBlob = recordRTC.getBlob();
    // Transfer the data
    %q.id%.sendBLOB(recordedBlob, onSendDone);
}

function onStream(stream) {
    var options = {
        mimeType: "audio/ogg",
        audioBitsPerSecond: 128000
    };
    recordRTC = new RecordRTC(stream, options);
    // Enable the start button
    buttonStart.disabled = false;
    // Remove warning
    document.getElementById("audioReload").style.display = "none";
}

function init() {
    buttonStart.disabled = true;
    buttonStop.disabled = true;

    var mediaConstraints = {
        video: false,
        audio: true
    };

    function onError(error) {
      alert("Ihr Computer oder Ihr Browser unterstützt derzeit keine Tonaufnahme." + "\n\n" + error);
    }
    navigator.mediaDevices.getUserMedia(mediaConstraints).then(onStream).catch(onError);
};

SoSciTools.attachEvent(window, "load", init);
SoSciTools.attachEvent(buttonStart, "click", startRecording);
SoSciTools.attachEvent(buttonStop, "click", stopRecording);

var audioSent = false;
function onSubmit() {
console.log("OnSubmit");
  // Check if already sent
  if (!audioSent) {
    stopRecording();
    return false;
  } else {
    return true;
  }
}
function onSendDone() {
  audioSent = true;
  SoSciTools.submitPage();
}
SoSciTools.questionnaire.attachCheck(onSubmit);

// -->
</script>
by s098891 (355 points)
edited by s098891
Vielen Dank, leider geht's trotzdem nicht. Verwende ich die zwei Codes so wird Start nicht aktiviert und nach Ablauf des Timers bleibt man auf der Seite:

https://www.soscisurvey.de/speaking123/?act=wQ2anykskG8azkGKfieb7PMu


Fehlerkonsole:
spec:   {"video":false,"audio":true}
gumadapter.js:31 chrome: {"video":false,"audio":true}
index.php?i=X0XQL5I7R75P&rnd=PGYW:154 OnSubmit
RecordRTC.min.js:15 It seems that recorder is destroyed or "startRecording" is not invoked for audio recorder.
warningLog @ RecordRTC.min.js:15
stopRecording @ RecordRTC.min.js:15
stopRecording @ index.php?i=X0XQL5I7R75P&rnd=PGYW:108
onSubmit @ index.php?i=X0XQL5I7R75P&rnd=PGYW:157
weiter @ index.php?i=X0XQL5I7R75P&rnd=PGYW:198
setTimeout (async)
(anonymous) @ index.php?i=X0XQL5I7R75P&rnd=PGYW:230
load (async)
SoSciTools.attachEvent @ SoSciTools.min.js?i=1285a:60
(anonymous) @ index.php?i=X0XQL5I7R75P&rnd=PGYW:223
by SoSci Survey (320k points)
> so wird Start nicht aktiviert

Das war ja auch noch eine dritte Modifikation - gefragt in einer anderen Frage. Ich ging davon aus, dass Sie aus den obigen Codes nur die für Sie relevanten Änderungen übernehmen.

> und nach Ablauf des Timers bleibt man auf der Seite:

Das dürfte daran liegen, weil die Aufnahme nicht gestartet wurde:

> "startRecording" is not invoked for audio recorder.
by SoSci Survey (320k points)
Wenn Sie den Code posten, der nicht funktioniert - oder einen Pretest-Link direkt (!) zur betroffenen Seite, kann ich gerne noch Tipps geben. Aber bitte haben Sie Verständnis, dass wir nicht alle individuellen Lösungswünsche für Sie implementieren können. Dafür gibt es aktuell zu viele Fragen hier im Support.

Willkommen im Online-Support von SoSci Survey.

Hier bekommen Sie schnelle und fundierte Antworten von anderen Projektleitern und direkt von SoSci Survey.

→ Eine Frage stellen


Welcome to the SoSci Survey online support.

Simply ask a question to quickly get answers from other professionals, and directly from SoSci Survey.

→ Ask a Question

...