aboutsummaryrefslogtreecommitdiff
path: root/src/libjin
diff options
context:
space:
mode:
Diffstat (limited to 'src/libjin')
-rw-r--r--src/libjin/audio/sdl/audio.cpp22
-rw-r--r--src/libjin/audio/sdl/audio.h11
-rw-r--r--src/libjin/audio/sdl/source.cpp216
-rw-r--r--src/libjin/audio/sdl/source.h23
-rw-r--r--src/libjin/audio/source.cpp11
-rw-r--r--src/libjin/utils/unittest.cpp76
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