diff options
Diffstat (limited to 'src/libjin/Audio/SDL/Source.cpp')
-rw-r--r-- | src/libjin/Audio/SDL/Source.cpp | 399 |
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 |