aboutsummaryrefslogtreecommitdiff
path: root/src/libjin/Audio/SDL/Source.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libjin/Audio/SDL/Source.cpp')
-rw-r--r--src/libjin/Audio/SDL/Source.cpp399
1 files changed, 399 insertions, 0 deletions
diff --git a/src/libjin/Audio/SDL/Source.cpp b/src/libjin/Audio/SDL/Source.cpp
new file mode 100644
index 0000000..0eedbba
--- /dev/null
+++ b/src/libjin/Audio/SDL/Source.cpp
@@ -0,0 +1,399 @@
+#include "../../modules.h"
+#if JIN_MODULES_AUDIO && JIN_AUDIO_SDLAUDIO
+
+#include <exception>
+#include <fstream>
+
+#include "../../math/math.h"
+#include "../../utils/macros.h"
+#include "source.h"
+#include "3rdparty/wav/wav.h"
+#define STB_VORBIS_HEADER_ONLY
+#include "3rdparty/stb/stb_vorbis.c"
+
+#include "audio.h"
+
+namespace jin
+{
+namespace audio
+{
+
+#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
+
+ shared std::queue<Command*> Manager::commands;
+ shared std::stack<Command*> Manager::commandsPool;
+ shared std::vector<SDLSource*> Manager::sources;
+ shared 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));
+ }
+
+ 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.bitdepth / 8;
+ raw.end = (char*)raw.data + raw.length;
+ raw.samplerate = wav.samplerate;
+ raw.bitdepth = wav.bitdepth;
+ raw.samples = raw.length / (wav.bitdepth / 8.f) / wav.channels;
+ raw.channels = clamp(wav.channels, CHANNEL::MONO, CHANNEL::STEREO);
+ }
+ 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;
+ raw.length = samples * channels * sizeof(short);
+ 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, volume);
+ }
+
+ bool SDLSource::setLoop(bool loop)
+ {
+ ActionBool(SetLoop, loop);
+ return false;
+ }
+
+ 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:
+ //float 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
+ unsigned int samples = size / SDLAUDIO_BYTEDEPTH;
+ short* sample;
+ short origin;
+
+ const char bitdepth = raw.bitdepth;
+ const char channles = raw.channels;
+
+ int pos = status.pos;
+ int pitch = status.pitch;
+ int state = status.state;
+ bool loop = status.loop;
+ int volume = status.volume;
+ short* clip16 = nullptr;
+ char* clip8 = nullptr;
+ int clip = 0;
+
+ if (bitdepth == 8)
+ clip8 = (char*)raw.data;
+ else if (bitdepth == 16)
+ clip16 = (short*)raw.data;
+
+ for (int i = 0; i < samples; i+=2 /*˫*/)
+ {
+ /* Ƶļsampleᱻ */
+ sample = buffer + i * SDLAUDIO_BYTEDEPTH;
+ origin = *sample;
+ if (bitdepth == 8)
+ {
+ clip = *clip8;
+ }
+ else if (bitdepth == 16)
+ clip = *clip16;
+
+ }
+ }
+
+ Manager* Manager::get()
+ {
+ return (manager == nullptr ? manager = new Manager() : manager);
+ }
+
+ shared 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 */
+ shared 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;
+ }
+ }
+
+ shared 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;
+ }
+ }
+
+ shared void Manager::pushSource(SDLSource* source)
+ {
+ if(source != nullptr)
+ sources.push_back(source);
+ }
+
+ shared void Manager::pushCommand(SDLSourceCommand* cmd)
+ {
+ commands.push(cmd);
+ }
+
+ shared Command* Manager::getCommand()
+ {
+ if (!commandsPool.empty())
+ {
+ Command* cmd = commandsPool.top();
+ commandsPool.pop();
+ return cmd;
+ }
+ return new Command();
+ }
+
+}
+}
+
+#endif // JIN_MODULES_AUDIO && JIN_AUDIO_SDLAUDIO \ No newline at end of file