Skip to content
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 record button and playback button in sampler widget #3813

Merged
merged 9 commits into from
Sep 14, 2024
5 changes: 5 additions & 0 deletions css/activities.css
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,11 @@ table {
cursor: not-allowed;
}

#playbackBtn.disabled {
opacity: 0.5;
cursor: not-allowed;
}

#right-arrow,
#left-arrow {
position: absolute;
Expand Down
5 changes: 5 additions & 0 deletions header-icons/mic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions header-icons/playback.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions header-icons/record.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
135 changes: 129 additions & 6 deletions js/widgets/sampler.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,35 @@ function SampleWidget() {
this.octaveCenter = parseInt(o);
};


/**
* Gets the length of the sample and displays a warning if it exceeds 1MB.
* @returns {void}
*/
this.getSampleLength = function () {
if (this.sampleData.length > 1333333) {
this.activity.errorMsg(_("Warning: Sample is bigger than 1MB."), this.timbreBlock);
}
};


/**
* Displays a message indicating that recording has started.
* @returns {void}
*/
function displayRecordingStartMessage() {
this.activity.textMsg(_("Recording started..."));
}

/**
* Displays a message indicating that recording has stopped.
* @returns {void}
*/
function displayRecordingStopMessage() {
this.activity.textMsg(_("Recording complete..."));
}


/**
* Displays an error message when the uploaded sample is not a .wav file.
* @returns {void}
Expand All @@ -236,16 +265,18 @@ function SampleWidget() {
that._addSample();

var newStack = [
[0,"settimbre",100,100,[null,1,null,5]],
[
0,
1,
["customsample", { value: [that.sampleName, that.sampleData, "do", 4] }],
100,
100,
[null, 1, 2, 3]
[0, 2, 3, 4]
],
[1, ["audiofile", { value: [that.sampleName, that.sampleData] }], 0, 0, [0]],
[2, ["solfege", { value: that.samplePitch }], 0, 0, [0]],
[3, ["number", { value: that.sampleOctave }], 0, 0, [0]]
[2, ["audiofile", { value: [that.sampleName, that.sampleData] }], 0, 0, [1]],
[3, ["solfege", { value: that.samplePitch }], 0, 0, [1]],
[4, ["number", { value: that.sampleOctave }], 0, 0, [1]],
[5,"hidden",0,0,[0,null]]
];

that.activity.blocks.loadNewBlocks(newStack);
Expand Down Expand Up @@ -336,8 +367,8 @@ function SampleWidget() {
} else {
if (!(this.sampleName == "")) {
this.resume();
this._playReferencePitch();
}
this._playReferencePitch();
}
};

Expand Down Expand Up @@ -411,6 +442,98 @@ function SampleWidget() {
}
};

this._recordBtn= widgetWindow.addButton(
"mic.svg",
ICONSIZE,
_("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.classList.add("disabled");


const togglePlaybackButtonState = () => {
if (!audioURL) {
this._playbackBtn.classList.add("disabled");
} else {
this._playbackBtn.classList.remove("disabled");
}
};

this._playbackBtn.onclick = () => {
playAudio();
};

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/webm-to-wav-converter@1.1.0/+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;

Expand Down
Loading