aboutsummaryrefslogtreecommitdiff
path: root/src/libjin/audio
diff options
context:
space:
mode:
Diffstat (limited to 'src/libjin/audio')
-rw-r--r--src/libjin/audio/SDL/je_sdl_audio.cpp146
-rw-r--r--src/libjin/audio/SDL/je_sdl_audio.h138
-rw-r--r--src/libjin/audio/SDL/je_sdl_source.cpp404
-rw-r--r--src/libjin/audio/SDL/je_sdl_source.h272
-rw-r--r--src/libjin/audio/je_audio_manager.cpp15
-rw-r--r--src/libjin/audio/je_audio_manager.h87
-rw-r--r--src/libjin/audio/je_source.cpp30
-rw-r--r--src/libjin/audio/je_source.h116
8 files changed, 1208 insertions, 0 deletions
diff --git a/src/libjin/audio/SDL/je_sdl_audio.cpp b/src/libjin/audio/SDL/je_sdl_audio.cpp
new file mode 100644
index 0000000..c21d077
--- /dev/null
+++ b/src/libjin/audio/SDL/je_sdl_audio.cpp
@@ -0,0 +1,146 @@
+#include "../../core/je_configuration.h"
+#if defined(jin_audio) && (jin_audio == jin_audio_sdl)
+
+#include <iostream>
+
+#include "../../math/je_math.h"
+#include "../../utils/je_log.h"
+
+#include "je_sdl_audio.h"
+#include "je_sdl_source.h"
+
+using namespace JinEngine::Math;
+
+namespace JinEngine
+{
+ namespace Audio
+ {
+ namespace SDL
+ {
+
+ /* עcallbackƵ̵߳ */
+ static void defaultCallback(void *userdata, Uint8 *stream, int size)
+ {
+ static SDLAudio* audio = static_cast<SDLAudio*>(userdata);
+ if (!audio->goOnProcess())
+ return;
+ audio->lock();
+ audio->processCommands();
+ audio->processSources(stream, size);
+ audio->processBuffer(stream, size);
+ audio->unlock();
+ }
+
+ /*call only once*/ bool SDLAudio::initSystem(const SettingBase* s)
+ {
+ jin_log_info("Initialize audio system.");
+
+ if (SDL_Init(SDL_INIT_AUDIO) < 0)
+ return false;
+
+ SDL_AudioSpec spec;
+ Setting* setting = (Setting*)s;
+ if (setting == nullptr)
+ return false;
+
+ unsigned int samplerate = setting->samplerate;
+ unsigned int samples = clamp<int>(setting->samples, 1, setting->samplerate);
+
+ spec.freq = samplerate; // ÿsample,õ 11025, 22050, 44100 and 48000 Hz.
+ spec.format = AUDIO_S16SYS; // signed 16-bit samples in native byte order
+ spec.channels = SDLAUDIO_CHANNELS; //
+ spec.samples = samples; // ÿβʱһã=setting->samplerateÿֻ1
+ spec.userdata = this;
+ spec.callback = defaultCallback;
+
+ audioDevice = SDL_OpenAudioDevice(NULL, 0, &spec, NULL, 0);
+ if (audioDevice == 0)
+ return false;
+ /* start audio */
+ SDL_PauseAudioDevice(audioDevice, 0);
+ return true;
+ }
+
+ /*call only once*/ void SDLAudio::quitSystem()
+ {
+ jin_log_info("Quit audio system.");
+ SDL_CloseAudio();
+ }
+
+ void SDLAudio::lock()
+ {
+ SDL_LockAudioDevice(audioDevice);
+ }
+
+ void SDLAudio::unlock()
+ {
+ SDL_UnlockAudioDevice(audioDevice);
+ }
+
+ bool SDLAudio::goOnProcess()
+ {
+ if (state == SDLAudio::State::STOP)
+ {
+ SDLSourceManager::get()->removeAllSource();
+ pause();
+ return false;
+ }
+ else if (state == SDLAudio::State::PAUSE)
+ return false;
+ else
+ return true;
+ }
+
+ void SDLAudio::processCommands()
+ {
+ SDLSourceManager::get()->processCommands();
+ }
+
+ void SDLAudio::processSources(void* buffer, size_t len)
+ {
+ SDLSourceManager::get()->processSources(buffer, len);
+ }
+
+ void SDLAudio::processBuffer(void* buff, size_t len)
+ {
+ short* buffer = (short*)buff;
+ int samples = (len / SDLAUDIO_BYTEDEPTH) >> 1; // ˫
+ const char L = 0, R = 1;
+ for (int i = 0; i < samples; ++i)
+ {
+ short* clip = buffer + (i << 1);
+ clip[L] *= volume;
+ clip[R] *= volume;
+ }
+ }
+
+ void SDLAudio::play()
+ {
+ state = State::PLAY;
+ }
+
+ void SDLAudio::stop()
+ {
+ state = State::STOP;
+ }
+
+ void SDLAudio::pause()
+ {
+ state = State::PAUSE;
+ }
+
+ void SDLAudio::resume()
+ {
+ state = State::PLAY;
+ }
+
+ void SDLAudio::setVolume(float volume)
+ {
+ this->volume = clamp(volume, 0.0f, 1.0f);
+ }
+
+ } // namespace SDL
+ } // namespace Audio
+} // namespace JinEngine
+
+#endif // (jin_audio) && (jin_audio == jin_audio_sdl) \ No newline at end of file
diff --git a/src/libjin/audio/SDL/je_sdl_audio.h b/src/libjin/audio/SDL/je_sdl_audio.h
new file mode 100644
index 0000000..77162d4
--- /dev/null
+++ b/src/libjin/audio/SDL/je_sdl_audio.h
@@ -0,0 +1,138 @@
+#ifndef __JE_AUDIO_SDL_H__
+#define __JE_AUDIO_SDL_H__
+#include "../../core/je_configuration.h"
+#if defined(jin_audio) && (jin_audio == jin_audio_sdl)
+
+#include <vector>
+
+#include "../je_audio_manager.h"
+
+#include "je_sdl_source.h"
+
+namespace JinEngine
+{
+ namespace Audio
+ {
+ namespace SDL
+ {
+
+#define SDLAUDIO_BITDEPTH 16
+#define SDLAUDIO_BYTEDEPTH (SDLAUDIO_BITDEPTH >> 3)
+#define SDLAUDIO_CHANNELS 2
+
+ ///
+ /// Audio system SDL implementation.
+ ///
+ class SDLAudio : public AudioManager<SDLAudio>
+ {
+ public:
+ ///
+ /// SDL audio setting.
+ ///
+ struct Setting : SettingBase
+ {
+ public:
+ int samplerate; // Ƶ
+ int samples; // sample<=samplerate
+ };
+
+ ///
+ /// Play all sources whose state is playing.
+ ///
+ void play() override;
+
+ ///
+ /// Stop and remove all sources from the queue.
+ ///
+ void stop() override;
+
+ ///
+ /// Pause audio.
+ ///
+ void pause() override;
+
+ ///
+ /// Resume audio.
+ ///
+ void resume() override;
+
+ ///
+ /// Set global audio volume.
+ ///
+ void setVolume(float volume) override;
+
+ ///
+ /// Process all commands in the queue.
+ ///
+ void processCommands();
+
+ ///
+ /// Process all sources.
+ ///
+ /// @param buffer Source buffer.
+ /// @param len Source length.
+ ///
+ void processSources(void* buffer, size_t len);
+
+ ///
+ /// Process audio buffer.
+ ///
+ /// @param buffer Audio stream buffer.
+ /// @param len Length of stream buffer.
+ ///
+ void processBuffer(void* buffer, size_t len);
+
+ ///
+ /// Goon process.
+ ///
+ /// @return True if sucessful, otherwise return false.
+ ///
+ bool goOnProcess();
+
+ ///
+ /// Lock audio device.
+ ///
+ void lock();
+
+ ///
+ /// Unlock audio device.
+ ///
+ void unlock();
+
+ private:
+ singleton(SDLAudio);
+
+ ///
+ /// SDL audio constructor.
+ ///
+ SDLAudio() {};
+
+ ///
+ /// SDL audio destructor.
+ ///
+ ~SDLAudio() {};
+
+ ///
+ /// Initialize audio system.
+ ///
+ /// @param setting Audio setting.
+ ///
+ bool initSystem(const SettingBase* setting) override;
+
+ ///
+ /// Quit audio system.
+ ///
+ void quitSystem() override;
+
+ // Audio device id.
+ unsigned int audioDevice;
+
+ };
+
+ } // namespace SDL
+ } // namespace Audio
+} // namespace JinEngine
+
+#endif // (jin_audio) && (jin_audio == jin_audio_sdl)
+
+#endif // __JE_AUDIO_SDL_H__ \ No newline at end of file
diff --git a/src/libjin/audio/SDL/je_sdl_source.cpp b/src/libjin/audio/SDL/je_sdl_source.cpp
new file mode 100644
index 0000000..d417bf7
--- /dev/null
+++ b/src/libjin/audio/SDL/je_sdl_source.cpp
@@ -0,0 +1,404 @@
+#include "../../core/je_configuration.h"
+#if defined(jin_audio) && (jin_audio == jin_audio_sdl)
+
+#include <exception>
+#include <fstream>
+#include <climits>
+
+#define STB_VORBIS_HEADER_ONLY
+#include "stb/stb_vorbis.c"
+#include "wav/wav.h"
+
+#include "../../math/je_math.h"
+#include "../../utils/je_macros.h"
+
+#include "je_sdl_audio.h"
+#include "je_sdl_source.h"
+
+using namespace JinEngine::Math;
+
+namespace JinEngine
+{
+ namespace Audio
+ {
+ namespace SDL
+ {
+
+
+#define BITS 8
+
+ typedef struct SDLSourceCommand
+ {
+ typedef enum Action
+ {
+ Nothing = 0,
+ Play,
+ Stop,
+ Pause,
+ Resume,
+ Rewind,
+ SetVolume,
+ SetLoop,
+ SetRate,
+ };
+ Action action;
+ union {
+ int _integer;
+ float _float;
+ bool _boolean;
+ const char* _string;
+ } parameter;
+
+ SDLSource* source;
+ };
+
+ typedef enum CHANNEL
+ {
+ MONO = 1, //
+ STEREO = 2, //
+ };
+
+ typedef /*mask*/ enum STATUS
+ {
+ PLAYING = 1,
+ PAUSED = 2,
+ STOPPED = 4
+ };
+
+#define Command SDLSourceCommand
+#define Action Command::Action
+#define Manager SDLSourceManager
+
+ //std::queue<Command*> Manager::commands;
+ //std::stack<Command*> Manager::commandsPool;
+ //std::vector<SDLSource*> Manager::sources;
+ Manager* Manager::manager = nullptr;
+
+ SDLSource* SDLSource::createSource(const char* file)
+ {
+ std::ifstream fs;
+ fs.open(file, std::ios::binary);
+ if (!fs.is_open())
+ {
+ fs.close();
+ return nullptr;
+ }
+ fs.seekg(0, std::ios::end);
+ int size = fs.tellg();
+ fs.seekg(0, std::ios::beg);
+ char* buffer = (char*)malloc(size);
+ memset(buffer, 0, size);
+ fs.read(buffer, size);
+ fs.close();
+ SDLSource* source = createSource(buffer, size);
+ free(buffer);
+ return source;
+ }
+
+ SDLSource* SDLSource::createSource(void* mem, size_t size)
+ {
+ if (mem == nullptr)
+ return nullptr;
+ SDLSource* source = new SDLSource();
+ try
+ {
+ SourceType format = getType(mem, size);
+ switch (format)
+ {
+ case OGG: source->decode_ogg(mem, size); break;
+ case WAV: source->decode_wav(mem, size); break;
+ }
+ }
+ catch (SourceException& exp)
+ {
+ delete source;
+ return nullptr;
+ };
+ return source;
+ }
+
+ SDLSource::SDLSource()
+ {
+ memset(&status, 0, sizeof(status));
+ memset(&raw, 0, sizeof(raw));
+ status.volume = 1;
+ }
+
+ SDLSource::~SDLSource()
+ {
+ delete raw.data;
+ raw.end = 0;
+ raw.data = 0;
+ }
+
+ void SDLSource::decode_wav(void* mem, int size)
+ {
+ wav_t wav;
+ if (wav_read(&wav, mem, size) == 0)
+ {
+ raw.data = wav.data;
+ raw.length = wav.length * wav.channels * wav.bitdepth / 8;
+ raw.channels = clamp<int>(wav.channels, CHANNEL::MONO, CHANNEL::STEREO);
+ raw.end = (char*)raw.data + raw.length;
+ raw.samplerate = wav.samplerate;
+ raw.bitdepth = wav.bitdepth;
+ raw.samples = wav.length;
+ }
+ else
+ throw SourceException();
+ }
+
+ void SDLSource::decode_ogg(void* _mem, int size)
+ {
+ unsigned char* mem = (unsigned char*)_mem;
+ int channels;
+ int samplerate;
+ short* data = (short*)raw.data;
+ int samples = stb_vorbis_decode_memory(
+ mem,
+ size,
+ &channels,
+ &samplerate,
+ &data
+ );
+ const int bitdepth = sizeof(short) * BITS;
+ raw.channels = channels;
+ raw.samplerate = samplerate;
+ raw.data = data;
+ raw.samples = samples; // һsample
+ raw.length = samples * channels * sizeof(short); // һsample
+ raw.bitdepth = bitdepth;
+ raw.end = (char*)data + raw.length;
+ }
+
+#define ActionNone(T)\
+ do{\
+ Command* cmd = Manager::get()->getCommand();\
+ cmd->action = Action::T; \
+ cmd->source = this; \
+ Manager::get()->pushCommand(cmd); \
+ } while (0)
+
+#define ActionArg(T, ARGT, ARG)\
+ do{\
+ Command* cmd = Manager::get()->getCommand();\
+ cmd->action = Action::T; \
+ cmd->parameter.ARGT = ARG; \
+ cmd->source = this; \
+ Manager::get()->pushCommand(cmd); \
+ }while(0)
+
+#define ActionInt(T, INT) ActionArg(T, _integer, INT)
+#define ActionFloat(T, FLT) ActionArg(T, _float, FLT)
+#define ActionString(T, STR) ActionArg(T, _string, STR)
+#define ActionBool(T, BOL) ActionArg(T, _boolean, BOL)
+
+ void SDLSource::play()
+ {
+ ActionNone(Play);
+ }
+
+ void SDLSource::stop()
+ {
+ ActionNone(Stop);
+ }
+
+ void SDLSource::pause()
+ {
+ ActionNone(Pause);
+ }
+
+ void SDLSource::resume()
+ {
+ ActionNone(Resume);
+ }
+
+ void SDLSource::rewind()
+ {
+ ActionNone(Rewind);
+ }
+
+ inline bool SDLSource::isStopped() const
+ {
+ return is(STOPPED);
+ }
+
+ bool SDLSource::isPaused() const
+ {
+ return is(PAUSED);
+ }
+
+ void SDLSource::setPitch(float pitch)
+ {
+ }
+
+ void SDLSource::setVolume(float volume)
+ {
+ ActionFloat(SetVolume, clamp(volume, 0.0f, 1.0f));
+ }
+
+ void SDLSource::setLoop(bool loop)
+ {
+ ActionBool(SetLoop, loop);
+ }
+
+ void SDLSource::setRate(float rate)
+ {
+ ActionFloat(SetRate, rate);
+ }
+
+ inline void SDLSource::handle(
+ SDLSourceManager* manager,
+ SDLSourceCommand* cmd
+ )
+ {
+ switch (cmd->action)
+ {
+ case Command::Action::Play:
+ manager->removeSource(this);
+ manager->pushSource(this);
+ status.state = PLAYING;
+ status.pos = 0; // rewind
+ break;
+ case Command::Action::Stop:
+ manager->removeSource(this);
+ status.state = STOPPED;
+ status.pos = 0; // rewind
+ break;
+ case Command::Action::Pause:
+ manager->removeSource(this);
+ status.state = PAUSED;
+ break;
+ case Command::Action::Resume:
+ manager->removeSource(this);
+ manager->pushSource(this);
+ status.state = PLAYING;
+ break;
+ case Command::Action::Rewind:
+ status.state = PLAYING;
+ status.pos = 0;
+ break;
+ case Command::Action::SetVolume:
+ status.volume = cmd->parameter._float;
+ break;
+ case Command::Action::SetLoop:
+ status.loop = cmd->parameter._boolean;
+ break;
+ }
+ }
+
+ inline void SDLSource::process(void* buf, size_t size)
+ {
+ short* buffer = (short*)buf; // AUDIO_S16SYS
+ int samples = (size / SDLAUDIO_BYTEDEPTH) >> 1; // ˫
+ const char L = 0, R = 1;
+ for (int i = 0; i < samples; ++i)
+ {
+ char* source = (char*)raw.data + status.pos * (raw.bitdepth / 8) * raw.channels;
+ short l = 0;
+ short r = 0;
+ if (raw.bitdepth == 16)
+ {
+ l = ((short*)source)[L] * status.volume;
+ r = ((short*)source)[L + raw.channels - 1] * status.volume;
+ }
+ else if (raw.bitdepth == 8)
+ {
+ l = source[L] << 8; // << 8 Ŵ16bits
+ r = source[L + raw.channels - 1] << 8;
+ }
+ short* sample = buffer + (i << 1);
+ sample[L] = clamp(sample[L] + l, SHRT_MIN, SHRT_MAX); //
+ sample[R] = clamp(sample[R] + r, SHRT_MIN, SHRT_MAX); //
+ ++status.pos;
+ if (status.pos > raw.samples && status.loop)
+ status.pos = 0; // rewind
+ else if (status.pos > raw.samples && !status.loop)
+ break;
+ }
+ }
+
+ Manager* Manager::get()
+ {
+ return (manager == nullptr ? manager = new Manager() : manager);
+ }
+
+ void Manager::processCommands()
+ {
+ Command* cmd = nullptr;
+ SDLSource* source = nullptr;
+ while (!commands.empty())
+ {
+ cmd = commands.front();
+ if (cmd != nullptr)
+ {
+ source = cmd->source;
+ if (source != nullptr)
+ source->handle(manager, cmd);
+ }
+ commands.pop();
+ }
+ }
+
+ /* AUDIO_S16SYS[size>>1] buffer */
+ void Manager::processSources(void* buf, size_t size)
+ {
+ /* clear render buffer */
+ memset(buf, 0, size);
+ SDLSource* src = nullptr;
+ std::vector<SDLSource*>::iterator it = sources.begin();
+ for (; it != sources.end();)
+ {
+ src = *it;
+ if (src != nullptr)
+ src->process(buf, size);
+ ++it;
+ }
+ }
+
+ void Manager::removeSource(SDLSource* source)
+ {
+ std::vector<SDLSource*>::iterator it = sources.begin();
+ for (it = sources.begin(); it != sources.end(); )
+ {
+ if (*it == source)
+ {
+ it = sources.erase(it);
+ return;
+ }
+ ++it;
+ }
+ }
+
+ void Manager::removeAllSource()
+ {
+ sources.clear();
+ }
+
+ void Manager::pushSource(SDLSource* source)
+ {
+ if (source != nullptr)
+ sources.push_back(source);
+ }
+
+ void Manager::pushCommand(SDLSourceCommand* cmd)
+ {
+ commands.push(cmd);
+ }
+
+ Command* Manager::getCommand()
+ {
+ if (!commandsPool.empty())
+ {
+ Command* cmd = commandsPool.top();
+ commandsPool.pop();
+ return cmd;
+ }
+ return new Command();
+ }
+
+ } // namespace SDL
+ } // namespace Audio
+} // namespace JinEngine
+
+#endif // (jin_audio) && (jin_audio == jin_audio_sdl) \ No newline at end of file
diff --git a/src/libjin/audio/SDL/je_sdl_source.h b/src/libjin/audio/SDL/je_sdl_source.h
new file mode 100644
index 0000000..bbc8c7a
--- /dev/null
+++ b/src/libjin/audio/SDL/je_sdl_source.h
@@ -0,0 +1,272 @@
+#ifndef __JE_SOURCE_SDL_H__
+#define __JE_SOURCE_SDL_H__
+#include "../../core/je_configuration.h"
+#if defined(jin_audio) && (jin_audio == jin_audio_sdl)
+
+#include <vector>
+#include <queue>
+#include <stack>
+#include <exception>
+
+#include "../je_source.h"
+
+namespace JinEngine
+{
+ namespace Audio
+ {
+ namespace SDL
+ {
+
+ typedef struct SDLSourceCommand;
+
+ class SDLSourceManager;
+
+ ///
+ /// Audio source SDL implementation.
+ ///
+ class SDLSource : public Source
+ {
+ public:
+ ///
+ /// Create source from raw source data file.
+ ///
+ /// @param file Audio source file.
+ /// @return Return source if create successful, otherwise return null.
+ ///
+ static SDLSource* createSource(const char* file);
+
+ ///
+ /// Create source from raw source data.
+ ///
+ /// @param mem Source data.
+ /// @param size Source data size.
+ /// @return Return source if create successful, otherwise return null.
+ ///
+ static SDLSource* createSource(void* mem, size_t size);
+
+ ///
+ /// Source destructor.
+ ///
+ ~SDLSource();
+
+ ///
+ /// Play source.
+ ///
+ void play() override;
+
+ ///
+ /// Stop source.
+ ///
+ void stop() override;
+
+ ///
+ /// Pause source.
+ ///
+ void pause() override;
+
+ ///
+ /// Resume source.
+ ///
+ void resume() override;
+
+ ///
+ /// Rewind source.
+ ///
+ void rewind() override;
+
+ ///
+ /// Return if the source is stopped.
+ ///
+ /// @return True if the source is stopped, otherwise return false.
+ ///
+ bool isStopped() const override;
+
+ ///
+ /// Return if the source is paused.
+ ///
+ /// @return True if the source is paused(, otherwise return false.
+ ///
+ bool isPaused() const override;
+
+ ///
+ /// Set pitch.
+ ///
+ /// @param pitch Pitch of source.
+ ///
+ void setPitch(float pitch) override;
+
+ ///
+ /// Set volume.
+ ///
+ /// @param volume Volume of source.
+ ///
+ void setVolume(float volume) override;
+
+ ///
+ /// Set source loop.
+ ///
+ /// @param loop Looping or not.
+ ///
+ void setLoop(bool loop) override;
+
+ ///
+ /// Set source rate.
+ ///
+ /// @param rate Rate of source.
+ ///
+ void setRate(float rate) override;
+
+ ///
+ /// Handle a specific command.
+ ///
+ /// @param manager Audio manager.
+ /// @param cmd Source commad.
+ ///
+ inline void handle(SDLSourceManager* manager, SDLSourceCommand* cmd);
+
+ ///
+ /// Process decoded source data.
+ ///
+ /// @param buffer Source data.
+ /// @param size Data size.
+ ///
+ inline void process(void* buffer, size_t size);
+
+ protected:
+ ///
+ /// Source constructor.
+ ///
+ SDLSource();
+
+ ///
+ /// Decode wav file.
+ ///
+ /// @param mem Wav file data.
+ /// @param size Wav data size.
+ ///
+ void decode_wav(void* mem, int size);
+
+ ///
+ /// Decode ogg file.
+ ///
+ /// @param mem ogg file data.
+ /// @param size ogg data size.
+ ///
+ void decode_ogg(void* mem, int size);
+
+ ///
+ /// Check source state.
+ ///
+ /// @param state State to be checked.
+ /// @return True if state is given state, otherwise return false.
+ ///
+ inline bool is(int state) const
+ {
+ return (status.state & state) == state;
+ }
+
+ // Source data.
+ struct {
+ const void* data; // Ƶ
+ int length; // dataֽڳ
+ const void* end; // dataβ = (unsigned char*)data + size
+ int samplerate; // Ƶ
+ unsigned char bitdepth; // ÿsampleıس
+ int samples; // sample = size / (bitdepth / 8)
+ unsigned char channels; // channel1(mono)2(stereo)
+ } raw;
+ // Procedure controller variable.
+ struct {
+ int pos; // ǰŵsample
+ int pitch; // pitch
+ int state; // ǰ״̬
+ bool loop; // loop or not
+ float volume; //
+ } status;
+
+ };
+
+ ///
+ /// Source manager.
+ ///
+ class SDLSourceManager
+ {
+ public:
+ ///
+ /// Get manager singleton.
+ ///
+ /// @return Singleton of SDL source manager.
+ ///
+ static SDLSourceManager* get();
+
+ ///
+ /// Process commands.
+ ///
+ void processCommands();
+
+ ///
+ /// Process sources.
+ ///
+ /// @param buffer Source data.
+ /// @param size Size of source data.
+ ///
+ void processSources(void* buffer, size_t size);
+
+ ///
+ /// Clear source queue.
+ ///
+ /// This function will stop all sources.
+ ///
+ void removeAllSource();
+
+ ///
+ /// Remove specific source.
+ ///
+ /// @param source SDL audio source.
+ ///
+ void removeSource(SDLSource* source);
+
+ ///
+ /// Push specific source into queue.
+ ///
+ /// @param source SDL audio source.
+ ///
+ void pushSource(SDLSource* source);
+
+ ///
+ /// Get command from queue.
+ ///
+ /// @return Command at first place.
+ ///
+ SDLSourceCommand* getCommand();
+
+ ///
+ /// Push command.
+ ///
+ /// @param cmd Spcific command.
+ ///
+ void pushCommand(SDLSourceCommand* cmd);
+
+ private:
+ std::queue<SDLSourceCommand*> commands;
+ std::stack<SDLSourceCommand*> commandsPool;
+ std::vector<SDLSource*> sources; // processing sources
+ static SDLSourceManager* manager;
+
+ };
+
+ class SourceException : public std::exception
+ {
+ const char* what() const throw ()
+ {
+ return "Load Source Exception";
+ }
+ };
+
+ } // namespace SDL
+ } // namespace Audio
+} // namespace JinEngine
+
+#endif // (jin_audio) && (jin_audio == jin_audio_sdl)
+
+#endif // __JE_SOURCE_SDL_H__ \ No newline at end of file
diff --git a/src/libjin/audio/je_audio_manager.cpp b/src/libjin/audio/je_audio_manager.cpp
new file mode 100644
index 0000000..e451aa3
--- /dev/null
+++ b/src/libjin/audio/je_audio_manager.cpp
@@ -0,0 +1,15 @@
+#include "../core/je_configuration.h"
+#if jin_audio
+
+#include "SDL2/SDL.h"
+#include "je_audio_manager.h"
+
+namespace JinEngine
+{
+ namespace Audio
+ {
+
+ } // namespace Audio
+} // namespace JinEngine
+
+#endif // jin_audio
diff --git a/src/libjin/audio/je_audio_manager.h b/src/libjin/audio/je_audio_manager.h
new file mode 100644
index 0000000..120d38e
--- /dev/null
+++ b/src/libjin/audio/je_audio_manager.h
@@ -0,0 +1,87 @@
+#ifndef __JE_AUDIO_H__
+#define __JE_AUDIO_H__
+
+#include "../core/je_configuration.h"
+#if defined(jin_audio)
+
+#include "../utils/je_macros.h"
+#include "../common/je_subsystem.hpp"
+
+#include "SDL2/SDL.h"
+
+namespace JinEngine
+{
+ namespace Audio
+ {
+
+ class Source;
+
+ ///
+ /// Audio manager.
+ ///
+ template<class SubAudio>
+ class AudioManager : public Subsystem<SubAudio>
+ {
+ public:
+ ///
+ /// Audio state.
+ ///
+ enum State
+ {
+ PLAY ,
+ STOP ,
+ PAUSE,
+ };
+
+ ///
+ /// Play all sources whose state is playing.
+ ///
+ virtual void play() = 0;
+
+ ///
+ /// Stop and remove all sources from the queue.
+ ///
+ virtual void stop() = 0;
+
+ ///
+ /// Pause audio.
+ ///
+ virtual void pause() = 0;
+
+ ///
+ /// Resume audio.
+ ///
+ virtual void resume() = 0;
+
+ ///
+ /// Set global audio volume.
+ ///
+ virtual void setVolume(float volume) = 0;
+
+ protected:
+ singleton(AudioManager);
+
+ ///
+ /// AudioManager constructor.
+ ///
+ AudioManager()
+ : volume(1)
+ , state(State::PLAY)
+ {};
+
+ ///
+ /// AudioManager destructor.
+ ///
+ virtual ~AudioManager() {};
+
+ float volume;
+ State state;
+
+ };
+
+ } // namespace Audio
+} // namespace JinEngine
+
+#endif // jin_audio
+
+#endif // __JE_AUDIO_H__ \ No newline at end of file
diff --git a/src/libjin/audio/je_source.cpp b/src/libjin/audio/je_source.cpp
new file mode 100644
index 0000000..6bc1f4f
--- /dev/null
+++ b/src/libjin/audio/je_source.cpp
@@ -0,0 +1,30 @@
+#include "../core/je_configuration.h"
+#if defined(jin_audio)
+
+#include <cstring>
+
+#include "je_source.h"
+
+namespace JinEngine
+{
+ namespace Audio
+ {
+
+ static int check_header(const void *data, int size, const char *str, int offset) {
+ int len = strlen(str);
+ return (size >= offset + len) && !memcmp((char*)data + offset, str, len);
+ }
+
+ SourceType Source::getType(const void* mem, int size)
+ {
+ if(check_header(mem, size, "WAVE", 8))
+ return SourceType::WAV;
+ if(check_header(mem, size, "OggS", 0))
+ return SourceType::OGG;
+ return SourceType::INVALID;
+ }
+
+ } // namespace Audio
+} // namespace JinEngine
+
+#endif // jin_audio
diff --git a/src/libjin/audio/je_source.h b/src/libjin/audio/je_source.h
new file mode 100644
index 0000000..f60daf9
--- /dev/null
+++ b/src/libjin/audio/je_source.h
@@ -0,0 +1,116 @@
+#ifndef __JE_AUDIO_SOURCE_H__
+#define __JE_AUDIO_SOURCE_H__
+#include "../core/je_configuration.h"
+#if defined(jin_audio)
+
+#include "SDL2/SDL.h"
+
+namespace JinEngine
+{
+ namespace Audio
+ {
+
+ ///
+ /// Audio source encoding type.
+ ///
+ enum SourceType
+ {
+ INVALID = 0,
+ WAV,
+ OGG,
+ };
+
+ ///
+ /// Audio source.
+ ///
+ class Source
+ {
+ public:
+ ///
+ /// Source constructor.
+ ///
+ Source() {};
+
+ ///
+ /// Source destructor.
+ ///
+ virtual ~Source() {};
+
+ ///
+ /// Start playing source.
+ ///
+ virtual void play() = 0;
+
+ ///
+ /// Stop playing source.
+ ///
+ virtual void stop() = 0;
+
+ ///
+ /// Pause source.
+ ///
+ virtual void pause() = 0;
+
+ ///
+ /// Resume source.
+ ///
+ virtual void resume() = 0;
+
+ ///
+ /// Rewind source.
+ ///
+ virtual void rewind() = 0;
+
+ ///
+ /// Whether the source is playing or not.
+ ///
+ virtual bool isStopped() const = 0;
+
+ ///
+ /// Whether the source is paused or not.
+ ///
+ virtual bool isPaused() const = 0;
+
+ ///
+ /// Set source pitch.
+ ///
+ /// @param pitch Pitch of source.
+ ///
+ virtual void setPitch(float pitch) = 0;
+
+ ///
+ /// Set volume of source.
+ ///
+ /// @param volume Volume of source.
+ ///
+ virtual void setVolume(float volume) = 0;
+
+ ///
+ /// Set source loop.
+ ///
+ /// @param loop Looping or not.
+ ///
+ virtual void setLoop(bool loop) = 0;
+
+ ///
+ /// Set source rate.
+ ///
+ /// @param rate Rate of source.
+ ///
+ virtual void setRate(float rate) = 0;
+
+ protected:
+
+ ///
+ /// Get type of source data.
+ ///
+ static SourceType getType(const void* mem, int size);
+
+ };
+
+ } // namespace Audio
+} // namespace JinEngine
+
+#endif // jin_audio
+
+#endif // __JE_AUDIO_SOURCE_H__ \ No newline at end of file