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 "TinyAudioExample.h"
#include "../CommonInterfaces/CommonExampleInterface.h" #include "../CommonInterfaces/CommonExampleInterface.h"
#include "../CommonInterfaces/CommonGUIHelperInterface.h" #include "../CommonInterfaces/CommonGUIHelperInterface.h"
#include "Bullet3Common/b3AlignedObjectArray.h"
#include "Bullet3Common/b3HashMap.h"
#include "b3SoundEngine.h" #include "b3SoundEngine.h"
#include "b3SoundSource.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 class TinyAudioExample : public CommonExampleInterface
{ {
GUIHelperInterface* m_guiHelper; GUIHelperInterface* m_guiHelper;
b3SoundEngine m_soundEngine; b3SoundEngine m_soundEngine;
b3SoundSource* m_soundSource; int m_wavId;
b3HashMap<MyHashString,int> m_keyToSoundSource;
public: public:
TinyAudioExample(struct GUIHelperInterface* helper) TinyAudioExample(struct GUIHelperInterface* helper)
:m_guiHelper(helper), :m_guiHelper(helper)
m_soundSource(0)
{ {
} }
@@ -32,21 +80,18 @@ public:
virtual void initPhysics() virtual void initPhysics()
{ {
m_soundEngine.init(); int numSoundSources = 8;
int sampleRate = m_soundEngine.getSampleRate(); bool useRealTimeDac = true;
m_soundSource = new b3SoundSource(); m_soundEngine.init(numSoundSources, useRealTimeDac);
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_wavId = m_soundEngine.loadWavFile("wav/xylophone.rosewood.ff.C5B5_1.wav");
m_soundSource->setOscillatorAmplitude(0,1); int sampleRate = m_soundEngine.getSampleRate();
m_soundSource->setOscillatorAmplitude(1,1);
m_soundEngine.addSoundSource(m_soundSource);
m_soundSource->startSound();
} }
virtual void exitPhysics() virtual void exitPhysics()
{ {
m_soundSource->stopSound();
m_soundEngine.exit(); m_soundEngine.exit();
} }
@@ -69,37 +114,75 @@ public:
{ {
return false; return false;
} }
virtual bool keyboardCallback(int key, int state) 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_target = 0.0;
m_value = 0.0; m_value = 0.0;
m_attackRate = 0.001; m_attackRate = 0.0001;
m_decayRate = 0.00001; m_decayRate = 0.00001;
m_releaseRate = 0.005; m_releaseRate = 0.0005;
m_sustainLevel = 0.5; m_sustainLevel = 0.5;
m_state = ADSR_IDLE; m_state = ADSR_IDLE;
} }
@@ -78,7 +78,7 @@ double b3ADSR::tick()
bool b3ADSR::isIdle() const bool b3ADSR::isIdle() const
{ {
return true; return m_state == ADSR_IDLE;
} }
void b3ADSR::keyOn() void b3ADSR::keyOn()

View File

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

View File

@@ -5,6 +5,9 @@
#include "b3AudioListener.h" #include "b3AudioListener.h"
#include "b3SoundSource.h" #include "b3SoundSource.h"
#include "Bullet3Common/b3AlignedObjectArray.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 // The default real-time audio input and output buffer size. If
// clicks are occuring in the input and/or output sound stream, a // clicks are occuring in the input and/or output sound stream, a
@@ -17,9 +20,17 @@ struct b3SoundEngineInternalData
{ {
b3AudioListener m_listener; b3AudioListener m_listener;
RtAudio m_dac; RtAudio m_dac;
bool m_useRealTimeDac;
b3AlignedObjectArray<b3SoundSource*> m_soundSources; b3AlignedObjectArray<b3SoundSource*> m_soundSources;
b3HashMap<b3HashInt, b3ReadWavFile*> m_wavFiles;
int m_wavFileUidGenerator;
b3SoundEngineInternalData()
: m_useRealTimeDac(false),
m_wavFileUidGenerator(123)
{
}
}; };
b3SoundEngine::b3SoundEngine() b3SoundEngine::b3SoundEngine()
@@ -33,49 +44,113 @@ b3SoundEngine::~b3SoundEngine()
delete m_data; 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; this->m_data->m_useRealTimeDac = useRealTimeDac;
RtAudio::StreamParameters parameters;
parameters.deviceId = m_data->m_dac.getDefaultOutputDevice();
parameters.nChannels = 2;
unsigned int bufferFrames = RT_BUFFER_SIZE; if (useRealTimeDac)
double sampleRate = m_data->m_listener.getSampleRate(); {
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, unsigned int bufferFrames = RT_BUFFER_SIZE;
(void *)m_data->m_listener.getTickData()); 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() void b3SoundEngine::exit()
{ {
m_data->m_dac.closeStream(); 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_listener.removeSoundSource(m_data->m_soundSources[i]);
m_data->m_soundSources[i]->stopSound();
delete m_data->m_soundSources[i]; delete m_data->m_soundSources[i];
} }
m_data->m_soundSources.clear(); m_data->m_soundSources.clear();
} }
int b3SoundEngine::getAvailableSoundSource()
void b3SoundEngine::addSoundSource(b3SoundSource* source)
{ {
m_data->m_soundSources.push_back(source); for (int i = 0; i < m_data->m_soundSources.size(); i++)
m_data->m_listener.addSoundSource(source); {
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) 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; return 0;
} }
@@ -83,5 +158,3 @@ double b3SoundEngine::getSampleRate() const
{ {
return m_data->m_listener.getSampleRate(); return m_data->m_listener.getSampleRate();
} }

View File

@@ -3,6 +3,20 @@
#include "Bullet3Common/b3Scalar.h" #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 class b3SoundEngine
{ {
struct b3SoundEngineInternalData* m_data; struct b3SoundEngineInternalData* m_data;
@@ -12,13 +26,12 @@ class b3SoundEngine
b3SoundEngine(); b3SoundEngine();
virtual ~b3SoundEngine(); virtual ~b3SoundEngine();
void init(); void init(int maxNumSoundSources, bool useRealTimeDac);
void exit(); void exit();
//int createListener(); int getAvailableSoundSource();
void startSound(int soundSourceIndex, b3SoundMessage msg);
void addSoundSource(class b3SoundSource* source); void releaseSound(int soundSourceIndex);
void removeSoundSource(class b3SoundSource* source);
int loadWavFile(const char* fileName); int loadWavFile(const char* fileName);

View File

@@ -3,10 +3,11 @@
#define MY2PI (2.*3.14159265) #define MY2PI (2.*3.14159265)
#include <math.h> #include <math.h>
#include "../Utils/b3ResourcePath.h"
#include "Bullet3Common/b3FileUtils.h" #include "Bullet3Common/b3FileUtils.h"
#include "b3ReadWavFile.h" #include "b3ReadWavFile.h"
#include "b3ADSR.h" #include "b3ADSR.h"
#include "b3Sound_C_Api.h"
struct b3SoundOscillator struct b3SoundOscillator
{ {
@@ -62,7 +63,11 @@ struct b3SoundSourceInternalData
{ {
b3SoundOscillator m_oscillators[MAX_OSCILLATORS]; b3SoundOscillator m_oscillators[MAX_OSCILLATORS];
b3ADSR m_envelope; b3ADSR m_envelope;
b3ReadWavFile m_wavFile; b3ReadWavFile* m_wavFilePtr;
b3SoundSourceInternalData()
:m_wavFilePtr(0)
{
}
}; };
b3SoundSource::b3SoundSource() b3SoundSource::b3SoundSource()
@@ -79,6 +84,7 @@ bool b3SoundSource::computeSamples(double* sampleBuffer, int numSamples, double
{ {
double* outputSamples = sampleBuffer; double* outputSamples = sampleBuffer;
int numActive = 0;
for (int i=0;i<numSamples;i++) 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(); double env = m_data->m_envelope.tick();
if (env) 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); //sample *= 1./double(MAX_OSCILLATORS);
double sampleLeft = samples[0]; double sampleLeft = samples[0];
double sampleRight = samples[1]; double sampleRight = samples[1];
if (sampleLeft != sampleRight)
{
}
*outputSamples++ = sampleRight; *outputSamples++ = sampleRight;
*outputSamples++ = sampleLeft ; *outputSamples++ = sampleLeft ;
@@ -127,7 +146,7 @@ bool b3SoundSource::computeSamples(double* sampleBuffer, int numSamples, double
} }
} }
*/ */
return true; return numActive>0;
// return false; // return false;
} }
@@ -152,22 +171,28 @@ void b3SoundSource::setOscillatorPhase(int oscillatorIndex, double phase)
m_data->m_oscillators[oscillatorIndex].m_phase = 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() void b3SoundSource::startSound()
{ {
if (m_data->m_envelope.isIdle()) if (m_data->m_envelope.isIdle())
{ {
for (int osc=0;osc<MAX_OSCILLATORS;osc++) for (int osc=0;osc<MAX_OSCILLATORS;osc++)
{ {
static int reset = 0; m_data->m_oscillators[osc].reset();
printf("reset %d!\n",reset++);
if (m_data->m_oscillators[osc].m_wavTicker.finished_) 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 //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) 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 } else
{ {
m_data->m_oscillators[osc].m_wavTicker.time_ = 0.f; 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_wavFilePtr = wavFilePtr;
m_data->m_wavFile.getWavInfo(resourcePath); m_data->m_oscillators[oscillatorIndex].m_wavTicker = m_data->m_wavFilePtr->createWavTicker(sampleRate);
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);
// waveIn.openFile(resourcePath); // waveIn.openFile(resourcePath);
double rate = 1.0; double rate = 1.0;

View File

@@ -1,6 +1,9 @@
#ifndef B3_SOUND_SOURCE_H #ifndef B3_SOUND_SOURCE_H
#define B3_SOUND_SOURCE_H #define B3_SOUND_SOURCE_H
#include "b3Sound_C_Api.h"
class b3SoundSource class b3SoundSource
{ {
struct b3SoundSourceInternalData* m_data; struct b3SoundSourceInternalData* m_data;
@@ -18,11 +21,12 @@ public:
void setOscillatorAmplitude(int oscillatorIndex, double amplitude); void setOscillatorAmplitude(int oscillatorIndex, double amplitude);
void setOscillatorPhase(int oscillatorIndex, double phase); 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 startSound();
void stopSound(); void stopSound();
bool isAvailable() const;
}; };
#endif //B3_SOUND_SOURCE_H #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