diff options
Diffstat (limited to 'src/libjin')
-rw-r--r-- | src/libjin/audio/sdl/audio.cpp | 22 | ||||
-rw-r--r-- | src/libjin/audio/sdl/audio.h | 11 | ||||
-rw-r--r-- | src/libjin/audio/sdl/source.cpp | 216 | ||||
-rw-r--r-- | src/libjin/audio/sdl/source.h | 23 | ||||
-rw-r--r-- | src/libjin/audio/source.cpp | 11 | ||||
-rw-r--r-- | src/libjin/utils/unittest.cpp | 76 |
6 files changed, 238 insertions, 121 deletions
diff --git a/src/libjin/audio/sdl/audio.cpp b/src/libjin/audio/sdl/audio.cpp index be4caa2..79ca3f2 100644 --- a/src/libjin/audio/sdl/audio.cpp +++ b/src/libjin/audio/sdl/audio.cpp @@ -1,6 +1,7 @@ #include <iostream> #include "audio.h" #include "source.h" +#include "../../math/math.h" namespace jin { @@ -8,9 +9,9 @@ namespace audio { /* עcallbackƵ̵߳ */ - void defaultCallback(void *userdata, Uint8 *stream, int size) + static void defaultCallback(void *userdata, Uint8 *stream, int size) { - SDLAudio* audio = static_cast<SDLAudio*>(userdata); + static SDLAudio* audio = static_cast<SDLAudio*>(userdata); audio->lock(); audio->processCommands(); audio->processSources(stream, size); @@ -22,14 +23,21 @@ namespace audio if (SDL_Init(SDL_INIT_AUDIO) < 0) return false; SDL_AudioSpec spec; - spec.freq = 44100; // 44100 Hz - spec.format = AUDIO_S16SYS; // signed 16bits - spec.channels = 2; // - spec.samples = 1 << 15; // Uin16Χ2 + Setting* setting = (Setting*)s; + if (setting == nullptr) + return false; + + unsigned int samplerate = setting->samplerate; + unsigned int samples = clamp(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); + audioDevice = SDL_OpenAudioDevice(NULL, 0, &spec, NULL, 0); if (audioDevice == 0) return false; /* start audio */ diff --git a/src/libjin/audio/sdl/audio.h b/src/libjin/audio/sdl/audio.h index 528fa7d..6e42690 100644 --- a/src/libjin/audio/sdl/audio.h +++ b/src/libjin/audio/sdl/audio.h @@ -9,11 +9,22 @@ namespace jin namespace audio { +#define SDLAUDIO_BITDEPTH 16 +#define SDLAUDIO_BYTEDEPTH (SDLAUDIO_BITDEPTH >> 3) +#define SDLAUDIO_CHANNELS 2 + class SDLAudio : public Audio { public: + struct Setting : SettingBase + { + public: + int samplerate; // Ƶ + int samples; // sample<=samplerate + }; + static inline Audio* get() { return audio != NULL ? audio : (audio = new SDLAudio()); diff --git a/src/libjin/audio/sdl/source.cpp b/src/libjin/audio/sdl/source.cpp index 1a5c5d1..51b67e4 100644 --- a/src/libjin/audio/sdl/source.cpp +++ b/src/libjin/audio/sdl/source.cpp @@ -8,11 +8,15 @@ #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 @@ -75,6 +79,7 @@ namespace audio char* buffer = (char*)malloc(size); memset(buffer, 0, size); fs.read(buffer, size); + fs.close(); SDLSource* source = createSource(buffer, size); free(buffer); return source; @@ -85,17 +90,15 @@ namespace audio if (mem == nullptr) return nullptr; SDLSource* source = new SDLSource(); -#define parse(FMT) case FMT : source->load##FMT(mem, size); break try { SourceType format = getType(mem, size); switch (format) { - parse(OGG); - parse(WAV); + case OGG: source->decode_ogg(mem, size); break; + case WAV: source->decode_wav(mem, size); break; } } -#undef parse catch (SourceException& exp) { delete source; @@ -117,37 +120,44 @@ namespace audio raw.data = 0; } - void SDLSource::loadWAV(void* mem, int size) + void SDLSource::decode_wav(void* mem, int size) { wav_t wav; if (wav_read(&wav, mem, size) == 0) { - raw.data = wav.data; - raw.size = wav.length * wav.bitdepth / 8; - raw.end = (char*)raw.data + raw.size; - raw.rate = wav.samplerate; - raw.bitdepth = wav.bitdepth; - raw.samples = raw.size / (wav.bitdepth / 8.f); - raw.channel = clamp(wav.channels, CHANNEL::MONO, CHANNEL::STEREO); - raw.silence = 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::loadOGG(void* mem, int size) + void SDLSource::decode_ogg(void* _mem, int size) { - raw.samples = stb_vorbis_decode_memory((unsigned char*)mem, size, (int*)&raw.channel, &raw.rate, (short**)&raw.data) << 1; - raw.channel = clamp(raw.channel, CHANNEL::MONO, CHANNEL::STEREO); - raw.size = raw.samples << 1; // 2 bytes each sample - raw.bitdepth = 16; - raw.end = (char*)raw.data + raw.size; - raw.silence = 0; - - if (raw.samples < 0) - { - throw SourceException(); - } + 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)\ @@ -227,6 +237,85 @@ Manager::get()->pushCommand(cmd); \ 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); @@ -243,43 +332,7 @@ Manager::get()->pushCommand(cmd); \ { source = cmd->source; if (source != nullptr) - { - switch (cmd->action) - { - case Command::Action::Play: - removeSource(source); - pushSource(source); - source->status.state = PLAYING; - source->status.pos = 0; // rewind - break; - case Command::Action::Stop: - manager->removeSource(source); - source->status.state = STOPPED; - source->status.pos = 0; // rewind - break; - case Command::Action::Pause: - manager->removeSource(source); - source->status.state = PAUSED; - break; - case Command::Action::Resume: - manager->removeSource(source); - manager->pushSource(source); - source->status.state = PLAYING; - break; - case Command::Action::Rewind: - source->status.state = PLAYING; - source->status.pos = 0; - break; - case Command::Action::SetVolume: - //float cmd->parameter._float; - break; - case Command::Action::SetLoop: - source->status.loop = cmd->parameter._boolean; - break; - /*case Command::Action::SetRate: - */ - } - } + source->handle(manager, cmd); } commands.pop(); } @@ -288,38 +341,15 @@ Manager::get()->pushCommand(cmd); \ /* AUDIO_S16SYS[size>>1] buffer */ shared void Manager::processSources(void* buf, size_t size) { - Sint16* buffer = (Sint16*)buf; - unsigned int samples = size >> 1; - memset(buffer, 0, 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) - goto next; - src->status.pos; - Sint16* source = (Sint16*)((char*)src->raw.data + src->status.pos); - unsigned int remain = src->raw.samples - (src->status.pos >> 1); - for (int i = 0; i < min(remain, samples); ++i) - { - buffer[i] += (src->raw.channel == CHANNEL::STEREO ? source[i] : source[(i % 2) * 2]); - } - if (remain < samples) - { - if (!src->status.loop) - { - it = sources.erase(it); - continue; - } - int j = 0; - for (int i = samples - remain; i < samples; ++i, ++j) - buffer[i] += (src->raw.channel == CHANNEL::STEREO ? source[j] : source[j % 2 * 2]); - src->status.pos = (j << 1); - continue; - } - src->status.pos += (samples << 1); - next: + if (src != nullptr) + src->process(buf, size); ++it; } } @@ -360,13 +390,5 @@ Manager::get()->pushCommand(cmd); \ return new Command(); } - shared void Manager::collectCommand(Command* cmd) - { - if (cmd != nullptr) - { - commandsPool.push(cmd); - } - } - } }
\ No newline at end of file diff --git a/src/libjin/audio/sdl/source.h b/src/libjin/audio/sdl/source.h index 365d6ff..ff311b6 100644 --- a/src/libjin/audio/sdl/source.h +++ b/src/libjin/audio/sdl/source.h @@ -14,6 +14,7 @@ namespace audio { typedef struct SDLSourceCommand; + class SDLSourceManager; class SDLSource : public Source { @@ -39,33 +40,33 @@ namespace audio bool setLoop(bool loop) override; void setRate(float rate) override; + inline void handle(SDLSourceManager* manager, SDLSourceCommand* cmd); + inline void process(void* buffer, size_t size); + private: SDLSource(); - friend class SDLSourceManager; - - void loadWAV(void* mem, int size); - void loadOGG(void* mem, int size); + void decode_wav(void* mem, int size); + void decode_ogg(void* mem, int size); inline bool is(int state) const { return (status.state & state) == state; } struct { const void* data; // Ƶ - int size; // dataֽڳ + int length; // dataֽڳ const void* end; // dataβ = (unsigned char*)data + size - int rate; // Ƶ + int samplerate; // Ƶ unsigned char bitdepth; // ÿsampleıس int samples; // sample = size / (bitdepth / 8) - unsigned char channel; // channel1(mono)2(stereo) - char silence; // 0 + unsigned char channels; // channel1(mono)2(stereo) } raw; /* Procedure controller variable */ struct { - int pos; // ǰŵλ + int pos; // ǰŵsample int pitch; // pitch int state; // ǰ״̬ bool loop; // loop or not @@ -85,10 +86,6 @@ namespace audio static void processCommands(); static void processSources(void* buffer, size_t size); - private: - - friend class SDLSource; - static void removeSource(SDLSource* source); static void pushSource(SDLSource* source); static SDLSourceCommand* getCommand(); diff --git a/src/libjin/audio/source.cpp b/src/libjin/audio/source.cpp index ec80a0f..a09bbab 100644 --- a/src/libjin/audio/source.cpp +++ b/src/libjin/audio/source.cpp @@ -6,14 +6,17 @@ namespace jin namespace audio { + static int check_header(const void *data, int size, 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) { - const char* p = (const char* )mem; - if (memcmp(p, "RIFF", 4) == 0 && memcmp(p + 8, "WAVE", 4) == 0) + if(check_header(mem, size, "WAVE", 8)) return SourceType::WAV; - if (memcmp(p, "OggS", 4) == 0) + if(check_header(mem, size, "OggS", 0)) return SourceType::OGG; - return SourceType::INVALID; } diff --git a/src/libjin/utils/unittest.cpp b/src/libjin/utils/unittest.cpp index ce72173..951b996 100644 --- a/src/libjin/utils/unittest.cpp +++ b/src/libjin/utils/unittest.cpp @@ -29,4 +29,80 @@ int main(int argc, char* argv[]) return 0; } +/* +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <SDL2/SDL.h> + +#include <3rdparty/cmixer/cmixer.h> + +static SDL_mutex* audio_mutex; + +static void lock_handler(cm_Event *e) { + if (e->type == CM_EVENT_LOCK) { + SDL_LockMutex(audio_mutex); + } + if (e->type == CM_EVENT_UNLOCK) { + SDL_UnlockMutex(audio_mutex); + } +} + + +static void audio_callback(void *udata, Uint8 *stream, int size) { + cm_process((cm_Int16*)stream, size / 2); +} + + +int main(int argc, char **argv) { + SDL_AudioDeviceID dev; + SDL_AudioSpec fmt, got; + cm_Source *src; + cm_Source* src2; + + + SDL_Init(SDL_INIT_AUDIO); + audio_mutex = SDL_CreateMutex(); + + memset(&fmt, 0, sizeof(fmt)); + fmt.freq = 44100; + fmt.format = AUDIO_S16; + fmt.channels = 2; + fmt.samples = 1024; + fmt.callback = audio_callback; + + dev = SDL_OpenAudioDevice(NULL, 0, &fmt, &got, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + if (dev == 0) { + fprintf(stderr, "Error: failed to open audio device '%s'\n", SDL_GetError()); + exit(EXIT_FAILURE); + } + + cm_init(got.freq); + cm_set_lock(lock_handler); + cm_set_master_gain(0.5); + + SDL_PauseAudioDevice(dev, 0); + + src = cm_new_source_from_file("a.ogg"); + src2 = cm_new_source_from_file("loop.wav"); + if (!src) { + fprintf(stderr, "Error: failed to create source '%s'\n", cm_get_error()); + exit(EXIT_FAILURE); + } + cm_set_loop(src2, 1); + + cm_play(src); + cm_play(src2); + + printf("Press [return] to exit\n"); + getchar(); + + cm_destroy_source(src); + SDL_CloseAudioDevice(dev); + SDL_Quit(); + + return EXIT_SUCCESS; +} +*/ + #endif
\ No newline at end of file |