-
Notifications
You must be signed in to change notification settings - Fork 160
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
DRAFT: IAGSAudioPlayer plugin api #2256
base: master
Are you sure you want to change the base?
DRAFT: IAGSAudioPlayer plugin api #2256
Conversation
Although formally working, there are few things that I do not like in this approach. Primarily, my mistake is that engine implements this interface as a specialized wrapper around existing "player" (OpenALSource). This will also be in line with existing interfaces such as FontRenderer, which is used by the engine directly. |
Uhm, the FontRenderer is an external thing that renders text and the Engine gets to use but this AudioPlayer is an external thing that (possibly decodes audio and) puts the audio data for the Engine to play. So the FontRenderer is for you to implement a font renderer and the audio player is for you to implement an audio ??? - a decoder? What I mean is they look like different things. This interface is for you to implement a new in-game audio "source" - which in the example usage, comes from the video plugin. |
Not necessarily external, engine has two internal font renderers: WFNFontRenderer and TTFFontRenderer. Both implement IAGSFontRenderer interface, identical to the plugin's. This allows the engine to use a font renderer similarly regardless of what it is (with some small exceptions).
I mean that internal audio player (currently - OpenALSource) could potentially be also switched by something given by plugin. EDIT: |
This is more sort of what I meant, the OpenALSource is the audio player but the interface named AudioPlayer is not for it, but for someone implementing their own AudioClip+Decoder of sorts - you read the audio file from the video in the plugin, decode and push the results here. So when you compare with FontRenderer it's confusing because it's meant for you to implement your own FontRenderer.
I think we would have to break down this pieces in an issue, the only catch is in Audio we have the decoding and mixing separately, so we can today have these split, but if later these are replaced by SDL_mixer, which does both in the same package we would have to do some adjustments - although still could work if ones decoder fed pcm chunks or similar which I think SDL_mixer supports, so perhaps this point is not an issue at all. |
I'm not sure, but maybe the misunderstanding is because I imply also hypothetical replacement of these parts inside the engine. If somebody implements their own IAudioPlayer, then in order to use it inside the engine, then engine would also need to use this interface IAudioPlayer, and not OpenALSource directly. Same of decoder, if engine would need to use a decoder implemented from an interface by someone else, they would need to be using that interface, and not class SDLDecoder directly. In other words, instead of using a chain SDLDecoder -> OpenALSource, like it does now, engine would need to have a IAudioDecoder -> IAudioPlayer chain (or, going further, IAudioDecoder -> IAudioFilter -> IAudioPlayer, or similar), where IAudioDecoder is SDLDecoder by default,and IAudioPlayer is OpenALSource by default.
Engine uses IAGSFontRenderer interface, which may have either internal (WFN, TTF) implementation, or a plugin's implementation. |
I'd like to repost my notes on a problem of "disposing" the objects involved in plugin api, which initially were posted in the comments to already closed stream api PR: #2227 (comment) Specifics of dynamically loaded plugin is that object allocated in plugin must be deallocated in plugin as well, same goes for the object allocated in the engine (it must be deallocated in the engine). The reason for this is that in theory engine and plugin may be compiled using different compilers, and even have different standard lib linked to them. This is why these objects cannot be deleted with "delete" outside of the module that created them. While "create" function is always a part of the factory interface, there are two approaches to "destroy" function:
Having "destroy" function in an object's interface has a advantage of easy access, because all you need is to have the object pointer itself. But if we want to pass internal engine's objects out to plugins, then we also would have to implement this "destroy" method in them. In other words, our own engine class should have a method that deletes object itself. This may be contradicting the class design, and not look pretty. This may also be not safe if the object of such class can be created as a local variable on stack, because calling that function then would corrupt the stack. Possible solutions here are:
In any case, it's important to note that whenever we have a place where the object may come both from engine and plugin, we must use the interface pointer and not any specific implementation. In such case also we must dispose such object using "destroy" method, and not Having "destroy" function in factory interface, besides the "create" function. This is easy at first, but if the object interface may be implemented by plugins too, there appear a new issue: you cannot tell which "destroy" function to use, as the object's interface itself has no means to tell this. It means that we have to somehow pair object's interface with a pointer to its "destroy" function anyway. This may be done by passing them together in any api function that accepts this interface. Either as 2 arguments, or grouped into a struct. To give an example of such struct:
So when any disposable object is passed from plugin to engine, it's done using this struct, for example:
The struct itself is not owned, and should not be deleted by the engine, of course. We simply take values out from it. |
Our setting is done wrong, because a) it's constants regardless of situation, b) it counts only number of buffers instead of total length (or size). First of all, buffers may be very small, and then if OpenAlSource is used by the system that does not care about it not being able to queue, then the playback will be skipping chunks. The low queue limit was necessary mostly to workaround effects lag, such as setting speed. This may be resolved differently.
229fb4d
to
c14efb3
Compare
Adds a IAGSAudioPlayer api for plugins, which wraps our OpenALSource object, and lets render any arbitrary sound data using engine's audio output.
This is immediately for the sake of the "Sprite Video" plugin, which cannot play sounds at the moment, having no audio output of its own. But of course this may have any other potential future uses, such as playing custom sounds from plugins.
I referred to the conversation in #1981 when organizing api.
The practical use of this may be found in this experimental branch of the "Sprite Video" plugin:
https://github.com/ivan-mogilko/ags-spritevideo/tree/experiment--agsaudioplayer
New API:
and...