-
Notifications
You must be signed in to change notification settings - Fork 936
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add live waveform visualization to sampler #4040
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -233,15 +233,15 @@ function SampleWidget() { | |
* Displays a message indicating that recording has started. | ||
* @returns {void} | ||
*/ | ||
function displayRecordingStartMessage() { | ||
this.displayRecordingStartMessage = function () { | ||
this.activity.textMsg(_("Recording started...")); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why ellipsis (...)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removed them |
||
} | ||
|
||
/** | ||
* Displays a message indicating that recording has stopped. | ||
* @returns {void} | ||
*/ | ||
function displayRecordingStopMessage() { | ||
this.displayRecordingStopMessage = function () { | ||
this.activity.textMsg(_("Recording complete...")); | ||
} | ||
|
||
|
@@ -448,92 +448,44 @@ function SampleWidget() { | |
_("Toggle Mic"), | ||
"" | ||
); | ||
this._recordBtn.onclick = function() { | ||
ToggleMic(this); | ||
}.bind(this._recordBtn); | ||
|
||
|
||
this._playbackBtn= widgetWindow.addButton( | ||
"playback.svg", | ||
ICONSIZE, | ||
_("Playback"), | ||
""); | ||
this._playbackBtn.id="playbackBtn" | ||
|
||
this._playbackBtn.id="playbackBtn"; | ||
this._playbackBtn.classList.add("disabled"); | ||
|
||
this.is_recording = false; | ||
|
||
this._recordBtn.onclick = async () => { | ||
if (!this.is_recording) { | ||
await this.activity.logo.synth.startRecording(); | ||
this.is_recording = true; | ||
this._recordBtn.getElementsByTagName('img')[0].src = "header-icons/record.svg"; | ||
this.displayRecordingStartMessage(); | ||
this.activity.logo.synth.LiveWaveForm(); | ||
|
||
const togglePlaybackButtonState = () => { | ||
if (!audioURL) { | ||
this._playbackBtn.classList.add("disabled"); | ||
// Display Live Waveform | ||
} else { | ||
this.recordingURL = await this.activity.logo.synth.stopRecording(); | ||
this.is_recording = false; | ||
this._recordBtn.getElementsByTagName('img')[0].src = "header-icons/mic.svg"; | ||
this.displayRecordingStopMessage(); | ||
this._playbackBtn.classList.remove("disabled"); | ||
} | ||
}; | ||
|
||
this._playbackBtn.onclick = () => { | ||
playAudio(); | ||
this.sampleData = this.recordingURL; | ||
this.sampleName = `Recorded Audio ${this.recordingURL}`; | ||
this._addSample(); | ||
this.activity.logo.synth.playRecording(); | ||
// Display Recorded Waveform | ||
}; | ||
|
||
let can_record = false; | ||
let is_recording = false; | ||
let recorder = null; | ||
let chunks = []; | ||
let audioURL = null; | ||
|
||
async function setUpAudio() { | ||
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { | ||
try { | ||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); | ||
recorder = new MediaRecorder(stream); | ||
recorder.ondataavailable = e => { | ||
chunks.push(e.data); | ||
}; | ||
recorder.onstop = async e => { | ||
let blob = new Blob(chunks, { type: 'audio/webm' }); | ||
chunks = []; | ||
audioURL = URL.createObjectURL(blob); | ||
displayRecordingStopMessage.call(that); | ||
togglePlaybackButtonState(); | ||
|
||
const module = await import("https://cdn.jsdelivr.net/npm/[email protected]/+esm"); | ||
const getWaveBlob = module.getWaveBlob; | ||
const wavBlob = await getWaveBlob(blob); | ||
const wavAudioURL = URL.createObjectURL(wavBlob); | ||
that.sampleData = wavAudioURL; | ||
that.sampleName = `Recorded Audio ${audioURL}`; | ||
that._addSample(); | ||
}; | ||
can_record = true; | ||
} catch (err) { | ||
console.log("The following error occurred: " + err); | ||
} | ||
} | ||
} | ||
function ToggleMic(buttonElement) { | ||
if (!can_record) return; | ||
|
||
is_recording = !is_recording; | ||
if (is_recording) { | ||
recorder.start(); | ||
buttonElement.getElementsByTagName('img')[0].src = "header-icons/record.svg"; | ||
displayRecordingStartMessage.call(that); | ||
} else { | ||
recorder.stop(); | ||
buttonElement.getElementsByTagName('img')[0].src = "header-icons/mic.svg"; | ||
} | ||
} | ||
|
||
function playAudio() { | ||
if (audioURL) { | ||
const audio = new Audio(audioURL); | ||
audio.play(); | ||
console.log("Playing audio."); | ||
} else { | ||
console.error("No recorded audio available."); | ||
} | ||
} | ||
|
||
setUpAudio(); | ||
|
||
widgetWindow.sendToCenter(); | ||
this.widgetWindow = widgetWindow; | ||
|
||
|
@@ -962,13 +914,13 @@ function SampleWidget() { | |
|
||
const draw = () => { | ||
this.drawVisualIDs[turtleIdx] = requestAnimationFrame(draw); | ||
if (this.pitchAnalysers[turtleIdx] && (this.running || resized)) { | ||
if (this.is_recording || (this.pitchAnalysers[turtleIdx] && (this.running || resized))) { | ||
canvasCtx.fillStyle = "#FFFFFF"; | ||
canvasCtx.font = "10px Verdana"; | ||
this.verticalOffset = -canvas.height / 4; | ||
this.zoomFactor = 40.0; | ||
canvasCtx.fillRect(0, 0, width, height); | ||
|
||
let oscText; | ||
if (turtleIdx >= 0) { | ||
//.TRANS: The sound sample that the user uploads. | ||
|
@@ -978,18 +930,27 @@ function SampleWidget() { | |
//.TRANS: The reference tone is a sound used for comparison. | ||
canvasCtx.fillText(_("reference tone"), 10, 10); | ||
canvasCtx.fillText(oscText, 10, canvas.height / 2 + 10); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please, no spaces on otherwise empty lines. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removed extra spaces |
||
for (let turtleIdx = 0; turtleIdx < 2; turtleIdx += 1) { | ||
const dataArray = this.pitchAnalysers[turtleIdx].getValue(); | ||
let dataArray; | ||
if (this.is_recording) { | ||
dataArray = turtleIdx === 0 | ||
? this.pitchAnalysers[0].getValue() | ||
: this.activity.logo.synth.getValues(); | ||
console.log(dataArray); | ||
} else { | ||
dataArray = this.pitchAnalysers[turtleIdx].getValue(); | ||
} | ||
|
||
const bufferLength = dataArray.length; | ||
const rbga = SAMPLEOSCCOLORS[turtleIdx]; | ||
const sliceWidth = (width * this.zoomFactor) / bufferLength; | ||
canvasCtx.lineWidth = 2; | ||
canvasCtx.strokeStyle = rbga; | ||
canvasCtx.beginPath(); | ||
|
||
let x = 0; | ||
|
||
for (let i = 0; i < bufferLength; i++) { | ||
const y = (height / 2) * (1 - dataArray[i]) + this.verticalOffset; | ||
if (i === 0) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe rename this function to reflect that it is getting values from the analyser?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated