mirror of
https://github.com/ctruLua/ctruLua.git
synced 2025-10-27 08:29:31 +00:00
ADDED AUDIO! WAV working perfectly
Added libogg, libvorbis and libvorbisfile to 3ds_portlibs. You will need to compile thems using make build-portlibs. Opening OGG files works but they play badly. Added a very simple error handler in main.lua Added audio example. Renamed isGfxInitialised to isGfxInitialized. Did you know? The audio module is the longest ctrµLua module.
This commit is contained in:
parent
716c42b849
commit
bda9de4d1c
10 changed files with 985 additions and 17 deletions
4
Makefile
4
Makefile
|
|
@ -55,7 +55,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
|
|||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lsfil -ljpeg -lsftd -lfreetype -lpng -lz -lsf2d -lctru -lm
|
||||
LIBS := -lsfil -ljpeg -lsftd -lfreetype -lpng -lz -lsf2d -lctru -lvorbisfile -lvorbis -logg -lm
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
|
|
@ -142,7 +142,7 @@ $(BUILD):
|
|||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
build-portlibs:
|
||||
@make -C libs/3ds_portlibs zlib freetype libjpeg-turbo libpng
|
||||
@make -C libs/3ds_portlibs zlib freetype libjpeg-turbo libpng libogg libvorbis
|
||||
|
||||
build-sf2dlib:
|
||||
@make -C libs/sf2dlib/libsf2d build
|
||||
|
|
|
|||
2
libs/3ds_portlibs/.gitignore
vendored
2
libs/3ds_portlibs/.gitignore
vendored
|
|
@ -4,4 +4,6 @@ libjpeg-*
|
|||
libpng-*
|
||||
sqlite-*
|
||||
zlib-*
|
||||
libogg-*
|
||||
libvorbis-*
|
||||
build/
|
||||
|
|
@ -28,6 +28,16 @@ ZLIB_VERSION := $(ZLIB)-1.2.8
|
|||
ZLIB_SRC := $(ZLIB_VERSION).tar.gz
|
||||
ZLIB_DOWNLOAD := "http://prdownloads.sourceforge.net/libpng/zlib-1.2.8.tar.gz"
|
||||
|
||||
LIBOGG := libogg
|
||||
LIBOGG_VERSION := $(LIBOGG)-1.3.2
|
||||
LIBOGG_SRC := $(LIBOGG_VERSION).tar.gz
|
||||
LIBOGG_DOWNLOAD := "http://downloads.xiph.org/releases/ogg/libogg-1.3.2.tar.gz"
|
||||
|
||||
LIBVORBIS := libvorbis
|
||||
LIBVORBIS_VERSION := $(LIBVORBIS)-1.3.5
|
||||
LIBVORBIS_SRC := $(LIBVORBIS_VERSION).tar.gz
|
||||
LIBVORBIS_DOWNLOAD := "http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.5.tar.gz"
|
||||
|
||||
export PORTLIBS := $(CURDIR)/build
|
||||
export PATH := $(DEVKITARM)/bin:$(PATH)
|
||||
export PKG_CONFIG_PATH := $(PORTLIBS)/lib/pkgconfig
|
||||
|
|
@ -66,8 +76,8 @@ $(FREETYPE): $(FREETYPE_SRC)
|
|||
./configure --prefix=$(PORTLIBS) --host=arm-none-eabi --disable-shared --enable-static --without-harfbuzz
|
||||
@$(MAKE) -C $(FREETYPE_VERSION)
|
||||
@make create_build_dir
|
||||
@cp -srf $(CURDIR)/freetype-2.6/include/. $(CURDIR)/build/include
|
||||
@cp -sf $(CURDIR)/freetype-2.6/objs/.libs/libfreetype.a $(CURDIR)/build/lib/libfreetype.a
|
||||
@cp -srf $(CURDIR)/$(FREETYPE_VERSION)/include/. $(CURDIR)/build/include
|
||||
@cp -sf $(CURDIR)/$(FREETYPE_VERSION)/objs/.libs/libfreetype.a $(CURDIR)/build/lib/libfreetype.a
|
||||
|
||||
$(LIBEXIF): $(LIBEXIF_SRC)
|
||||
@[ -d $(LIBEXIF_VERSION) ] || tar -xf $<
|
||||
|
|
@ -81,8 +91,8 @@ $(LIBJPEGTURBO): $(LIBJPEGTURBO_SRC)
|
|||
./configure --prefix=$(PORTLIBS) --host=arm-none-eabi --disable-shared --enable-static
|
||||
@$(MAKE) CFLAGS+="\"-Drandom()=rand()\"" -C $(LIBJPEGTURBO_VERSION)
|
||||
@make create_build_dir
|
||||
@cp -sf $(CURDIR)/libjpeg-turbo-*/*.h $(CURDIR)/build/include
|
||||
@cp -sf $(CURDIR)/libjpeg-turbo-*/.libs/libjpeg.a $(CURDIR)/build/lib/libjpeg.a
|
||||
@cp -sf $(CURDIR)/$(LIBJPEGTURBO_VERSION)/*.h $(CURDIR)/build/include
|
||||
@cp -sf $(CURDIR)/$(LIBJPEGTURBO_VERSION)/.libs/libjpeg.a $(CURDIR)/build/lib/libjpeg.a
|
||||
|
||||
$(LIBPNG): $(LIBPNG_SRC)
|
||||
@[ -d $(LIBPNG_VERSION) ] || tar -xf $<
|
||||
|
|
@ -90,8 +100,8 @@ $(LIBPNG): $(LIBPNG_SRC)
|
|||
./configure --prefix=$(PORTLIBS) --host=arm-none-eabi --disable-shared --enable-static
|
||||
@$(MAKE) -C $(LIBPNG_VERSION)
|
||||
@make create_build_dir
|
||||
@cp -sf $(CURDIR)/libpng-*/*.h $(CURDIR)/build/include
|
||||
@cp -sf $(CURDIR)/libpng-*/.libs/*.a $(CURDIR)/build/lib/libpng.a
|
||||
@cp -sf $(CURDIR)/$(LIBPNG_VERSION)/*.h $(CURDIR)/build/include
|
||||
@cp -sf $(CURDIR)/$(LIBPNG_VERSION)/.libs/*.a $(CURDIR)/build/lib/libpng.a
|
||||
|
||||
# sqlite won't work with -ffast-math
|
||||
$(SQLITE): $(SQLITE_SRC)
|
||||
|
|
@ -107,8 +117,26 @@ $(ZLIB): $(ZLIB_SRC)
|
|||
CHOST=arm-none-eabi ./configure --static --prefix=$(PORTLIBS)
|
||||
@$(MAKE) -C $(ZLIB_VERSION)
|
||||
@make create_build_dir
|
||||
@cp -sf $(CURDIR)/zlib-*/*.h $(CURDIR)/build/include
|
||||
@cp -sf $(CURDIR)/zlib-*/libz.a $(CURDIR)/build/lib/libz.a
|
||||
@cp -sf $(CURDIR)/$(ZLIB_VERSION)/*.h $(CURDIR)/build/include
|
||||
@cp -sf $(CURDIR)/$(ZLIB_VERSION)/libz.a $(CURDIR)/build/lib/libz.a
|
||||
|
||||
$(LIBOGG): $(LIBOGG_SRC)
|
||||
@[ -d $(LIBOGG_VERSION) ] || tar -xf $<
|
||||
@cd $(LIBOGG_VERSION) && \
|
||||
./configure --prefix=$(PORTLIBS) --host=arm-none-eabi --disable-shared --enable-static
|
||||
@$(MAKE) -C $(LIBOGG_VERSION)
|
||||
@make create_build_dir
|
||||
@cp -srf $(CURDIR)/$(LIBOGG_VERSION)/include/. $(CURDIR)/build/include
|
||||
@cp -sf $(CURDIR)/$(LIBOGG_VERSION)/src/.libs/*.a $(CURDIR)/build/lib
|
||||
|
||||
$(LIBVORBIS): $(LIBVORBIS_SRC)
|
||||
@[ -d $(LIBVORBIS_VERSION) ] || tar -xf $<
|
||||
@cd $(LIBVORBIS_VERSION) && \
|
||||
./configure --prefix=$(PORTLIBS) --host=arm-none-eabi --disable-shared --enable-static
|
||||
@$(MAKE) -C $(LIBVORBIS_VERSION)
|
||||
@make create_build_dir
|
||||
@cp -srf $(CURDIR)/$(LIBVORBIS_VERSION)/include/. $(CURDIR)/build/include
|
||||
@cp -sf $(CURDIR)/$(LIBVORBIS_VERSION)/lib/.libs/*.a $(CURDIR)/build/lib
|
||||
|
||||
# Downloads
|
||||
$(ZLIB_SRC):
|
||||
|
|
@ -129,6 +157,12 @@ $(LIBPNG_SRC):
|
|||
$(SQLITE_SRC):
|
||||
wget -O $@ $(SQLITE_DOWNLOAD)
|
||||
|
||||
$(LIBOGG_SRC):
|
||||
wget -O $@ $(LIBOGG_DOWNLOAD)
|
||||
|
||||
$(LIBVORBIS_SRC):
|
||||
wget -O $@ $(LIBVORBIS_DOWNLOAD)
|
||||
|
||||
install-zlib:
|
||||
@$(MAKE) -C $(ZLIB_VERSION) install
|
||||
|
||||
|
|
@ -138,6 +172,8 @@ install:
|
|||
@[ ! -d $(LIBJPEGTURBO_VERSION) ] || $(MAKE) -C $(LIBJPEGTURBO_VERSION) install
|
||||
@[ ! -d $(LIBPNG_VERSION) ] || $(MAKE) -C $(LIBPNG_VERSION) install
|
||||
@[ ! -d $(SQLITE_VERSION) ] || $(MAKE) -C $(SQLITE_VERSION) install-libLTLIBRARIES install-data
|
||||
@[ ! -d $(LIBOGG_VERSION) ] || $(MAKE) -C $(LIBOGG_VERSION) install
|
||||
@[ ! -d $(LIBVORBIS_VERSION) ] || $(MAKE) -C $(LIBVORBIS_VERSION) install
|
||||
|
||||
clean:
|
||||
@$(RM) -r $(FREETYPE_VERSION)
|
||||
|
|
@ -146,5 +182,7 @@ clean:
|
|||
@$(RM) -r $(LIBPNG_VERSION)
|
||||
@$(RM) -r $(SQLITE_VERSION)
|
||||
@$(RM) -r $(ZLIB_VERSION)
|
||||
@$(RM) -r $(LIBOGG_VERSION)
|
||||
@$(RM) -r $(LIBVORBIS_VERSION)
|
||||
@rm -rf $(CURDIR)/build
|
||||
@rm -f $(CURDIR)/*.tar.*
|
||||
|
|
|
|||
|
|
@ -8,6 +8,22 @@ repeat
|
|||
local file = require("openfile")("Choose a Lua file to execute", nil, ".lua", "exist")
|
||||
if file then
|
||||
fs.setDirectory(file:match("^(.-)[^/]*$"))
|
||||
dofile(file)
|
||||
local success, err = pcall(dofile, file)
|
||||
if not success then
|
||||
local gfx = require("ctr.gfx")
|
||||
local hid = require("ctr.hid")
|
||||
gfx.set3D(false)
|
||||
gfx.color.setDefault(0xFFFFFFFF)
|
||||
gfx.color.setBackground(0xFF000000)
|
||||
gfx.font.setDefault()
|
||||
while true do
|
||||
hid.read()
|
||||
if hid.keys().down.start then break end
|
||||
gfx.startFrame(gfx.GFX_TOP)
|
||||
gfx.wrappedText(0, 0, err, gfx.TOP_WIDTH)
|
||||
gfx.endFrame()
|
||||
gfx.render()
|
||||
end
|
||||
end
|
||||
end
|
||||
until not file
|
||||
52
sdcard/3ds/ctruLua/tests/audio/main.lua
Normal file
52
sdcard/3ds/ctruLua/tests/audio/main.lua
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
local ctr = require("ctr")
|
||||
local hid = require("ctr.hid")
|
||||
local gfx = require("ctr.gfx")
|
||||
local audio = require("ctr.audio")
|
||||
|
||||
local test = assert(audio.load("test.wav"))
|
||||
|
||||
local channel = -1
|
||||
local speed = 1
|
||||
local leftBalance = 0.5
|
||||
|
||||
while true do
|
||||
hid.read()
|
||||
local keys = hid.keys()
|
||||
if keys.down.start then break end
|
||||
|
||||
if keys.down.a then
|
||||
channel = test:play()
|
||||
end
|
||||
|
||||
if keys.down.up then
|
||||
speed = speed + 0.01
|
||||
test:speed(speed)
|
||||
audio.speed(nil, speed)
|
||||
end
|
||||
if keys.down.down then
|
||||
speed = speed - 0.01
|
||||
test:speed(speed)
|
||||
audio.speed(nil, speed)
|
||||
end
|
||||
|
||||
if keys.down.left then
|
||||
leftBalance = leftBalance + 0.1
|
||||
test:mix(1-leftBalance, leftBalance)
|
||||
audio.mix(nil, leftBalance, 1-leftBalance)
|
||||
end
|
||||
if keys.down.right then
|
||||
leftBalance = leftBalance - 0.1
|
||||
test:mix(1-leftBalance, leftBalance)
|
||||
audio.mix(nil, leftBalance, 1-leftBalance)
|
||||
end
|
||||
|
||||
gfx.startFrame(gfx.GFX_TOP)
|
||||
gfx.text(5, 5, "Audio test! "..tostring(test:time()).."/"..tostring(test:duration()).."s")
|
||||
gfx.text(5, 25, "Last audio played on channel "..tostring(channel))
|
||||
gfx.text(5, 65, "Speed: "..(speed*100).."% - Left balance: "..(leftBalance*100).."%")
|
||||
gfx.endFrame()
|
||||
|
||||
gfx.render()
|
||||
end
|
||||
|
||||
test:unload()
|
||||
BIN
sdcard/3ds/ctruLua/tests/audio/test.wav
Normal file
BIN
sdcard/3ds/ctruLua/tests/audio/test.wav
Normal file
Binary file not shown.
851
source/audio.c
Normal file
851
source/audio.c
Normal file
|
|
@ -0,0 +1,851 @@
|
|||
/***
|
||||
The `audio` module.
|
||||
An audio channel can play only one audio object at a time.
|
||||
There are 24 audio channels available, numbered from 0 to 23.
|
||||
@module ctr.audio
|
||||
@usage local audio = require("ctr.audio")
|
||||
*/
|
||||
|
||||
#include <3ds.h>
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
#include <vorbis/codec.h>
|
||||
#include <vorbis/vorbisfile.h>
|
||||
|
||||
// Audio object type
|
||||
typedef enum {
|
||||
TYPE_UNKNOWN = -1,
|
||||
TYPE_OGG = 0,
|
||||
TYPE_WAV = 1
|
||||
} filetype;
|
||||
|
||||
// Audio object userdata
|
||||
typedef struct {
|
||||
filetype type; // file type
|
||||
|
||||
// OGG Vorbis specific
|
||||
OggVorbis_File vf; // ogg vorbis file
|
||||
|
||||
// Needed for playback
|
||||
float rate; // sample rate (per channel) (Hz)
|
||||
u32 channels; // channel count
|
||||
u32 encoding; // data encoding (NDSP_ENCODING_*)
|
||||
u32 nsamples; // numbers of samples in the audio (per channel, not the total)
|
||||
u32 size; // number of bytes in the audio (total, ie data size)
|
||||
char* data; // raw audio data
|
||||
|
||||
// Playing parameters (type-independant)
|
||||
float mix[12]; // mix parameters
|
||||
ndspInterpType interp; // interpolation type
|
||||
double speed; // playing speed
|
||||
} audio_userdata;
|
||||
|
||||
// Indicate if NDSP was initialized or not.
|
||||
// NDSP doesn't work on citra yet.
|
||||
// Please only throw an error related to this when using a ndsp function, so other parts of the
|
||||
// audio module are still available on citra, like audio.load() and audio:info().
|
||||
bool isAudioInitialized = false;
|
||||
|
||||
// Array of the last audio_userdata sent to each channel; channels range from 0 to 23
|
||||
audio_userdata* channels[24];
|
||||
|
||||
/***
|
||||
Load an audio file.
|
||||
OGG Vorbis and PCM WAV file format are currently supported.
|
||||
(Most WAV files use the PCM encoding).
|
||||
@function load
|
||||
@tparam string path path to the file
|
||||
@tparam[opt=detect] string type file type, `"ogg"` or `"wav"`.
|
||||
If set to `"detect"`, will try to deduce the type from the filename.
|
||||
@treturn[1] audio the loaded audio object
|
||||
@treturn[2] nil if a error happened
|
||||
@treturn[2] string error message
|
||||
*/
|
||||
static int audio_load(lua_State *L) {
|
||||
const char *path = luaL_checkstring(L, 1);
|
||||
const char* argType = luaL_optstring(L, 2, "detect");
|
||||
|
||||
// Create userdata
|
||||
audio_userdata *audio = lua_newuserdata(L, sizeof(*audio));
|
||||
luaL_getmetatable(L, "LAudio");
|
||||
lua_setmetatable(L, -2);
|
||||
for (int i=0; i<12; i++) audio->mix[i] = 1;
|
||||
audio->interp = NDSP_INTERP_LINEAR;
|
||||
audio->speed = 1;
|
||||
|
||||
// Get file type
|
||||
filetype type = TYPE_UNKNOWN;
|
||||
if (strcmp(argType, "detect") == 0) {
|
||||
const char *dot = strrchr(path, '.');
|
||||
if (!dot || dot == path) dot = "";
|
||||
const char *ext = dot + 1;
|
||||
if (strncmp(ext, "ogg", 3) == 0) type = TYPE_OGG;
|
||||
else if (strncmp(ext, "wav", 3) == 0) type = TYPE_WAV;
|
||||
} else if (strcmp(argType, "ogg") == 0) {
|
||||
type = TYPE_OGG;
|
||||
} else if (strcmp(argType, "wav") == 0) {
|
||||
type = TYPE_WAV;
|
||||
}
|
||||
|
||||
// Open and read file
|
||||
if (type == TYPE_OGG) {
|
||||
audio->type = TYPE_OGG;
|
||||
|
||||
// Load audio file
|
||||
if (ov_fopen(path, &audio->vf) < 0) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "input does not appear to be a valid ogg vorbis file or doesn't exist");
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Some Ogg Vorbis decoding parameters
|
||||
bool bigendianp = false; // bigendianness
|
||||
u8 word = 2; // word size; 1 or 2 (8bits/sample or 16bits/sample)
|
||||
bool sgned = true; // signed data
|
||||
|
||||
// Decoding Ogg Vorbis bitstream
|
||||
vorbis_info* vi = ov_info(&audio->vf, -1);
|
||||
if (vi == NULL) luaL_error(L, "could not retrieve ogg audio stream informations");
|
||||
|
||||
audio->rate = vi->rate;
|
||||
audio->channels = vi->channels;
|
||||
audio->encoding = NDSP_ENCODING_PCM16;
|
||||
audio->nsamples = ov_pcm_total(&audio->vf, -1);
|
||||
audio->size = audio->nsamples * word;
|
||||
|
||||
if (linearSpaceFree() < audio->size) luaL_error(L, "not enough linear memory available");
|
||||
audio->data = linearAlloc(audio->size);
|
||||
|
||||
// Decoding loop
|
||||
int offset = 0;
|
||||
int eof = 0;
|
||||
int current_section;
|
||||
while (!eof) {
|
||||
long ret = ov_read(&audio->vf, &audio->data[offset], 4096, bigendianp, word, sgned, ¤t_section);
|
||||
|
||||
if (ret == 0) {
|
||||
eof = 1;
|
||||
} else if (ret < 0) {
|
||||
ov_clear(&audio->vf);
|
||||
linearFree(audio->data);
|
||||
luaL_error(L, "error in the ogg vorbis stream");
|
||||
return 0;
|
||||
} else {
|
||||
// TODO handle multiple links (http://xiph.org/vorbis/doc/vorbisfile/decoding.html)
|
||||
offset += ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (type == TYPE_WAV) {
|
||||
audio->type = TYPE_WAV;
|
||||
|
||||
// Used this as a reference for the WAV format: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
|
||||
|
||||
// Load file
|
||||
FILE *file = fopen(path, "rb");
|
||||
if (file) {
|
||||
bool valid = true; // if something goes wrong, this will be false
|
||||
|
||||
char buff[8];
|
||||
|
||||
// Master chunk
|
||||
fread(buff, 4, 1, file); // ckId
|
||||
if (strncmp(buff, "RIFF", 4) != 0) valid = false;
|
||||
|
||||
fseek(file, 4, SEEK_CUR); // skip ckSize
|
||||
|
||||
fread(buff, 4, 1, file); // WAVEID
|
||||
if (strncmp(buff, "WAVE", 4) != 0) valid = false;
|
||||
|
||||
// fmt Chunk
|
||||
fread(buff, 4, 1, file); // ckId
|
||||
if (strncmp(buff, "fmt ", 4) != 0) valid = false;
|
||||
|
||||
fread(buff, 4, 1, file); // ckSize
|
||||
if (*buff != 16) valid = false; // should be 16 for PCM format
|
||||
|
||||
fread(buff, 2, 1, file); // wFormatTag
|
||||
if (*buff != 0x0001) valid = false; // PCM format
|
||||
|
||||
u16 channels;
|
||||
fread(&channels, 2, 1, file); // nChannels
|
||||
audio->channels = channels;
|
||||
|
||||
u32 rate;
|
||||
fread(&rate, 4, 1, file); // nSamplesPerSec
|
||||
audio->rate = rate;
|
||||
|
||||
fseek(file, 4, SEEK_CUR); // skip nAvgBytesPerSec
|
||||
|
||||
u16 byte_per_block; // 1 block = 1*channelCount samples
|
||||
fread(&byte_per_block, 2, 1, file); // nBlockAlign
|
||||
|
||||
u16 byte_per_sample;
|
||||
fread(&byte_per_sample, 2, 1, file); // wBitsPerSample
|
||||
byte_per_sample /= 8; // bits -> bytes
|
||||
|
||||
// There may be some additionals chunks between fmt and data
|
||||
// TODO handle some usefull chunks that may be here
|
||||
fread(&buff, 4, 1, file); // ckId
|
||||
while (strncmp(buff, "data", 4) != 0) {
|
||||
u32 size;
|
||||
fread(&size, 4, 1, file); // ckSize
|
||||
|
||||
fseek(file, size, SEEK_CUR); // skip chunk
|
||||
|
||||
int i = fread(&buff, 4, 1, file); // next chunk ckId
|
||||
|
||||
if (i < 4) { // reached EOF before finding a data chunk
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// data Chunk (ckId already read)
|
||||
u32 size;
|
||||
fread(&size, 4, 1, file); // ckSize
|
||||
audio->size = size;
|
||||
|
||||
audio->nsamples = audio->size / byte_per_block;
|
||||
|
||||
if (byte_per_sample == 1) audio->encoding = NDSP_ENCODING_PCM8;
|
||||
else if (byte_per_sample == 2) audio->encoding = NDSP_ENCODING_PCM16;
|
||||
else luaL_error(L, "unknown encoding, needs to be PCM8 or PCM16");
|
||||
|
||||
if (!valid) {
|
||||
fclose(file);
|
||||
luaL_error(L, "invalid PCM wav file");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read data
|
||||
if (linearSpaceFree() < audio->size) luaL_error(L, "not enough linear memory available");
|
||||
audio->data = linearAlloc(audio->size);
|
||||
|
||||
fread(audio->data, audio->size, 1, file);
|
||||
|
||||
fclose(file);
|
||||
return 1;
|
||||
|
||||
} else {
|
||||
lua_pushnil(L);
|
||||
lua_pushfstring(L, "error while opening wav file: %s", strerror(errno));;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
luaL_error(L, "unknown audio type");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
Check if audio is currently playing on a channel.
|
||||
@function playing
|
||||
@tparam[opt] integer channel number; if `nil` will search the first channel playing an audio
|
||||
@treturn boolean `true` if the channel is currently playing the audio, `false` otherwise.
|
||||
If channel is not set, `false` means no audio is playing at all.
|
||||
*/
|
||||
static int audio_playing(lua_State *L) {
|
||||
if (!isAudioInitialized) {
|
||||
lua_pushboolean(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int channel = luaL_optinteger(L, 1, -1);
|
||||
if (channel < -1 || channel > 23) luaL_error(L, "channel number must be between 0 and 23");
|
||||
|
||||
// Search a channel playing audio
|
||||
if (channel == -1) {
|
||||
for (int i = 0; i <= 23; i++) {
|
||||
if (ndspChnIsPlaying(i)) {
|
||||
lua_pushboolean(L, true);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
lua_pushboolean(L, false);
|
||||
return 1;
|
||||
|
||||
} else {
|
||||
lua_pushboolean(L, ndspChnIsPlaying(channel));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
Set the mix parameters (volumes) of a channel.
|
||||
Volumes go from 0 (0%) to 1 (100%).
|
||||
Note that when a new audio object will play on this channel, theses parameters will be
|
||||
reset with the new audio object defaults (set in `audio:mix()`).
|
||||
@function mix
|
||||
@tparam[opt] integer channel the channel number, if `nil` will change the mix parmaters of all channels
|
||||
@tparam[opt=1] number frontLeft front left volume
|
||||
@tparam[opt=frontLeft] number frontRight front right volume
|
||||
@tparam[opt=frontLeft] number backLeft back left volume
|
||||
@tparam[opt=frontRight] number backRight back right volume
|
||||
*/
|
||||
static int audio_mix(lua_State *L) {
|
||||
if (!isAudioInitialized) luaL_error(L, "audio wasn't initialized correctly");
|
||||
|
||||
int channel = luaL_optinteger(L, 1, -1);
|
||||
if (channel < -1 || channel > 23) luaL_error(L, "channel number must be between 0 and 23");
|
||||
|
||||
float mix[12];
|
||||
mix[0] = luaL_optnumber(L, 2, 1);
|
||||
mix[1] = luaL_optnumber(L, 3, mix[0]);
|
||||
mix[2] = luaL_optnumber(L, 4, mix[0]);
|
||||
mix[3] = luaL_optnumber(L, 5, mix[2]);
|
||||
|
||||
if (channel == -1) {
|
||||
for (int i=0; i<=23; i++) ndspChnSetMix(i, mix);
|
||||
} else {
|
||||
ndspChnSetMix(channel, mix);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
Set the interpolation type of a channel.
|
||||
Note that when a new audio object will play on this channel, this parameter will be
|
||||
reset with the new audio object default (set in `audio:interpolation()`).
|
||||
@function interpolation
|
||||
@tparam[opt] integer channel stop playing audio on this channel; if `nil` will change interpolation type on all channels
|
||||
@tparam[opt=linear] string "none", "linear" or "polyphase"
|
||||
*/
|
||||
static int audio_interpolation(lua_State *L) {
|
||||
if (!isAudioInitialized) luaL_error(L, "audio wasn't initialized correctly");
|
||||
|
||||
int channel = luaL_optinteger(L, 1, -1);
|
||||
if (channel < -1 || channel > 23) luaL_error(L, "channel number must be between 0 and 23");
|
||||
|
||||
const char* interpArg = luaL_optstring(L, 2, "linear");
|
||||
|
||||
ndspInterpType interp;
|
||||
if (strcmp(interpArg, "none") == 0)
|
||||
interp = NDSP_INTERP_NONE;
|
||||
else if (strcmp(interpArg, "linear") == 0)
|
||||
interp = NDSP_INTERP_LINEAR;
|
||||
else if (strcmp(interpArg, "polyphase") == 0)
|
||||
interp = NDSP_INTERP_POLYPHASE;
|
||||
else {
|
||||
luaL_error(L, "unknown interpolation type");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (channel == -1) {
|
||||
for (int i=0; i<=23; i++) ndspChnSetInterp(i, interp);
|
||||
} else {
|
||||
ndspChnSetInterp(channel, interp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
Set the speed of the audio playing in a channel.
|
||||
Speed is expressed as a percentage of the normal playing speed.
|
||||
1 is 100% speed and 2 is 200%, etc.
|
||||
Note that when a new audio object will play on this channel, this parameter will be
|
||||
reset with the new audio object default (set in `audio:speed()`).
|
||||
@function speed
|
||||
@tparam[opt] integer channel stop playing audio on this channel; if `nil` will change interpolation type on all channels
|
||||
@tparam[opt=1] number speed percentage
|
||||
*/
|
||||
static int audio_speed(lua_State *L) {
|
||||
if (!isAudioInitialized) luaL_error(L, "audio wasn't initialized correctly");
|
||||
|
||||
int channel = luaL_optinteger(L, 1, -1);
|
||||
if (channel < -1 || channel > 23) luaL_error(L, "channel number must be between 0 and 23");
|
||||
|
||||
double speed = luaL_optnumber(L, 2, 1);
|
||||
|
||||
if (channel == -1) {
|
||||
for (int i=0; i<=23; i++) {
|
||||
if (channels[i]) ndspChnSetRate(i, channels[i]->rate * speed);
|
||||
}
|
||||
} else {
|
||||
if (channels[channel]) ndspChnSetRate(channel, channels[channel]->rate * speed);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
Stop playing all audio on all channels or a specific channel.
|
||||
@function stop
|
||||
@tparam[opt] integer channel stop playing audio on this channel; if `nil` will stop audio on all channels
|
||||
@treturn integer number of channels where audio was stopped
|
||||
*/
|
||||
static int audio_stop(lua_State *L) {
|
||||
if (!isAudioInitialized) {
|
||||
lua_pushinteger(L, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int channel = luaL_optinteger(L, 1, -1);
|
||||
|
||||
int n = 0;
|
||||
|
||||
if (channel == -1) {
|
||||
for (int i = 0; i <= 23; i++) {
|
||||
if (ndspChnIsPlaying(i)) {
|
||||
ndspChnWaveBufClear(i);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
} else if (channel < 0 || channel > 23) {
|
||||
luaL_error(L, "channel number must be between 0 and 23");
|
||||
} else {
|
||||
if (ndspChnIsPlaying(channel)) {
|
||||
ndspChnWaveBufClear(channel);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
lua_pushinteger(L, n);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/***
|
||||
audio object
|
||||
@section Methods
|
||||
*/
|
||||
|
||||
/***
|
||||
Returns the audio object duration.
|
||||
@function :duration
|
||||
@treturn number duration in seconds
|
||||
*/
|
||||
static int audio_object_duration(lua_State *L) {
|
||||
audio_userdata *audio = luaL_checkudata(L, 1, "LAudio");
|
||||
|
||||
lua_pushnumber(L, (double)(audio->nsamples) / audio->rate);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/***
|
||||
Returns the current playing position.
|
||||
@function :time
|
||||
@tparam[opt] integer channel number; if `nil` will use the first channel found which played this audio
|
||||
@treturn number time in seconds
|
||||
*/
|
||||
static int audio_object_time(lua_State *L) {
|
||||
audio_userdata *audio = luaL_checkudata(L, 1, "LAudio");
|
||||
|
||||
int channel = luaL_optinteger(L, 2, -1);
|
||||
if (channel < -1 || channel > 23) luaL_error(L, "channel number must be between 0 and 23");
|
||||
|
||||
// Search a channel playing the audio object
|
||||
if (channel == -1) {
|
||||
for (int i = 0; i <= 23; i++) {
|
||||
if (channels[i] == audio) {
|
||||
channel = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (channel == -1 || channels[channel] != audio || !isAudioInitialized) // audio not playing
|
||||
lua_pushnumber(L, 0);
|
||||
else
|
||||
lua_pushnumber(L, (double)(ndspChnGetSamplePos(channel)) / audio->rate);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/***
|
||||
Check if the audio is currently playing.
|
||||
@function :playing
|
||||
@tparam[opt] integer channel channel number; if `nil` will search the first channel playing this audio
|
||||
@treturn boolean true if the channel is currently playing the audio, false otherwise
|
||||
*/
|
||||
static int audio_object_playing(lua_State *L) {
|
||||
if (!isAudioInitialized) {
|
||||
lua_pushboolean(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
audio_userdata *audio = luaL_checkudata(L, 1, "LAudio");
|
||||
|
||||
int channel = luaL_optinteger(L, 2, -1);
|
||||
if (channel < -1 || channel > 23) luaL_error(L, "channel number must be between 0 and 23");
|
||||
|
||||
// Search a channel playing the audio object
|
||||
if (channel == -1) {
|
||||
for (int i = 0; i <= 23; i++) {
|
||||
if (channels[i] == audio && ndspChnIsPlaying(i)) {
|
||||
lua_pushboolean(L, true);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
lua_pushboolean(L, false);
|
||||
return 1;
|
||||
|
||||
} else {
|
||||
lua_pushboolean(L, channels[channel] == audio && ndspChnIsPlaying(channel));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
Set the mix parameters (volumes) of the audio.
|
||||
Volumes go from 0 (0%) to 1 (100%).
|
||||
@function :mix
|
||||
@tparam[opt=1] number frontLeft front left volume
|
||||
@tparam[opt=frontLeft] number frontRight front right volume
|
||||
@tparam[opt=frontLeft] number backLeft back left volume
|
||||
@tparam[opt=frontRight] number backRight back right volume
|
||||
*/
|
||||
static int audio_object_mix(lua_State *L) {
|
||||
audio_userdata *audio = luaL_checkudata(L, 1, "LAudio");
|
||||
|
||||
audio->mix[0] = luaL_optnumber(L, 2, 1);
|
||||
audio->mix[1] = luaL_optnumber(L, 3, audio->mix[0]);
|
||||
audio->mix[2] = luaL_optnumber(L, 4, audio->mix[0]);
|
||||
audio->mix[3] = luaL_optnumber(L, 5, audio->mix[2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
Set the interpolation type of the audio.
|
||||
@function :interpolation
|
||||
@tparam[opt=linear] string "none", "linear" or "polyphase"
|
||||
*/
|
||||
static int audio_object_interpolation(lua_State *L) {
|
||||
audio_userdata *audio = luaL_checkudata(L, 1, "LAudio");
|
||||
|
||||
const char* interp = luaL_optstring(L, 2, "linear");
|
||||
|
||||
if (strcmp(interp, "none") == 0)
|
||||
audio->interp = NDSP_INTERP_NONE;
|
||||
else if (strcmp(interp, "linear") == 0)
|
||||
audio->interp = NDSP_INTERP_LINEAR;
|
||||
else if (strcmp(interp, "polyphase") == 0)
|
||||
audio->interp = NDSP_INTERP_POLYPHASE;
|
||||
else {
|
||||
luaL_error(L, "unknown interpolation type");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
Set the speed of the audio.
|
||||
Speed is expressed as a percentage of the normal playing speed.
|
||||
1 is 100% speed and 2 is 200%, etc.
|
||||
@function :speed
|
||||
@tparam[opt=1] number speed percentage
|
||||
*/
|
||||
static int audio_object_speed(lua_State *L) {
|
||||
audio_userdata *audio = luaL_checkudata(L, 1, "LAudio");
|
||||
|
||||
audio->speed = luaL_optnumber(L, 2, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
Plays the audio file.
|
||||
@function :play
|
||||
@tparam[opt=false] boolean loop if the audio should loop or not
|
||||
@tparam[opt] integer channel the channel to play the audio on (0-23); if `nil` will use the first available channel.
|
||||
If the channel was playing another audio, it will be stopped and replaced by this audio.
|
||||
If not set and no channel is available, will return nil plus an error message.
|
||||
@treturn[1] integer channel number the audio is playing on
|
||||
@treturn[2] nil an error happened and the audio was not played
|
||||
@treturn[2] error the error message
|
||||
*/
|
||||
static int audio_object_play(lua_State *L) {
|
||||
if (!isAudioInitialized) luaL_error(L, "audio wasn't initialized correctly");
|
||||
|
||||
audio_userdata *audio = luaL_checkudata(L, 1, "LAudio");
|
||||
bool loop = lua_toboolean(L, 2);
|
||||
int channel = luaL_optinteger(L, 3, -1);
|
||||
|
||||
// Find a free channel
|
||||
if (channel == -1) {
|
||||
for (int i = 0; i <= 23; i++) {
|
||||
if (!ndspChnIsPlaying(i)) {
|
||||
channel = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (channel == -1) {
|
||||
lua_pushnil(L);
|
||||
lua_pushstring(L, "no audio channel is currently available");
|
||||
return 2;
|
||||
}
|
||||
if (channel < 0 || channel > 23) luaL_error(L, "channel number must be between 0 and 23");
|
||||
|
||||
// Set channel parameters
|
||||
ndspChnWaveBufClear(channel);
|
||||
ndspChnReset(channel);
|
||||
ndspChnInitParams(channel);
|
||||
ndspChnSetMix(channel, audio->mix);
|
||||
ndspChnSetInterp(channel, audio->interp);
|
||||
ndspChnSetRate(channel, audio->rate * audio->speed); // maybe hackish way to set a different speed, but it works
|
||||
ndspChnSetFormat(channel, NDSP_CHANNELS(audio->channels) | NDSP_ENCODING(audio->encoding));
|
||||
|
||||
// Send & play audio data
|
||||
ndspWaveBuf* waveBuf = calloc(1, sizeof(ndspWaveBuf));
|
||||
|
||||
waveBuf->data_vaddr = audio->data;
|
||||
waveBuf->nsamples = audio->nsamples;
|
||||
waveBuf->looping = loop;
|
||||
|
||||
DSP_FlushDataCache((u32*)audio->data, audio->size);
|
||||
|
||||
ndspChnWaveBufAdd(channel, waveBuf);
|
||||
channels[channel] = audio;
|
||||
|
||||
lua_pushinteger(L, channel);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/***
|
||||
Stop playing an audio object.
|
||||
@function :stop
|
||||
@tparam[opt] integer channel stop playing the audio on this channel; if `nil` will stop all channels playing this audio.
|
||||
If the channel is playing another audio object, this function will do nothing.
|
||||
@treturn integer number of channels where this audio was stopped
|
||||
*/
|
||||
static int audio_object_stop(lua_State *L) {
|
||||
if (!isAudioInitialized) {
|
||||
lua_pushinteger(L, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
audio_userdata *audio = luaL_checkudata(L, 1, "LAudio");
|
||||
int channel = luaL_optinteger(L, 2, -1);
|
||||
|
||||
int n = 0;
|
||||
|
||||
if (channel == -1) {
|
||||
for (int i = 0; i <= 23; i++) {
|
||||
if (channels[i] == audio && ndspChnIsPlaying(i)) {
|
||||
ndspChnWaveBufClear(i);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
} else if (channel < 0 || channel > 23) {
|
||||
luaL_error(L, "channel number must be between 0 and 23");
|
||||
} else {
|
||||
if (channels[channel] == audio && ndspChnIsPlaying(channel)) {
|
||||
ndspChnWaveBufClear(channel);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
lua_pushinteger(L, n);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/***
|
||||
Returns the audio object type.
|
||||
@function :type
|
||||
@treturn string "ogg" or "wav"
|
||||
*/
|
||||
static int audio_object_type(lua_State *L) {
|
||||
audio_userdata *audio = luaL_checkudata(L, 1, "LAudio");
|
||||
|
||||
if (audio->type == TYPE_OGG)
|
||||
lua_pushstring(L, "ogg");
|
||||
else if (audio->type == TYPE_WAV)
|
||||
lua_pushstring(L, "wav");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/***
|
||||
Unload an audio object.
|
||||
@function :unload
|
||||
*/
|
||||
static int audio_object_unload(lua_State *L) {
|
||||
audio_userdata *audio = luaL_checkudata(L, 1, "LAudio");
|
||||
|
||||
// Stop playing the audio
|
||||
if (isAudioInitialized) {
|
||||
for (int i = 0; i <= 23; i++) {
|
||||
if (channels[i] == audio) {
|
||||
ndspChnWaveBufClear(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->type == TYPE_OGG) ov_clear(&audio->vf);
|
||||
|
||||
// Free memory
|
||||
linearFree(audio->data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
audio object (ogg-only).
|
||||
Ogg Vorbis files specific methods.
|
||||
Using one of theses methods will throw an error if used on an non-ogg audio object.
|
||||
@section Methods
|
||||
*/
|
||||
|
||||
/***
|
||||
Returns basic information about the audio in a vorbis bitstream.
|
||||
@function :info
|
||||
@treturn infoTable information table
|
||||
*/
|
||||
static int audio_object_info(lua_State *L) {
|
||||
audio_userdata *audio = luaL_checkudata(L, 1, "LAudio");
|
||||
|
||||
if (audio->type != TYPE_OGG) luaL_error(L, "only avaible on OGG audio objects");
|
||||
|
||||
vorbis_info* vi = ov_info(&audio->vf, -1);
|
||||
if (vi == NULL) luaL_error(L, "could not retrieve audio stream informations");
|
||||
|
||||
lua_createtable(L, 0, 6);
|
||||
|
||||
lua_pushinteger(L, vi->version);
|
||||
lua_setfield(L, -2, "version");
|
||||
|
||||
lua_pushinteger(L, vi->channels);
|
||||
lua_setfield(L, -2, "channels");
|
||||
|
||||
lua_pushinteger(L, vi->rate);
|
||||
lua_setfield(L, -2, "rate");
|
||||
|
||||
lua_pushinteger(L, vi->bitrate_upper);
|
||||
lua_setfield(L, -2, "bitrateUpper");
|
||||
|
||||
lua_pushinteger(L, vi->bitrate_nominal);
|
||||
lua_setfield(L, -2, "bitrateNominal");
|
||||
|
||||
lua_pushinteger(L, vi->bitrate_lower);
|
||||
lua_setfield(L, -2, "bitrateLower");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/***
|
||||
Returns the Ogg Vorbis bitstream comment.
|
||||
@function :comment
|
||||
@treturn commentTable comment table
|
||||
*/
|
||||
static int audio_object_comment(lua_State *L) {
|
||||
audio_userdata *audio = luaL_checkudata(L, 1, "LAudio");
|
||||
|
||||
if (audio->type != TYPE_OGG) luaL_error(L, "only avaible on OGG audio objects");
|
||||
|
||||
vorbis_comment *vc = ov_comment(&audio->vf, -1);
|
||||
|
||||
if (vc == NULL) luaL_error(L, "could not retrieve audio stream comment");
|
||||
|
||||
lua_createtable(L, 0, 5);
|
||||
|
||||
lua_newtable(L);
|
||||
for (int i=0; i<vc->comments; i++) {
|
||||
lua_pushstring(L, vc->user_comments[i]);
|
||||
lua_seti(L, -2, i+1);
|
||||
}
|
||||
lua_setfield(L, -2, "userComments");
|
||||
|
||||
lua_pushstring(L, vc->vendor);
|
||||
lua_setfield(L, -2, "vendor");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/***
|
||||
Tables return.
|
||||
The detailled table structures returned by some methods of audio objects.
|
||||
@section
|
||||
*/
|
||||
|
||||
/***
|
||||
Vorbis bitstream information, returned by audio:info().
|
||||
If bitrateLower == bitrateNominal == bitrateUpper, the stream is fixed bitrate.
|
||||
@table infoTable
|
||||
@tfield integer version Vorbis encoder version used to create this bitstream
|
||||
@tfield integer channels number of channels in bitstream
|
||||
@tfield integer rate sampling rate of the bitstream
|
||||
@tfield integer bitrateUpper the upper limit in a VBR bitstream; may be unset if no limit exists
|
||||
@tfield integer bitrateNominal the average bitrate for a VBR bitstream; may be unset
|
||||
@tfield integer bitrateLower the lower limit in a VBR bitstream; may be unset if no limit exists
|
||||
*/
|
||||
|
||||
/***
|
||||
Vorbis bitstream comment, returned by audio:comment().
|
||||
@table commentTable
|
||||
@tfield table userComments list of all the user comment
|
||||
@tfield string vendor information about the Vorbis implementation that encoded the file
|
||||
*/
|
||||
|
||||
// Audio object methods
|
||||
static const struct luaL_Reg audio_object_methods[] = {
|
||||
// common
|
||||
{ "duration", audio_object_duration },
|
||||
{ "time", audio_object_time },
|
||||
{ "playing", audio_object_playing },
|
||||
{ "mix", audio_object_mix },
|
||||
{ "interpolation", audio_object_interpolation },
|
||||
{ "speed", audio_object_speed },
|
||||
{ "play", audio_object_play },
|
||||
{ "stop", audio_object_stop },
|
||||
{ "type", audio_object_type },
|
||||
{ "unload", audio_object_unload },
|
||||
{ "__gc", audio_object_unload },
|
||||
// ogg only
|
||||
{ "info", audio_object_info },
|
||||
{ "comment", audio_object_comment },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
// Library functions
|
||||
static const struct luaL_Reg audio_lib[] = {
|
||||
{ "load", audio_load },
|
||||
{ "playing", audio_playing },
|
||||
{ "mix", audio_mix },
|
||||
{ "interpolation", audio_interpolation },
|
||||
{ "speed", audio_speed },
|
||||
{ "stop", audio_stop },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
int luaopen_audio_lib(lua_State *L) {
|
||||
luaL_newmetatable(L, "LAudio");
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -2, "__index");
|
||||
luaL_setfuncs(L, audio_object_methods, 0);
|
||||
|
||||
luaL_newlib(L, audio_lib);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void load_audio_lib(lua_State *L) {
|
||||
isAudioInitialized = !ndspInit(); // ndspInit returns 0 in case of success
|
||||
|
||||
luaL_requiref(L, "ctr.audio", luaopen_audio_lib, false);
|
||||
}
|
||||
|
||||
void unload_audio_lib(lua_State *L) {
|
||||
if (isAudioInitialized) ndspExit();
|
||||
}
|
||||
11
source/ctr.c
11
source/ctr.c
|
|
@ -91,6 +91,14 @@ The `ctr.cam` module.
|
|||
*/
|
||||
void load_cam_lib(lua_State *L);
|
||||
|
||||
/***
|
||||
The `ctr.audio` module.
|
||||
@table audio
|
||||
@see ctr.audio
|
||||
*/
|
||||
void load_audio_lib(lua_State *L);
|
||||
void unload_audio_lib(lua_State *L);
|
||||
|
||||
/***
|
||||
Return whether or not the program should continue.
|
||||
@function run
|
||||
|
|
@ -132,7 +140,8 @@ struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); }
|
|||
{ "qtm", load_qtm_lib, NULL },
|
||||
{ "cfgu", load_cfgu_lib, NULL },
|
||||
{ "socket", load_socket_lib, NULL },
|
||||
{ "cam", load_cam_lib, NULL },
|
||||
{ "cam", load_cam_lib, NULL },
|
||||
{ "audio", load_audio_lib, unload_audio_lib },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ The `gfx` module.
|
|||
|
||||
#include "font.h"
|
||||
|
||||
bool isGfxInitialised = false;
|
||||
bool isGfxInitialized = false;
|
||||
bool is3DEnabled = false; //TODO: add a function for this in the ctrulib/sf2dlib.
|
||||
|
||||
/***
|
||||
|
|
@ -459,7 +459,7 @@ void load_gfx_lib(lua_State *L) {
|
|||
sf2d_init();
|
||||
sftd_init();
|
||||
|
||||
isGfxInitialised = true;
|
||||
isGfxInitialized = true;
|
||||
|
||||
luaL_requiref(L, "ctr.gfx", luaopen_gfx_lib, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@
|
|||
void load_ctr_lib(lua_State *L);
|
||||
void unload_ctr_lib(lua_State *L);
|
||||
|
||||
bool isGfxInitialised;
|
||||
bool isGfxInitialized;
|
||||
|
||||
// Display an error
|
||||
void error(const char *error) {
|
||||
if (!isGfxInitialised) gfxInitDefault();
|
||||
if (!isGfxInitialized) gfxInitDefault();
|
||||
gfxSet3D(false);
|
||||
|
||||
consoleInit(GFX_TOP, NULL);
|
||||
|
|
@ -28,7 +28,7 @@ void error(const char *error) {
|
|||
gspWaitForVBlank();
|
||||
}
|
||||
|
||||
if (!isGfxInitialised) gfxExit();
|
||||
if (!isGfxInitialized) gfxExit();
|
||||
}
|
||||
|
||||
// Main loop
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue