Created
May 22, 2011 00:39
-
-
Save szastupov/985035 to your computer and use it in GitHub Desktop.
audio experiments
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cassert> | |
#include <cstdlib> | |
#include <vector> | |
#include <boost/intrusive_ptr.hpp> | |
#include <AL/al.h> | |
#include <AL/alc.h> | |
#include <vorbis/vorbisfile.h> | |
#include "bicycles/sp.h" | |
#include "gfxmath/vec.h" | |
class ALBuffer : public RefCounted<ALBuffer> { | |
public: | |
ALBuffer(); | |
~ALBuffer(); | |
void setData(ALenum format, const void *data, int size, int freq); | |
ALuint getID() const | |
{ | |
return m_id; | |
} | |
private: | |
ALuint m_id; | |
}; | |
typedef boost::intrusive_ptr<ALBuffer> ALBufferPtr; | |
class ALSource { | |
public: | |
ALSource(); | |
~ALSource(); | |
void setBuffer(ALBufferPtr buf); | |
void setPosition(const vec3f &pos); | |
void play(); | |
void stop(); | |
int getState() const; | |
private: | |
ALBufferPtr m_buf; | |
ALuint m_id; | |
}; | |
ALBuffer::ALBuffer() | |
{ | |
alGenBuffers(1, &m_id); | |
} | |
ALBuffer::~ALBuffer() | |
{ | |
alDeleteBuffers(1, &m_id); | |
} | |
void ALBuffer::setData(ALenum format, const void *data, int size, int freq) | |
{ | |
alBufferData(m_id, format, data, size, freq); | |
} | |
ALSource::ALSource() | |
{ | |
alGenSources(1, &m_id); | |
} | |
ALSource::~ALSource() | |
{ | |
alDeleteSources(1, &m_id); | |
} | |
void ALSource::setBuffer(ALBufferPtr buf) | |
{ | |
m_buf = buf; | |
if (buf) | |
alSourcei(m_id, AL_BUFFER, buf->getID()); | |
} | |
void ALSource::setPosition(const vec3f &pos) | |
{ | |
alSourcefv(m_id, AL_POSITION, pos.data()); | |
} | |
void ALSource::play() | |
{ | |
alSourcePlay(m_id); | |
} | |
void ALSource::stop() | |
{ | |
alSourceStop(m_id); | |
} | |
int ALSource::getState() const | |
{ | |
int state; | |
alGetSourcei(m_id, AL_SOURCE_STATE, &state); | |
return state; | |
} | |
void readOGG(const char *path, ALBuffer &buffer) | |
{ | |
OggVorbis_File file; | |
int ret; | |
ret = ov_open(fopen(path, "r"), &file, NULL, 0); | |
assert(ret == 0); | |
ALenum format; | |
ALsizei freq; | |
vorbis_info *info = ov_info(&file, -1); | |
if (info->channels == 1) | |
format = AL_FORMAT_MONO16; | |
else | |
format = AL_FORMAT_STEREO16; | |
freq = info->rate; | |
size_t size = ov_pcm_total(&file, -1) * info->channels * 2; | |
std::vector<char> data; | |
long bytes; | |
int bitsream; | |
data.reserve(size); | |
do { | |
char tmp[1024]; | |
bytes = ov_read(&file, tmp, sizeof(tmp), 0, 2, 1, &bitsream); | |
data.insert(data.end(), tmp, tmp+bytes); | |
} while (bytes > 0); | |
buffer.setData(format, &data[0], data.size(), freq); | |
ov_clear(&file); | |
} | |
double noteFreq(int note, int octave) | |
{ | |
int e = (octave*12+note)-48; | |
return pow(2, (e/12.0))*261.63; | |
} | |
double getDuration(char c) | |
{ | |
return 1.0/(c-'0'); | |
} | |
double getNoteFreq(char c) | |
{ | |
if (c == ' ') | |
return 0; | |
static const struct { | |
char c; | |
int note; | |
int octave; | |
} noteMap[] = { | |
{ 'D', 2, 4 }, | |
{ 'e', 4, 4 }, | |
{ 'f', 5, 4 }, | |
{ 'g', 7, 4 }, | |
{ 'a', 9, 4 }, | |
{ 'b', 11, 4 }, | |
{ 'c', 0, 5 }, | |
{ 'd', 2, 5 } | |
}; | |
for (size_t i = 0; i < sizeof(noteMap)/sizeof(noteMap[0]); i++) | |
if (noteMap[i].c == c) | |
return noteFreq(noteMap[i].note, noteMap[i].octave); | |
assert(0); | |
} | |
void parseNotes(ALBuffer &buffer, const char *notes, int rate) | |
{ | |
std::vector<short> data; | |
while (*notes) { | |
double freq = getNoteFreq(*notes++); | |
double duration = getDuration(*notes++); | |
double step = M_PI*2*freq/rate; | |
int nsamples = duration*rate; | |
for (int i = 0; i < nsamples; i++) | |
data.push_back(20000*sin(i*step)); | |
} | |
buffer.setData(AL_FORMAT_MONO16, &data[0], data.size()*2, rate); | |
} | |
int main() | |
{ | |
ALCdevice *dev = alcOpenDevice(NULL); | |
assert(dev); | |
ALCcontext *ctx = alcCreateContext(dev, NULL); | |
assert(ctx); | |
alcMakeContextCurrent(ctx); | |
{ | |
ALBufferPtr buffer = new ALBuffer(); | |
//readOGG("/home/stepan/science.ogg", *buffer); | |
const char music[] = | |
"D4" "D4b4a4g4" "D2 4D4" "D4b4a4g4" | |
"e2 4e4" "e4c4b4a4" "f2 4d4" "d4d4c4a4" | |
"b2g4D4" "D4b4a4g4" "D2 4D4" "D4b4a4g4" | |
"e2 4e4" "e4c4b4a4" "d4d4d4d4" "e4d4c4a4" | |
"g2g4 4" "b4b4b2" "b4b4b2" "b4d4g4a8" "b2" | |
"c4c4c4c8" "c4b4b4b8b8" "b4a4a4b4" "a2d2" | |
"b4b4b2" "b4b4b2" "b4d4g4a8" "b2 4" "c4c4" | |
"c4b4b4b8b8" "d4d4c4a4" "g2"; | |
parseNotes(*buffer, music, 44100); | |
ALSource source; | |
source.setBuffer(buffer); | |
source.play(); | |
while (source.getState() != AL_STOPPED) | |
sleep(1); | |
} | |
alcMakeContextCurrent(NULL); | |
alcDestroyContext(ctx); | |
alcCloseDevice(dev); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment