#ifdef _MSC_VER #pragma warning(disable : 4786) #endif #include #include "device_mixer.h" #include "resampler.h" #include "utility.h" namespace audiere { MixerDevice::MixerDevice(int rate) { m_rate = rate; } OutputStream* MixerDevice::openStream(SampleSource* source) { return (source ? new MixerStream(this, source, m_rate) : 0); } OutputStream* MixerDevice::openBuffer( void* samples, int frame_count, int channel_count, int sample_rate, SampleFormat sample_format) { return openStream(OpenBufferStream( samples, frame_count, channel_count, sample_rate, sample_format)); } int MixerDevice::read(const int sample_count, void* samples) { // ADR_GUARD("MixerDevice::read"); bool any_playing = false; typedef RefPtr MixerStreamPtr; std::list streams; { SYNCHRONIZED(this); // ADR_LOG("done locking mixer device"); // are any sources playing? for (std::list::iterator i = m_streams.begin(); i != m_streams.end(); ++i) { if ((*i)->m_is_playing) { MixerStream* stream = *i; streams.push_back(stream); any_playing = true; } } } // if not, return zeroed samples if (!any_playing) { memset(samples, 0, 4 * sample_count); return sample_count; } ADR_LOG("at least one stream is playing"); // buffer in which to mix the audio static const int BUFFER_SIZE = 4096; // mix the output in chunks of BUFFER_SIZE samples s16* out = (s16*)samples; int left = sample_count; while (left > 0) { int to_mix = std::min(BUFFER_SIZE, left); s32 mix_buffer[BUFFER_SIZE * 2]; memset(mix_buffer, 0, sizeof(mix_buffer)); for (std::list::iterator s = streams.begin(); s != streams.end(); ++s) { if ((*s)->m_is_playing) { s16 stream_buffer[BUFFER_SIZE * 2]; (*s)->read(to_mix, stream_buffer); for (int i = 0; i < to_mix * 2; ++i) { mix_buffer[i] += stream_buffer[i]; } } } // clamp each value in the buffer to the valid s16 range for (int i = 0; i < to_mix * 2; ++i) { s32 mixed = mix_buffer[i]; if (mixed < -32768) { mixed = -32768; } else if (mixed > 32767) { mixed = 32767; } *out++ = (s16)mixed; } left -= to_mix; } return sample_count; } MixerStream::MixerStream( MixerDevice* device, SampleSource* source, int rate) : m_device(device) { m_source = new Resampler(source, rate); m_last_l = 0; m_last_r = 0; m_is_playing = false; m_volume = 255; m_pan = 0; SYNCHRONIZED(m_device.get()); m_device->m_streams.push_back(this); } MixerStream::~MixerStream() { SYNCHRONIZED(m_device.get()); m_device->m_streams.remove(this); } void MixerStream::play() { SYNCHRONIZED(this); m_is_playing = true; } void MixerStream::stop() { SYNCHRONIZED(this); if (m_is_playing) { m_is_playing = false; SYNCHRONIZED(m_device.get()); m_device->fireStopEvent(this, StopEvent::STOP_CALLED); } else { m_is_playing = false; } } bool MixerStream::isPlaying() { return m_is_playing; } void MixerStream::reset() { SYNCHRONIZED(this); m_source->reset(); } void MixerStream::setRepeat(bool repeat) { SYNCHRONIZED(this); m_source->setRepeat(repeat); } bool MixerStream::getRepeat() { SYNCHRONIZED(this); return m_source->getRepeat(); } void MixerStream::setVolume(float volume) { SYNCHRONIZED(this); m_volume = int(volume * 255.0f + 0.5f); } float MixerStream::getVolume() { return (m_volume / 255.0f); } void MixerStream::setPan(float pan) { SYNCHRONIZED(this); m_pan = int(pan * 255.0f); } float MixerStream::getPan() { return m_pan / 255.0f; } void MixerStream::setPitchShift(float shift) { SYNCHRONIZED(this); m_source->setPitchShift(shift); } float MixerStream::getPitchShift() { SYNCHRONIZED(this); return m_source->getPitchShift(); } bool MixerStream::isSeekable() { return m_source->isSeekable(); } int MixerStream::getLength() { return m_source->getLength(); } void MixerStream::setPosition(int position) { SYNCHRONIZED(this); m_source->setPosition(position); } int MixerStream::getPosition() { SYNCHRONIZED(this); return m_source->getPosition(); } void MixerStream::read(int frame_count, s16* buffer) { SYNCHRONIZED(this); unsigned read = m_source->read(frame_count, buffer); s16* out = buffer; // if we are done with the sample source, stop and reset it if (read == 0) { m_source->reset(); if (m_is_playing) { m_is_playing = false; // let subscribers know that the sound was stopped SYNCHRONIZED(m_device.get()); m_device->fireStopEvent(this, StopEvent::STREAM_ENDED); } else { m_is_playing = false; } } else { // do panning and volume normalization int l_volume, r_volume; if (m_pan < 0) { l_volume = 255; r_volume = 255 + m_pan; } else { l_volume = 255 - m_pan; r_volume = 255; } l_volume *= m_volume; r_volume *= m_volume; for (unsigned i = 0; i < read; ++i) { *out = *out * l_volume / 255 / 255; ++out; *out = *out * r_volume / 255 / 255; ++out; } } // if we ready any frames, we can replace the old values // for the last left and right channel states int new_l = m_last_l; int new_r = m_last_r; if (read > 0) { new_l = out[-2]; new_r = out[-1]; } // and apply the last state to the rest of the buffer for (int i = read; i < frame_count; ++i) { *out++ = m_last_l; *out++ = m_last_r; } m_last_l = new_l; m_last_r = new_r; } AudioQueue::AudioQueue(int blockSize, int queueLen) { m_block_size = blockSize; m_max_queue_len = queueLen; m_shutdown = false; for (int i = 0; i < queueLen + 1; i++) { char* buf = new char[blockSize]; m_buffer_list.push_back(AudioBuffer(buf, blockSize)); m_free_list.push_back(&m_buffer_list.back()); } } AudioQueue::~AudioQueue() { shutdown(); m_mutex.lock(); m_mutex.unlock(); while (!m_buffer_list.empty()) { delete [] m_buffer_list.front().m_buf; m_buffer_list.pop_front(); } } AudioBuffer* AudioQueue::get_buffer(bool wait) { SYNCHRONIZED(m_buffer_mutex); if (m_shutdown) return 0; if (!wait && m_free_list.empty()) return 0; while (m_free_list.empty()) { m_buffer_cond.wait(m_buffer_mutex, 1); if (m_shutdown) return 0; } AudioBuffer* buf = m_free_list.front(); m_free_list.pop_front(); return buf; } void AudioQueue::put_buffer(AudioBuffer* buffer) { SYNCHRONIZED(m_buffer_mutex); bool donotify = m_free_list.empty(); m_free_list.push_back(buffer); if (donotify) m_buffer_cond.notify(); } bool AudioQueue::push(AudioBuffer* buffer) { SYNCHRONIZED(m_mutex); if (m_shutdown) return false; bool donotify = m_queue.empty(); while (m_queue.size() >= size_t(m_max_queue_len)) { m_cond.wait(m_mutex, 1); if (m_shutdown) return false; } m_queue.push_back(buffer); if (donotify) m_cond.notify(); return true; } AudioBuffer* AudioQueue::pop(bool wait) { SYNCHRONIZED(m_mutex); if (m_shutdown) return 0; if (!wait && m_queue.empty()) return 0; while (m_queue.empty()) { m_cond.wait(m_mutex, 1); if (m_shutdown) return 0; } bool donotify = m_queue.size() >= size_t(m_max_queue_len); AudioBuffer* buf = m_queue.front(); m_queue.pop_front(); if (donotify) m_cond.notify(); return buf; } void AudioQueue::shutdown() { SYNCHRONIZED(m_mutex); m_shutdown = true; m_cond.notify(); m_buffer_cond.notify(); } ThreadedMixerDevice::ThreadedMixerDevice(int rate, int block_size, int queue_len) : MixerDevice(rate) { m_thread_should_die = false; m_thread_exists = false; m_queue = new AudioQueue(block_size, queue_len); } ThreadedMixerDevice::~ThreadedMixerDevice() { ADR_GUARD("ThreadedMixerDevice::~ThreadedMixerDevice"); shutdown(); delete m_queue; } void ThreadedMixerDevice::start() { ADR_GUARD("ThreadedMixerDevice::start"); m_thread_should_die = false; m_thread_exists = false; AI_CreateThread(outputThreadRoutine, this); } void ThreadedMixerDevice::shutdown() { ADR_GUARD("ThreadedMixerDevice::shutdown"); m_thread_should_die = true; m_queue->shutdown(); while (m_thread_exists) AI_Sleep(10); } void ThreadedMixerDevice::outputThreadRoutine(void* arg) { ADR_GUARD("ThreadedDevice::outputThreadRoutine"); if (arg) { ADR_LOG("arg is valid"); } else { ADR_LOG("arg is not valid"); } ThreadedMixerDevice* This = (ThreadedMixerDevice*)arg; This->output(); } void ThreadedMixerDevice::output() { ADR_GUARD("ThreadedMixerDevice::output"); m_thread_exists = true; while (!m_thread_should_die) { AudioBuffer* buffer = m_queue->pop(); if (!buffer) continue; outputBuffer(buffer->m_buf, buffer->m_size); m_queue->put_buffer(buffer); } m_thread_exists = false; } void ADR_CALL ThreadedMixerDevice::update() { AudioBuffer* buffer = m_queue->get_buffer(); if (!buffer) { AI_Sleep(5); return; } int sample_len; size_t sample_left; char* sample_buf; sample_buf = buffer->m_buf; sample_len = buffer->m_size / 4; sample_left = read(sample_len, sample_buf); (void)sample_left; if (!m_queue->push(buffer)) m_queue->put_buffer(buffer); } }