From 37a16f7e6bb5ed68b6ad37506144c194a0cda234 Mon Sep 17 00:00:00 2001 From: Erwin Coumans Date: Fri, 28 Apr 2017 11:35:00 -0700 Subject: [PATCH] move work on TinyAudio. --- examples/TinyAudio/TinyAudioExample.cpp | 153 ++++++++++++++++++------ examples/TinyAudio/b3ADSR.cpp | 6 +- examples/TinyAudio/b3AudioListener.cpp | 5 +- examples/TinyAudio/b3SoundEngine.cpp | 111 ++++++++++++++--- examples/TinyAudio/b3SoundEngine.h | 23 +++- examples/TinyAudio/b3SoundSource.cpp | 92 ++++++++------ examples/TinyAudio/b3SoundSource.h | 6 +- examples/TinyAudio/b3Sound_C_Api.h | 31 +++++ 8 files changed, 325 insertions(+), 102 deletions(-) create mode 100644 examples/TinyAudio/b3Sound_C_Api.h diff --git a/examples/TinyAudio/TinyAudioExample.cpp b/examples/TinyAudio/TinyAudioExample.cpp index 330339458..9fb17970f 100644 --- a/examples/TinyAudio/TinyAudioExample.cpp +++ b/examples/TinyAudio/TinyAudioExample.cpp @@ -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 + +///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 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 + } } diff --git a/examples/TinyAudio/b3ADSR.cpp b/examples/TinyAudio/b3ADSR.cpp index f2d743632..4f5b09b14 100644 --- a/examples/TinyAudio/b3ADSR.cpp +++ b/examples/TinyAudio/b3ADSR.cpp @@ -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() diff --git a/examples/TinyAudio/b3AudioListener.cpp b/examples/TinyAudio/b3AudioListener.cpp index e2c1377ee..dc78777c9 100644 --- a/examples/TinyAudio/b3AudioListener.cpp +++ b/examples/TinyAudio/b3AudioListener.cpp @@ -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]; diff --git a/examples/TinyAudio/b3SoundEngine.cpp b/examples/TinyAudio/b3SoundEngine.cpp index a72821bf3..851010a70 100644 --- a/examples/TinyAudio/b3SoundEngine.cpp +++ b/examples/TinyAudio/b3SoundEngine.cpp @@ -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 m_soundSources; + b3HashMap 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( ¶meters, 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(¶meters, 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;im_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(); } - - \ No newline at end of file diff --git a/examples/TinyAudio/b3SoundEngine.h b/examples/TinyAudio/b3SoundEngine.h index b83dadd9c..850a74ca5 100644 --- a/examples/TinyAudio/b3SoundEngine.h +++ b/examples/TinyAudio/b3SoundEngine.h @@ -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); diff --git a/examples/TinyAudio/b3SoundSource.cpp b/examples/TinyAudio/b3SoundSource.cpp index 9d65cc451..527f28137 100644 --- a/examples/TinyAudio/b3SoundSource.cpp +++ b/examples/TinyAudio/b3SoundSource.cpp @@ -3,10 +3,11 @@ #define MY2PI (2.*3.14159265) #include -#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;im_envelope.tick(); if (env) { - for (int osc=0;oscm_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;oscm_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;oscm_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; diff --git a/examples/TinyAudio/b3SoundSource.h b/examples/TinyAudio/b3SoundSource.h index 08c90475c..fc6080ecc 100644 --- a/examples/TinyAudio/b3SoundSource.h +++ b/examples/TinyAudio/b3SoundSource.h @@ -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 diff --git a/examples/TinyAudio/b3Sound_C_Api.h b/examples/TinyAudio/b3Sound_C_Api.h new file mode 100644 index 000000000..15b1bfc0b --- /dev/null +++ b/examples/TinyAudio/b3Sound_C_Api.h @@ -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