diff --git a/src/board/audio/index.ts b/src/board/audio/index.ts index 9bb7f5f9..9aae3089 100644 --- a/src/board/audio/index.ts +++ b/src/board/audio/index.ts @@ -13,7 +13,7 @@ interface AudioOptions { speechAudioCallback: () => void; } -export class Audio { +export class BoardAudio { private frequency: number = 440; // You can mute the sim before it's running so we can't immediately write to the muteNode. private muted: boolean = false; @@ -66,6 +66,46 @@ export class Audio { ); } + async record() { + let mediaRecorder: MediaRecorder | undefined; + if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { + try { + const stream = await navigator.mediaDevices.getUserMedia({ video: false, audio: true }); + mediaRecorder = new MediaRecorder(stream); + mediaRecorder.start(); + + setTimeout(() => { + if (mediaRecorder) { + mediaRecorder.stop(); + } + }, 5000) + + const chunks:Blob[] = [] + mediaRecorder.ondataavailable = (e: BlobEvent) => { + chunks.push(e.data); + } + + mediaRecorder.onstop = async () => { + if (chunks.length > 0) { + const recordingType = 'audio/wav; codecs=MS_PCM' + const blob = new Blob(chunks, { type: recordingType }); + const audioURL = window.URL.createObjectURL(blob); + const recording = new Audio(audioURL); + await recording.play() + } + } + + } catch (error) { + console.log("An error occurred, could not get microphone access"); + if (mediaRecorder) { + mediaRecorder.stop(); + } + } + } else { + console.log("getUserMedia not supported on your browser!"); + } + } + async createAudioContextFromUserInteraction(): Promise { this.context = this.context ?? diff --git a/src/board/index.ts b/src/board/index.ts index 8a736413..b416279b 100644 --- a/src/board/index.ts +++ b/src/board/index.ts @@ -1,6 +1,6 @@ import svgText from "../microbit-drawing.svg"; import { Accelerometer } from "./accelerometer"; -import { Audio } from "./audio"; +import { BoardAudio } from "./audio"; import { Button } from "./buttons"; import { Compass } from "./compass"; import { @@ -92,7 +92,7 @@ export class Board { display: Display; buttons: Button[]; pins: Pin[]; - audio: Audio; + audio: BoardAudio; temperature: RangeSensor; microphone: Microphone; accelerometer: Accelerometer; @@ -202,7 +202,7 @@ export class Board { this.pins[MICROBIT_HAL_PIN_P19] = new StubPin("pin19"); this.pins[MICROBIT_HAL_PIN_P20] = new StubPin("pin20"); - this.audio = new Audio(); + this.audio = new BoardAudio(); this.temperature = new RangeSensor("temperature", -5, 50, 21, "°C"); this.accelerometer = new Accelerometer(onChange); this.compass = new Compass(); @@ -513,6 +513,10 @@ export class Board { return this.runningPromise; } + async record(): Promise { + await this.audio.record() + } + /** * An external reset. */ @@ -782,6 +786,10 @@ export const createMessageListener = (board: Board) => (e: MessageEvent) => { board.stop(); break; } + case "record": { + board.record(); + break; + } case "reset": { board.reset(); break; diff --git a/src/demo.html b/src/demo.html index 75036482..4901e1d9 100644 --- a/src/demo.html +++ b/src/demo.html @@ -115,6 +115,7 @@

MicroPython-micro:bit simulator example embedding

+
@@ -241,6 +242,15 @@

MicroPython-micro:bit simulator example embedding

); }); + document.querySelector("#record").addEventListener("click", async () => { + simulator.postMessage( + { + kind: "record", + }, + "*" + ); + }); + document.querySelector("#reset").addEventListener("click", async () => { simulator.postMessage( {