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

DRAFT: IAGSAudioPlayer plugin api #2256

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Engine/media/audio/audio_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ AudioCoreSlot::AudioCoreSlot(int handle, std::unique_ptr<SDLDecoder> decoder)
: handle_(handle), _decoder(std::move(decoder))
{
_source = std::make_unique<OpenAlSource>(
_decoder->GetFormat(), _decoder->GetChannels(), _decoder->GetFreq());
_decoder->GetFormat(), _decoder->GetChannels(), _decoder->GetFreq(),
2 /* TODO: make this a constant,
and add comment explaining why we keep this queue limit low for now */);
}

void AudioCoreSlot::Init()
Expand Down
7 changes: 4 additions & 3 deletions Engine/media/audio/openalsource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,13 @@ static struct
// OpenAlSource
//-----------------------------------------------------------------------------

OpenAlSource::OpenAlSource(SDL_AudioFormat format, int channels, int freq)
OpenAlSource::OpenAlSource(SDL_AudioFormat format, int channels, int freq, uint32_t max_queue)
{
_inputFmt.format = format;
_inputFmt.channels = static_cast<Uint8>(channels);
_inputFmt.rate = freq;
_alFormat = OpenAlFormatFromSDLFormat(_inputFmt, _recvFmt);;
_alFormat = OpenAlFormatFromSDLFormat(_inputFmt, _recvFmt);
_maxQueue = max_queue;
alGenSources(1, &_source);
dump_al_errors();
_resampler.Setup(_inputFmt, _recvFmt);
Expand Down Expand Up @@ -156,7 +157,7 @@ size_t OpenAlSource::PutData(const SoundBuffer &data)
{
Unqueue();
// If queue is full, bail out
if (_queued >= MaxQueue) { return 0u; }
if (_queued >= _maxQueue) { return 0u; }
// Input buffer is empty?
if (!data.Data || (data.Size == 0)) { return 0u; }
// Check for free buffers, generate more if necessary
Expand Down
13 changes: 8 additions & 5 deletions Engine/media/audio/openalsource.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,9 @@ namespace Engine
class OpenAlSource
{
public:
// Max sound buffers to queue before/during processing
static const ALuint MaxQueue = 2;

// Initializes Al source for the given format; if there's no direct format equivalent
// found, setups a resampler.
OpenAlSource(SDL_AudioFormat format, int channels, int freq);
OpenAlSource(SDL_AudioFormat format, int channels, int freq, uint32_t max_queue = 1024u);
OpenAlSource(OpenAlSource&& src);
~OpenAlSource();

Expand Down Expand Up @@ -87,7 +84,13 @@ class OpenAlSource
PlaybackState _playState = PlayStateInitial;
float _speed = 1.f; // change in playback rate
float _predictTs = 0.f; // next timestamp prediction
unsigned _queued = 0u;
// Max sound buffers to queue during processing.
// TODO: replace limit of buffer count with limit of memory and/or duration.
// TODO: add a comment explaining why do we have to set very small queue limit
// for regular audio slot play (to decrease custom effects lag),
// alternatively have this logic externally to OpenALSource, somewhere in AudioCoreSlot.
uint32_t _maxQueue = 1024u;
uint32_t _queued = 0u;

// SDL resampler state, in case dynamic resampling in necessary
SDLResampler _resampler;
Expand Down
5 changes: 3 additions & 2 deletions Engine/media/video/video.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@
#include "gfx/ddb.h"
#include "gfx/graphicsdriver.h"
#include "main/game_run.h"
#include "util/stream.h"
#include "media/audio/audio_system.h"
#include "media/audio/openal.h"
#include "util/memory_compat.h"
#include "util/stream.h"

using namespace AGS::Common;
using namespace AGS::Engine;
Expand Down Expand Up @@ -73,7 +74,7 @@ HError VideoPlayer::Open(const String &name, int flags)
{
if ((_audioFormat > 0) && (_audioChannels > 0) && (_audioFreq > 0))
{
_audioOut.reset(new OpenAlSource(_audioFormat, _audioChannels, _audioFreq));
_audioOut = std::make_unique<OpenAlSource>(_audioFormat, _audioChannels, _audioFreq);
_audioOut->Play();
_wantAudio = true;
}
Expand Down
59 changes: 58 additions & 1 deletion Engine/plugin/agsplugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,14 @@
#include "main/engine.h"
#include "main/game_run.h"
#include "media/audio/audio_system.h"
#include "media/audio/openalsource.h"
#include "media/audio/sdldecoder.h"
#include "script/script.h"
#include "script/script_runtime.h"
#include "plugin/plugin_engine.h"
#include "plugin/plugin_builtin.h"
#include "util/library.h"
#include "util/memory_compat.h"
#include "util/string.h"
#include "util/wgt2allg.h"

Expand Down Expand Up @@ -97,7 +100,7 @@ extern RuntimeScriptValue GlobalReturnValue;
// **************** PLUGIN IMPLEMENTATION ****************


const int PLUGIN_API_VERSION = 28;
const int PLUGIN_API_VERSION = 29;
struct EnginePlugin
{
EnginePlugin() {
Expand Down Expand Up @@ -827,6 +830,60 @@ ::IAGSStream *IAGSEngine::GetFileStreamByHandle(int32 fhandle)
get_file_stream(fhandle, "IAGSEngine::GetFileStreamByHandle"));
}

class AGSAudioPlayer : public IAGSAudioPlayer
{
public:
AGSAudioPlayer(std::unique_ptr<OpenAlSource> &&src)
: _audioOut(std::move(src))
{
_audioOut->Play();
}
virtual ~AGSAudioPlayer() = default;

// Tells which version of the plugin API this object corresponds to;
// this lets users know which of the following methods are valid to use.
int GetVersion() override
{
return PLUGIN_API_VERSION;
}
void SetConfig(AGSAudioPlayConfig *config) override
{
_audioOut->SetVolume(config->Volume);
}
void Close() override
{
delete this; // darn...
}
void Pause() override
{
_audioOut->Pause();
}
void Resume() override
{
_audioOut->Resume();
}
size_t PutData(AGSAudioFrame *frame) override
{
SoundBuffer buf(frame->Data, frame->DataSize, frame->Timestamp);
size_t put_sz = _audioOut->PutData(buf);
_audioOut->Poll();
return put_sz;
}

private:
std::unique_ptr<OpenAlSource> _audioOut;
};

IAGSAudioPlayer *IAGSEngine::OpenAudioPlayer(AGSAudioFormat *in_format, AGSAudioPlayConfig *config)
{
SDL_AudioFormat sdl_fmt = in_format->Format; // todo convert
int chans = in_format->Channels;
int freq = in_format->Freq;
auto *player = new AGSAudioPlayer(
std::make_unique<OpenAlSource>(sdl_fmt, chans, freq));
return player; // todo: should save somewhere for safe close on exit??
}

// *********** General plugin implementation **********

void pl_stop_plugins() {
Expand Down
69 changes: 64 additions & 5 deletions Engine/plugin/agsplugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,15 +229,15 @@ class IAGSScriptManagedObject {
virtual int Serialize(void *address, char *buffer, int bufsize) = 0;
protected:
IAGSScriptManagedObject() {};
~IAGSScriptManagedObject() {};
virtual ~IAGSScriptManagedObject() {};
};

class IAGSManagedObjectReader {
public:
virtual void Unserialize(int key, const char *serializedData, int dataSize) = 0;
protected:
IAGSManagedObjectReader() {};
~IAGSManagedObjectReader() {};
virtual ~IAGSManagedObjectReader() {};
};

class IAGSFontRenderer {
Expand All @@ -252,7 +252,7 @@ class IAGSFontRenderer {
virtual void EnsureTextValidForFont(char *text, int fontNumber) = 0;
protected:
IAGSFontRenderer() = default;
~IAGSFontRenderer() = default;
virtual ~IAGSFontRenderer() = default;
};

class IAGSFontRenderer2 : public IAGSFontRenderer {
Expand All @@ -263,7 +263,7 @@ class IAGSFontRenderer2 : public IAGSFontRenderer {
virtual int GetLineSpacing(int fontNumber) = 0;
protected:
IAGSFontRenderer2() = default;
~IAGSFontRenderer2() = default;
virtual ~IAGSFontRenderer2() = default;
};

struct AGSRenderMatrixes {
Expand Down Expand Up @@ -375,7 +375,61 @@ class IAGSStream {

protected:
IAGSStream() = default;
~IAGSStream() = default;
virtual ~IAGSStream() = default;
};

#define AGS_AUDIOPLAY_FLD_VOLUME 0x0001

#define AGS_AUDIOFRAME_FLD_DATA 0x0001
#define AGS_AUDIOFRAME_FLD_TIMESTAMP 0x0002

// use sdl2 format for test, but maybe switch to our own custom values just in case?
#define AGS_AUDIOFORMAT_F32 0x8120

struct AGSAudioFormat
{
// Set which fields are valid, see AGS_AUDIOFORMAT_FLD_* flags
uint32_t Fields;
int Format; // s16, s32, f16, f32, etc
int Channels;
int Freq;
};

struct AGSAudioPlayConfig
{
// Set which fields are valid, see AGS_AUDIOPLAY_FLD_* flags
uint32_t Fields;
float Volume;
};

struct AGSAudioFrame
{
// Set which fields are valid, see AGS_AUDIOFRAME_FLD_* flags
uint32_t Fields;
void *Data;
size_t DataSize;
int64_t Timestamp;
};

// Something that plays the sound data
class IAGSAudioPlayer {
public:
// Tells which version of the plugin API this object corresponds to;
// this lets users know which of the following methods are valid to use.
virtual int GetVersion() = 0;
virtual void SetConfig(AGSAudioPlayConfig *config) = 0;
// Flushes and closes the AudioPlayer.
// After calling this the IAGSAudioPlayer pointer becomes INVALID.
virtual void Close() = 0;
virtual void Pause() = 0;
virtual void Resume() = 0;
// *copies* data on call, does not hold the frame (??? need different rule?)
// returns amount of data copied, or 0 if data cannot be accepted at the moment.
virtual size_t PutData(AGSAudioFrame *frame) = 0;

protected:
IAGSAudioPlayer() = default;
~IAGSAudioPlayer() = default;
};


Expand Down Expand Up @@ -652,6 +706,11 @@ class IAGSEngine {
// this stream should not be closed or disposed, doing so will lead to errors in the engine.
// Returns null if handle is invalid.
AGSIFUNC(IAGSStream*) GetFileStreamByHandle(int32 fhandle);

// *** BELOW ARE INTERFACE VERSION 29 AND ABOVE ONLY
// open audio player, telling the format of the sound data that will be SENT into this player by user.
// optionally pass AGSAudioPlayConfig setting playback parameters (these may be reset later).
AGSIFUNC(IAGSAudioPlayer*) OpenAudioPlayer(AGSAudioFormat *in_format, AGSAudioPlayConfig *config);
};


Expand Down
Loading