#include "shatter.h" #include "audioloop.h" #include "sndfile.h" #include "ticktimer.h" #include "deathball.h" using namespace std; AudioLoop::AudioLoop(FMOD::System* sound_sys) : _volume(.7) , _cur_sample(0) , _death_ball(NULL) { _tick_timer = new TickTimer; _channel = 0; FMOD_RESULT result; FMOD_MODE mode = FMOD_3D | FMOD_OPENUSER | FMOD_LOOP_NORMAL | FMOD_HARDWARE | FMOD_CREATESTREAM; FMOD_CREATESOUNDEXINFO createsoundexinfo; memset(&createsoundexinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); createsoundexinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); /* required. */ createsoundexinfo.decodebuffersize = 2000; /* Chunk size of stream update in samples. This will be the amount of data passed to the user callback. */ createsoundexinfo.length = SAMPLE_RATE * sizeof(signed short) * 5; /* Length of PCM data in bytes of whole song (for Sound::getLength) */ createsoundexinfo.numchannels = 1; /* Number of channels in the sound. */ createsoundexinfo.defaultfrequency = SAMPLE_RATE; /* Default playback rate of sound. */ createsoundexinfo.format = FMOD_SOUND_FORMAT_PCM16; /* Data format of sound. */ createsoundexinfo.pcmreadcallback = pcmreadcallback; /* User callback for reading. */ createsoundexinfo.pcmsetposcallback = pcmsetposcallback; /* User callback for seeking. */ createsoundexinfo.userdata = this; result = sound_sys->createSound(0, mode, &createsoundexinfo, &_sound); FMODERRCHECK(result); result = _sound->set3DMinMaxDistance(.5f, 1000.0f); FMODERRCHECK(result); result = sound_sys->playSound(FMOD_CHANNEL_FREE, _sound, true, &_channel); FMODERRCHECK(result); result = _channel->setPaused(true); FMODERRCHECK(result); /* somewhere far away! */ set_3D(STVector3::eZ * 1000, STVector3::Zero); } AudioLoop::~AudioLoop() { _sound->release(); } void AudioLoop::play() { _channel->setPaused(false); _tick_timer->tick(); } void AudioLoop::set_3D(const STVector3 & pos, const STVector3 & vel) { FMOD_VECTOR pos_v = { pos.x, pos.y, pos.z }; FMOD_VECTOR vel_v = { vel.x, vel.y, vel.z }; FMOD_RESULT result = _channel->set3DAttributes(&pos_v, &vel_v); FMODERRCHECK(result); } void AudioLoop::set_death_handle(DeathBall* death_ball) { _death_ball = death_ball; } /* realtime audio callback */ FMOD_RESULT AudioLoop::pcmreadcallback(FMOD_SOUND *sound, void *data, unsigned int datalen) { unsigned int count; SAMPLE* mono16bitbuffer = (SAMPLE*)data; unsigned int n_samples = datalen>>1; // >>1 = 16bit mono (2 bytes per sample) AudioLoop* self; ((FMOD::Sound*)sound)->getUserData((void**)&self); if (self == NULL) return FMOD_OK; for (count=0; count < n_samples; count++) { mono16bitbuffer[count] = self->get_next_sample() * self->_volume; } if (self->_death_ball != NULL) self->_death_ball->distort_handle(mono16bitbuffer, n_samples); return FMOD_OK; } FMOD_RESULT AudioLoop::pcmsetposcallback(FMOD_SOUND *sound, int subsound, unsigned int position, FMOD_TIMEUNIT postype) { /* This is useful if the user calls Sound::setPosition and you want to seek your data accordingly. */ return FMOD_OK; } void AudioLoop::stutter() { unsigned index1 = _cur_sample; unsigned index2 = index1 - rand()%50000 + 30000; do_stutter(index1, index2); for (int i = 0; i < .2*_audio_vector.size()/SAMPLE_RATE; i++) { index1 = rand()%_audio_vector.size(); index2 = index1 - rand()%50000 + 30000; do_stutter(index1, index2); } } void AudioLoop::do_stutter(unsigned long index1, unsigned long index2) { unsigned sustain = 20000 + rand()%40000; for (unsigned i = 0; i < sustain; i++) { if (index1 >= _audio_vector.size()) { index1 = 0; } if (index2 >= _audio_vector.size()) { index2 = 0; } _audio_vector[index1] = _audio_vector[index2]; index1++; index2++; } } void AudioLoop::glitch() { unsigned index1 = _cur_sample; unsigned index2 = index1 - rand()%1500 + 50; do_glitch(index1, index2); for (unsigned i = 0; i < 2 * _audio_vector.size()/SAMPLE_RATE; i++) { index1 = rand()%_audio_vector.size(); index2 = index1 - rand()%1500 + 50; do_glitch(index1, index2); } } void AudioLoop::do_glitch(unsigned long index1, unsigned long index2) { unsigned sustain = 5000 + rand()%10000; for (unsigned i = 0; i < sustain; i++) { if (index1 >= _audio_vector.size()) { index1 = 0; } if (index2 >= _audio_vector.size()) { index2 = 0; } _audio_vector[index1] = _audio_vector[index2]; index1++; index2++; } } void AudioLoop::scramble() { unsigned index1 = _cur_sample; unsigned index2 = index1 + rand()%80000 + 30000; do_scramble(index1, index2); for (unsigned i = 0; i < .2*_audio_vector.size()/SAMPLE_RATE; i++) { index1 = rand()%_audio_vector.size(); index2 = rand()%_audio_vector.size(); do_scramble(index1, index2); } } void AudioLoop::do_scramble(unsigned long index1, unsigned long index2) { unsigned sustain = 40000 + rand()%40000; for (unsigned i = 0; i < sustain; i++) { if (index1 >= _audio_vector.size()) { index1 = index1%_audio_vector.size(); } if (index2 >= _audio_vector.size()) { index2 = index1%_audio_vector.size(); } if (index2 < 0) { index2 = _audio_vector.size(); } _audio_vector[index1] = _audio_vector[index2]; index1++; index2--; } } int AudioLoop::load_file(const char* fname) { SNDFILE *infile = NULL ; SF_INFO sfinfo; if ((infile = sf_open (fname, SFM_READ, &sfinfo)) == NULL) { printf ("Not able to open input file %s.\n", fname); puts (sf_strerror (NULL)); return -1; } #define BLOCK_SIZE 512 signed short buf[sfinfo.channels * BLOCK_SIZE]; int readcount = 0; while ((readcount = sf_readf_short (infile, buf, BLOCK_SIZE)) > 0) { for (int k = 0; k < readcount ; k++) { add_sample( buf[k*sfinfo.channels] ); } } sf_close (infile) ; smooth_the_gap(); return 0; } //get rid of any click between the end and start of the loop void AudioLoop::smooth_the_gap() { #define LERP_N 60 /* lerp the last LERP_N samples */ long n_samples = _audio_vector.size(); if (n_samples < LERP_N) return; SAMPLE lerp_from = _audio_vector[n_samples - LERP_N]; SAMPLE lerp_to = _audio_vector[0]; for (unsigned k = 0; k < LERP_N; k++) { double alpha = double(k) / LERP_N; SAMPLE lerped = alpha * lerp_to + (1 - alpha) * lerp_from; _audio_vector[n_samples - LERP_N + k] = lerped; } } void AudioLoop::add_samples(const double* samples, unsigned long n_samples) { for (unsigned int i = 0; i < n_samples; i++) { _audio_vector.push_back(samples[i]); } } void AudioLoop::add_sample(double sample) { _audio_vector.push_back(sample); } SAMPLE AudioLoop::get_sample(int offset) { unsigned long cur_pos; cur_pos = _tick_timer->probe() * SAMPLE_RATE; unsigned long req_pos; req_pos = cur_pos + offset; if (_audio_vector.size() > 0) { req_pos = req_pos % _audio_vector.size(); return _audio_vector[req_pos] * _volume; } else { return 0; } } SAMPLE AudioLoop::get_next_sample() { if (_audio_vector.size() < 10) { return 0; } if (_cur_sample >= _audio_vector.size()) { _cur_sample = 0; } return _audio_vector[_cur_sample++] * _volume; } void AudioLoop::clear() { _audio_vector.clear(); _cur_sample = 0; } void AudioLoop::render() { // glEnable(GL_POINT_SMOOTH); // glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); // glPointSize(3); // glLineWidth(2); // double size_mult = 1.0/(1<<15);//volume; // // glColor4f(1,1,1,.2); // glBegin( GL_LINE_STRIP ); { // // // unsigned long x_sample_pos = _cur_sample; // unsigned long y_sample_pos = _cur_sample + 1024/3; // unsigned long z_sample_pos = _cur_sample + 2*1024/3; // // for( unsigned int i = 0; i < 1024; i++ ) // { // if (x_sample_pos >= _audio_vector.size()) { // x_sample_pos = 0; // } // // if (y_sample_pos >= _audio_vector.size()) { // y_sample_pos = 0; // } // // if (z_sample_pos >= _audio_vector.size()) { // z_sample_pos = 0; // } // // glVertex3f( size_mult * _audio_vector[x_sample_pos] + x, size_mult * _audio_vector[y_sample_pos] + y, size_mult * _audio_vector[z_sample_pos] + z); // // x_sample_pos+=2; // y_sample_pos+=2; // z_sample_pos+=2; // } // // } glEnd(); // glColor4f(1,1,1,.8); // glBegin( GL_POINTS ); { // // // unsigned long x_sample_pos = _cur_sample; // unsigned long y_sample_pos = _cur_sample + 1024/3; // unsigned long z_sample_pos = _cur_sample + 2*1024/3; // // for( unsigned int i = 0; i < 1024; i++ ) // { // if (x_sample_pos >= _audio_vector.size()) { // x_sample_pos = 0; // } // // if (y_sample_pos >= _audio_vector.size()) { // y_sample_pos = 0; // } // // if (z_sample_pos >= _audio_vector.size()) { // z_sample_pos = 0; // } // // glVertex3f( size_mult * _audio_vector[x_sample_pos] + x, size_mult * _audio_vector[y_sample_pos] + y, size_mult * _audio_vector[z_sample_pos] + z); // // x_sample_pos+=2; // y_sample_pos+=2; // z_sample_pos+=2; // } // // } glEnd(); }