/* * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ /* jshint jasmine: true */ /* global Windows, Media, MediaError, LocalFileSystem, halfSpeedBtn */ // increased timeout for actual playback to give device chance to download and play mp3 file // some emulators can be REALLY slow at this, so two minutes var ACTUAL_PLAYBACK_TEST_TIMEOUT = 2 * 60 * 1000; var isWindows = cordova.platformId == 'windows8' || cordova.platformId == 'windows'; // detect whether audio hardware is available and enabled var isAudioSupported = isWindows ? Windows.Media.Devices.MediaDevice.getDefaultAudioRenderId(Windows.Media.Devices.AudioDeviceRole.default) : true; exports.defineAutoTests = function () { var failed = function (done, msg, context) { if (context && context.done) return; context.done = true; var info = typeof msg == 'undefined' ? 'Unexpected error callback' : msg; expect(true).toFailWithMessage(info); done(); }; var succeed = function (done, msg, context) { if (context && context.done) return; context.done = true; var info = typeof msg == 'undefined' ? 'Unexpected success callback' : msg; expect(true).toFailWithMessage(info); done(); }; describe('Media', function () { beforeEach(function () { // Custom Matcher jasmine.Expectation.addMatchers({ toFailWithMessage : function () { return { compare : function (error, message) { var pass = false; return { pass : pass, message : message }; } }; } }); }); it("media.spec.1 should exist", function () { expect(Media).toBeDefined(); expect(typeof Media).toBe("function"); }); it("media.spec.2 should have the following properties", function () { var media1 = new Media("dummy"); expect(media1.id).toBeDefined(); expect(media1.src).toBeDefined(); expect(media1._duration).toBeDefined(); expect(media1._position).toBeDefined(); media1.release(); }); it("media.spec.3 should define constants for Media status", function () { expect(Media).toBeDefined(); expect(Media.MEDIA_NONE).toBe(0); expect(Media.MEDIA_STARTING).toBe(1); expect(Media.MEDIA_RUNNING).toBe(2); expect(Media.MEDIA_PAUSED).toBe(3); expect(Media.MEDIA_STOPPED).toBe(4); }); it("media.spec.4 should define constants for Media errors", function () { expect(MediaError).toBeDefined(); expect(MediaError.MEDIA_ERR_NONE_ACTIVE).toBe(0); expect(MediaError.MEDIA_ERR_ABORTED).toBe(1); expect(MediaError.MEDIA_ERR_NETWORK).toBe(2); expect(MediaError.MEDIA_ERR_DECODE).toBe(3); expect(MediaError.MEDIA_ERR_NONE_SUPPORTED).toBe(4); }); it("media.spec.5 should contain a play function", function () { var media1 = new Media("dummy"); expect(media1.play).toBeDefined(); expect(typeof media1.play).toBe('function'); media1.release(); }); it("media.spec.6 should contain a stop function", function () { var media1 = new Media("dummy"); expect(media1.stop).toBeDefined(); expect(typeof media1.stop).toBe('function'); media1.release(); }); it("media.spec.7 should contain a seekTo function", function () { var media1 = new Media("dummy"); expect(media1.seekTo).toBeDefined(); expect(typeof media1.seekTo).toBe('function'); media1.release(); }); it("media.spec.8 should contain a pause function", function () { var media1 = new Media("dummy"); expect(media1.pause).toBeDefined(); expect(typeof media1.pause).toBe('function'); media1.release(); }); it("media.spec.9 should contain a getDuration function", function () { var media1 = new Media("dummy"); expect(media1.getDuration).toBeDefined(); expect(typeof media1.getDuration).toBe('function'); media1.release(); }); it("media.spec.10 should contain a getCurrentPosition function", function () { var media1 = new Media("dummy"); expect(media1.getCurrentPosition).toBeDefined(); expect(typeof media1.getCurrentPosition).toBe('function'); media1.release(); }); it("media.spec.11 should contain a startRecord function", function () { var media1 = new Media("dummy"); expect(media1.startRecord).toBeDefined(); expect(typeof media1.startRecord).toBe('function'); media1.release(); }); it("media.spec.12 should contain a stopRecord function", function () { var media1 = new Media("dummy"); expect(media1.stopRecord).toBeDefined(); expect(typeof media1.stopRecord).toBe('function'); media1.release(); }); it("media.spec.13 should contain a release function", function () { var media1 = new Media("dummy"); expect(media1.release).toBeDefined(); expect(typeof media1.release).toBe('function'); media1.release(); }); it("media.spec.14 should contain a setVolume function", function () { var media1 = new Media("dummy"); expect(media1.setVolume).toBeDefined(); expect(typeof media1.setVolume).toBe('function'); media1.release(); }); it("media.spec.15 should contain a getCurrentAmplitude function", function () { var media1 = new Media("dummy"); expect(media1.getCurrentAmplitude).toBeDefined(); expect(typeof media1.getCurrentAmplitude).toBe('function'); media1.release(); }); it("media.spec.16 should contain a pauseRecord function", function () { var media1 = new Media("dummy"); expect(media1.pauseRecord).toBeDefined(); expect(typeof media1.pauseRecord).toBe('function'); media1.release(); }); it("media.spec.17 should contain a resumeRecord function", function () { var media1 = new Media("dummy"); expect(media1.resumeRecord).toBeDefined(); expect(typeof media1.resumeRecord).toBe('function'); media1.release(); }); it("media.spec.18 should return MediaError for bad filename", function (done) { //bb10 dialog pops up, preventing tests from running if (cordova.platformId === 'blackberry10') { pending(); } var context = this, fileName = 'invalid.file.name', badMedia = new Media(fileName, succeed.bind(null, done, ' badMedia = new Media , Unexpected succees callback, it should not create Media object with invalid file name'), function (result) { if (context.done) return; context.done = true; expect(result).toBeDefined(); expect(result.code).toBe(MediaError.MEDIA_ERR_ABORTED); if (badMedia) { badMedia.release(); } done(); }); badMedia.play(); }); describe('actual playback', function() { var checkInterval, media; afterEach(function() { clearInterval(checkInterval); if (media) { media.stop(); media.release(); media = null; } }); it("media.spec.19 position should be set properly", function (done) { // no audio hardware available if (!isAudioSupported) { pending(); } //context variable used as an extra security statement to ensure that the callback is processed only once, //in case the statusChange callback is reached more than one time with the same status code. //Some information about this kind of behaviour can be found at JIRA: CB-7099. var context = this, mediaFile = 'https://cordova.apache.org/downloads/BlueZedEx.mp3', successCallback = function () { }, statusChange = function (statusCode) { if (!context.done && statusCode == Media.MEDIA_RUNNING) { checkInterval = setInterval(function () { if (context.done) return; media.getCurrentPosition(function successCallback(position) { if (position > 0.0) { context.done = true; expect(true).toBe(true); done(); } }, failed.bind(null, done, 'media1.getCurrentPosition - Error getting media current position', context)); }, 1000); } }; media = new Media(mediaFile, successCallback, failed.bind(null, done, 'media1 = new Media - Error creating Media object. Media file: ' + mediaFile, context), statusChange); media.play(); }, ACTUAL_PLAYBACK_TEST_TIMEOUT); it("media.spec.20 duration should be set properly", function (done) { if (!isAudioSupported || cordova.platformId === 'blackberry10') { pending(); } //context variable used as an extra security statement to ensure that the callback is processed only once, //in case the statusChange callback is reached more than one time with the same status code. //Some information about this kind of behaviour can be found at JIRA: CB-7099. var context = this, mediaFile = 'https://cordova.apache.org/downloads/BlueZedEx.mp3', successCallback = function () { }, statusChange = function (statusCode) { if (!context.done && statusCode == Media.MEDIA_RUNNING) { checkInterval = setInterval(function () { if (context.done) return; media.getCurrentPosition(function (position) { if (position > 0.0) { context.done = true; expect(media.getDuration()).toBeGreaterThan(0.0); done(); } }, failed.bind(null, done, 'media1.getCurrentPosition - Error getting media current position', context)); }, 1000); } }; media = new Media(mediaFile, successCallback, failed.bind(null, done, 'media1 = new Media - Error creating Media object. Media file: ' + mediaFile, context), statusChange); media.play(); }, ACTUAL_PLAYBACK_TEST_TIMEOUT); it("media.spec.21 should be able to resume playback after pause", function (done) { if (!isAudioSupported || cordova.platformId === 'blackberry10') { pending(); } //context variable used as an extra security statement to ensure that the callback is processed only once, //in case the statusChange callback is reached more than one time with the same status code. //Some information about this kind of behaviour can be found at JIRA: CB-7099. var context = this; var resumed = false; var mediaFile = 'https://cordova.apache.org/downloads/BlueZedEx.mp3'; var successCallback = function () { }; var statusChange = function (statusCode) { if (context.done) return; if (statusCode == Media.MEDIA_RUNNING) { if (!resumed) { media.seekTo(20000); media.pause(); return; } media.getCurrentPosition(function (position) { expect(position).toBeCloseTo(20, 0); context.done = true; done(); }, failed.bind(null, done, 'media1.getCurrentPosition - Error getting media current position', context)); } if (statusCode == Media.MEDIA_PAUSED) { resumed = true; media.play(); } }; media = new Media(mediaFile, successCallback, failed.bind(null, done, 'media1 = new Media - Error creating Media object. Media file: ' + mediaFile, context), statusChange); // CB-10535: Play after a few secs, to give allow enough buffering of media file before seeking setTimeout(function() { media.play(); }, 4000); }, ACTUAL_PLAYBACK_TEST_TIMEOUT); it("media.spec.22 should be able to seek through file", function (done) { if (!isAudioSupported || cordova.platformId === 'blackberry10') { pending(); } //context variable used as an extra security statement to ensure that the callback is processed only once, //in case the statusChange callback is reached more than one time with the same status code. //Some information about this kind of behaviour can be found at JIRA: CB-7099. var context = this; var mediaFile = 'https://cordova.apache.org/downloads/BlueZedEx.mp3'; var successCallback = function () { }; var statusChange = function (statusCode) { if (!context.done && statusCode == Media.MEDIA_RUNNING) { checkInterval = setInterval(function () { if (context.done) return; media.seekTo(5000); media.getCurrentPosition(function (position) { expect(position).toBeCloseTo(5, 0); context.done = true; done(); }, failed.bind(null, done, 'media1.getCurrentPosition - Error getting media current position', context)); }, 1000); } }; media = new Media(mediaFile, successCallback, failed.bind(null, done, 'media1 = new Media - Error creating Media object. Media file: ' + mediaFile, context), statusChange); // CB-10535: Play after a few secs, to give allow enough buffering of media file before seeking setTimeout(function() { media.play(); }, 4000); }, ACTUAL_PLAYBACK_TEST_TIMEOUT); }); it("media.spec.23 should contain a setRate function", function () { var media1 = new Media("dummy"); expect(media1.setRate).toBeDefined(); expect(typeof media1.setRate).toBe('function'); media1.release(); }); it("media.spec.24 playback rate should be set properly using setRate", function (done) { if (cordova.platformId !== 'ios') { expect(true).toFailWithMessage('Platform does not supported this feature'); pending(); return; } var mediaFile = 'https://cordova.apache.org/downloads/BlueZedEx.mp3', successCallback, context = this, flag = true, statusChange = function (statusCode) { console.log("status code: " + statusCode); if (statusCode == Media.MEDIA_RUNNING && flag) { //flag variable used to ensure an extra security statement to ensure that the callback is processed only once, //in case for some reason the statusChange callback is reached more than one time with the same status code. //Some information about this kind of behavior it can be found at JIRA: CB-7099 flag = false; setTimeout(function () { media1.getCurrentPosition(function (position) { //in four seconds expect position to be between 4 & 10. Here, the values are chosen to give //a large enough buffer range for the position to fall in and are not based on any calculation. expect(position >= 4 && position < 10).toBeTruthy(); media1.stop(); media1.release(); done(); }, failed.bind(null, done, 'media1.getCurrentPosition - Error getting media current position'),context); }, 4000); } }; var media1 = new Media(mediaFile, successCallback, failed.bind(null, done, 'media1 = new Media - Error creating Media object. Media file: ' + mediaFile, context), statusChange); // jshint ignore:line //make audio playback two times faster media1.setRate(2); media1.play(); }, ACTUAL_PLAYBACK_TEST_TIMEOUT); }); }; //****************************************************************************************** //***************************************Manual Tests*************************************** //****************************************************************************************** exports.defineManualTests = function (contentEl, createActionButton) { //------------------------------------------------------------------------- // Audio player //------------------------------------------------------------------------- var media1 = null; var media1Timer = null; var audioSrc = null; var defaultaudio = "https://cordova.apache.org/downloads/BlueZedEx.mp3"; //Play audio function function playAudio(url) { console.log("playAudio()"); console.log(" -- media=" + media1); var src = defaultaudio; if (url) { src = url; } // Stop playing if src is different from currently playing source if (src !== audioSrc) { if (media1 !== null) { stopAudio(); media1 = null; } } if (media1 === null) { // TEST STREAMING AUDIO PLAYBACK //var src = "http://nunzioweb.com/misc/Bon_Jovi-Crush_Mystery_Train.mp3"; // works //var src = "http://nunzioweb.com/misc/Bon_Jovi-Crush_Mystery_Train.m3u"; // doesn't work //var src = "http://www.wav-sounds.com/cartoon/bugsbunny1.wav"; // works //var src = "http://www.angelfire.com/fl5/html-tutorial/a/couldyou.mid"; // doesn't work //var src = "MusicSearch/mp3/train.mp3"; // works //var src = "bryce.mp3"; // works //var src = "/android_asset/www/bryce.mp3"; // works media1 = new Media(src, function () { console.log("playAudio():Audio Success"); }, function (err) { console.log("playAudio():Audio Error: " + err.code); setAudioStatus("Error: " + err.code); }, function (status) { console.log("playAudio():Audio Status: " + status); setAudioStatus(Media.MEDIA_MSG[status]); // If stopped, then stop getting current position if (Media.MEDIA_STOPPED == status) { clearInterval(media1Timer); media1Timer = null; setAudioPosition("0 sec"); } }); } audioSrc = src; document.getElementById('durationValue').innerHTML = ""; // Play audio media1.play(); if (media1Timer === null && media1.getCurrentPosition) { media1Timer = setInterval( function () { media1.getCurrentPosition( function (position) { if (position >= 0.0) { setAudioPosition(position + " sec"); } }, function (e) { console.log("Error getting pos=" + e); setAudioPosition("Error: " + e); }); }, 1000); } // Get duration var counter = 0; var timerDur = setInterval( function () { counter = counter + 100; if (counter > 2000) { clearInterval(timerDur); } var dur = media1.getDuration(); if (dur > 0) { clearInterval(timerDur); document.getElementById('durationValue').innerHTML = dur + " sec"; } }, 100); } //Pause audio playback function pauseAudio() { console.log("pauseAudio()"); if (media1) { media1.pause(); } } //Stop audio function stopAudio() { console.log("stopAudio()"); if (media1) { media1.stop(); } clearInterval(media1Timer); media1Timer = null; } //Release audio function releaseAudio() { console.log("releaseAudio()"); if (media1) { media1.stop(); //imlied stop of playback, resets timer media1.release(); } } //Set audio status function setAudioStatus(status) { document.getElementById('statusValue').innerHTML = status; } //Set audio position function setAudioPosition(position) { document.getElementById('positionValue').innerHTML = position; } //Seek audio function seekAudio(mode) { var time = document.getElementById("seekInput").value; if (time === "") { time = 5000; } else { time = time * 1000; //we expect the input to be in seconds } if (media1 === null) { console.log("seekTo requested while media1 is null"); if (audioSrc === null) { audioSrc = defaultaudio; } media1 = new Media(audioSrc, function () { console.log("seekToAudio():Audio Success"); }, function (err) { console.log("seekAudio():Audio Error: " + err.code); setAudioStatus("Error: " + err.code); }, function (status) { console.log("seekAudio():Audio Status: " + status); setAudioStatus(Media.MEDIA_MSG[status]); // If stopped, then stop getting current position if (Media.MEDIA_STOPPED == status) { clearInterval(media1Timer); media1Timer = null; setAudioPosition("0 sec"); } }); } media1.getCurrentPosition( function (position) { var deltat = time; if (mode === "by") { deltat = time + position * 1000; } media1.seekTo(deltat, function () { console.log("seekAudioTo():Audio Success"); //force an update on the position display updatePosition(); }, function (err) { console.log("seekAudioTo():Audio Error: " + err.code); }); }, function (e) { console.log("Error getting pos=" + e); setAudioPosition("Error: " + e); }); } //for forced updates of position after a successful seek function updatePosition() { media1.getCurrentPosition( function (position) { if (position >= 0.0) { setAudioPosition(position + " sec"); } }, function (e) { console.log("Error getting pos=" + e); setAudioPosition("Error: " + e); }); } //------------------------------------------------------------------------- // Audio recorder //------------------------------------------------------------------------- var mediaRec = null; var recTime = 0; var recordSrc = "myRecording.mp3"; //Record audio function recordAudio() { console.log("recordAudio()"); console.log(" -- media=" + mediaRec); releaseAudio(); if (!mediaRec) { var src = recordSrc; mediaRec = new Media(src, function () { console.log("recordAudio():Audio Success"); }, function (err) { console.log("recordAudio():Audio Error: " + err.code); setAudioStatus("Error: " + err.code); }, function (status) { console.log("recordAudio():Audio Status: " + status); setAudioStatus(Media.MEDIA_MSG[status]); }); } // Record audio mediaRec.startRecord(); // Stop recording after 10 sec recTime = 0; var recInterval = setInterval(function () { recTime = recTime + 1; setAudioPosition(recTime + " sec"); if (recTime >= 10) { clearInterval(recInterval); if (mediaRec.stopAudioRecord) { mediaRec.stopAudioRecord(); } else { mediaRec.stopRecord(); } console.log("recordAudio(): stop"); } }, 1000); } //Play back recorded audio function playRecording() { playAudio(recordSrc); } //Function to create a file for iOS recording function getRecordSrc() { var fsFail = function (error) { console.log("error creating file for iOS recording"); }; var gotFile = function (file) { recordSrc = file.fullPath; //console.log("recording Src: " + recordSrc); }; var gotFS = function (fileSystem) { fileSystem.root.getFile("iOSRecording.wav", { create : true }, gotFile, fsFail); }; window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, gotFS, fsFail); } //Function to create a file for BB recording function getRecordSrcBB() { var fsFail = function (error) { console.log("error creating file for BB recording"); }; var gotFile = function (file) { recordSrc = file.fullPath; }; var gotFS = function (fileSystem) { fileSystem.root.getFile("BBRecording.amr", { create : true }, gotFile, fsFail); }; window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, gotFS, fsFail); } //Function to create a file for Windows recording function getRecordSrcWin() { var fsFail = function (error) { console.log("error creating file for Win recording"); }; var gotFile = function (file) { recordSrc = file.name; }; var gotFS = function (fileSystem) { fileSystem.root.getFile("WinRecording.m4a", { create: true }, gotFile, fsFail); }; window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, gotFS, fsFail); } //Generate Dynamic Table function generateTable(tableId, rows, cells, elements) { var table = document.createElement('table'); for (var r = 0; r < rows; r++) { var row = table.insertRow(r); for (var c = 0; c < cells; c++) { var cell = row.insertCell(c); cell.setAttribute("align", "center"); for (var e in elements) { if (elements[e].position.row == r && elements[e].position.cell == c) { var htmlElement = document.createElement(elements[e].tag); var content; if (elements[e].content !== "") { content = document.createTextNode(elements[e].content); htmlElement.appendChild(content); } if (elements[e].type) { htmlElement.type = elements[e].type; } htmlElement.setAttribute("id", elements[e].id); cell.appendChild(htmlElement); } } } } table.setAttribute("align", "center"); table.setAttribute("id", tableId); return table; } //Audio && Record Elements var elementsResultsAudio= [{ id : "statusTag", content : "Status:", tag : "div", position : { row : 0, cell : 0 } }, { id : "statusValue", content : "", tag : "div", position : { row : 0, cell : 2 } }, { id : "durationTag", content : "Duration:", tag : "div", position : { row : 1, cell : 0 } }, { id : "durationValue", content : "", tag : "div", position : { row : 1, cell : 2 } }, { id : "positionTag", content : "Position:", tag : "div", position : { row : 2, cell : 0 } }, { id : "positionValue", content : "", tag : "div", position : { row : 2, cell : 2 } }], elementsAudio = [{ id : "playBtn", content : "", tag : "div", position : { row : 0, cell : 0 } }, { id : "pauseBtn", content : "", tag : "div", position : { row : 0, cell : 1 } }, { id : "stopBtn", content : "", tag : "div", position : { row : 1, cell : 0 } }, { id : "releaseBtn", content : "", tag : "div", position : { row : 1, cell : 1 } }, { id : "seekByBtn", content : "", tag : "div", position : { row : 2, cell : 0 } }, { id : "seekToBtn", content : "", tag : "div", position : { row : 2, cell : 1 } }, { id : "seekInput", content : "", tag : "input", type : "number", position : { row : 2, cell : 2 } }, { id: "halfSpeedBtn", content:"", tag:"div", position:{ row:0, cell:2 } } ], elementsRecord = [{ id : "recordbtn", content : "", tag : "div", position : { row : 1, cell : 0 } }, { id : "recordplayBtn", content : "", tag : "div", position : { row : 1, cell : 1 } }, { id : "recordpauseBtn", content : "", tag : "div", position : { row : 2, cell : 0 } }, { id : "recordstopBtn", content : "", tag : "div", position : { row : 2, cell : 1 } } ]; //Title audio results var div = document.createElement('h2'); div.appendChild(document.createTextNode('Audio')); div.setAttribute("align", "center"); contentEl.appendChild(div); //Generate and add results table contentEl.appendChild(generateTable('info', 3, 3, elementsResultsAudio)); //Title audio actions div = document.createElement('h2'); div.appendChild(document.createTextNode('Actions')); div.setAttribute("align", "center"); contentEl.appendChild(div); //Generate and add buttons table contentEl.appendChild(generateTable('audioActions', 3, 3, elementsAudio)); createActionButton('Play', function () { playAudio(); }, 'playBtn'); createActionButton('Pause', function () { pauseAudio(); }, 'pauseBtn'); createActionButton('HalfSpeed', function() { if(halfSpeedBtn.firstChild.firstChild.innerText == 'HalfSpeed') { halfSpeedBtn.firstChild.firstChild.innerText = 'FullSpeed'; media1.setRate(0.5); } else if(halfSpeedBtn.firstChild.firstChild.innerText == 'FullSpeed') { halfSpeedBtn.firstChild.firstChild.innerText = 'DoubleSpeed'; media1.setRate(1.0); } else { halfSpeedBtn.firstChild.firstChild.innerText = 'HalfSpeed'; media1.setRate(2.0); } }, 'halfSpeedBtn'); createActionButton('Stop', function () { stopAudio(); }, 'stopBtn'); createActionButton('Release', function () { releaseAudio(); }, 'releaseBtn'); createActionButton('Seek By', function () { seekAudio('by'); }, 'seekByBtn'); createActionButton('Seek To', function () { seekAudio('to'); }, 'seekToBtn'); //get Special path to record if iOS || Blackberry if (cordova.platformId === 'ios') getRecordSrc(); else if (cordova.platformId === 'blackberry') getRecordSrcBB(); else if (cordova.platformId === 'windows' || cordova.platformId === 'windows8') getRecordSrcWin(); //testing process and details function addItemToList(_list, _text) { var item = document.createElement('li'); item.appendChild(document.createTextNode(_text)); _list.appendChild(item); } div = document.createElement('h4'); div.appendChild(document.createTextNode('Recommended Test Procedure')); contentEl.appendChild(div); var list = document.createElement('ol'); addItemToList(list, 'Press play - Will start playing audio. Status: Running, Duration: 61.333 sec, Position: Current position of audio track'); addItemToList(list, 'Press pause - Will pause the audio. Status: Paused, Duration: 61.333 sec, Position: Position where track was paused'); addItemToList(list, 'Press play - Will begin playing where track left off from the pause'); addItemToList(list, 'Press stop - Will stop the audio. Status: Stopped, Duration: 61.333 sec, Position: 0 sec'); addItemToList(list, 'Press play - Will begin playing the audio track from the beginning'); addItemToList(list, 'Press release - Will stop the audio. Status: Stopped, Duration: 61.333 sec, Position: 0 sec'); addItemToList(list, 'Press play - Will begin playing the audio track from the beginning'); addItemToList(list, 'Type 10 in the text box beside Seek To button'); addItemToList(list, 'Press seek by - Will jump 10 seconds ahead in the audio track. Position: should jump by 10 sec'); addItemToList(list, 'Press stop if track is not already stopped'); addItemToList(list, 'Type 30 in the text box beside Seek To button'); addItemToList(list, 'Press play then seek to - Should jump to Position 30 sec'); addItemToList(list, 'Press stop'); addItemToList(list, 'Type 5 in the text box beside Seek To button'); addItemToList(list, 'Press play, let play past 10 seconds then press seek to - should jump back to position 5 sec'); div = document.createElement('div'); div.setAttribute("style", "background:#B0C4DE;border:1px solid #FFA07A;margin:15px 6px 0px;min-width:295px;max-width:97%;padding:4px 0px 2px 10px;min-height:160px;max-height:200px;overflow:auto"); div.appendChild(list); contentEl.appendChild(div); //Title Record Audio div = document.createElement('h2'); div.appendChild(document.createTextNode('Record Audio')); div.setAttribute("align", "center"); contentEl.appendChild(div); //Generate and add Record buttons table contentEl.appendChild(generateTable('recordContent', 3, 3, elementsRecord)); createActionButton('Record Audio \n 10 sec', function () { recordAudio(); }, 'recordbtn'); createActionButton('Play', function () { playRecording(); }, 'recordplayBtn'); createActionButton('Pause', function () { pauseAudio(); }, 'recordpauseBtn'); createActionButton('Stop', function () { stopAudio(); }, 'recordstopBtn'); //testing process and details div = document.createElement('h4'); div.appendChild(document.createTextNode('Recommended Test Procedure')); contentEl.appendChild(div); list = document.createElement('ol'); addItemToList(list, 'Press Record Audio 10 sec - Will start recording audio for 10 seconds. Status: Running, Position: number of seconds recorded (will stop at 10)'); addItemToList(list, 'Status will change to Stopped when finished recording'); addItemToList(list, 'Press play - Will begin playing the recording. Status: Running, Duration: # of seconds of recording, Position: Current position of recording'); addItemToList(list, 'Press stop - Will stop playing the recording. Status: Stopped, Duration: # of seconds of recording, Position: 0 sec'); addItemToList(list, 'Press play - Will begin playing the recording from the beginning'); addItemToList(list, 'Press pause - Will pause the playback of the recording. Status: Paused, Duration: # of seconds of recording, Position: Position where recording was paused'); addItemToList(list, 'Press play - Will begin playing the recording from where it was paused'); div = document.createElement('div'); div.setAttribute("style", "background:#B0C4DE;border:1px solid #FFA07A;margin:15px 6px 0px;min-width:295px;max-width:97%;padding:4px 0px 2px 10px;min-height:160px;max-height:200px;overflow:auto"); div.appendChild(list); contentEl.appendChild(div); };