diff --git a/.gitmodules b/.gitmodules index cb03a84..639cb6c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -27,3 +27,6 @@ [submodule "vendor/gorilla-audio"] path = vendor/gorilla-audio url = https://github.com/IceDragon200/gorilla-audio.git +[submodule "vendor/libsoundio"] + path = vendor/libsoundio + url = https://github.com/andrewrk/libsoundio diff --git a/.travis.yml b/.travis.yml index 8de6a85..b563101 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,18 +2,25 @@ language: cpp sudo: false addons: apt: - sources: + sources: &sources - george-edison55-precise-backports - packages: + - ubuntu-toolchain-r-test + packages: &deps - cmake - cmake-data - freeglut3-dev - freetype + - libasound2-dev - libdevil-dev + - libflac-dev - libgles2-mesa-dev - libglew-dev - libglu1-mesa-dev + - libogg-dev - libopenal-dev + - libpulse-dev + - libvorbis-dev + - libvorbisenc2 - libx11-dev - libxi-dev - libxmu-dev @@ -23,15 +30,40 @@ matrix: include: - os: linux compiler: gcc - env: MOON_MRUBY_TOOLCHAIN=gcc - + env: + - MOON_MRUBY_TOOLCHAIN=gcc + - COMPILER=g++-4.9 + before_install: + - export CC="gcc-4.9" + - export CXX="g++-4.9" + addons: + apt: + sources: + - *sources + packages: + - *deps + - gcc-4.9 + - g++-4.9 - os: linux compiler: clang env: - MOON_MRUBY_TOOLCHAIN=clang + - COMPILER=clang++-3.7 - LD_LIBRARY_PATH: "${TRAVIS_BUILD_DIR}/build/vendor/glfw/src" + before_install: + - export CC="clang-3.7" + - export CXX="clang++-3.7" + addons: + apt: + sources: + - *sources + - llvm-toolchain-precise-3.7 + packages: + - *deps + - clang-3.7 - os: osx + osx_image: xcode7.3 compiler: clang env: - MOON_MRUBY_TOOLCHAIN=clang diff --git a/CMakeLists.txt b/CMakeLists.txt index b5c714e..196c789 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,12 +4,19 @@ project(moon) find_library(YAML yaml) +include(cmake/BuildSndfile.cmake) + add_subdirectory("vendor/sil") # cmake doesn't like relative paths for include dirs, so we'll need the absolute # path instead get_filename_component(SIL_INCLUDE_DIR "vendor/sil/include" ABSOLUTE) add_subdirectory("vendor/soil") add_subdirectory("vendor/gorilla-audio/build") +set(ENABLE_JACK OFF) +set(BUILD_EXAMPLE_PROGRAMS OFF) +set(BUILD_TESTS OFF) +set(BUILD_DYNAMIC_LIBS OFF) +add_subdirectory("vendor/libsoundio") #add_subdirectory("vendor/glm") option(freetype-gl_BUILD_DEMOS OFF) @@ -30,7 +37,7 @@ if(RAKE_VERBOSE) endif(RAKE_VERBOSE) add_custom_target(moon-mruby ALL - DEPENDS SIL SOIL glfw freetype-gl gorilla + DEPENDS SIL SOIL glfw freetype-gl gorilla libsoundio_static libsndfile COMMAND rake ${RAKE_VERBOSE_FLAG} MRUBY_CONFIG="${CMAKE_SOURCE_DIR}/mrb_config.rb" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/vendor/mruby" ) diff --git a/README.md b/README.md index 73cf57c..003dd94 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ [![Coverity Scan Build Status](https://img.shields.io/coverity/scan/5666.svg)](https://scan.coverity.com/projects/5666) ## Introduction -Moon is a experimental prototyping 2D Game Engine written in C++ and scripted using [mruby](https://github.com/mruby/mruby). +Moon is a experimental 2D game engine prototype written in C++ and scripted using [mruby](https://github.com/mruby/mruby). ## Supported -Currently testing is done linux, moon may not run or even compile on other systems. If you managed to get Moon running on your system, please open a PR and share your changes to get it built. +Currently we have builds fo Linux and OS X. Moon may not run or even compile on other systems. If you managed to get Moon running on your system, please open a PR and share your changes to get it built. ## Requirements @@ -24,7 +24,7 @@ ruby >= 1.9.2 yard (for docs) # runtime -OpenAL +OpenAL # for gorilla audio OpenGL # For dependency docs @@ -32,6 +32,11 @@ doxygen # for mruby-yaml yaml + +# for libsndfile +flac +libogg +libvorbis ``` ## Support diff --git a/cmake/BuildSndfile.cmake b/cmake/BuildSndfile.cmake new file mode 100644 index 0000000..c2572cc --- /dev/null +++ b/cmake/BuildSndfile.cmake @@ -0,0 +1,12 @@ +include(ExternalProject) + +set(SNDFILE_TARBALL "libsndfile-1.0.27") + +ExternalProject_Add(libsndfile + URL http://www.mega-nerd.com/libsndfile/files/${SNDFILE_TARBALL}.tar.gz + BUILD_IN_SOURCE 1 + INSTALL_DIR vendor/libsndfile + CONFIGURE_COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/vendor/libsndfile --disable-shared + BUILD_COMMAND $(MAKE) V=1 + INSTALL_COMMAND $(MAKE) install +) diff --git a/modules/audio-gorilla/include/moon/gorilla/audio.hxx b/modules/audio-gorilla/include/moon/audio/gorilla/audio.hxx similarity index 76% rename from modules/audio-gorilla/include/moon/gorilla/audio.hxx rename to modules/audio-gorilla/include/moon/audio/gorilla/audio.hxx index 0215b19..70f4850 100644 --- a/modules/audio-gorilla/include/moon/gorilla/audio.hxx +++ b/modules/audio-gorilla/include/moon/audio/gorilla/audio.hxx @@ -1,9 +1,10 @@ -#ifndef MOON_AUDIO_H -#define MOON_AUDIO_H +#ifndef MOON_AUDIO_GORILLA_H +#define MOON_AUDIO_GORILLA_H /* Gorilla Audio */ #include #include +#define MOON_AUDIO_BACKEND "gorilla" namespace Moon { class Audio { @@ -14,6 +15,7 @@ namespace Moon { static ga_Mixer* GetMixer(); static ga_StreamManager* GetStreamMgr(); protected: + static bool m_initialized; static gau_Manager* m_mgr; static ga_Mixer* m_mixer; static ga_StreamManager* m_streamMgr; diff --git a/modules/audio-gorilla/include/moon/gorilla/mrb/music.hxx b/modules/audio-gorilla/include/moon/audio/gorilla/mrb/music.hxx similarity index 100% rename from modules/audio-gorilla/include/moon/gorilla/mrb/music.hxx rename to modules/audio-gorilla/include/moon/audio/gorilla/mrb/music.hxx diff --git a/modules/audio-gorilla/include/moon/gorilla/mrb/sound.hxx b/modules/audio-gorilla/include/moon/audio/gorilla/mrb/sound.hxx similarity index 100% rename from modules/audio-gorilla/include/moon/gorilla/mrb/sound.hxx rename to modules/audio-gorilla/include/moon/audio/gorilla/mrb/sound.hxx diff --git a/modules/audio-gorilla/include/moon/gorilla/music.hxx b/modules/audio-gorilla/include/moon/audio/gorilla/music.hxx similarity index 83% rename from modules/audio-gorilla/include/moon/gorilla/music.hxx rename to modules/audio-gorilla/include/moon/audio/gorilla/music.hxx index 8e97307..157f65b 100644 --- a/modules/audio-gorilla/include/moon/gorilla/music.hxx +++ b/modules/audio-gorilla/include/moon/audio/gorilla/music.hxx @@ -1,7 +1,7 @@ #ifndef MOON_MUSIC_H #define MOON_MUSIC_H -#include "moon/gorilla/audio.hxx" +#include "moon/audio/gorilla/audio.hxx" namespace Moon { struct Music { diff --git a/modules/audio-gorilla/mrbgem.rake b/modules/audio-gorilla/mrbgem.rake index 77fc52b..aa5e0b5 100644 --- a/modules/audio-gorilla/mrbgem.rake +++ b/modules/audio-gorilla/mrbgem.rake @@ -17,9 +17,9 @@ MRuby::Gem::Specification.new('mruby-moon-audio-gorilla') do |spec| cc.flags << '-Wextra' end - #spec.cxx do |cxx| - # cxx.flags << '-std=c++11' - #end + spec.linker do |l| + l.libraries << 'gorilla' + end spec.add_dependency 'mruby-moon-system' end diff --git a/modules/audio-gorilla/src/audio.cxx b/modules/audio-gorilla/src/audio.cxx index 3871200..49e3869 100644 --- a/modules/audio-gorilla/src/audio.cxx +++ b/modules/audio-gorilla/src/audio.cxx @@ -1,4 +1,4 @@ -#include "moon/gorilla/audio.hxx" +#include "moon/audio/gorilla/audio.hxx" namespace Moon { gau_Manager* Audio::m_mgr = NULL; @@ -41,4 +41,3 @@ namespace Moon { return m_streamMgr; }; } - diff --git a/modules/audio-gorilla/src/mrb_moon_audio.cxx b/modules/audio-gorilla/src/mrb_moon_audio.cxx index cd82a80..86aa53a 100644 --- a/modules/audio-gorilla/src/mrb_moon_audio.cxx +++ b/modules/audio-gorilla/src/mrb_moon_audio.cxx @@ -1,8 +1,8 @@ #include #include -#include "moon/gorilla/audio.hxx" -#include "moon/gorilla/mrb/music.hxx" -#include "moon/gorilla/mrb/sound.hxx" +#include "moon/audio/gorilla/audio.hxx" +#include "moon/audio/gorilla/mrb/music.hxx" +#include "moon/audio/gorilla/mrb/sound.hxx" /* Call once per step/frame to update the internal Audio module. */ diff --git a/modules/audio-gorilla/src/mrb_music.cxx b/modules/audio-gorilla/src/mrb_music.cxx index 1cc01be..3f40445 100644 --- a/modules/audio-gorilla/src/mrb_music.cxx +++ b/modules/audio-gorilla/src/mrb_music.cxx @@ -2,8 +2,8 @@ #include #include #include -#include "moon/gorilla/mrb/music.hxx" -#include "moon/gorilla/music.hxx" +#include "moon/audio/gorilla/mrb/music.hxx" +#include "moon/audio/gorilla/music.hxx" #include "moon/api.h" #include "moon/intern.h" diff --git a/modules/audio-gorilla/src/mrb_sound.cxx b/modules/audio-gorilla/src/mrb_sound.cxx index e7e22de..77aab1c 100644 --- a/modules/audio-gorilla/src/mrb_sound.cxx +++ b/modules/audio-gorilla/src/mrb_sound.cxx @@ -2,8 +2,8 @@ #include #include #include -#include "moon/gorilla/mrb/sound.hxx" -#include "moon/gorilla/audio.hxx" +#include "moon/audio/gorilla/mrb/sound.hxx" +#include "moon/audio/gorilla/audio.hxx" #include "moon/api.h" #include "moon/intern.h" @@ -16,7 +16,7 @@ sound_free(mrb_state *mrb, void *p) } } -MOON_C_API const struct mrb_data_type sound_data_type = { "Sound", sound_free }; +MOON_C_API const struct mrb_data_type sound_data_type = { "Moon::Sound", sound_free }; static inline ga_Sound* get_sound(mrb_state *mrb, mrb_value self) diff --git a/modules/audio-libsoundio/include/moon/audio/libsoundio/audio.hxx b/modules/audio-libsoundio/include/moon/audio/libsoundio/audio.hxx new file mode 100644 index 0000000..18daa07 --- /dev/null +++ b/modules/audio-libsoundio/include/moon/audio/libsoundio/audio.hxx @@ -0,0 +1,45 @@ +#ifndef MOON_AUDIO_H +#define MOON_AUDIO_H + +#include "moon/intern.h" +#include +#include +#define MOON_AUDIO_BACKEND "libsoundio" +#include "moon/audio/libsoundio/mixer.hxx" +#include "moon/audio/libsoundio/music.hxx" +#include "moon/audio/libsoundio/sound.hxx" + +namespace Moon { + class Audio { + public: + enum ErrorCode { + MOON_AUDIO_OK, + // SoundIO failed to create + MOON_AUDIO_CREATE_ERROR, + // could not establish a connection to the audio server/device + MOON_AUDIO_CONNECTION_ERROR, + // There is no default device present + MOON_AUDIO_NO_DEVICE, + // The device specified by the index, has gone away or does not exist + MOON_AUDIO_DEVICE_MISSING, + // Could not open a stream for audio output + MOON_AUDIO_COULD_NOT_OPEN_STREAM, + // The specified channel layout is not supported + MOON_AUDIO_STREAM_CHANNEL_LAYOUT_ERROR, + // Stream could not be started + MOON_AUDIO_STREAM_START_ERROR + }; + + static ErrorCode Initialize(); + static void Update(); + static void Terminate(); + static Mixer* m_mixer; + protected: + static bool m_initialized; + static struct SoundIo* m_soundIO; + static struct SoundIoDevice* m_device; + static struct SoundIoOutStream* m_outStream; + }; +}; + +#endif diff --git a/modules/audio-libsoundio/include/moon/audio/libsoundio/handle.hxx b/modules/audio-libsoundio/include/moon/audio/libsoundio/handle.hxx new file mode 100644 index 0000000..edfac08 --- /dev/null +++ b/modules/audio-libsoundio/include/moon/audio/libsoundio/handle.hxx @@ -0,0 +1,28 @@ +#ifndef MOON_AUDIO_LIBSOUNDIO_HANDLE_H +#define MOON_AUDIO_LIBSOUNDIO_HANDLE_H + +#include "moon/intern.h" +#include +#include +#include "moon/audio/libsoundio/source.hxx" +#include "moon/audio/libsoundio/music.hxx" +#include "moon/audio/libsoundio/sound.hxx" +#include "moon/audio/libsoundio/loop.hxx" + +namespace Moon { + class Handle { + public: + Handle(Moon::Source* source); + ~Handle(); + + void mix(struct SoundIoChannelArea *areas, const struct SoundIoChannelLayout &layout, const float sampleRate, unsigned int frames); + + float pan; + float pitch; + float gain; + private: + Moon::Source* source; // TODO:mrb_sound/music sources will need to be wrapped... if a source deallocates, the handle will be broken (shared_ptr) + }; +}; + +#endif diff --git a/modules/audio-libsoundio/include/moon/audio/libsoundio/loop.hxx b/modules/audio-libsoundio/include/moon/audio/libsoundio/loop.hxx new file mode 100644 index 0000000..5df06d5 --- /dev/null +++ b/modules/audio-libsoundio/include/moon/audio/libsoundio/loop.hxx @@ -0,0 +1,29 @@ +#ifndef MOON_AUDIO_LIBSOUNDIO_LOOP_H +#define MOON_AUDIO_LIBSOUNDIO_LOOP_H + +#include "moon/intern.h" +#include +#include +#include "moon/audio/libsoundio/source.hxx" +#include "moon/audio/libsoundio/mixer.hxx" + +namespace Moon +{ + class Loop : public Source { + public: + Loop(Moon::Source* source, std::uint32_t trigger, std::uint32_t target); + virtual ~Loop(); + + int read(float* dst, int frames); + std::uint32_t seek(std::uint32_t pos); + + int channels(); + int sampleRate(); + private: + std::uint32_t trigger; + std::uint32_t target; + Moon::Source* source; // TODO:mrb_sound/music sources will need to be wrapped... if a source deallocates, the handle will be broken (shared_ptr) + }; +}; + +#endif diff --git a/modules/audio-libsoundio/include/moon/audio/libsoundio/mixer.hxx b/modules/audio-libsoundio/include/moon/audio/libsoundio/mixer.hxx new file mode 100644 index 0000000..80df4b4 --- /dev/null +++ b/modules/audio-libsoundio/include/moon/audio/libsoundio/mixer.hxx @@ -0,0 +1,21 @@ +#ifndef MOON_AUDIO_LIBSOUNDIO_MIXER_H +#define MOON_AUDIO_LIBSOUNDIO_MIXER_H + +#include "moon/intern.h" +#include +#include +#include "moon/audio/libsoundio/handle.hxx" + +namespace Moon +{ + class Mixer { + public: + Mixer(); + + void mix(struct SoundIoChannelArea *areas, const struct SoundIoChannelLayout &layout, const float sampleRate, unsigned int frames); + std::vector handles; + private: + }; +} + +#endif diff --git a/modules/audio-libsoundio/include/moon/audio/libsoundio/mrb/music.hxx b/modules/audio-libsoundio/include/moon/audio/libsoundio/mrb/music.hxx new file mode 100644 index 0000000..4789e26 --- /dev/null +++ b/modules/audio-libsoundio/include/moon/audio/libsoundio/mrb/music.hxx @@ -0,0 +1,12 @@ +#ifndef MMRB_MUSIC_H +#define MMRB_MUSIC_H + +#include +#include +#include +#include "moon/api.h" + +MOON_C_API const struct mrb_data_type music_data_type; +MOON_C_API void mmrb_music_init(mrb_state *mrb, struct RClass *mod); + +#endif diff --git a/modules/audio-libsoundio/include/moon/audio/libsoundio/mrb/sound.hxx b/modules/audio-libsoundio/include/moon/audio/libsoundio/mrb/sound.hxx new file mode 100644 index 0000000..8a6e3d5 --- /dev/null +++ b/modules/audio-libsoundio/include/moon/audio/libsoundio/mrb/sound.hxx @@ -0,0 +1,12 @@ +#ifndef MMRB_SOUND_H +#define MMRB_SOUND_H + +#include +#include +#include +#include "moon/api.h" + +MOON_C_API const struct mrb_data_type sound_data_type; +MOON_C_API void mmrb_sound_init(mrb_state *mrb, struct RClass *mod); + +#endif diff --git a/modules/audio-libsoundio/include/moon/audio/libsoundio/mrb/sound_buffer.hxx b/modules/audio-libsoundio/include/moon/audio/libsoundio/mrb/sound_buffer.hxx new file mode 100644 index 0000000..ec63e80 --- /dev/null +++ b/modules/audio-libsoundio/include/moon/audio/libsoundio/mrb/sound_buffer.hxx @@ -0,0 +1,12 @@ +#ifndef MMRB_SOUND_BUFFER_H +#define MMRB_SOUND_BUFFER_H + +#include +#include +#include +#include "moon/api.h" + +MOON_C_API const struct mrb_data_type sound_buffer_data_type; +MOON_C_API void mmrb_sound_buffer_init(mrb_state *mrb, struct RClass *mod); + +#endif diff --git a/modules/audio-libsoundio/include/moon/audio/libsoundio/music.hxx b/modules/audio-libsoundio/include/moon/audio/libsoundio/music.hxx new file mode 100644 index 0000000..2cf3496 --- /dev/null +++ b/modules/audio-libsoundio/include/moon/audio/libsoundio/music.hxx @@ -0,0 +1,27 @@ +#ifndef MOON_AUDIO_LIBSOUNDIO_MUSIC_H +#define MOON_AUDIO_LIBSOUNDIO_MUSIC_H + +#include "moon/intern.h" +#include +#include +#include "moon/audio/libsoundio/source.hxx" +#include "moon/audio/libsoundio/mixer.hxx" + +namespace Moon +{ + class Music : public Source { + public: + Music(const std::string filename); + virtual ~Music(); + + int read(float* dst, int frames); + std::uint32_t seek(std::uint32_t pos); + + int channels(); + int sampleRate(); + private: + SndfileHandle file; + }; +}; + +#endif diff --git a/modules/audio-libsoundio/include/moon/audio/libsoundio/sound.hxx b/modules/audio-libsoundio/include/moon/audio/libsoundio/sound.hxx new file mode 100644 index 0000000..c5eab20 --- /dev/null +++ b/modules/audio-libsoundio/include/moon/audio/libsoundio/sound.hxx @@ -0,0 +1,32 @@ +#ifndef MOON_AUDIO_LIBSOUNDIO_SOUND_H +#define MOON_AUDIO_LIBSOUNDIO_SOUND_H + +#include "moon/intern.h" +#include +#include +#include "moon/audio/libsoundio/source.hxx" +#include "moon/audio/libsoundio/mixer.hxx" + +namespace Moon +{ + class Sound : public Source { + public: + Sound(const std::string filename); + virtual ~Sound(); + + int read(float* dst, int frames); + std::uint32_t seek(std::uint32_t pos); + + int channels(); + int sampleRate(); + private: + float* source; // [] ? + int m_channels; + int m_sampleRate; + + std::uint32_t totalFrames; + std::uint32_t currentFrame; + }; +}; + +#endif diff --git a/modules/audio-libsoundio/include/moon/audio/libsoundio/source.hxx b/modules/audio-libsoundio/include/moon/audio/libsoundio/source.hxx new file mode 100644 index 0000000..187f437 --- /dev/null +++ b/modules/audio-libsoundio/include/moon/audio/libsoundio/source.hxx @@ -0,0 +1,19 @@ +#ifndef MOON_AUDIO_LIBSOUNDIO_SOURCE_H +#define MOON_AUDIO_LIBSOUNDIO_SOURCE_H + +#include "moon/intern.h" + +namespace Moon +{ + class Source { + public: + virtual int read(float* dst, int frames) = 0; + virtual std::uint32_t seek(std::uint32_t pos) = 0; + virtual std::uint32_t tell() { return seek(0); }; + // + virtual int channels() = 0; + virtual int sampleRate() = 0; + }; +} + +#endif diff --git a/modules/audio-portaudio/mrbgem.rake b/modules/audio-libsoundio/mrbgem.rake similarity index 75% rename from modules/audio-portaudio/mrbgem.rake rename to modules/audio-libsoundio/mrbgem.rake index 2d26341..f679f40 100644 --- a/modules/audio-portaudio/mrbgem.rake +++ b/modules/audio-libsoundio/mrbgem.rake @@ -1,10 +1,10 @@ #encoding:UTF-8 -MRuby::Gem::Specification.new('mruby-moon-audio-portaudio') do |spec| +MRuby::Gem::Specification.new('mruby-moon-audio-libsoundio') do |spec| spec.license = 'MIT' spec.authors = ['Blaž Hrastnik', 'Corey Powell'] spec.version = '0.0.0' spec.summary = 'Moon audio modules' - spec.description = 'Moon Engine\'s audio module (using Port Audio)' + spec.description = 'Moon Engine\'s audio module (using SoundIO)' spec.homepage = 'https://github.com/polyfox/moon' # compiler config spec.cc.include_paths << ["#{build.root}/src", "#{build.root}/include"] @@ -17,5 +17,10 @@ MRuby::Gem::Specification.new('mruby-moon-audio-portaudio') do |spec| cc.flags << '-Wextra' end + spec.linker do |l| + l.libraries << 'soundio' + l.libraries << 'sndfile' + end + spec.add_dependency 'mruby-moon-system' end diff --git a/modules/audio-portaudio/mrblib/audio.rb b/modules/audio-libsoundio/mrblib/audio.rb similarity index 51% rename from modules/audio-portaudio/mrblib/audio.rb rename to modules/audio-libsoundio/mrblib/audio.rb index 7740a33..640f823 100644 --- a/modules/audio-portaudio/mrblib/audio.rb +++ b/modules/audio-libsoundio/mrblib/audio.rb @@ -1,5 +1,5 @@ module Moon module Audio - NAME = 'moon-audio-portaudio' + NAME = 'moon-audio-libsoundio' end end diff --git a/modules/audio-libsoundio/mrblib/sound_buffer.rb b/modules/audio-libsoundio/mrblib/sound_buffer.rb new file mode 100644 index 0000000..1f551a8 --- /dev/null +++ b/modules/audio-libsoundio/mrblib/sound_buffer.rb @@ -0,0 +1,16 @@ +module Moon + class SoundBuffer + private :initialize_create + private :initialize_by_filename + + def initialize(*args) + if args.size == 1 + initialize_by_filename(args.first) + elsif args.size >= 2 + initialize_create(*args) + else + raise ArgumentError, "expected wrong argument count, (expected 1, 2 or 3)" + end + end + end +end diff --git a/modules/audio-libsoundio/src/audio.cxx b/modules/audio-libsoundio/src/audio.cxx new file mode 100644 index 0000000..3c11f20 --- /dev/null +++ b/modules/audio-libsoundio/src/audio.cxx @@ -0,0 +1,113 @@ +#include "moon/intern.h" +#include "moon/audio/libsoundio/audio.hxx" +#include + +static void Moon_AudioWrite(struct SoundIoOutStream *outstream, int frameCountMin, int frameCountMax) { + struct SoundIoChannelArea *areas; + const struct SoundIoChannelLayout *layout = &outstream->layout; + const float sampleRate = outstream->sample_rate; + const float secondsPerFrame = 1.0f / sampleRate; + int framesLeft = frameCountMax; + //printf("Doing an audio write: %d\n", frameCountMax); + while (framesLeft > 0) { + int frameCount = framesLeft; + int err = soundio_outstream_begin_write(outstream, &areas, &frameCount); + if (err) { + // device has died or something worse D; + printf("Error writing to outstream\n"); + break; + } + if (!frameCount) { + printf("No more frames, jumping ship\n"); + break; + } + + // let mixer fill the buffer! + Moon::Audio::m_mixer->mix(areas, *layout, sampleRate, frameCount); + + err = soundio_outstream_end_write(outstream); + if (err) { + printf("Error ending writing to outstream\n"); + // something terrible happened. >: + break; + } + framesLeft -= frameCount; + } +} + +namespace Moon +{ + struct SoundIo* Audio::m_soundIO = NULL; + struct SoundIoDevice* Audio::m_device = NULL; + struct SoundIoOutStream* Audio::m_outStream = NULL; + + Mixer* Audio::m_mixer = NULL; + + Audio::ErrorCode Audio::Initialize() + { + m_soundIO = soundio_create(); + // Should I use assert, or set a flag and leave it for mruby to pick up? + if (!m_soundIO) { + return Moon::Audio::ErrorCode::MOON_AUDIO_CREATE_ERROR; + } + int err = soundio_connect(m_soundIO); + if (err) { + return Moon::Audio::ErrorCode::MOON_AUDIO_CONNECTION_ERROR; + } + soundio_flush_events(m_soundIO); + // Determine the default device + const int defaultDeviceIndex = soundio_default_output_device_index(m_soundIO); + if (defaultDeviceIndex < 0) { + return Moon::Audio::ErrorCode::MOON_AUDIO_NO_DEVICE; + } + // retrieve the device specified by the index + m_device = soundio_get_output_device(m_soundIO, defaultDeviceIndex); + if (!m_device) { + return Moon::Audio::ErrorCode::MOON_AUDIO_DEVICE_MISSING; + } + // create an output stream + m_outStream = soundio_outstream_create(m_device); + // 100ms, I hope that's enough + m_outStream->software_latency = 0.01; + m_outStream->format = SoundIoFormatFloat32NE; + + m_outStream->sample_rate = 44100; // defaults to 48khz,but most audiofiles are 44.1 + // would be lovely to be able to specify bits per sample + //ret->format.bitsPerSample = 16; + + m_outStream->write_callback = Moon_AudioWrite; + + // open the stream + err = soundio_outstream_open(m_outStream); + if (err) { + return Moon::Audio::ErrorCode::MOON_AUDIO_COULD_NOT_OPEN_STREAM; + } + if (m_outStream->layout_error) { + // Could be a warning + //return Moon::Audio::ErrorCode::MOON_AUDIO_STREAM_CHANNEL_LAYOUT_ERROR; + printf("WARN: Requested channel could not be completed\n"); + } + + // create a new mixer + m_mixer = new Mixer(); + + err = soundio_outstream_start(m_outStream); + if (err) { + return Moon::Audio::ErrorCode::MOON_AUDIO_STREAM_START_ERROR; + } + // Finally, after bashing your head against the table, you can finally have audio? + return Moon::Audio::ErrorCode::MOON_AUDIO_OK; + } + + void Audio::Update() + { + } + + void Audio::Terminate() + { + soundio_outstream_destroy(m_outStream); + soundio_device_unref(m_device); + soundio_destroy(m_soundIO); + delete m_mixer; + } +} diff --git a/modules/audio-libsoundio/src/handle.cxx b/modules/audio-libsoundio/src/handle.cxx new file mode 100644 index 0000000..30e5186 --- /dev/null +++ b/modules/audio-libsoundio/src/handle.cxx @@ -0,0 +1,48 @@ +#include "moon/glm.h" +#include "moon/intern.h" +#include "moon/audio/libsoundio/music.hxx" + +namespace Moon +{ + Handle::Handle(Moon::Source* source) // this can take mixer as arg in the future if we have more than 1 + { + this->source = source; + this->pan = 0.0f; + this->pitch = 1.0f; + this->gain = 1.0f; + }; + + + Handle::~Handle() { + } + + + void Handle::mix(struct SoundIoChannelArea *areas, const struct SoundIoChannelLayout &layout, const float sampleRate, unsigned int frames) { + float pan = this->pan; + pan = (pan + 1.0f) / 2.0f; + pan = glm::clamp(pan, 0.0f, 1.0f); + + // resample and pitch + float sampleScale = source->sampleRate() / sampleRate * pitch; + + // TODO: compare source.channels() with layout.channel_count + int channels = source->channels(); + int totalSamples = frames * channels * source->sampleRate(); + float* chunk = new float[totalSamples]; + + int actual = source->read(chunk, frames); + //printf("Read n frames: %d\n", actual); + + // TODO: mixer should complain if we don't have stereo + + for (int frame = 0; frame < actual; ++frame) { + float* left = (float*)(areas[0].ptr + areas[0].step * frame); + float* right = (float*)(areas[1].ptr + areas[1].step * frame); + // mix the sample! + int offset = frame * channels * sampleScale; + *left += chunk[offset] * gain * (1.0f - pan) * 2; + *right += chunk[offset + ((channels == 1) ? 0 : 1)] * gain * pan * 2; + } + delete[] chunk; + } +} diff --git a/modules/audio-libsoundio/src/loop.cxx b/modules/audio-libsoundio/src/loop.cxx new file mode 100644 index 0000000..f3897ed --- /dev/null +++ b/modules/audio-libsoundio/src/loop.cxx @@ -0,0 +1,47 @@ +#include "moon/intern.h" +#include "moon/audio/libsoundio/music.hxx" + +namespace Moon +{ + Loop::Loop(Moon::Source* source, std::uint32_t trigger, std::uint32_t target) { + this->source = source; + this->trigger = trigger; + this->target = target; + + // TODO: bounds checking (trigger/target being further than file length) + }; + + Loop::~Loop() { + } + + int Loop::channels() { + return source->channels(); + } + + int Loop::sampleRate() { + return source->sampleRate(); + } + + // returns how many frames we actually read + int Loop::read(float* dst, int frames) + { + std::uint32_t current = source->tell(); + std::uint32_t diff = trigger - current; + + // frames fit without seeking + if (frames <= diff) { + return source->read(dst, frames); + } else { // we need to read then seek + source->read(dst, diff); + source->seek(target); + dst += diff * source->channels(); + source->read(dst, frames); + } + return frames; + } + + // seeks to a given offset (in frames) from the start of the file + std::uint32_t Loop::seek(std::uint32_t pos) { + return source->seek(pos); + } +} diff --git a/modules/audio-libsoundio/src/mixer.cxx b/modules/audio-libsoundio/src/mixer.cxx new file mode 100644 index 0000000..683f146 --- /dev/null +++ b/modules/audio-libsoundio/src/mixer.cxx @@ -0,0 +1,26 @@ +#include "moon/audio/libsoundio/mixer.hxx" + +namespace Moon { + Mixer::Mixer() { + } + + void Mixer::mix(struct SoundIoChannelArea *areas, const struct SoundIoChannelLayout &layout, const float sampleRate, unsigned int frames) + { + // silence the buffer first + for (int channel = 0; channel < layout.channel_count; ++channel) { + memset(areas[channel].ptr, 0, areas[channel].step * frames); + } + for(auto const& handle: handles) { + handle->mix(areas, layout, sampleRate, frames); + } + + // clipping, to avoid overdrive + for (size_t frame = 0; frame < frames; ++frame) { + for (int channel = 0; channel < layout.channel_count; ++channel) { + float* buffer = (float*)(areas[channel].ptr + areas[channel].step * frame); + float sample = *buffer; + *buffer = sample > 1.0f ? 1.0f : (sample < -1.0f ? -1.0f : sample); + } + } + } +} diff --git a/modules/audio-libsoundio/src/mrb_moon_audio.cxx b/modules/audio-libsoundio/src/mrb_moon_audio.cxx new file mode 100644 index 0000000..d6d377e --- /dev/null +++ b/modules/audio-libsoundio/src/mrb_moon_audio.cxx @@ -0,0 +1,57 @@ +#include +#include +#include "moon/audio/libsoundio/audio.hxx" +#include "moon/audio/libsoundio/mrb/music.hxx" +#include "moon/audio/libsoundio/mrb/sound.hxx" + +static mrb_value +audio_update(mrb_state *mrb, mrb_value klass) +{ + Moon::Audio::Update(); + return klass; +} + +MOON_C_API void +mrb_mruby_moon_audio_libsoundio_gem_init(mrb_state* mrb) +{ + struct RClass *moon_module = mrb_define_module(mrb, "Moon"); + struct RClass *audio_module = mrb_define_module_under(mrb, moon_module, "Audio"); + struct RClass *audio_error = mrb_define_class_under(mrb, moon_module, "AudioError", E_RUNTIME_ERROR); + mrb_define_class_method(mrb, audio_module, "update", audio_update, MRB_ARGS_NONE()); + //mmrb_sound_buffer_init(mrb, moon_module); + mmrb_music_init(mrb, moon_module); + mmrb_sound_init(mrb, moon_module); + + const Moon::Audio::ErrorCode err = Moon::Audio::Initialize(); + switch (err) { + case Moon::Audio::ErrorCode::MOON_AUDIO_CREATE_ERROR: + mrb_raise(mrb, audio_error, "Underlying backend could not be created"); + break; + case Moon::Audio::ErrorCode::MOON_AUDIO_CONNECTION_ERROR: + mrb_raise(mrb, audio_error, "Could not estabish connection to backend"); + break; + case Moon::Audio::ErrorCode::MOON_AUDIO_NO_DEVICE: + mrb_raise(mrb, audio_error, "No default device present"); + break; + case Moon::Audio::ErrorCode::MOON_AUDIO_DEVICE_MISSING: + mrb_raise(mrb, audio_error, "Default device is missing"); + break; + case Moon::Audio::ErrorCode::MOON_AUDIO_COULD_NOT_OPEN_STREAM: + mrb_raise(mrb, audio_error, "Output stream could not be opened, does the selected device support it?"); + break; + case Moon::Audio::ErrorCode::MOON_AUDIO_STREAM_CHANNEL_LAYOUT_ERROR: + mrb_raise(mrb, audio_error, "Channel layout is invalid for the stream"); + break; + case Moon::Audio::ErrorCode::MOON_AUDIO_STREAM_START_ERROR: + mrb_raise(mrb, audio_error, "Stream could not be started"); + break; + default: + break; + } +} + +MOON_C_API void +mrb_mruby_moon_audio_libsoundio_gem_final(mrb_state* mrb) +{ + Moon::Audio::Terminate(); +} diff --git a/modules/audio-libsoundio/src/mrb_music.cxx b/modules/audio-libsoundio/src/mrb_music.cxx new file mode 100644 index 0000000..6ca0fe4 --- /dev/null +++ b/modules/audio-libsoundio/src/mrb_music.cxx @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include "moon/audio/libsoundio/mrb/music.hxx" +#include "moon/audio/libsoundio/music.hxx" +#include "moon/api.h" +#include "moon/intern.h" +#include "moon/mrb/helpers.hxx" + +static void +music_free(mrb_state *mrb, void *p) +{ + Moon::Music *music = static_cast(p); + if (music) { + delete(music); + } +} + +MOON_C_API const struct mrb_data_type music_data_type = { "Moon::Music", music_free }; + +static mrb_value +music_initialize(mrb_state* mrb, mrb_value self) +{ + char* filename; + mrb_get_args(mrb, "z", &filename); + moon_data_cleanup(mrb, self); + if (exists(filename)) { + Moon::Music* music = new Moon::Music(std::string(filename)); + mrb_data_init(self, music, &music_data_type); + } else { + mrb_raisef(mrb, E_SCRIPT_ERROR, + "cannot load such file -- %S", + mrb_str_new_cstr(mrb, filename)); + } + return self; +} + + +static mrb_value +music_play(mrb_state* mrb, mrb_value self) +{ + mrb_float gain = 1.0f; + mrb_float pitch = 1.0f; + mrb_float pan = 1.0f; + mrb_get_args(mrb, "|fff", &gain, &pitch, &pan); + // TODO + return self; +} + +static mrb_value +music_stop(mrb_state* mrb, mrb_value self) +{ + // TODO + return self; +} + +static mrb_value +music_seek(mrb_state* mrb, mrb_value self) +{ + return self; +} + +static mrb_value +music_pos(mrb_state* mrb, mrb_value self) +{ + return mrb_fixnum_value(0); +} + +static mrb_value +music_length(mrb_state* mrb, mrb_value self) +{ + return mrb_fixnum_value(0); +} + +static mrb_value +music_loop(mrb_state* mrb, mrb_value self) +{ + return self; +} + +static mrb_value +music_clear_loop(mrb_state* mrb, mrb_value self) +{ + return self; +} + +static mrb_value +music_is_playing(mrb_state* mrb, mrb_value self) +{ + // TODO + return mrb_bool_value(true); +} + +static mrb_value +music_is_stopped(mrb_state* mrb, mrb_value self) +{ + // TODO + return mrb_bool_value(false); +} + +static mrb_value +music_is_finished(mrb_state* mrb, mrb_value self) +{ + // TODO + return mrb_bool_value(false); +} + +MOON_C_API void +mmrb_music_init(mrb_state* mrb, struct RClass* mod) +{ + struct RClass *music_class = mrb_define_class_under(mrb, mod, "Music", mrb->object_class); + MRB_SET_INSTANCE_TT(music_class, MRB_TT_DATA); + + mrb_define_method(mrb, music_class, "initialize", music_initialize, MRB_ARGS_REQ(2)); + + /* Playback */ + mrb_define_method(mrb, music_class, "play", music_play, MRB_ARGS_OPT(3)); + mrb_define_method(mrb, music_class, "stop", music_stop, MRB_ARGS_NONE()); + + /* Position */ + mrb_define_method(mrb, music_class, "seek", music_seek, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, music_class, "pos", music_pos, MRB_ARGS_NONE()); + mrb_define_method(mrb, music_class, "length", music_length, MRB_ARGS_NONE()); + mrb_define_method(mrb, music_class, "loop", music_loop, MRB_ARGS_OPT(2)); + mrb_define_method(mrb, music_class, "clear_loop", music_clear_loop, MRB_ARGS_NONE()); + + /* Query */ + mrb_define_method(mrb, music_class, "playing?", music_is_playing, MRB_ARGS_NONE()); + mrb_define_method(mrb, music_class, "stopped?", music_is_stopped, MRB_ARGS_NONE()); + mrb_define_method(mrb, music_class, "finished?", music_is_finished, MRB_ARGS_NONE()); +} diff --git a/modules/audio-libsoundio/src/mrb_sound.cxx b/modules/audio-libsoundio/src/mrb_sound.cxx new file mode 100644 index 0000000..bdee9a2 --- /dev/null +++ b/modules/audio-libsoundio/src/mrb_sound.cxx @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include "moon/audio/libsoundio/mrb/sound.hxx" +#include "moon/audio/libsoundio/sound.hxx" +#include "moon/api.h" +#include "moon/intern.h" +#include "moon/mrb/helpers.hxx" + +static void +sound_free(mrb_state* mrb, void *p) +{ + Moon::Sound *sound = static_cast(p); + if (sound) { + delete(sound); + } +} + +MOON_C_API const struct mrb_data_type sound_data_type = { "Moon::Sound", sound_free }; + +static mrb_value +sound_initialize(mrb_state* mrb, mrb_value self) +{ + char* filename; + mrb_get_args(mrb, "z", &filename); + moon_data_cleanup(mrb, self); + Moon::Sound* sound = new Moon::Sound(std::string(filename)); + mrb_data_init(self, sound, &sound_data_type); + return self; +} + +static mrb_value +sound_play(mrb_state* mrb, mrb_value self) +{ + mrb_float gain = 1.0; + mrb_float pitch = 1.0; + mrb_float pan = 0.0; + mrb_get_args(mrb, "|fff", &gain, &pitch, &pan); + // TODO + return self; +} + +MOON_C_API void +mmrb_sound_init(mrb_state* mrb, struct RClass *mod) +{ + struct RClass *sound_class = mrb_define_class_under(mrb, mod, "Sound", mrb->object_class); + MRB_SET_INSTANCE_TT(sound_class, MRB_TT_DATA); + + mrb_define_method(mrb, sound_class, "initialize", sound_initialize, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, sound_class, "play", sound_play, MRB_ARGS_OPT(3)); +} diff --git a/modules/audio-libsoundio/src/music.cxx b/modules/audio-libsoundio/src/music.cxx new file mode 100644 index 0000000..a2593f0 --- /dev/null +++ b/modules/audio-libsoundio/src/music.cxx @@ -0,0 +1,36 @@ +#include "moon/intern.h" +#include "moon/audio/libsoundio/music.hxx" + +namespace Moon +{ + Music::Music(const std::string filename) : + file(filename, SFM_READ) + { + printf("Opened file '%s'\n", filename.c_str()); + printf(" Sample rate : %d\n", file.samplerate()); + printf(" Channels : %d\n", file.channels()); + }; + + + Music::~Music() { + } + + int Music::channels() { + return file.channels(); + } + + int Music::sampleRate() { + return file.samplerate(); + } + + // returns how many frames we actually read + int Music::read(float* dst, int frames) + { + return file.readf(dst, frames); + } + + // seeks to a given offset (in frames) from the start of the file + std::uint32_t Music::seek(std::uint32_t pos) { + return file.seek(pos, SEEK_SET); + } +} diff --git a/modules/audio-libsoundio/src/sound.cxx b/modules/audio-libsoundio/src/sound.cxx new file mode 100644 index 0000000..28f9e7e --- /dev/null +++ b/modules/audio-libsoundio/src/sound.cxx @@ -0,0 +1,57 @@ +#include "moon/intern.h" +#include "moon/audio/libsoundio/sound.hxx" + +namespace Moon +{ + Sound::Sound(const std::string filename) + { + SndfileHandle file(filename, SFM_READ); + printf("Opened file '%s'\n", filename.c_str()); + printf(" Sample rate : %d\n", file.samplerate ()); + printf(" Channels : %d\n", file.channels ()); + printf(" Frames : %ld\n", file.frames()); + + m_channels = file.channels(); + m_sampleRate = file.samplerate(); + + currentFrame = 0; + + int totalSamples = file.frames() * m_channels * m_sampleRate; + source = new float[totalSamples]; + + totalFrames = file.readf(source, file.frames()); + printf("total frames: %d\n", totalFrames); + // TODO: shout if totalFrames didn't match file.frames() + }; + + + Sound::~Sound() { + delete[] source; + } + + int Sound::channels() { + return m_channels; + } + + int Sound::sampleRate() { + return m_sampleRate; + } + + // returns how many frames we actually read + int Sound::read(float* dst, int frames) + { + if(currentFrame > totalFrames) { return 0; } + // handle buffer edges (don't point past edge) + int actual = (currentFrame + frames > totalFrames) ? totalFrames - currentFrame : frames; + memcpy(dst, &source[currentFrame * m_channels], sizeof(float) * actual * m_channels); + + currentFrame += actual; + return actual; + } + + // seeks to a given offset (in frames) from the start of the file + std::uint32_t Sound::seek(std::uint32_t pos) { + currentFrame = pos; + return currentFrame; + } +} diff --git a/modules/audio-portaudio/src/audio.cxx b/modules/audio-portaudio/src/audio.cxx deleted file mode 100644 index b34e020..0000000 --- a/modules/audio-portaudio/src/audio.cxx +++ /dev/null @@ -1,19 +0,0 @@ -#include "moon/portaudio/audio.hxx" - -namespace Moon -{ - void Audio::Initialize() - { - - } - - void Audio::Update() - { - - } - - void Audio::Terminate() - { - - } -} diff --git a/modules/audio/include/moon/audio_stream.hxx b/modules/audio/include/moon/audio_stream.hxx new file mode 100644 index 0000000..637de67 --- /dev/null +++ b/modules/audio/include/moon/audio_stream.hxx @@ -0,0 +1,14 @@ +#ifndef MOON_STREAM_H +#define MOON_STREAM_H + +#include "moon/audio.hxx" + +namespace Moon { + struct Stream { + Stream() : handle(NULL), loopSrc(NULL) { }; + ga_Handle* handle; + gau_SampleSourceLoop* loopSrc; + }; +}; + +#endif diff --git a/modules/audio/include/moon/mrb/audio_stream.hxx b/modules/audio/include/moon/mrb/audio_stream.hxx new file mode 100644 index 0000000..b8544f5 --- /dev/null +++ b/modules/audio/include/moon/mrb/audio_stream.hxx @@ -0,0 +1,12 @@ +#ifndef MMRB_AUDIO_STREAM_H +#define MMRB_AUDIO_STREAM_H + +#include +#include +#include +#include "moon/api.h" + +MOON_C_API const struct mrb_data_type audio_stream_data_type; +MOON_C_API void mmrb_audio_stream_init(mrb_state *mrb, struct RClass *mod); + +#endif diff --git a/modules/audio/src/mrb_audio_stream.cxx b/modules/audio/src/mrb_audio_stream.cxx new file mode 100644 index 0000000..c113176 --- /dev/null +++ b/modules/audio/src/mrb_audio_stream.cxx @@ -0,0 +1,168 @@ +#include +#include +#include +#include +#include "moon/mrb/stream.hxx" +#include "moon/stream.hxx" +#include "moon/api.h" +#include "moon/intern.h" + +static void +audio_stream_free(mrb_state *mrb, void *p) +{ + Moon::AudioStream* music = (Moon::AudioStream*)p; + if (music) { + if (music->handle) { + ga_handle_destroy(music->handle); + } + delete(music); + } +} + +MOON_C_API const struct mrb_data_type audio_stream_data_type = { "Moon::AudioStream", audio_stream_free }; + +static inline Moon::AudioStream* +get_music(mrb_state *mrb, mrb_value self) +{ + return (Moon::AudioStream*)mrb_data_get_ptr(mrb, self, &audio_stream_data_type); +} + +static mrb_value +audio_stream_initialize(mrb_state *mrb, mrb_value self) +{ + char *filename; + char *format; + Moon::AudioStream *music; + + mrb_get_args(mrb, "zz", &filename, &format); + + music = (Moon::AudioStream*)DATA_PTR(self); + if (music) { + audio_stream_free(mrb, (void*)music); + } + music = new Moon::AudioStream(); + + if (exists(filename)) { + music->handle = gau_create_handle_buffered_file(Moon::Audio::GetMixer(), Moon::Audio::GetStreamMgr(), + filename, format, + NULL, 0, &(music->loopSrc)); + } else { + mrb_raisef(mrb, E_SCRIPT_ERROR, "cannot load such file -- %S", mrb_str_new_cstr(mrb, filename)); + } + + mrb_data_init(self, music, &audio_stream_data_type); + + return self; +} + +static mrb_value +audio_stream_play(mrb_state *mrb, mrb_value self) +{ + mrb_float gain = 1.0; + mrb_float pitch = 1.0; + mrb_float pan = 0.0; + Moon::AudioStream *music = get_music(mrb, self); + mrb_get_args(mrb, "|fff", &gain, &pitch, &pan); + ga_handle_setParamf(music->handle, GA_HANDLE_PARAM_GAIN, gain); + ga_handle_setParamf(music->handle, GA_HANDLE_PARAM_PITCH, pitch); + ga_handle_setParamf(music->handle, GA_HANDLE_PARAM_PAN, pan); + ga_handle_play(music->handle); + + return mrb_nil_value(); +}; + +static mrb_value +audio_stream_stop(mrb_state *mrb, mrb_value self) +{ + Moon::AudioStream *music = get_music(mrb, self); + ga_handle_stop(music->handle); + return mrb_nil_value(); +}; + +static mrb_value +audio_stream_is_playing(mrb_state *mrb, mrb_value self) +{ + Moon::AudioStream *music = get_music(mrb, self); + return mrb_bool_value(ga_handle_playing(music->handle)); +} + +static mrb_value +audio_stream_is_stopped(mrb_state *mrb, mrb_value self) +{ + Moon::AudioStream *music = get_music(mrb, self); + return mrb_bool_value(ga_handle_stopped(music->handle)); +} + +static mrb_value +audio_stream_is_finished(mrb_state *mrb, mrb_value self) +{ + Moon::AudioStream *music = get_music(mrb, self); + return mrb_bool_value(ga_handle_finished(music->handle)); +} + +static mrb_value +audio_stream_seek(mrb_state *mrb, mrb_value self) +{ + mrb_int offset; + Moon::AudioStream *music = get_music(mrb, self); + mrb_get_args(mrb, "i", &offset); + return mrb_bool_value(ga_handle_seek(music->handle, offset)); +} + +static mrb_value +audio_stream_pos(mrb_state *mrb, mrb_value self) +{ + Moon::AudioStream *music = get_music(mrb, self); + return mrb_fixnum_value(ga_handle_tell(music->handle, GA_TELL_PARAM_CURRENT)); +} + +static mrb_value +audio_stream_length(mrb_state *mrb, mrb_value self) +{ + Moon::AudioStream *music = get_music(mrb, self); + return mrb_fixnum_value(ga_handle_tell(music->handle, GA_TELL_PARAM_TOTAL)); +} + +static mrb_value +audio_stream_loop(mrb_state *mrb, mrb_value self) +{ + mrb_int trigger = -1; + mrb_int target = 0; + Moon::AudioStream *music = get_music(mrb, self); + mrb_get_args(mrb, "|ii", &trigger, &target); + gau_sample_source_loop_set(music->loopSrc, trigger, target); + return mrb_nil_value(); +} + +static mrb_value +audio_stream_clear_loop(mrb_state *mrb, mrb_value self) +{ + Moon::AudioStream *music = get_music(mrb, self); + gau_sample_source_loop_clear(music->loopSrc); + return mrb_bool_value(true); +} + +MOON_C_API void +mmrb_audio_stream_init(mrb_state *mrb, struct RClass *mod) +{ + struct RClass *audio_stream_class = mrb_define_class_under(mrb, mod, "Stream", mrb->object_class); + MRB_SET_INSTANCE_TT(audio_stream_class, MRB_TT_DATA); + + mrb_define_method(mrb, audio_stream_class, "initialize", audio_stream_initialize, MRB_ARGS_REQ(2)); + + /* Playback */ + mrb_define_method(mrb, audio_stream_class, "play", audio_stream_play, MRB_ARGS_OPT(3)); + mrb_define_method(mrb, audio_stream_class, "stop", audio_stream_stop, MRB_ARGS_NONE()); + + /* Position */ + mrb_define_method(mrb, audio_stream_class, "seek", audio_stream_seek, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, audio_stream_class, "pos", audio_stream_pos, MRB_ARGS_NONE()); + mrb_define_method(mrb, audio_stream_class, "length", audio_stream_length, MRB_ARGS_NONE()); + mrb_define_method(mrb, audio_stream_class, "loop", audio_stream_loop, MRB_ARGS_OPT(2)); + mrb_define_method(mrb, audio_stream_class, "clear_loop", audio_stream_clear_loop, MRB_ARGS_NONE()); + + /* Query */ + mrb_define_method(mrb, audio_stream_class, "playing?", audio_stream_is_playing, MRB_ARGS_NONE()); + mrb_define_method(mrb, audio_stream_class, "stopped?", audio_stream_is_stopped, MRB_ARGS_NONE()); + mrb_define_method(mrb, audio_stream_class, "finished?", audio_stream_is_finished, MRB_ARGS_NONE()); +} diff --git a/modules/moon.gembox b/modules/moon.gembox index 41cd0a8..aad4043 100644 --- a/modules/moon.gembox +++ b/modules/moon.gembox @@ -14,9 +14,11 @@ MRuby::GemBox.new do |conf| # moon-graphics conf.gem File.join(d, 'graphics') # moon-audio-gorilla - conf.gem File.join(d, 'audio-gorilla') + #conf.gem File.join(d, 'audio-gorilla') # moon-audio-portaudio #conf.gem File.join(d, 'audio-portaudio') + # moon-audio-libsoundio + conf.gem File.join(d, 'audio-libsoundio') # moon-audio-portaudio #conf.gem File.join(d, 'audio-null') # moon-engine diff --git a/modules/system/include/moon/intern.h b/modules/system/include/moon/intern.h index 6e998d9..57e56b5 100644 --- a/modules/system/include/moon/intern.h +++ b/modules/system/include/moon/intern.h @@ -6,16 +6,24 @@ #include #include +#include #include #include #include +#include +#include + // _WIN32 is set for both 32 and 64 bit #ifdef _WIN32 # define stat _stat #endif +#ifndef M_PI + #define M_PI 3.1415926535f +#endif + static inline bool exists(const std::string& name) { @@ -23,4 +31,27 @@ exists(const std::string& name) return (stat(name.c_str(), &buffer) == 0); }; +static inline void* +moon_memzerof(float* ptr, size_t len) +{ + for (size_t i = 0; i < len; ++i) { + ptr[i] = 0.0f; + } + return ptr; +} + +static inline void* +moon_mallocset(size_t len, int value) +{ + void* ptr = malloc(len); + memset(ptr, value, len); + return ptr; +} + +static inline void* +moon_malloczero(size_t len) +{ + return moon_mallocset(len, 0); +} + #endif diff --git a/modules/system/include/moon/mrb/helpers.hxx b/modules/system/include/moon/mrb/helpers.hxx index 86234d0..e09286e 100644 --- a/modules/system/include/moon/mrb/helpers.hxx +++ b/modules/system/include/moon/mrb/helpers.hxx @@ -1,30 +1,9 @@ #ifndef MOON_MRB_HELPERS #define MOON_MRB_HELPERS -#include -#include -#include -#include - -#define IVget(_name_) mrb_iv_get(mrb, self, mrb_intern_lit(mrb, _name_)) -#define IVset(_name_, _value_) mrb_iv_set(mrb, self, mrb_intern_lit(mrb, _name_), _value_) - -#define KEY_SHADER "@shader" -#define KEY_VBO "@vbo" -#define KEY_ORIGIN "@origin" - -#define MOON_GET_CLASS(__name__) mrb_class_get_under(mrb, mrb_module_get(mrb, "Moon"), __name__) -#define MOON_GET_MODULE(__name__) mrb_module_get_under(mrb, mrb_module_get(mrb, "Moon"), __name__) -; - -static inline mrb_value -moon_iv_get(mrb_state *mrb, mrb_value self, const char *name) -{ - mrb_value iv_value = mrb_iv_get(mrb, self, mrb_intern_cstr(mrb, name)); - if (mrb_nil_p(iv_value)) { - mrb_raisef(mrb, E_ARGUMENT_ERROR, "Cannot use a nil %S", mrb_str_new_cstr(mrb, name)); - } - return iv_value; -} +#include "moon/mrb/helpers/class.hxx" +#include "moon/mrb/helpers/variable.hxx" +#include "moon/mrb/helpers/error.hxx" +#include "moon/mrb/helpers/data.hxx" #endif diff --git a/modules/system/include/moon/mrb/helpers/class.hxx b/modules/system/include/moon/mrb/helpers/class.hxx new file mode 100644 index 0000000..bba5384 --- /dev/null +++ b/modules/system/include/moon/mrb/helpers/class.hxx @@ -0,0 +1,10 @@ +#ifndef MOON_MRB_CLASS_HELPERS +#define MOON_MRB_CLASS_HELPERS + +#include +#include + +#define MOON_GET_CLASS(__name__) mrb_class_get_under(mrb, mrb_module_get(mrb, "Moon"), __name__) +#define MOON_GET_MODULE(__name__) mrb_module_get_under(mrb, mrb_module_get(mrb, "Moon"), __name__) + +#endif diff --git a/modules/system/include/moon/mrb/helpers/data.hxx b/modules/system/include/moon/mrb/helpers/data.hxx new file mode 100644 index 0000000..7b7e57a --- /dev/null +++ b/modules/system/include/moon/mrb/helpers/data.hxx @@ -0,0 +1,18 @@ +#ifndef MOON_MRB_DATA_HELPERS +#define MOON_MRB_DATA_HELPERS + +#include +#include + +static inline void +moon_data_cleanup(mrb_state* mrb, mrb_value self) +{ + void *ptr = DATA_PTR(self); + const struct mrb_data_type* type = DATA_TYPE(self); + if (ptr && type) { + type->dfree(mrb, ptr); + } + DATA_PTR(self) = NULL; +} + +#endif diff --git a/modules/system/include/moon/mrb/helpers/error.hxx b/modules/system/include/moon/mrb/helpers/error.hxx new file mode 100644 index 0000000..9537ca3 --- /dev/null +++ b/modules/system/include/moon/mrb/helpers/error.hxx @@ -0,0 +1,8 @@ +#ifndef MOON_MRB_ERROR_HELPERS +#define MOON_MRB_ERROR_HELPERS + +#include "moon/mrb/helpers/class.hxx" + +#define MOON_E_FILE_NOT_FOUND MOON_GET_CLASS("FileNotFoundError") + +#endif diff --git a/modules/system/include/moon/mrb/helpers/variable.hxx b/modules/system/include/moon/mrb/helpers/variable.hxx new file mode 100644 index 0000000..5c6a4ac --- /dev/null +++ b/modules/system/include/moon/mrb/helpers/variable.hxx @@ -0,0 +1,23 @@ +#ifndef MOON_MRB_VARIABLE_HELPERS +#define MOON_MRB_VARIABLE_HELPERS + +#include +#include +#include + +#define IVget(_name_) mrb_iv_get(mrb, self, mrb_intern_lit(mrb, _name_)) +#define IVset(_name_, _value_) mrb_iv_set(mrb, self, mrb_intern_lit(mrb, _name_), _value_) + +static inline mrb_value +moon_iv_get(mrb_state* mrb, mrb_value self, const char* name) +{ + mrb_value iv_value = mrb_iv_get(mrb, self, mrb_intern_cstr(mrb, name)); + if (mrb_nil_p(iv_value)) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "Cannot use a nil %S", mrb_str_new_cstr(mrb, name)); + } + return iv_value; +} + +#define KEY_VBO "@vbo" + +#endif diff --git a/modules/system/include/moon/mrb_err.hxx b/modules/system/include/moon/mrb_err.hxx index 6f4ab47..1d15cf3 100644 --- a/modules/system/include/moon/mrb_err.hxx +++ b/modules/system/include/moon/mrb_err.hxx @@ -6,6 +6,6 @@ #include #include -extern bool mmrb_check_class(mrb_state *mrb, mrb_value obj, struct RClass *klass, bool quiet); +extern bool mmrb_check_class(mrb_state *mrb, mrb_value obj, struct RClass* klass, bool quiet); #endif diff --git a/modules/system/mrblib/file_not_found_error.rb b/modules/system/mrblib/file_not_found_error.rb new file mode 100644 index 0000000..b243369 --- /dev/null +++ b/modules/system/mrblib/file_not_found_error.rb @@ -0,0 +1,5 @@ +module Moon + # Error raised when a resource cannot be found on disk + class FileNotFoundError < RuntimeError + end +end diff --git a/mrb_config.rb b/mrb_config.rb index 7c02b88..32ea78c 100644 --- a/mrb_config.rb +++ b/mrb_config.rb @@ -100,6 +100,8 @@ c.include_paths << File.expand_path('soil/include', vd) c.include_paths << File.expand_path('sil/include', vd) c.include_paths << File.expand_path('freetype-gl', vd) + c.include_paths << File.expand_path('libsoundio', vd) + c.include_paths << File.expand_path('libsndfile/include', bvd) # required audio includes c.include_paths << File.expand_path('gorilla-audio/include', vd) c.include_paths.uniq! @@ -111,30 +113,46 @@ l.library_paths << File.expand_path('glfw/src', bvd) l.library_paths << File.expand_path('freetype-gl', bvd) l.library_paths << File.expand_path('gorilla-audio/build', bvd) + l.library_paths << File.expand_path('libsoundio', bvd) + l.library_paths << File.expand_path('libsndfile/lib', bvd) l.library_paths << File.expand_path('sil', bvd) l.library_paths << File.expand_path('soil', bvd) l.library_paths.uniq! l.libraries << 'glfw' l.libraries << 'freetype-gl' - l.libraries << 'gorilla' l.libraries << 'freetype' l.libraries << 'SOIL' l.libraries << 'SIL' + l.libraries << 'ogg' + l.libraries << 'vorbis' + l.libraries << 'vorbisenc' + if platform.linux? + l.libraries << 'FLAC' l.libraries << 'GLEW' l.libraries << 'GL' l.libraries << 'openal' + l.libraries << 'asound' + # if you want to use jack, just enable it here + #l.libraries << 'jack' + l.libraries << 'pulse' elsif platform.windows? l.libraries << 'glew32' l.libraries << 'opengl32' l.libraries << 'OpenAL32' elsif platform.darwin? + l.libraries << 'flac' l.libraries << 'GLEW' l.flags_after_libraries << '-framework OpenGL' + # gorilla l.flags_after_libraries << '-framework OpenAL' + # l.flags_after_libraries << '-framework CoreFoundation' + # libsoundio + l.flags_after_libraries << '-framework CoreAudio' + l.flags_after_libraries << '-framework AudioUnit' end if platform.unix? diff --git a/vendor/libsoundio b/vendor/libsoundio new file mode 160000 index 0000000..7469bee --- /dev/null +++ b/vendor/libsoundio @@ -0,0 +1 @@ +Subproject commit 7469bee05829b62218e862eb6347f531b1869ac7