{"id":28127,"date":"2023-08-25T15:32:58","date_gmt":"2023-08-25T07:32:58","guid":{"rendered":"https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/?page_id=28127"},"modified":"2023-08-25T15:48:59","modified_gmt":"2023-08-25T07:48:59","slug":"test-recording","status":"publish","type":"page","link":"https:\/\/www.junyiacademy.org\/event\/test-recording\/","title":{"rendered":"test-recording"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"28127\" class=\"elementor elementor-28127\">\n\t\t\t\t\t\t<div class=\"elementor-inner\">\n\t\t\t\t<div class=\"elementor-section-wrap\">\n\t\t\t\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-8af7465 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"8af7465\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t\t\t<div class=\"elementor-row\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-feafc34\" data-id=\"feafc34\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-column-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t<div class=\"elementor-widget-wrap\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-88fb73f elementor-widget elementor-widget-html\" data-id=\"88fb73f\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<script>\r\nfunction WzRecorder(config) {\r\n\r\n    config = config || {};\r\n\r\n    var self = this;\r\n    var audioInput;\r\n    var audioNode;\r\n    var bufferSize = config.bufferSize || 4096;\r\n    var recordedData = [];\r\n    var recording = false;\r\n    var recordingLength = 0;\r\n\tvar startDate;\r\n\tvar audioCtx;\r\n\t\r\n\tthis.toggleRecording = function()\r\n\t{\r\n\t\trecording ? self.stop() : self.start();\r\n\t}\r\n\t\r\n\r\n    this.start = function() {\r\n\r\n\t\t\/\/ reset any previous data\r\n\t\trecordedData = [];\r\n\t\trecordingLength = 0;\r\n\t\t\r\n\t\t\/\/ webkit audio context shim\r\n\t\taudioCtx = new (window.AudioContext || window.webkitAudioContext)();\r\n\r\n        if (audioCtx.createJavaScriptNode) {\r\n            audioNode = audioCtx.createJavaScriptNode(bufferSize, 1, 1);\r\n        } else if (audioCtx.createScriptProcessor) {\r\n            audioNode = audioCtx.createScriptProcessor(bufferSize, 1, 1);\r\n        } else {\r\n            throw 'WebAudio not supported!';\r\n        }\r\n\r\n        audioNode.connect(audioCtx.destination);\r\n\r\n        navigator.mediaDevices.getUserMedia({audio: true})\r\n            .then(onMicrophoneCaptured)\r\n            .catch(onMicrophoneError);\r\n    };\r\n\r\n    this.stop = function() {\r\n        stopRecording(function(blob) {\r\n\t\t\tself.blob = blob;\r\n\t\t\tconfig.onRecordingStop && config.onRecordingStop(blob);\r\n        });\r\n    };\r\n\t\r\n\tthis.upload = function (url, params, callback) {\r\n        var formData = new FormData();\r\n        formData.append(\"audio\", self.blob, config.filename || 'recording.wav');\r\n        \r\n        for (var i in params)\r\n            formData.append(i, params[i]);\r\n\r\n        var request = new XMLHttpRequest();\r\n        request.upload.addEventListener(\"progress\", function (e) {\r\n            callback('progress', e, request);\r\n        });\r\n        request.upload.addEventListener(\"load\", function (e) {\r\n            callback('load', e, request);\r\n        });\r\n\r\n\t\trequest.onreadystatechange = function (e) {\r\n\t\t\tvar status = 'loading';\r\n\t\t\tif (request.readyState == 4)\r\n\t\t\t{\r\n\t\t\t\tstatus = request.status == 200 ? 'done' : 'error';\r\n\t\t\t}\r\n\t\t\tcallback(status, e, request);\r\n\t\t};\r\n  \r\n        request.open(\"POST\", url);\r\n        request.send(formData);\r\n    };\r\n\r\n\r\n    function stopRecording(callback) {\r\n        \/\/ stop recording\r\n        recording = false;\r\n\r\n        \/\/ to make sure onaudioprocess stops firing\r\n\t\twindow.localStream.getTracks().forEach( (track) => { track.stop(); });\r\n        audioInput.disconnect();\r\n        audioNode.disconnect();\r\n\t\t\r\n        exportWav({\r\n            sampleRate: sampleRate,\r\n            recordingLength: recordingLength,\r\n            data: recordedData\r\n        }, function(buffer, view) {\r\n            self.blob = new Blob([view], { type: 'audio\/wav' });\r\n            callback && callback(self.blob);\r\n        });\r\n    }\r\n\r\n\r\n    function onMicrophoneCaptured(microphone) {\r\n\r\n\t\tif (config.visualizer)\r\n\t\t\tvisualize(microphone);\r\n\t\t\r\n\t\t\/\/ save the stream so we can disconnect it when we're done\r\n\t\twindow.localStream = microphone;\r\n\r\n        audioInput = audioCtx.createMediaStreamSource(microphone);\r\n        audioInput.connect(audioNode);\r\n\r\n        audioNode.onaudioprocess = onAudioProcess;\r\n\r\n        recording = true;\r\n\t\tself.startDate = new Date();\r\n\t\t\r\n\t\tconfig.onRecordingStart && config.onRecordingStart();\r\n\t\tsampleRate = audioCtx.sampleRate;\r\n    }\r\n\r\n    function onMicrophoneError(e) {\r\n\t\tconsole.log(e);\r\n\t\talert('Unable to access the microphone.');\r\n    }\r\n\r\n    function onAudioProcess(e) {\r\n        if (!recording) {\r\n            return;\r\n        }\r\n\r\n        recordedData.push(new Float32Array(e.inputBuffer.getChannelData(0)));\r\n        recordingLength += bufferSize;\r\n\r\n        self.recordingLength = recordingLength;\r\n\t\tself.duration = new Date().getTime() - self.startDate.getTime();\r\n\r\n\t\tconfig.onRecording && config.onRecording(self.duration);\r\n    }\r\n\r\n\t\r\n\tfunction visualize(stream) {\r\n\t\tvar canvas = config.visualizer.element;\r\n\t\tif (!canvas)\r\n\t\t\treturn;\r\n\t\t\t\r\n\t\tvar canvasCtx = canvas.getContext(\"2d\");\r\n\t\tvar source = audioCtx.createMediaStreamSource(stream);\r\n\r\n\t\tvar analyser = audioCtx.createAnalyser();\r\n\t\tanalyser.fftSize = 2048;\r\n\t\tvar bufferLength = analyser.frequencyBinCount;\r\n\t\tvar dataArray = new Uint8Array(bufferLength);\r\n\r\n\t\tsource.connect(analyser);\r\n\r\n\t\tfunction draw() {\r\n\t\t\t\/\/ get the canvas dimensions\r\n\t\t\tvar width = canvas.width, height = canvas.height;\r\n\r\n\t\t\t\/\/ ask the browser to schedule a redraw before the next repaint\r\n\t\t\trequestAnimationFrame(draw);\r\n\r\n\t\t\t\/\/ clear the canvas\r\n\t\t\tcanvasCtx.fillStyle = config.visualizer.backcolor || '#fff';\r\n\t\t\tcanvasCtx.fillRect(0, 0, width, height);\r\n\r\n\t\t\tif (!recording)\r\n\t\t\t\treturn;\r\n\t\t\t\r\n\t\t\tcanvasCtx.lineWidth = config.visualizer.linewidth || 2;\r\n\t\t\tcanvasCtx.strokeStyle = config.visualizer.forecolor || '#f00';\r\n\r\n\t\t\tcanvasCtx.beginPath();\r\n\r\n\t\t\tvar sliceWidth = width * 1.0 \/ bufferLength;\r\n\t\t\tvar x = 0;\r\n\r\n\t\t\t\r\n\t\t\tanalyser.getByteTimeDomainData(dataArray);\r\n\r\n\t\t\tfor (var i = 0; i < bufferLength; i++) {\r\n\t\t\t\r\n\t\t\t\tvar v = dataArray[i] \/ 128.0;\r\n\t\t\t\tvar y = v * height \/ 2;\r\n\r\n\t\t\t\ti == 0 ? canvasCtx.moveTo(x, y) : canvasCtx.lineTo(x, y);\r\n\t\t\t\tx += sliceWidth;\r\n\t\t\t}\r\n\t\t\r\n\t\t\tcanvasCtx.lineTo(canvas.width, canvas.height\/2);\r\n\t\t\tcanvasCtx.stroke();\r\n\t\t}\r\n\t\t\r\n\t\tdraw();\r\n\t}\r\n\t\r\n    function exportWav(config, callback) {\r\n        function inlineWebWorker(config, cb) {\r\n\r\n            var data = config.data.slice(0);\r\n            var sampleRate = config.sampleRate;          \r\n\t\t\tdata = joinBuffers(data, config.recordingLength);\r\n\t\t\r\n            function joinBuffers(channelBuffer, count) {\r\n                var result = new Float64Array(count);\r\n                var offset = 0;\r\n                var lng = channelBuffer.length;\r\n\r\n                for (var i = 0; i < lng; i++) {\r\n                    var buffer = channelBuffer[i];\r\n                    result.set(buffer, offset);\r\n                    offset += buffer.length;\r\n                }\r\n\r\n                return result;\r\n            }\r\n\r\n            function writeUTFBytes(view, offset, string) {\r\n                var lng = string.length;\r\n                for (var i = 0; i < lng; i++) {\r\n                    view.setUint8(offset + i, string.charCodeAt(i));\r\n                }\r\n            }\r\n\r\n            var dataLength = data.length;\r\n\r\n            \/\/ create wav file\r\n            var buffer = new ArrayBuffer(44 + dataLength * 2);\r\n            var view = new DataView(buffer);\r\n\t\t\t\r\n            writeUTFBytes(view, 0, 'RIFF'); \/\/ RIFF chunk descriptor\/identifier\r\n            view.setUint32(4, 44 + dataLength * 2, true); \/\/ RIFF chunk length\r\n            writeUTFBytes(view, 8, 'WAVE'); \/\/ RIFF type\r\n            writeUTFBytes(view, 12, 'fmt '); \/\/ format chunk identifier, FMT sub-chunk\r\n            view.setUint32(16, 16, true); \/\/ format chunk length\r\n            view.setUint16(20, 1, true); \/\/ sample format (raw)\r\n            view.setUint16(22, 1, true); \/\/ mono (1 channel)\r\n            view.setUint32(24, sampleRate, true); \/\/ sample rate\r\n            view.setUint32(28, sampleRate * 2, true); \/\/ byte rate (sample rate * block align)\r\n            view.setUint16(32, 2, true); \/\/ block align (channel count * bytes per sample)\r\n            view.setUint16(34, 16, true); \/\/ bits per sample\r\n            writeUTFBytes(view, 36, 'data'); \/\/ data sub-chunk identifier\r\n            view.setUint32(40, dataLength * 2, true); \/\/ data chunk length\r\n\r\n            \/\/ write the PCM samples\r\n            var index = 44;\r\n            for (var i = 0; i < dataLength; i++) {\r\n                view.setInt16(index, data[i] * 0x7FFF, true);\r\n                index += 2;\r\n            }\r\n\r\n            if (cb) {\r\n                return cb({\r\n                    buffer: buffer,\r\n                    view: view\r\n                });\r\n            }\r\n\r\n            postMessage({\r\n                buffer: buffer,\r\n                view: view\r\n            });\r\n        }\r\n\r\n        var webWorker = processInWebWorker(inlineWebWorker);\r\n\r\n        webWorker.onmessage = function(event) {\r\n            callback(event.data.buffer, event.data.view);\r\n\r\n            \/\/ release memory\r\n            URL.revokeObjectURL(webWorker.workerURL);\r\n        };\r\n\r\n        webWorker.postMessage(config);\r\n    }\r\n\r\n    function processInWebWorker(_function) {\r\n        var workerURL = URL.createObjectURL(new Blob([_function.toString(),\r\n            ';this.onmessage = function (e) {' + _function.name + '(e.data);}'\r\n        ], {\r\n            type: 'application\/javascript'\r\n        }));\r\n\r\n        var worker = new Worker(workerURL);\r\n        worker.workerURL = workerURL;\r\n        return worker;\r\n    }\r\n}\r\n<\/script>\r\n\r\n<body>\r\n\t<link rel=\"stylesheet\" href=\"https:\/\/maxcdn.bootstrapcdn.com\/bootstrap\/4.0.0\/css\/bootstrap.min.css\">\r\n    <div class=\"container\" style=\"margin:3rem\">\r\n\t\t<p>\r\n\t\t\t<button id=record>Toggle recording<\/button>\r\n\t\t<\/p>\r\n\t\t<p>\r\n\t\t\tDuration: <span id=\"duration\">0ms<\/span>\r\n\t\t<\/p>\r\n\t\t<p>\r\n\t\t\t<audio id=\"player\" controls><\/audio>\t\r\n\t\t<\/p>\r\n\t<\/div>\r\n\t\r\n\r\n    <script>\r\n\t\r\n\tvar recorder = new WzRecorder({\r\n\t\tonRecordingStop: function(blob) {\r\n\t\t\tdocument.getElementById('player').src = URL.createObjectURL(blob);\r\n\t\t},\r\n\t\tonRecording: function(milliseconds) {\r\n\t\t\tdocument.getElementById('duration').innerText = milliseconds + 'ms';\r\n\t\t}\r\n\t});\r\n\r\n\t\/\/ wire up the microphone button to toggle recording\r\n\tdocument.getElementById('record').onclick = recorder.toggleRecording;\r\n\t\r\n\t<\/script>\r\n<\/body>\r\n\r\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Toggle recording Duration: 0ms<\/p>\n","protected":false},"author":15,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v19.6.1 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>test-recording - \u5747\u4e00\u6559\u80b2\u5e73\u53f0<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/test-recording\/\" \/>\n<meta property=\"og:locale\" content=\"zh_TW\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"test-recording - \u5747\u4e00\u6559\u80b2\u5e73\u53f0\" \/>\n<meta property=\"og:description\" content=\"Toggle recording Duration: 0ms\" \/>\n<meta property=\"og:url\" content=\"https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/test-recording\/\" \/>\n<meta property=\"og:site_name\" content=\"\u5747\u4e00\u6559\u80b2\u5e73\u53f0\" \/>\n<meta property=\"article:modified_time\" content=\"2023-08-25T07:48:59+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"3 \u5206\u9418\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/test-recording\/\",\"url\":\"https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/test-recording\/\",\"name\":\"test-recording - \u5747\u4e00\u6559\u80b2\u5e73\u53f0\",\"isPartOf\":{\"@id\":\"https:\/\/www.junyiacademy.org\/event\/#website\"},\"datePublished\":\"2023-08-25T07:32:58+00:00\",\"dateModified\":\"2023-08-25T07:48:59+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/test-recording\/#breadcrumb\"},\"inLanguage\":\"zh-TW\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/test-recording\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/test-recording\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"test-recording\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.junyiacademy.org\/event\/#website\",\"url\":\"https:\/\/www.junyiacademy.org\/event\/\",\"name\":\"\u5747\u4e00\u6559\u80b2\u5e73\u53f0\",\"description\":\"\u8b93\u6bcf\u4e00\u4f4d\u5b69\u5b50\u90fd\u6709\u6a5f\u6703\u6210\u70ba\u7d42\u8eab\u5b78\u7fd2\u8005\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.junyiacademy.org\/event\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"zh-TW\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"test-recording - \u5747\u4e00\u6559\u80b2\u5e73\u53f0","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/test-recording\/","og_locale":"zh_TW","og_type":"article","og_title":"test-recording - \u5747\u4e00\u6559\u80b2\u5e73\u53f0","og_description":"Toggle recording Duration: 0ms","og_url":"https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/test-recording\/","og_site_name":"\u5747\u4e00\u6559\u80b2\u5e73\u53f0","article_modified_time":"2023-08-25T07:48:59+00:00","twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"3 \u5206\u9418"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/test-recording\/","url":"https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/test-recording\/","name":"test-recording - \u5747\u4e00\u6559\u80b2\u5e73\u53f0","isPartOf":{"@id":"https:\/\/www.junyiacademy.org\/event\/#website"},"datePublished":"2023-08-25T07:32:58+00:00","dateModified":"2023-08-25T07:48:59+00:00","breadcrumb":{"@id":"https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/test-recording\/#breadcrumb"},"inLanguage":"zh-TW","potentialAction":[{"@type":"ReadAction","target":["https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/test-recording\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/test-recording\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/edit-dot-gaewordpress-dot-junyiacademy.appspot.com\/"},{"@type":"ListItem","position":2,"name":"test-recording"}]},{"@type":"WebSite","@id":"https:\/\/www.junyiacademy.org\/event\/#website","url":"https:\/\/www.junyiacademy.org\/event\/","name":"\u5747\u4e00\u6559\u80b2\u5e73\u53f0","description":"\u8b93\u6bcf\u4e00\u4f4d\u5b69\u5b50\u90fd\u6709\u6a5f\u6703\u6210\u70ba\u7d42\u8eab\u5b78\u7fd2\u8005","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.junyiacademy.org\/event\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"zh-TW"}]}},"_links":{"self":[{"href":"https:\/\/www.junyiacademy.org\/event\/wp-json\/wp\/v2\/pages\/28127"}],"collection":[{"href":"https:\/\/www.junyiacademy.org\/event\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.junyiacademy.org\/event\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.junyiacademy.org\/event\/wp-json\/wp\/v2\/users\/15"}],"replies":[{"embeddable":true,"href":"https:\/\/www.junyiacademy.org\/event\/wp-json\/wp\/v2\/comments?post=28127"}],"version-history":[{"count":8,"href":"https:\/\/www.junyiacademy.org\/event\/wp-json\/wp\/v2\/pages\/28127\/revisions"}],"predecessor-version":[{"id":28137,"href":"https:\/\/www.junyiacademy.org\/event\/wp-json\/wp\/v2\/pages\/28127\/revisions\/28137"}],"wp:attachment":[{"href":"https:\/\/www.junyiacademy.org\/event\/wp-json\/wp\/v2\/media?parent=28127"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}