nuclear@54: #include nuclear@56: #include nuclear@53: #include "stream.h" nuclear@54: #include "openal.h" nuclear@54: nuclear@54: AudioStream::AudioStream() nuclear@54: { nuclear@54: alsrc = 0; nuclear@54: poll_interval = 250; nuclear@54: done = true; nuclear@54: loop = false; nuclear@56: volume = 1.0; nuclear@54: } nuclear@53: nuclear@53: AudioStream::~AudioStream() nuclear@53: { nuclear@54: stop(); nuclear@53: } nuclear@53: nuclear@56: void AudioStream::set_volume(float vol) nuclear@56: { nuclear@56: volume = vol; nuclear@56: nuclear@56: std::lock_guard lock(mutex); nuclear@56: if(alsrc) { nuclear@56: alSourcef(alsrc, AL_GAIN, vol); nuclear@56: } nuclear@56: } nuclear@56: nuclear@56: float AudioStream::get_volume() const nuclear@56: { nuclear@56: return volume; nuclear@56: } nuclear@56: nuclear@69: void AudioStream::play(PlayMode mode) nuclear@53: { nuclear@55: loop = (mode == PlayMode::loop); nuclear@54: done = false; nuclear@54: play_thread = std::thread(&AudioStream::poll_loop, this); nuclear@53: } nuclear@53: nuclear@53: void AudioStream::stop() nuclear@53: { nuclear@56: mutex.lock(); nuclear@54: if(alsrc) { nuclear@54: done = true; nuclear@54: alSourceStop(alsrc); nuclear@56: printf("waiting for the music thread to stop\n"); nuclear@56: mutex.unlock(); nuclear@54: play_thread.join(); nuclear@56: } else { nuclear@56: mutex.unlock(); nuclear@54: } nuclear@53: } nuclear@54: nuclear@54: static ALenum alformat(AudioStreamBuffer *buf) nuclear@54: { nuclear@54: return buf->channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; nuclear@54: } nuclear@54: nuclear@54: // thread function nuclear@54: void AudioStream::poll_loop() nuclear@54: { nuclear@54: static const int num_buffers = 3; nuclear@54: unsigned int albuf[num_buffers]; nuclear@54: nuclear@55: mutex.lock(); nuclear@55: nuclear@54: alGenSources(1, &alsrc); nuclear@56: alSourcei(alsrc, AL_LOOPING, AL_FALSE); nuclear@56: alSourcef(alsrc, AL_GAIN, volume); nuclear@56: nuclear@54: alGenBuffers(num_buffers, albuf); nuclear@54: nuclear@55: AudioStreamBuffer *buf = new AudioStreamBuffer; nuclear@55: nuclear@54: for(int i=0; inum_samples * buf->channels * 2; // 2 is for 16bit samples nuclear@55: alBufferData(albuf[i], alformat(buf), buf->samples, bufsz, buf->sample_rate); nuclear@55: if(alGetError()) { nuclear@55: fprintf(stderr, "failed to load sample data into OpenAL buffer\n"); nuclear@55: } nuclear@55: nuclear@54: alSourceQueueBuffers(alsrc, 1, albuf + i); nuclear@55: if(alGetError()) { nuclear@55: fprintf(stderr, "failed to start streaming audio buffers\n"); nuclear@55: } nuclear@54: } else { nuclear@54: break; nuclear@54: } nuclear@54: } nuclear@55: // start playback nuclear@55: alSourcePlay(alsrc); nuclear@54: nuclear@54: while(!done) { nuclear@54: /* find out how many (if any) of the queued buffers are nuclear@54: * done, and free to be reused. nuclear@54: */ nuclear@54: int num_buf_done; nuclear@54: alGetSourcei(alsrc, AL_BUFFERS_PROCESSED, &num_buf_done); nuclear@54: for(int i=0; inum_samples * buf->channels * 2; // 2 is for 16bit samples nuclear@55: alBufferData(buf_id, alformat(buf), buf->samples, bufsz, buf->sample_rate); nuclear@56: if((err = alGetError())) { nuclear@56: fprintf(stderr, "failed to load sample data into OpenAL buffer (error: %x)\n", err); nuclear@54: } nuclear@55: nuclear@56: alSourceQueueBuffers(alsrc, 1, &buf_id); nuclear@55: if(alGetError()) { nuclear@55: fprintf(stderr, "failed to start streaming audio buffers\n"); nuclear@55: } nuclear@54: } else { nuclear@54: // no more data... nuclear@54: if(loop) { nuclear@54: rewind(); nuclear@54: } else { nuclear@54: done = true; nuclear@54: } nuclear@54: } nuclear@54: } nuclear@54: nuclear@54: if(num_buf_done) { nuclear@54: // make sure playback didn't stop nuclear@54: int state; nuclear@54: alGetSourcei(alsrc, AL_SOURCE_STATE, &state); nuclear@54: if(state != AL_PLAYING) { nuclear@54: alSourcePlay(alsrc); nuclear@54: } nuclear@54: } nuclear@54: nuclear@55: mutex.unlock(); nuclear@55: nuclear@69: std::chrono::milliseconds dur(poll_interval); nuclear@54: std::this_thread::sleep_for(dur); nuclear@56: nuclear@56: mutex.lock(); nuclear@54: } nuclear@54: nuclear@54: // done with the data, wait for the source to stop playing before cleanup nuclear@54: int state; nuclear@54: while(alGetSourcei(alsrc, AL_SOURCE_STATE, &state), state == AL_PLAYING) { nuclear@54: std::this_thread::yield(); nuclear@54: } nuclear@54: nuclear@54: alDeleteBuffers(num_buffers, albuf); nuclear@54: alDeleteSources(1, &alsrc); nuclear@54: alsrc = 0; nuclear@55: nuclear@55: mutex.unlock(); nuclear@54: }