-
Notifications
You must be signed in to change notification settings - Fork 192
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
Fix: Implemented STOP button behavior to toggle audio playback #353
base: main
Are you sure you want to change the base?
Fix: Implemented STOP button behavior to toggle audio playback #353
Conversation
…nfo.json, index.njk, and main.js files for various audio worklet demos. Also, updated styles.css.
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
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.
We appreciate your excellent contribution! You scoped the work and created a meaningful PR in a good shape.
My suggestion is more of a directional change for the PR, but I believe it is necessary. Please take a look and let me know if you have more questions.
@@ -3,24 +3,54 @@ | |||
// found in the LICENSE file. | |||
|
|||
const audioContext = new AudioContext(); | |||
let oscillator; |
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.
To make it clear, how about oscillatorNode
?
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.
Thank you for the suggestion. I've updated the variable name to oscillatorNode for better clarity. Please let me know if you have any further suggestions.
buttonEl.addEventListener('click', async () => { | ||
const waveformType = | ||
if (!oscillatorProcessor) { // If audio is not playing, start audio | ||
const waveformType = | ||
document.querySelector('#demo-select-waveform-type').value; |
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.
nit: 4-space indent
@@ -3,24 +3,54 @@ | |||
// found in the LICENSE file. | |||
|
|||
const audioContext = new AudioContext(); | |||
let oscillator; | |||
let isPlaying = false; | |||
|
|||
const startAudio = async (context) => { | |||
await context.audioWorklet.addModule('bypass-processor.js'); |
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.
Ditto here; calling addModule
every call of startAudio
is not recommended.
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.
Done the requested changes as per the feedback.
const bypasser = new AudioWorkletNode(context, 'bypass-processor'); | ||
oscillator.connect(bypasser).connect(context.destination); | ||
oscillator.start(); | ||
}; | ||
|
||
const stopAudio = () => { |
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.
We should leave the rest and simply suspend the context.
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.
Thanks for the suggestion. I've modified the stopAudio() function to suspend the audio context instead of stopping and disconnecting the oscillator node. Please review the changes and let me know if they meet the requirements.
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.
This also has the same problem with src/audio-worklet/basic/audio-worklet-node-options/main.js
above. See my comments for the explanation.
@@ -3,21 +3,22 @@ | |||
// found in the LICENSE file. | |||
|
|||
const audioContext = new AudioContext(); | |||
let oscillator; | |||
let oscillatorNode; |
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.
Changed the name to oscillatorNode
Any update on the PR? |
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.
Thanks for updating the PR, but I think we have a design problem to solve:
addModule()
can only be called once throughout the document lifecycle.
My recommendation is to leave the most of the code intact, and use suspend
and resume
for starting/stopping the AudioContext.
audioContext.resume(); | ||
buttonEl.disabled = true; | ||
buttonEl.textContent = 'Playing...'; | ||
if (!oscillatorProcessor) { // If audio is not playing, start audio |
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.
nit: Let's move the comment below the if()
.
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.
Done.
await context.audioWorklet.addModule('oscillator-processor.js'); | ||
const oscillatorProcessor = | ||
if (!oscillatorProcessor) { | ||
await context.audioWorklet.addModule('oscillator-processor.js'); |
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.
This can't be called multiple times in any scenario. I think you'll get an error if you do this. Have you checked?
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.
Yes, it can't be called multiple times, so I have modified it. No, I didn't get any error though!
await startAudio(audioContext, { waveformType, frequency }); | ||
audioContext.resume(); | ||
buttonEl.textContent = 'STOP'; | ||
} else { // If audio is playing, stop audio |
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.
nit: Ditto on comments.
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.
done
const bypasser = new AudioWorkletNode(context, 'bypass-processor'); | ||
oscillator.connect(bypasser).connect(context.destination); | ||
oscillator.start(); | ||
}; | ||
|
||
const stopAudio = () => { |
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.
This also has the same problem with src/audio-worklet/basic/audio-worklet-node-options/main.js
above. See my comments for the explanation.
buttonEl.disabled = true; | ||
buttonEl.textContent = 'Playing...'; | ||
}, false); | ||
if (!isPlaying) { // If not playing, start audio |
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.
nit: Google's JS style doesn't use this type of inline comments.
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.
moved the comments to next line.
Hi @hoch! I have made the required changes, let me know if they commit is now goog and ready to be merged. |
Hi @hoch Any update on this PR? I know that I created this issue under the GSoC tag, but please don't close this PR as it is great improvement in the web audio samples for chrome labs. Thanks |
Of course. We'll continue to work on this. The team is currently a bit behind schedule, so reviewing this PR will take some time. Thanks for your patience! |
Hi @hoch Any update on this PR? |
Is this PR still ongoing? |
Yes. We will review the PR soon. Thanks for your patience. |
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.
@tarunsinghofficial Thanks for your patience, and we appreciate your contribution. The PR still seems to have outstanding issues, and it needs more review and local testing.
}, false); | ||
if (!oscillatorNode) { | ||
await startAudio(audioContext); | ||
audioContext.resume(); |
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.
Once oscillatorNode
is valid, resume()
doesn't get called from anywhere. How does this recover from suspended status?
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.
I modified the button click event handler to check if the AudioContext is suspended and call resume() accordingly.
}, false); | ||
if (!isPlaying) { | ||
// If not playing, start the audio. | ||
await startAudio(audioContext); |
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.
This will call L10 (addModule) multiple times depending on isPlaying
. Have you tested locally? Loading the same script twice should throw an exception.
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.
I have updated the code to ensure that the module is added only once by using a flag (moduleAdded) to track whether the module has already been loaded.
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.
I believe this example is pretty much the same with the other oscillator example. Why do we need a different flag (moduleAdded
) only for this example?
buttonEl.textContent = 'Playing...'; | ||
}, false); | ||
if (!isPlaying) { | ||
await startAudio(audioContext); |
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.
Ditto here.
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.
added the flag check here same as above.
buttonEl.disabled = true; | ||
buttonEl.textContent = 'Playing...'; | ||
if (!mediaStream) { // If audio is not playing, start audio | ||
await startAudio(audioContext, meterEl); |
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.
Same here.
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.
I made the requested changes here:
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.
added the flag check here too.
…let, one-pole-filter, volume-meter, noise-generator
Hi @hoch, I hope you're doing well! I have made all the necessary changes, and tested them locally, and as far as I know, it is ready to be merged to the main branch since all the requested changes have been completed. |
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.
Apologies for the slow response, but I think this PR needs more work. Overall, many examples have a same structure, so perhaps it's better to break down the PR into smaller ones and focus on getting a small thing done first?
isPlaying = false; | ||
buttonEl.textContent = 'START'; | ||
buttonEl.classList.add('start-button'); | ||
} |
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.
Similarly to the previous comment, the logic between L24~L40 is slightly confusing.
I think it's possible to split the check of oscillatorNode
out of this if/else block, so the block only handles the context state.
await audioContext.resume(); | ||
isPlaying = true; | ||
buttonEl.textContent = 'Playing...'; | ||
buttonEl.classList.remove('start-button'); |
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.
L26-L29 are exactly same with L31-L34. Perhaps this block can be simplified further?
|
||
const startAudio = async (context) => { | ||
await context.audioWorklet.addModule('noise-generator.js'); | ||
|
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.
Please remove this empty line.
}, false); | ||
if (!isPlaying) { | ||
// If not playing, start the audio. | ||
await startAudio(audioContext); |
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.
I believe this example is pretty much the same with the other oscillator example. Why do we need a different flag (moduleAdded
) only for this example?
The web audio samples lack a stop button functionality, making it difficult for users to pause or stop audio playback once it has started. This limitation hinders the user experience and can be frustrating for individuals testing the demos.
Fix: This PR addresses the issue by implementing a stop button feature in the web audio samples.
Changes Made
build_info.json
to reflect changes in the project.index.njk
andmain.js
files for several audio worklet demos:audio-worklet-node-options
: Updated options handling for audio worklet nodes.hello-audio-worklet
: Improved initialization and error handling.noise-generator
: Enhanced noise generation functionality.one-pole-filter
: Refinements to the one-pole filter algorithm.volume-meter
: Improved volume meter visualization and accuracy.styles.css
for better styling and consistency across demos.Purpose
The purpose of this PR is to ensure that the web audio samples provide a smooth and engaging experience for users. By addressing reported bugs and implementing enhancements, we aim to make the demos more useful and accessible to developers interested in web audio technologies.
Screenshots
4-4f8b-4650-bbdf-f652b23fcba0)