bullet3/examples/TinyAudio/b3SoundSource.cpp
Erwin Coumans af6bf8ddc8 plumb URDF/SDF audio_source into PhysicsServerCommandProcessor, allow to play sounds on collision !
See also https://youtu.be/eppOjTfx5Jg for a first test, and this modified URDF how to add sounds:
https://github.com/bulletphysics/bullet3/blob/master/data/plane_with_collision_audio.urdf
Add the --audio flag to enable sound in pybullet/Bullet-C-API
2017-05-01 11:14:09 -07:00

239 lines
5.2 KiB
C++

#include "b3SoundSource.h"
#define MY2PI (2.*3.14159265)
#include <math.h>
#include "Bullet3Common/b3FileUtils.h"
#include "b3ReadWavFile.h"
#include "b3ADSR.h"
#include "b3Sound_C_Api.h"
struct b3SoundOscillator
{
int m_type;
double m_frequency;
double m_amplitude;
double m_phase;
b3WavTicker m_wavTicker;
double sampleSineWaveForm(double sampleRate)
{
while (m_phase >= MY2PI)
m_phase -= MY2PI;
double z = sinf(m_phase);
double sample = m_amplitude*z;
m_phase += MY2PI * (1./sampleRate) * m_frequency;
return sample;
}
double sampleSawWaveForm(double sampleRate)
{
while (m_phase >= MY2PI)
m_phase -= MY2PI;
double z = 2.*(m_phase)/MY2PI-1.;
double sample = m_amplitude*z;
m_phase += MY2PI * (1./sampleRate) * m_frequency;
return sample;
}
void reset()
{
m_phase = 0;
}
b3SoundOscillator()
:m_type(0),
m_frequency(442.),
m_amplitude(1),
m_phase(0)
{
}
};
#define MAX_OSCILLATORS 2
struct b3SoundSourceInternalData
{
b3SoundOscillator m_oscillators[MAX_OSCILLATORS];
b3ADSR m_envelope;
b3ReadWavFile* m_wavFilePtr;
b3SoundSourceInternalData()
:m_wavFilePtr(0)
{
}
};
b3SoundSource::b3SoundSource()
{
m_data = new b3SoundSourceInternalData();
}
b3SoundSource::~b3SoundSource()
{
delete m_data;
}
void b3SoundSource::setADSR( double attack, double decay, double sustain, double release)
{
m_data->m_envelope.setValues(attack,decay,sustain,release);
}
bool b3SoundSource::computeSamples(double* sampleBuffer, int numSamples, double sampleRate)
{
double* outputSamples = sampleBuffer;
int numActive = 0;
for (int i=0;i<numSamples;i++)
{
double samples[MAX_OSCILLATORS] ={0};
double env = m_data->m_envelope.tick();
if (env)
{
for (int osc=0;osc<MAX_OSCILLATORS;osc++)
{
if (m_data->m_oscillators[osc].m_type == 0)
{
samples[osc] += env * m_data->m_oscillators[osc].sampleSineWaveForm(sampleRate);
numActive++;
}
if (m_data->m_oscillators[osc].m_type == 1)
{
samples[osc] += env * m_data->m_oscillators[osc].sampleSawWaveForm(sampleRate);
numActive++;
}
if (m_data->m_oscillators[osc].m_type == 128)
{
int frame = 0;
double data = env * m_data->m_oscillators[osc].m_amplitude * m_data->m_wavFilePtr->tick(frame,&m_data->m_oscillators[osc].m_wavTicker);
samples[osc] += data;
numActive++;
}
}
} else
{
for (int osc=0;osc<MAX_OSCILLATORS;osc++)
{
if (m_data->m_oscillators[osc].m_type == 128)
{
m_data->m_oscillators[osc].m_wavTicker.finished_ = true;
}
}
}
//sample *= 1./double(MAX_OSCILLATORS);
double sampleLeft = samples[0];
double sampleRight = samples[1];
if (sampleLeft != sampleRight)
{
}
*outputSamples++ = sampleRight;
*outputSamples++ = sampleLeft ;
}
/* if (m_data->m_flags & looping)
{
for (int osc=0;osc<MAX_OSCILLATORS;osc++)
{
if (m_data->m_oscillators[osc].m_waveIn.isFinished())
m_data->m_oscillators[osc].m_waveIn.reset();
}
}
*/
return numActive>0;
// return false;
}
int b3SoundSource::getNumOscillators() const
{
return MAX_OSCILLATORS;
}
void b3SoundSource::setOscillatorType(int oscillatorIndex, int type)
{
m_data->m_oscillators[oscillatorIndex].m_type = type;
}
void b3SoundSource::setOscillatorFrequency(int oscillatorIndex, double frequency)
{
m_data->m_oscillators[oscillatorIndex].m_frequency = frequency;
}
void b3SoundSource::setOscillatorAmplitude(int oscillatorIndex, double amplitude)
{
m_data->m_oscillators[oscillatorIndex].m_amplitude = amplitude;
}
void b3SoundSource::setOscillatorPhase(int oscillatorIndex, double phase)
{
m_data->m_oscillators[oscillatorIndex].m_phase = phase;
}
bool b3SoundSource::isAvailable() const
{
//available if ADSR is idle and wavticker is finished
return m_data->m_envelope.isIdle();
}
void b3SoundSource::startSound(bool autoKeyOff)
{
if (m_data->m_envelope.isIdle())
{
for (int osc=0;osc<MAX_OSCILLATORS;osc++)
{
m_data->m_oscillators[osc].reset();
if (m_data->m_oscillators[osc].m_type == B3_SOUND_SOURCE_WAV_FILE)// .m_wavTicker.finished_)
{
//test reverse playback of wav
//m_data->m_oscillators[osc].m_wavTicker.rate_ *= -1;
if (m_data->m_oscillators[osc].m_wavTicker.rate_<0)
{
m_data->m_oscillators[osc].m_wavTicker.time_ = m_data->m_wavFilePtr->getNumFrames()-1.;
} else
{
m_data->m_oscillators[osc].m_wavTicker.time_ = 0.f;
}
m_data->m_oscillators[osc].m_wavTicker.finished_ = false;
}
}
}
m_data->m_envelope.keyOn(autoKeyOff);
}
void b3SoundSource::stopSound()
{
m_data->m_envelope.keyOff();
}
bool b3SoundSource::setWavFile(int oscillatorIndex, b3ReadWavFile* wavFilePtr, int sampleRate)
{
{
m_data->m_wavFilePtr = wavFilePtr;
m_data->m_oscillators[oscillatorIndex].m_wavTicker = m_data->m_wavFilePtr->createWavTicker(sampleRate);
// waveIn.openFile(resourcePath);
double rate = 1.0;
// rate = waveIn.getFileRate() / stkSampleRate;
// waveIn.setRate( rate );
// waveIn.ignoreSampleRateChange();
// Find out how many channels we have.
// int channels = waveIn.channelsOut();
// m_data->m_oscillators[oscillatorIndex].m_frames.resize( 1, channels );
m_data->m_oscillators[oscillatorIndex].m_type = 128;
return true;
}
return false;
}