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

Add CEF audio rerouting to audio_data signal #31

Closed

Conversation

pixaline
Copy link

@pixaline pixaline commented May 6, 2023

Hello there,
I found myself wanting to route the audio produced by CEF in to Godot. I implemented some prototype code from skimming other open source projects. I intend this to be a starting point for something better, perhaps added comments, audio buffer, or a toggle to toggle it on or off. The code only plays mono audio for now.

You can use this in Godot with the following example code:

audioPlayer = AudioStreamPlayer3D.new()
audioPlayer.stream = AudioStreamGenerator.new()
audioPlayer.playing = true

var browser = cef.create_browser(...)
browser.connect("audio_data", self, "on_audio_data")

func on_audio_data(data : PoolVector2Array):
	var playback = audioPlayer.get_stream_playback()
	if playback.can_push_buffer(data.size()):
		playback.push_buffer(data)

@@ -458,6 +498,11 @@ class GDBrowserView : public godot::Node
godot::Ref<godot::Image> m_image;
godot::PoolByteArray m_data;

// Audio
godot::PoolVector2Array audioBuffer;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use "m_" (m_audioBuffer) for member variables.

@@ -458,6 +498,11 @@ class GDBrowserView : public godot::Node
godot::Ref<godot::Image> m_image;
godot::PoolByteArray m_data;

// Audio
godot::PoolVector2Array audioBuffer;
int audioChannelLayoutSize = -1;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cannot be size_t and set to 0 instead of int ?

@@ -434,6 +468,12 @@ class GDBrowserView : public godot::Node
CefRenderHandler::PaintElementType type,
const CefRenderHandler::RectList& dirtyRects,
const void* buffer, int width, int height);

void onAudioStart(CefRefPtr<CefBrowser> browser,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please indent to follow my coding style

@@ -434,6 +468,12 @@ class GDBrowserView : public godot::Node
CefRenderHandler::PaintElementType type,
const CefRenderHandler::RectList& dirtyRects,
const void* buffer, int width, int height);

void onAudioStart(CefRefPtr<CefBrowser> browser,
const CefAudioParameters& params, int channels);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cannot channels be size_t instead ?

@@ -176,6 +177,37 @@ void GDBrowserView::onPaint(CefRefPtr<CefBrowser> /*browser*/,
m_texture->create_from_image(m_image, godot::Texture::FLAG_VIDEO_SURFACE);
}


//------------------------------------------------------------------------------
/// Called on the audio stream thread when a PCM packet is received for the
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use //! \brief instead of /// and move this inside the header file on the associated method


audioBuffer.resize(frames);
for (int i = 0; i < frames; i++){
audioBuffer.set(i, godot::Vector2(data[0][i], data[0][i]));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure of Vector2(data[0][i], data[0][i]) ? Should not be Vector2(data[i][0], data[i][1]) instead ?

}

audioBuffer.resize(frames);
for (int i = 0; i < frames; i++){
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you break the line before { to follow my coding style ?

void GDBrowserView::onAudio(CefRefPtr<CefBrowser> browser,
const float** data, int frames, int64 pts)
{
if (data == nullptr || frames == 0 || audioChannelLayoutSize == -1) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer:
if ((data == nullptr) || (frames < 0) ||( audioChannelLayoutSize == 0))
because:

  • frames is int but used as unsigned
  • audioChannelLayoutSize shall not be 0 when no channel used ?

return;
}

audioBuffer.resize(frames);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

audioBuffer.resize(size_t(frames)); else the compiler probably complains of possible negative values

void GDBrowserView::onAudioStart(CefRefPtr<CefBrowser> browser,
const CefAudioParameters& params, int channels)
{
audioChannelLayoutSize = (int)params.channel_layout;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(int) is C cast, not C++, the compiler will complain.
Why not audioChannelLayoutSize = size_t(params.channel_layout);
and audioChannelLayoutSize seems not be used.

@Lecrapouille
Copy link
Owner

Lecrapouille commented May 7, 2023

@parulina Thank you for your pull request! I'm not sure to understand what is the utility of rerouting the sound (for doing signal processing ?), but why not ! I made reviews. Feel free to modify the 2D demo https://github.com/Lecrapouille/gdcef/tree/master/addons/gdcef/demos/2D by adding your example. The 2D demo is made for showing the most features of this module, like a sandbox.

PS: I give a try: I hear no sound :( and I have an error inside your callback when halting the demo2D. In addition, the rerouting pipeline should be set only if desired by the user when creating a browser var browser = $CEF.create_browser(url, name, S.x, S.y, {"sound_rerouting":true}), else let the default sound pipeline like currently made, this will avoid forcing people to add a Stream node a callback for something they have freely without any additional line of code :)

@@ -131,6 +132,11 @@ class GDBrowserView : public godot::Node
return this;
}

virtual CefRefPtr<CefAudioHandler> GetAudioHandler() override
{
return this;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code will force using your audio callback. I do not like that since your code is still experimental and this force to add a AudioStreamPlayer3D node in all projects. I would return this if and only if godot::Dictionary browser_settings has a field {"audio_rerouting", STATE_ENABLED} else return nullptrto use the default sound pipeline.

See:

// -------------------------------------------------------------------------
//! \brief Create a browser view and store its instance inside the internal
//! container. Return the browser identifier or return nullptr in case of failure.
//! \param[in] url the page link (by copy needed by Godot).
//! \param[in] name the browser name (by copy needed by Godot).
//! \param[in] w the width dimension of the document.
//! \param[in] h the height dimension of the document.
//! \param[in] Return the address of the newly created browser (or nullptr
//! in case of error).
//! \param[in] browser_settings dictionary of Browser config with default values:
//! - {"frame_rate", 30}
//! - {"javascript", STATE_ENABLED}
//! - {"javascript_close_windows", STATE_DISABLED}
//! - {"javascript_access_clipboard", STATE_DISABLED}
//! - {"javascript_dom_paste", STATE_DISABLED}
//! - {"image_loading", STATE_ENABLED}
//! - {"databases", STATE_ENABLED}
//! - {"webgl", STATE_ENABLED}
//! Wherer STATE_DISABLED / STATE_ENABLED == false / true
// -------------------------------------------------------------------------
GDBrowserView* createBrowser(godot::String const url, godot::String const name,
int w, int h, godot::Dictionary browser_settings);

@pixaline
Copy link
Author

pixaline commented May 7, 2023

Thank you for the detailed feedback, useful to know what parts exactly to improve. I agree about the optional parameter. I can get back with the added improvements in a bit and double check if the demo would work.

I'm not sure to understand what is the utility of rerouting the sound (for doing signal processing ?)

Similar to showing the CEF texture on a mesh in-game, this allows you to route the audio CEF produces to AudioStreamPlayer or AudioStreamPlayer3D, making the audio play in the game instead of from the subprocess, I give an example:
https://user-images.githubusercontent.com/6361957/236702464-669c51d6-ce41-45f0-a8da-660e548ad939.mp4

This is a game recording, normally this recording would exclude the played audio since CEF is a separate process. I route the CEF audio to my game's audio bus that also has spectrum analyzer, so that's how I can add visualizer at the bottom left as well as do subtle camera effects like screen shake or bloom depending on the lower frequencies like drums etc.

Routing the audio ingame also allows the user to change the volume in-game rather than having to adjust the volume of the subprocess (at least in Windows you have to right click audio device > volume mixer, find the cryptic "gdcefSubprocess.exe" with no icon, adjust its volume that it will also forget) making it more user friendly.

@Lecrapouille
Copy link
Owner

@pixaline Any news ?

@Lecrapouille Lecrapouille changed the base branch from master to dev-audio-routing January 28, 2024 15:40
@Lecrapouille
Copy link
Owner

@pixaline sorry I crapped the merged conflict, I do not how to use this poor Github UX. Commited manually

Lecrapouille added a commit that referenced this pull request Jan 28, 2024
Based on PR made by pixaline #31
Lecrapouille added a commit that referenced this pull request Jan 29, 2024
Update 2d with AudioStreamPlayer2D and 3d demos with AudioStreamPlayer3D
Lecrapouille added a commit that referenced this pull request Jan 29, 2024
Update 2d with AudioStreamPlayer2D and 3d demos with AudioStreamPlayer3D
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants