Merge pull request #171 from MihailRis/lua_audio

lua audio api
This commit is contained in:
MihailRis 2024-03-07 00:29:33 +03:00 committed by GitHub
commit a6d666e580
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 556 additions and 21 deletions

View File

@ -25,7 +25,10 @@ Speaker* ALSound::newInstance(int priority, int channel) const {
return nullptr;
}
AL_CHECK(alSourcei(source, AL_BUFFER, buffer));
return new ALSpeaker(al, source, priority, channel);
auto speaker = new ALSpeaker(al, source, priority, channel);
speaker->duration = duration;
return speaker;
}
ALStream::ALStream(ALAudio* al, std::shared_ptr<PCMStream> source, bool keepSource)
@ -77,12 +80,51 @@ void ALStream::bindSpeaker(speakerid_t speaker) {
sp->stop();
}
this->speaker = speaker;
sp = audio::get_speaker(speaker);
if (sp) {
auto alspeaker = dynamic_cast<ALSpeaker*>(sp);
alspeaker->stream = this;
alspeaker->duration = source->getTotalDuration();
}
}
speakerid_t ALStream::getSpeaker() const {
return speaker;
}
void ALStream::unqueueBuffers(uint alsource) {
uint processed = AL::getSourcei(alsource, AL_BUFFERS_PROCESSED);
while (processed--) {
uint buffer;
AL_CHECK(alSourceUnqueueBuffers(alsource, 1, &buffer));
unusedBuffers.push(buffer);
uint bps = source->getBitsPerSample()/8;
uint channels = source->getChannels();
ALint bufferSize;
alGetBufferi(buffer, AL_SIZE, &bufferSize);
totalPlayedSamples += bufferSize / bps / channels;
if (source->isSeekable()) {
totalPlayedSamples %= source->getTotalSamples();
}
}
}
uint ALStream::enqueueBuffers(uint alsource) {
uint preloaded = 0;
if (!unusedBuffers.empty()) {
uint buffer = unusedBuffers.front();
if (preloadBuffer(buffer, loop)) {
preloaded++;
unusedBuffers.pop();
AL_CHECK(alSourceQueueBuffers(alsource, 1, &buffer));
}
}
return preloaded;
}
void ALStream::update(double delta) {
if (this->speaker == 0) {
return;
@ -93,24 +135,11 @@ void ALStream::update(double delta) {
return;
}
ALSpeaker* alspeaker = dynamic_cast<ALSpeaker*>(speaker);
uint source = alspeaker->source;
uint processed = AL::getSourcei(source, AL_BUFFERS_PROCESSED);
while (processed--) {
uint buffer;
AL_CHECK(alSourceUnqueueBuffers(source, 1, &buffer));
unusedBuffers.push(buffer);
}
uint preloaded = 0;
if (!unusedBuffers.empty()) {
uint buffer = unusedBuffers.front();
if (preloadBuffer(buffer, loop)) {
preloaded++;
unusedBuffers.pop();
AL_CHECK(alSourceQueueBuffers(source, 1, &buffer));
}
}
uint alsource = alspeaker->source;
unqueueBuffers(alsource);
uint preloaded = enqueueBuffers(alsource);
if (speaker->isStopped() && !alspeaker->stopped) {
if (preloaded) {
speaker->play();
@ -120,8 +149,38 @@ void ALStream::update(double delta) {
}
}
duration_t ALStream::getTime() const {
uint total = totalPlayedSamples;
auto alspeaker = dynamic_cast<ALSpeaker*>(audio::get_speaker(this->speaker));
if (alspeaker) {
uint alsource = alspeaker->source;
total += static_cast<duration_t>(AL::getSourcef(alsource, AL_SAMPLE_OFFSET));
if (source->isSeekable()) {
total %= source->getTotalSamples();
}
}
return total / static_cast<duration_t>(source->getSampleRate());
}
void ALStream::setTime(duration_t time) {
// TODO: implement
if (!source->isSeekable())
return;
uint sample = time * source->getSampleRate();
source->seek(sample);
auto alspeaker = dynamic_cast<ALSpeaker*>(audio::get_speaker(this->speaker));
if (alspeaker) {
bool paused = alspeaker->isPaused();
AL_CHECK(alSourceStop(alspeaker->source));
unqueueBuffers(alspeaker->source);
totalPlayedSamples = sample;
enqueueBuffers(alspeaker->source);
AL_CHECK(alSourcePlay(alspeaker->source));
if (paused) {
AL_CHECK(alSourcePause(alspeaker->source));
}
} else {
totalPlayedSamples = sample;
}
}
ALSpeaker::ALSpeaker(ALAudio* al, uint source, int priority, int channel)
@ -199,15 +258,32 @@ void ALSpeaker::stop() {
stopped = true;
if (source) {
AL_CHECK(alSourceStop(source));
uint processed = AL::getSourcei(source, AL_BUFFERS_PROCESSED);
while (processed--) {
uint buffer;
AL_CHECK(alSourceUnqueueBuffers(source, 1, &buffer));
al->freeBuffer(buffer);
}
al->freeSource(source);
}
}
duration_t ALSpeaker::getTime() const {
if (stream) {
return stream->getTime();
}
return static_cast<duration_t>(AL::getSourcef(source, AL_SEC_OFFSET));
}
duration_t ALSpeaker::getDuration() const {
return duration;
}
void ALSpeaker::setTime(duration_t time) {
if (stream) {
return stream->setTime(time);
}
AL_CHECK(alSourcef(source, AL_SEC_OFFSET, static_cast<float>(time)));
}

View File

@ -55,7 +55,11 @@ namespace audio {
bool loop = false;
bool preloadBuffer(uint buffer, bool loop);
void unqueueBuffers(uint alsource);
uint enqueueBuffers(uint alsource);
public:
size_t totalPlayedSamples = 0;
ALStream(ALAudio* al, std::shared_ptr<PCMStream> source, bool keepSource);
~ALStream();
@ -64,6 +68,8 @@ namespace audio {
Speaker* createSpeaker(bool loop, int channel) override;
speakerid_t getSpeaker() const override;
void update(double delta) override;
duration_t getTime() const override;
void setTime(duration_t time) override;
static inline constexpr uint STREAM_BUFFERS = 3;
@ -76,9 +82,11 @@ namespace audio {
int channel;
float volume = 0.0f;
public:
ALStream* stream = nullptr;
bool stopped = true;
bool paused = false;
uint source;
duration_t duration = 0.0f;
ALSpeaker(ALAudio* al, uint source, int priority, int channel);
~ALSpeaker();
@ -102,6 +110,7 @@ namespace audio {
void stop() override;
duration_t getTime() const override;
duration_t getDuration() const override;
void setTime(duration_t time) override;
void setPosition(glm::vec3 pos) override;

View File

@ -53,6 +53,10 @@ namespace audio {
void update(double delta) override {
}
duration_t getTime() const override {
return 0.0;
}
void setTime(duration_t time) override {
}
};

View File

@ -347,6 +347,14 @@ Channel* audio::get_channel(int index) {
return channels.at(index).get();
}
std::shared_ptr<Stream> audio::get_associated_stream(speakerid_t id) {
auto found = streams.find(id);
if (found != streams.end()) {
return found->second;
}
return nullptr;
}
size_t audio::count_speakers() {
return speakers.size();
}

View File

@ -182,6 +182,9 @@ namespace audio {
/// @param delta time elapsed since the last update
virtual void update(double delta) = 0;
/// @brief Get current stream time
virtual duration_t getTime() const = 0;
/// @brief Set playhead to the selected time
/// @param time selected time
virtual void setTime(duration_t time) = 0;
@ -269,6 +272,9 @@ namespace audio {
/// @return time position in seconds
virtual duration_t getTime() const = 0;
/// @brief Get playing audio total duration
virtual duration_t getDuration() const = 0;
/// @brief Set playing audio time position
/// @param time time position in seconds
virtual void setTime(duration_t time) = 0;
@ -470,6 +476,11 @@ namespace audio {
/// @return channel or nullptr
extern Channel* get_channel(int index);
/// @brief Get stream associated with speaker
/// @param id speaker id
/// @return stream or nullptr
extern std::shared_ptr<Stream> get_associated_stream(speakerid_t id);
/// @brief Get alive speakers number (including paused)
extern size_t count_speakers();

View File

@ -134,7 +134,7 @@ public:
void seek(size_t position) override {
if (!closed && seekable) {
ov_raw_seek(&vf, position);
ov_pcm_seek(&vf, position);
}
}
};

View File

@ -276,6 +276,10 @@ EnginePaths* Engine::getPaths() {
return paths;
}
ResPaths* Engine::getResPaths() {
return resPaths.get();
}
std::shared_ptr<Screen> Engine::getScreen() {
return screen;
}

View File

@ -106,6 +106,9 @@ public:
/** Get engine filesystem paths source */
EnginePaths* getPaths();
/** Get engine resource paths controller */
ResPaths* getResPaths();
/** Get current Content instance */
const Content* getContent() const;

View File

@ -122,6 +122,7 @@ void lua::LuaState::createLibs() {
openlib("time", timelib, 0);
openlib("file", filelib, 0);
openlib("gui", guilib, 0);
openlib("audio", audiolib, 0);
addfunc("print", lua_wrap_errors<l_print>);
}

View File

@ -15,6 +15,7 @@ extern const luaL_Reg playerlib [];
extern const luaL_Reg inventorylib [];
extern const luaL_Reg guilib [];
extern const luaL_Reg hudlib [];
extern const luaL_Reg audiolib [];
// Lua Overrides

View File

@ -0,0 +1,418 @@
#include "api_lua.h"
#include "lua_commons.h"
#include "lua_util.h"
#include "../../../audio/audio.h"
#include "../../../engine.h"
#include "../scripting.h"
inline const char* DEFAULT_CHANNEL = "regular";
inline int extract_channel_index(lua_State* L, int idx) {
const char* channel = DEFAULT_CHANNEL;
if (!lua_isnoneornil(L, idx)) {
channel = lua_tostring(L, idx);
}
return audio::get_channel_index(channel);
}
inline audio::speakerid_t play_sound(
const char* name,
bool relative,
lua::luanumber x,
lua::luanumber y,
lua::luanumber z,
lua::luanumber volume,
lua::luanumber pitch,
bool loop,
int channel
) {
if (channel == -1)
return 0;
auto assets = scripting::engine->getAssets();
auto sound = assets->getSound(name);
if (sound == nullptr) {
return 0;
}
return audio::play(
sound,
glm::vec3(
static_cast<float>(x),
static_cast<float>(y),
static_cast<float>(z)
),
false,
volume,
pitch,
false,
audio::PRIORITY_NORMAL,
channel
);
}
inline audio::speakerid_t play_stream(
const char* filename,
bool relative,
lua::luanumber x,
lua::luanumber y,
lua::luanumber z,
lua::luanumber volume,
lua::luanumber pitch,
bool loop,
int channel
) {
if (channel == -1)
return 0;
auto paths = scripting::engine->getResPaths();
fs::path file = paths->find(fs::path(filename));
return audio::play_stream(
file,
glm::vec3(
static_cast<float>(x),
static_cast<float>(y),
static_cast<float>(z)
),
relative,
volume,
pitch,
loop,
channel
);
}
/// @brief audio.play_stream(
/// name: string,
/// x: number,
/// y: number,
/// z: number,
/// volume: number,
/// pitch: number,
/// channel: string = "regular",
/// loop: bool = false)
static int l_audio_play_stream(lua_State* L) {
lua_pushinteger(L, static_cast<lua::luaint>(
play_stream(
lua_tostring(L, 1),
false,
lua_tonumber(L, 2),
lua_tonumber(L, 3),
lua_tonumber(L, 4),
lua_tonumber(L, 5),
lua_tonumber(L, 6),
lua_toboolean(L, 8),
extract_channel_index(L, 7)
)
));
return 1;
}
/// @brief audio.play_stream_2d(
/// name: string,
/// volume: number,
/// pitch: number,
/// channel: string = "regular",
/// loop: bool = false)
static int l_audio_play_stream_2d(lua_State* L) {
lua_pushinteger(L, static_cast<lua::luaint>(
play_stream(
lua_tostring(L, 1),
true,
0.0, 0.0, 0.0,
lua_tonumber(L, 2),
lua_tonumber(L, 3),
lua_toboolean(L, 5),
extract_channel_index(L, 4)
)
));
return 1;
}
/// @brief audio.play_sound(
/// name: string,
/// x: number,
/// y: number,
/// z: number,
/// volume: number,
/// pitch: number,
/// channel: string = "regular",
/// loop: bool = false)
static int l_audio_play_sound(lua_State* L) {
lua_pushinteger(L, static_cast<lua::luaint>(
play_sound(
lua_tostring(L, 1),
false,
lua_tonumber(L, 2),
lua_tonumber(L, 3),
lua_tonumber(L, 4),
lua_tonumber(L, 5),
lua_tonumber(L, 6),
lua_toboolean(L, 8),
extract_channel_index(L, 7)
)
));
return 1;
}
/// @brief audio.play_sound_2d(
/// name: string,
/// volume: number,
/// pitch: number,
/// channel: string = "regular",
/// loop: bool = false)
static int l_audio_play_sound_2d(lua_State* L) {
lua_pushinteger(L, static_cast<lua::luaint>(
play_sound(
lua_tostring(L, 1),
true,
0.0, 0.0, 0.0,
lua_tonumber(L, 2),
lua_tonumber(L, 3),
lua_toboolean(L, 5),
extract_channel_index(L, 4)
)
));
return 1;
}
/// @brief audio.stop(speakerid: integer) -> nil
static int l_audio_stop(lua_State* L) {
lua::luaint id = lua_tonumber(L, 1);
auto speaker = audio::get_speaker(id);
if (speaker != nullptr) {
speaker->stop();
}
return 0;
}
/// @brief audio.pause(speakerid: integer) -> nil
static int l_audio_pause(lua_State* L) {
lua::luaint id = lua_tonumber(L, 1);
auto speaker = audio::get_speaker(id);
if (speaker != nullptr) {
speaker->pause();
}
return 0;
}
/// @brief audio.resume(speakerid: integer) -> nil
static int l_audio_resume(lua_State* L) {
lua::luaint id = lua_tonumber(L, 1);
auto speaker = audio::get_speaker(id);
if (speaker != nullptr && speaker->isPaused()) {
speaker->play();
}
return 0;
}
/// @brief audio.set_loop(speakerid: integer, value: bool) -> nil
static int l_audio_set_loop(lua_State* L) {
lua::luaint id = lua_tonumber(L, 1);
auto speaker = audio::get_speaker(id);
if (speaker != nullptr) {
bool value = lua_toboolean(L, 2);
speaker->setLoop(value);
}
return 0;
}
/// @brief audio.set_volume(speakerid: integer, value: number) -> nil
static int l_audio_set_volume(lua_State* L) {
lua::luaint id = lua_tonumber(L, 1);
auto speaker = audio::get_speaker(id);
if (speaker != nullptr) {
lua::luanumber value = lua_tonumber(L, 2);
speaker->setVolume(static_cast<float>(value));
}
return 0;
}
/// @brief audio.set_pitch(speakerid: integer, value: number) -> nil
static int l_audio_set_pitch(lua_State* L) {
lua::luaint id = lua_tonumber(L, 1);
auto speaker = audio::get_speaker(id);
if (speaker != nullptr) {
lua::luanumber value = lua_tonumber(L, 2);
speaker->setPitch(static_cast<float>(value));
}
return 0;
}
/// @brief audio.set_time(speakerid: integer, value: number) -> nil
static int l_audio_set_time(lua_State* L) {
lua::luaint id = lua_tonumber(L, 1);
auto speaker = audio::get_speaker(id);
if (speaker != nullptr) {
lua::luanumber value = lua_tonumber(L, 2);
speaker->setTime(static_cast<audio::duration_t>(value));
}
return 0;
}
/// @brief audio.set_position(speakerid: integer, x: number, y: number, z: number) -> nil
static int l_audio_set_position(lua_State* L) {
lua::luaint id = lua_tonumber(L, 1);
auto speaker = audio::get_speaker(id);
if (speaker != nullptr) {
lua::luanumber x = lua_tonumber(L, 2);
lua::luanumber y = lua_tonumber(L, 3);
lua::luanumber z = lua_tonumber(L, 4);
speaker->setPosition(glm::vec3(
static_cast<float>(x),
static_cast<float>(y),
static_cast<float>(z)
));
}
return 0;
}
/// @brief audio.set_velocity(speakerid: integer, x: number, y: number, z: number) -> nil
static int l_audio_set_velocity(lua_State* L) {
lua::luaint id = lua_tonumber(L, 1);
auto speaker = audio::get_speaker(id);
if (speaker != nullptr) {
lua::luanumber x = lua_tonumber(L, 2);
lua::luanumber y = lua_tonumber(L, 3);
lua::luanumber z = lua_tonumber(L, 4);
speaker->setVelocity(glm::vec3(
static_cast<float>(x),
static_cast<float>(y),
static_cast<float>(z)
));
}
return 0;
}
/// @brief audio.is_playing(speakerid: integer) -> bool
static int l_audio_is_playing(lua_State* L) {
lua::luaint id = lua_tonumber(L, 1);
auto speaker = audio::get_speaker(id);
if (speaker != nullptr) {
lua_pushboolean(L, speaker->isPlaying());
return 1;
}
lua_pushboolean(L, false);
return 1;
}
/// @brief audio.is_paused(speakerid: integer) -> bool
static int l_audio_is_paused(lua_State* L) {
lua::luaint id = lua_tonumber(L, 1);
auto speaker = audio::get_speaker(id);
if (speaker != nullptr) {
lua_pushboolean(L, speaker->isPaused());
return 1;
}
lua_pushboolean(L, false);
return 1;
}
/// @brief audio.is_loop(speakerid: integer) -> bool
static int l_audio_is_loop(lua_State* L) {
lua::luaint id = lua_tonumber(L, 1);
auto speaker = audio::get_speaker(id);
if (speaker != nullptr) {
lua_pushboolean(L, speaker->isLoop());
return 1;
}
lua_pushboolean(L, false);
return 1;
}
/// @brief audio.get_volume(speakerid: integer) -> number
static int l_audio_get_volume(lua_State* L) {
lua::luaint id = lua_tonumber(L, 1);
auto speaker = audio::get_speaker(id);
if (speaker != nullptr) {
lua_pushnumber(L, speaker->getVolume());
return 1;
}
lua_pushnumber(L, 0.0);
return 1;
}
/// @brief audio.get_pitch(speakerid: integer) -> number
static int l_audio_get_pitch(lua_State* L) {
lua::luaint id = lua_tonumber(L, 1);
auto speaker = audio::get_speaker(id);
if (speaker != nullptr) {
lua_pushnumber(L, speaker->getPitch());
return 1;
}
lua_pushnumber(L, 1.0);
return 1;
}
/// @brief audio.get_time(speakerid: integer) -> number
static int l_audio_get_time(lua_State* L) {
lua::luaint id = lua_tonumber(L, 1);
auto speaker = audio::get_speaker(id);
if (speaker != nullptr) {
lua_pushnumber(L, speaker->getTime());
return 1;
}
lua_pushnumber(L, 0.0);
return 1;
}
/// @brief audio.get_duration(speakerid: integer) -> number
static int l_audio_get_duration(lua_State* L) {
lua::luaint id = lua_tonumber(L, 1);
auto speaker = audio::get_speaker(id);
if (speaker != nullptr) {
lua_pushnumber(L, speaker->getDuration());
return 1;
}
lua_pushnumber(L, 0.0);
return 1;
}
/// @brief audio.get_position(speakerid: integer) -> number, number, number
static int l_audio_get_position(lua_State* L) {
lua::luaint id = lua_tonumber(L, 1);
auto speaker = audio::get_speaker(id);
if (speaker != nullptr) {
auto vec = speaker->getPosition();
lua::pushvec3(L, vec);
return 1;
}
return 0;
}
/// @brief audio.get_velocity(speakerid: integer) -> number, number, number
static int l_audio_get_velocity(lua_State* L) {
lua::luaint id = lua_tonumber(L, 1);
auto speaker = audio::get_speaker(id);
if (speaker != nullptr) {
auto vec = speaker->getVelocity();
lua::pushvec3(L, vec);
return 1;
}
return 0;
}
const luaL_Reg audiolib [] = {
{"play_sound", lua_wrap_errors<l_audio_play_sound>},
{"play_sound_2d", lua_wrap_errors<l_audio_play_sound_2d>},
{"play_stream", lua_wrap_errors<l_audio_play_stream>},
{"play_stream_2d", lua_wrap_errors<l_audio_play_stream_2d>},
{"stop", lua_wrap_errors<l_audio_stop>},
{"pause", lua_wrap_errors<l_audio_pause>},
{"resume", lua_wrap_errors<l_audio_resume>},
{"set_loop", lua_wrap_errors<l_audio_set_loop>},
{"set_volume", lua_wrap_errors<l_audio_set_volume>},
{"set_pitch", lua_wrap_errors<l_audio_set_pitch>},
{"set_time", lua_wrap_errors<l_audio_set_time>},
{"set_position", lua_wrap_errors<l_audio_set_position>},
{"set_velocity", lua_wrap_errors<l_audio_set_velocity>},
{"is_playing", lua_wrap_errors<l_audio_is_playing>},
{"is_paused", lua_wrap_errors<l_audio_is_paused>},
{"is_loop", lua_wrap_errors<l_audio_is_loop>},
{"get_volume", lua_wrap_errors<l_audio_get_volume>},
{"get_pitch", lua_wrap_errors<l_audio_get_pitch>},
{"get_time", lua_wrap_errors<l_audio_get_time>},
{"get_duration", lua_wrap_errors<l_audio_get_duration>},
{"get_position", lua_wrap_errors<l_audio_get_position>},
{"get_velocity", lua_wrap_errors<l_audio_get_velocity>},
{NULL, NULL}
};