diff --git a/src/Recorder.js b/src/Recorder.js new file mode 100644 index 0000000..1991bb6 --- /dev/null +++ b/src/Recorder.js @@ -0,0 +1,127 @@ +import { Context as ToneContext } from "tone/build/esm/core/context/Context.js"; +import { Recorder as ToneRecorder } from "tone/build/esm/component/channel/Recorder.js"; + +/** + * A Recorder effect to capture audio input and save as a file. + * @class Recorder + * @constructor + * @example +*
+ * + * let osc, recorder; + * let recording = false; + * let recordStartTime = 0; + * let recordDuration = 3000; // in ms + * + * function setup() { + * let cnv = createCanvas(200, 100); + * textAlign(CENTER, CENTER); + * textSize(16); + * + * osc = new p5.Oscillator('sine'); + * osc.amp(0.5); + * + * recorder = new p5.Recorder(); + * osc.connect(recorder); + * + * cnv.mousePressed(startRecording); + * describe('Click to record a 3-second sine wave and download it.'); + * } + * + * async function startRecording() { + * if (recording) return; + * recording = true; + * recordStartTime = millis(); + * + * recorder.start(); + * osc.start(); + * + * setTimeout(async () => { + * let audioBlob = await recorder.stop(); + * recorder.download(audioBlob, 'recording.webm'); + * osc.stop(); + * recording = false; + * }, recordDuration); + * } + * + * function draw() { + * background(220); + * + * if (recording) { + * let remaining = Math.ceil((recordDuration - (millis() - recordStartTime)) / 1000); + * remaining = max(0, remaining); + * text(`Recording: ${remaining}`, width / 2, height / 2); + * } else { + * text('Click to record', width / 2, height / 2); + * } + * } + * + *
+ */ +class Recorder { + constructor(mimeType = 'audio/webm'){ + if (!MediaRecorder.isTypeSupported(mimeType)) { + throw new Error(`MIME type "${mimeType}" is not supported by this browser.`); + } + this.recorder = new ToneRecorder({mimeType}); + } + + /** + * Start recording audio. + * @method start + */ + start() { + this.recorder.start(); + } + + /** + * Stop recording and return the audio Blob. + * @method stop + * @return {Promise} A Promise that resolves to the recorded audio Blob. + */ + async stop() { + return await this.recorder.stop(); + } + + /** + * Download the recorded audio as a file. + * @method download + * @param {Blob} recording The recorded audio Blob. + * @param {String} filename The name of the downloaded file. + */ + download(recording, filename = "recording.webm") { + const url = URL.createObjectURL(recording); + const anchor = document.createElement("a"); + anchor.download = filename; + anchor.href = url; + anchor.click(); + } + + /** + * Get the underlying node for connecting inputs. + * @method getNode + * @return {Object} The recorder node. + */ + getNode() { + return this.recorder; + } + + /** + * Connect an input source to the recorder. + * @method connect + * @param {Object} source A p5.sound source (Oscillator, Soundfile, etc.). + */ + connect(source) { + if (typeof source.getNode === "function") { + source.getNode().connect(this.recorder); + } else { + source.connect(this.recorder); + } + } + + disconnect() { + this.recorder.disconnect(ToneContext.destination); + } +} + +export default Recorder; diff --git a/src/app.js b/src/app.js index e49dc4f..4f3f117 100644 --- a/src/app.js +++ b/src/app.js @@ -58,5 +58,5 @@ p5.prototype.loadSound = loadSound; import AudioIn from './AudioIn'; p5.AudioIn = AudioIn; -//import Recorder from './Recorder'; -//p5.prototype.Recorder = Recorder; \ No newline at end of file +import Recorder from './Recorder'; +p5.Recorder = Recorder; \ No newline at end of file