move work on TinyAudio.

This commit is contained in:
Erwin Coumans 2017-04-28 11:35:00 -07:00
parent e6759cfa4f
commit 37a16f7e6b
8 changed files with 325 additions and 102 deletions

View File

@ -1,28 +1,76 @@
#include "TinyAudioExample.h"
#include "../CommonInterfaces/CommonExampleInterface.h"
#include "../CommonInterfaces/CommonGUIHelperInterface.h"
#include "Bullet3Common/b3AlignedObjectArray.h"
#include "Bullet3Common/b3HashMap.h"
#include "b3SoundEngine.h"
#include "b3SoundSource.h"
#include <string>
///very basic hashable string implementation, compatible with b3HashMap
struct MyHashString
{
std::string m_string;
unsigned int m_hash;
B3_FORCE_INLINE unsigned int getHash()const
{
return m_hash;
}
MyHashString(const char* name)
:m_string(name)
{
/* magic numbers from http://www.isthe.com/chongo/tech/comp/fnv/ */
static const unsigned int InitialFNV = 2166136261u;
static const unsigned int FNVMultiple = 16777619u;
/* Fowler / Noll / Vo (FNV) Hash */
unsigned int hash = InitialFNV;
for(int i = 0; m_string[i]; i++)
{
hash = hash ^ (m_string[i]); /* xor the low 8 bits */
hash = hash * FNVMultiple; /* multiply by the magic number */
}
m_hash = hash;
}
bool equals(const MyHashString& other) const
{
return (m_string == other.m_string);
}
};
double base_frequency = 440.0;
double base_pitch = 69.0;
double MidiPitch2Frequency(double incoming_note) {
return base_frequency * pow (2.0, (incoming_note - base_pitch) / 12.0);
}
double FrequencytoMidiPitch(double incoming_frequency) {
return base_pitch + (12.0 * log(incoming_frequency / base_frequency) / log(2));
}
class TinyAudioExample : public CommonExampleInterface
{
GUIHelperInterface* m_guiHelper;
b3SoundEngine m_soundEngine;
b3SoundSource* m_soundSource;
int m_wavId;
b3HashMap<MyHashString,int> m_keyToSoundSource;
public:
TinyAudioExample(struct GUIHelperInterface* helper)
:m_guiHelper(helper),
m_soundSource(0)
:m_guiHelper(helper)
{
}
@ -32,21 +80,18 @@ public:
virtual void initPhysics()
{
m_soundEngine.init();
int sampleRate = m_soundEngine.getSampleRate();
int numSoundSources = 8;
bool useRealTimeDac = true;
m_soundSource = new b3SoundSource();
m_soundSource->setWavFile(1,"wav/xylophone.rosewood.ff.C5B5_1.wav", sampleRate);
m_soundSource->setWavFile(0,"wav/xylophone.rosewood.ff.C5B5_1.wav", sampleRate);
m_soundSource->setOscillatorAmplitude(0,1);
m_soundSource->setOscillatorAmplitude(1,1);
m_soundEngine.addSoundSource(m_soundSource);
m_soundSource->startSound();
m_soundEngine.init(numSoundSources, useRealTimeDac);
m_wavId = m_soundEngine.loadWavFile("wav/xylophone.rosewood.ff.C5B5_1.wav");
int sampleRate = m_soundEngine.getSampleRate();
}
virtual void exitPhysics()
{
m_soundSource->stopSound();
m_soundEngine.exit();
}
@ -69,37 +114,75 @@ public:
{
return false;
}
virtual bool keyboardCallback(int key, int state)
{
if (key=='v' || key=='b')
if (key>='a' && key<='z')
{
char keyStr[2];
keyStr[0] = (char)key;
keyStr[1] = 0;
MyHashString hs (keyStr);
if (state)
if (state)
{
int soundSourceIndex = m_soundEngine.getAvailableSoundSource();
if (soundSourceIndex>=0)
{
if (key=='b')
{
m_soundSource->setOscillatorFrequency(0, 442);
m_soundSource->setOscillatorFrequency(1, 442);
}
if (key=='v')
{
m_soundSource->setOscillatorFrequency(0, 2*442);
m_soundSource->setOscillatorFrequency(1, 2*442);
}
}
if (state==1)
{
m_soundSource->startSound();
int note = key-(97-58);
double freq = MidiPitch2Frequency(note);
b3SoundMessage msg;
msg.m_type = B3_SOUND_SOURCE_SINE_OSCILLATOR;
msg.m_frequency = freq;
msg.m_amplitude = .25;
//msg.m_type = B3_SOUND_SOURCE_WAV_FILE;
//msg.m_wavId = m_wavId;
m_soundEngine.startSound(soundSourceIndex, msg);
m_keyToSoundSource.insert(hs,soundSourceIndex);
//printf("soundSourceIndex:%d\n", soundSourceIndex);
#if 0
b3SoundSource* soundSource = this->m_soundSourcesPool[soundSourceIndex];
soundSource->setOscillatorFrequency(0, freq );
soundSource->setOscillatorFrequency(1, freq );
soundSource->startSound();
{
int* soundSourceIndexPtr = m_keyToSoundSource[hs];
if (soundSourceIndexPtr)
{
int newIndex = *soundSourceIndexPtr;
printf("just inserted: %d\n", newIndex);
}
}
#endif
}
else
} else
{
int* soundSourceIndexPtr = m_keyToSoundSource[hs];
if (soundSourceIndexPtr)
{
m_soundSource->stopSound();
int soundSourceIndex = *soundSourceIndexPtr;
//printf("releaseSound: %d\n", soundSourceIndex);
m_soundEngine.releaseSound(soundSourceIndex);
}
#if 0
if (soundSourceIndex>=0)
{
printf("releasing %d\n", soundSourceIndex);
b3SoundSource* soundSource = this->m_soundSourcesPool[soundSourceIndex];
soundSource->stopSound();
}
}
#endif
}
}

View File

@ -18,9 +18,9 @@ b3ADSR::b3ADSR()
{
m_target = 0.0;
m_value = 0.0;
m_attackRate = 0.001;
m_attackRate = 0.0001;
m_decayRate = 0.00001;
m_releaseRate = 0.005;
m_releaseRate = 0.0005;
m_sustainLevel = 0.5;
m_state = ADSR_IDLE;
}
@ -78,7 +78,7 @@ double b3ADSR::tick()
bool b3ADSR::isIdle() const
{
return true;
return m_state == ADSR_IDLE;
}
void b3ADSR::keyOn()

View File

@ -141,8 +141,9 @@ int b3AudioListener::tick(void *outputBuffer,void *inputBuffer1,unsigned int nBu
//simple mixer
if (numActiveSources)
{
outs[0] *= .3/numActiveSources;
outs[1] *= .3/numActiveSources;
outs[0] *= 1./4.;
outs[1] *= 1./4.;
}
*samples++ = outs[0];

View File

@ -5,6 +5,9 @@
#include "b3AudioListener.h"
#include "b3SoundSource.h"
#include "Bullet3Common/b3AlignedObjectArray.h"
#include "b3ReadWavFile.h"
#include "../Utils/b3ResourcePath.h"
#include "Bullet3Common/b3HashMap.h"
// The default real-time audio input and output buffer size. If
// clicks are occuring in the input and/or output sound stream, a
@ -17,9 +20,17 @@ struct b3SoundEngineInternalData
{
b3AudioListener m_listener;
RtAudio m_dac;
bool m_useRealTimeDac;
b3AlignedObjectArray<b3SoundSource*> m_soundSources;
b3HashMap<b3HashInt, b3ReadWavFile*> m_wavFiles;
int m_wavFileUidGenerator;
b3SoundEngineInternalData()
: m_useRealTimeDac(false),
m_wavFileUidGenerator(123)
{
}
};
b3SoundEngine::b3SoundEngine()
@ -33,49 +44,113 @@ b3SoundEngine::~b3SoundEngine()
delete m_data;
}
void b3SoundEngine::init()
void b3SoundEngine::init(int maxNumSoundSources, bool useRealTimeDac)
{
for (int i = 0; i < maxNumSoundSources; i++)
{
b3SoundSource* source = new b3SoundSource();
m_data->m_soundSources.push_back(source);
m_data->m_listener.addSoundSource(source);
}
RtAudioFormat format = ( sizeof(double) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32;
RtAudio::StreamParameters parameters;
parameters.deviceId = m_data->m_dac.getDefaultOutputDevice();
parameters.nChannels = 2;
this->m_data->m_useRealTimeDac = useRealTimeDac;
unsigned int bufferFrames = RT_BUFFER_SIZE;
double sampleRate = m_data->m_listener.getSampleRate();
if (useRealTimeDac)
{
RtAudioFormat format = (sizeof(double) == 8) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32;
RtAudio::StreamParameters parameters;
parameters.deviceId = m_data->m_dac.getDefaultOutputDevice();
parameters.nChannels = 2;
m_data->m_dac.openStream( &parameters, NULL, format, (unsigned int)sampleRate, &bufferFrames, &b3AudioListener::tick,
(void *)m_data->m_listener.getTickData());
unsigned int bufferFrames = RT_BUFFER_SIZE;
double sampleRate = m_data->m_listener.getSampleRate();
m_data->m_dac.startStream();
m_data->m_dac.openStream(&parameters, NULL, format, (unsigned int)sampleRate, &bufferFrames, &b3AudioListener::tick,
(void*)m_data->m_listener.getTickData());
m_data->m_dac.startStream();
}
}
void b3SoundEngine::exit()
{
m_data->m_dac.closeStream();
m_data->m_useRealTimeDac = false;
for (int i=0;i<m_data->m_soundSources.size();i++)
for (int i = 0; i < m_data->m_soundSources.size(); i++)
{
m_data->m_listener.removeSoundSource(m_data->m_soundSources[i]);
m_data->m_soundSources[i]->stopSound();
delete m_data->m_soundSources[i];
}
m_data->m_soundSources.clear();
}
void b3SoundEngine::addSoundSource(b3SoundSource* source)
int b3SoundEngine::getAvailableSoundSource()
{
m_data->m_soundSources.push_back(source);
m_data->m_listener.addSoundSource(source);
for (int i = 0; i < m_data->m_soundSources.size(); i++)
{
if (m_data->m_soundSources[i]->isAvailable())
{
return i;
}
}
return -1;
}
void b3SoundEngine::removeSoundSource(b3SoundSource* source)
void b3SoundEngine::startSound(int soundSourceIndex, b3SoundMessage msg)
{
m_data->m_soundSources.remove(source);
b3SoundSource* soundSource = m_data->m_soundSources[soundSourceIndex];
switch (msg.m_type)
{
case B3_SOUND_SOURCE_SINE_OSCILLATOR:
{
soundSource->setOscillatorFrequency(0, msg.m_frequency);
soundSource->setOscillatorFrequency(1, msg.m_frequency);
soundSource->setOscillatorAmplitude(0,msg.m_amplitude);
soundSource->setOscillatorAmplitude(1,msg.m_amplitude);
soundSource->startSound();
break;
}
case B3_SOUND_SOURCE_WAV_FILE:
{
b3ReadWavFile** wavFilePtr = m_data->m_wavFiles[msg.m_wavId];
if (wavFilePtr)
{
b3ReadWavFile* wavFile = *wavFilePtr;
soundSource->setWavFile(0,wavFile,getSampleRate());
soundSource->setWavFile(1,wavFile,getSampleRate());
soundSource->startSound();
}
break;
}
default:
{
}
}
}
void b3SoundEngine::releaseSound(int soundSourceIndex)
{
b3SoundSource* soundSource = m_data->m_soundSources[soundSourceIndex];
soundSource->stopSound();
}
int b3SoundEngine::loadWavFile(const char* fileName)
{
char resourcePath[1024];
if (b3ResourcePath::findResourcePath(fileName, resourcePath, 1024))
{
b3ReadWavFile* wavFile = new b3ReadWavFile();
wavFile->getWavInfo(resourcePath);
wavFile->resize();
wavFile->read(0, true);
wavFile->normalize(1);
int wavUID = m_data->m_wavFileUidGenerator++;
m_data->m_wavFiles.insert(wavUID, wavFile);
return wavUID;
}
return 0;
}
@ -83,5 +158,3 @@ double b3SoundEngine::getSampleRate() const
{
return m_data->m_listener.getSampleRate();
}

View File

@ -3,6 +3,20 @@
#include "Bullet3Common/b3Scalar.h"
struct b3SoundMessage
{
int m_type;//B3_SOUND_SOURCE_TYPE
double m_amplitude;
double m_frequency;
int m_wavId;
double m_attack;
double m_decay;
double m_sustain;
double m_release;
};
class b3SoundEngine
{
struct b3SoundEngineInternalData* m_data;
@ -12,13 +26,12 @@ class b3SoundEngine
b3SoundEngine();
virtual ~b3SoundEngine();
void init();
void init(int maxNumSoundSources, bool useRealTimeDac);
void exit();
//int createListener();
void addSoundSource(class b3SoundSource* source);
void removeSoundSource(class b3SoundSource* source);
int getAvailableSoundSource();
void startSound(int soundSourceIndex, b3SoundMessage msg);
void releaseSound(int soundSourceIndex);
int loadWavFile(const char* fileName);

View File

@ -3,10 +3,11 @@
#define MY2PI (2.*3.14159265)
#include <math.h>
#include "../Utils/b3ResourcePath.h"
#include "Bullet3Common/b3FileUtils.h"
#include "b3ReadWavFile.h"
#include "b3ADSR.h"
#include "b3Sound_C_Api.h"
struct b3SoundOscillator
{
@ -62,7 +63,11 @@ struct b3SoundSourceInternalData
{
b3SoundOscillator m_oscillators[MAX_OSCILLATORS];
b3ADSR m_envelope;
b3ReadWavFile m_wavFile;
b3ReadWavFile* m_wavFilePtr;
b3SoundSourceInternalData()
:m_wavFilePtr(0)
{
}
};
b3SoundSource::b3SoundSource()
@ -79,6 +84,7 @@ bool b3SoundSource::computeSamples(double* sampleBuffer, int numSamples, double
{
double* outputSamples = sampleBuffer;
int numActive = 0;
for (int i=0;i<numSamples;i++)
{
@ -87,32 +93,45 @@ bool b3SoundSource::computeSamples(double* sampleBuffer, int numSamples, double
double env = m_data->m_envelope.tick();
if (env)
{
for (int osc=0;osc<MAX_OSCILLATORS;osc++)
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 = 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
{
if (m_data->m_oscillators[osc].m_type == 0)
for (int osc=0;osc<MAX_OSCILLATORS;osc++)
{
samples[osc] += env * m_data->m_oscillators[osc].sampleSineWaveForm(sampleRate);
if (m_data->m_oscillators[osc].m_type == 128)
{
m_data->m_oscillators[osc].m_wavTicker.finished_ = true;
}
}
if (m_data->m_oscillators[osc].m_type == 1)
{
samples[osc] += env * m_data->m_oscillators[osc].sampleSawWaveForm(sampleRate);
}
if (m_data->m_oscillators[osc].m_type == 128)
{
int frame = 0;
double data = m_data->m_oscillators[osc].m_amplitude * m_data->m_wavFile.tick(frame,&m_data->m_oscillators[osc].m_wavTicker);
samples[osc] += data;
}
}
}
//sample *= 1./double(MAX_OSCILLATORS);
double sampleLeft = samples[0];
double sampleRight = samples[1];
if (sampleLeft != sampleRight)
{
}
*outputSamples++ = sampleRight;
*outputSamples++ = sampleLeft ;
@ -127,7 +146,7 @@ bool b3SoundSource::computeSamples(double* sampleBuffer, int numSamples, double
}
}
*/
return true;
return numActive>0;
// return false;
}
@ -152,22 +171,28 @@ 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()
{
if (m_data->m_envelope.isIdle())
{
for (int osc=0;osc<MAX_OSCILLATORS;osc++)
{
static int reset = 0;
printf("reset %d!\n",reset++);
if (m_data->m_oscillators[osc].m_wavTicker.finished_)
m_data->m_oscillators[osc].reset();
if (m_data->m_oscillators[osc].m_type == B3_SOUND_SOURCE_WAV_FILE)// .m_wavTicker.finished_)
{
m_data->m_oscillators[osc].reset();
//test reverse playback of wav
m_data->m_oscillators[osc].m_wavTicker.rate_ *= -1;
//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_wavFile.getNumFrames()-1.;
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;
@ -187,18 +212,11 @@ void b3SoundSource::stopSound()
}
bool b3SoundSource::setWavFile(int oscillatorIndex, const char* fileName, int sampleRate)
bool b3SoundSource::setWavFile(int oscillatorIndex, b3ReadWavFile* wavFilePtr, int sampleRate)
{
char resourcePath[1024];
if (b3ResourcePath::findResourcePath(fileName,resourcePath,1024))
{
m_data->m_wavFile.getWavInfo(resourcePath);
m_data->m_wavFile.resize();
m_data->m_wavFile.read(0,true);
m_data->m_wavFile.normalize(1);
m_data->m_oscillators[oscillatorIndex].m_wavTicker = m_data->m_wavFile.createWavTicker(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;

View File

@ -1,6 +1,9 @@
#ifndef B3_SOUND_SOURCE_H
#define B3_SOUND_SOURCE_H
#include "b3Sound_C_Api.h"
class b3SoundSource
{
struct b3SoundSourceInternalData* m_data;
@ -18,11 +21,12 @@ public:
void setOscillatorAmplitude(int oscillatorIndex, double amplitude);
void setOscillatorPhase(int oscillatorIndex, double phase);
bool setWavFile(int oscillatorIndex, const char* fileName, int sampleRate);
bool setWavFile(int oscillatorIndex, class b3ReadWavFile* wavFilePtr, int sampleRate);
void startSound();
void stopSound();
bool isAvailable() const;
};
#endif //B3_SOUND_SOURCE_H

View File

@ -0,0 +1,31 @@
#ifndef B3_SOUND_C_API_H
#define B3_SOUND_C_API_H
//todo: create a sound C-API
//create sound engine
//destroy sound engine
//getSoundSource
//startSound
//releaseSound
//etc
enum B3_SOUND_SOURCE_TYPE
{
B3_SOUND_SOURCE_SINE_OSCILLATOR=1,
B3_SOUND_SOURCE_SAW_OSCILLATOR,
B3_SOUND_SOURCE_WAV_FILE,
};
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif///B3_SOUND_C_API_H