mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-12 23:24:41 +00:00
* Adjustment: Update Bullet version to 3.24.
This commit is contained in:
parent
35de012ee7
commit
4a3f31df2a
6148 changed files with 2112532 additions and 56873 deletions
11347
Engine/lib/bullet/examples/TinyAudio/RtAudio.cpp
Normal file
11347
Engine/lib/bullet/examples/TinyAudio/RtAudio.cpp
Normal file
File diff suppressed because it is too large
Load diff
1171
Engine/lib/bullet/examples/TinyAudio/RtAudio.h
Normal file
1171
Engine/lib/bullet/examples/TinyAudio/RtAudio.h
Normal file
File diff suppressed because it is too large
Load diff
203
Engine/lib/bullet/examples/TinyAudio/TinyAudioExample.cpp
Normal file
203
Engine/lib/bullet/examples/TinyAudio/TinyAudioExample.cpp
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
#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;
|
||||
int m_wavId;
|
||||
|
||||
b3HashMap<MyHashString, int> m_keyToSoundSource;
|
||||
|
||||
public:
|
||||
TinyAudioExample(struct GUIHelperInterface* helper)
|
||||
: m_guiHelper(helper)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~TinyAudioExample()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void initPhysics()
|
||||
{
|
||||
int numSoundSources = 32;
|
||||
bool useRealTimeDac = true;
|
||||
|
||||
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_soundEngine.exit();
|
||||
}
|
||||
|
||||
virtual void renderScene()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void stepSimulation(float deltaTime)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void physicsDebugDraw(int debugFlags)
|
||||
{
|
||||
}
|
||||
virtual bool mouseMoveCallback(float x, float y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool mouseButtonCallback(int button, int state, float x, float y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool keyboardCallback(int key, int state)
|
||||
{
|
||||
if (key >= 'a' && key <= 'z')
|
||||
{
|
||||
char keyStr[2];
|
||||
keyStr[0] = (char)key;
|
||||
keyStr[1] = 0;
|
||||
MyHashString hs(keyStr);
|
||||
|
||||
if (state)
|
||||
{
|
||||
int soundSourceIndex = m_soundEngine.getAvailableSoundSource();
|
||||
if (soundSourceIndex >= 0)
|
||||
{
|
||||
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 = 1;
|
||||
|
||||
msg.m_type = B3_SOUND_SOURCE_WAV_FILE;
|
||||
msg.m_wavId = m_wavId;
|
||||
msg.m_attackRate = 1;
|
||||
msg.m_sustainLevel = 1;
|
||||
msg.m_releaseRate = 0.001;
|
||||
|
||||
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
|
||||
{
|
||||
int* soundSourceIndexPtr = m_keyToSoundSource[hs];
|
||||
if (soundSourceIndexPtr)
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void resetCamera()
|
||||
{
|
||||
float dist = 4;
|
||||
float pitch = 52;
|
||||
float yaw = 35;
|
||||
float targetPos[3] = {0, 0, 0};
|
||||
m_guiHelper->resetCamera(dist, pitch, yaw, targetPos[0], targetPos[1], targetPos[2]);
|
||||
}
|
||||
};
|
||||
|
||||
CommonExampleInterface* TinyAudioExampleCreateFunc(CommonExampleOptions& options)
|
||||
{
|
||||
return new TinyAudioExample(options.m_guiHelper);
|
||||
}
|
||||
|
||||
B3_STANDALONE_EXAMPLE(TinyAudioExampleCreateFunc)
|
||||
7
Engine/lib/bullet/examples/TinyAudio/TinyAudioExample.h
Normal file
7
Engine/lib/bullet/examples/TinyAudio/TinyAudioExample.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
#ifndef TINY_AUDIO_EXAMPLE_H
|
||||
#define TINY_AUDIO_EXAMPLE_H
|
||||
|
||||
class CommonExampleInterface* TinyAudioExampleCreateFunc(struct CommonExampleOptions& options);
|
||||
|
||||
#endif //TINY_AUDIO_EXAMPLE_H
|
||||
109
Engine/lib/bullet/examples/TinyAudio/b3ADSR.cpp
Normal file
109
Engine/lib/bullet/examples/TinyAudio/b3ADSR.cpp
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
#include "b3ADSR.h"
|
||||
//ADSR mostly copied/reimplemented from Stk, see
|
||||
//http://github.com/thestk/stk
|
||||
|
||||
//! ADSR envelope states.
|
||||
|
||||
enum
|
||||
{
|
||||
ADSR_ATTACK, /*!< Attack */
|
||||
ADSR_DECAY, /*!< Decay */
|
||||
ADSR_SUSTAIN, /*!< Sustain */
|
||||
ADSR_RELEASE, /*!< Release */
|
||||
ADSR_IDLE /*!< Before attack / after release */
|
||||
};
|
||||
|
||||
b3ADSR::b3ADSR()
|
||||
{
|
||||
m_target = 0.0;
|
||||
m_value = 0.0;
|
||||
m_attackRate = 0.001;
|
||||
m_decayRate = 0.00001;
|
||||
m_releaseRate = 0.0005;
|
||||
m_sustainLevel = 0.5;
|
||||
m_state = ADSR_IDLE;
|
||||
m_autoKeyOff = false;
|
||||
}
|
||||
|
||||
b3ADSR::~b3ADSR()
|
||||
{
|
||||
}
|
||||
|
||||
double b3ADSR::tick()
|
||||
{
|
||||
switch (m_state)
|
||||
{
|
||||
case ADSR_ATTACK:
|
||||
m_value += m_attackRate;
|
||||
if (m_value >= m_target)
|
||||
{
|
||||
m_value = m_target;
|
||||
m_target = m_sustainLevel;
|
||||
m_state = ADSR_DECAY;
|
||||
}
|
||||
break;
|
||||
|
||||
case ADSR_DECAY:
|
||||
if (m_value > m_sustainLevel)
|
||||
{
|
||||
m_value -= m_decayRate;
|
||||
if (m_value <= m_sustainLevel)
|
||||
{
|
||||
m_value = m_sustainLevel;
|
||||
m_state = ADSR_SUSTAIN;
|
||||
if (m_autoKeyOff)
|
||||
{
|
||||
keyOff();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_value += m_decayRate; // attack target < sustain level
|
||||
if (m_value >= m_sustainLevel)
|
||||
{
|
||||
m_value = m_sustainLevel;
|
||||
m_state = ADSR_SUSTAIN;
|
||||
if (m_autoKeyOff)
|
||||
{
|
||||
keyOff();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ADSR_RELEASE:
|
||||
m_value -= m_releaseRate;
|
||||
if (m_value <= 0.0)
|
||||
{
|
||||
m_value = 0.0;
|
||||
m_state = ADSR_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
return m_value;
|
||||
}
|
||||
|
||||
bool b3ADSR::isIdle() const
|
||||
{
|
||||
return m_state == ADSR_IDLE;
|
||||
}
|
||||
|
||||
void b3ADSR::keyOn(bool autoKeyOff)
|
||||
{
|
||||
m_autoKeyOff = autoKeyOff;
|
||||
if (m_target <= 0.0)
|
||||
m_target = 1.0;
|
||||
if (m_attackRate == 1)
|
||||
{
|
||||
m_value = 1.0;
|
||||
}
|
||||
m_state = ADSR_ATTACK;
|
||||
}
|
||||
|
||||
void b3ADSR::keyOff()
|
||||
{
|
||||
m_autoKeyOff = false;
|
||||
m_target = 0.0;
|
||||
m_state = ADSR_RELEASE;
|
||||
}
|
||||
34
Engine/lib/bullet/examples/TinyAudio/b3ADSR.h
Normal file
34
Engine/lib/bullet/examples/TinyAudio/b3ADSR.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef B3_ADSR_H
|
||||
#define B3_ADSR_H
|
||||
|
||||
class b3ADSR
|
||||
{
|
||||
int m_state;
|
||||
double m_value;
|
||||
double m_target;
|
||||
double m_attackRate;
|
||||
double m_decayRate;
|
||||
double m_releaseRate;
|
||||
double m_releaseTime;
|
||||
double m_sustainLevel;
|
||||
bool m_autoKeyOff;
|
||||
|
||||
public:
|
||||
b3ADSR();
|
||||
virtual ~b3ADSR();
|
||||
|
||||
double tick();
|
||||
bool isIdle() const;
|
||||
void keyOn(bool autoKeyOff);
|
||||
void keyOff();
|
||||
|
||||
void setValues(double attack, double decay, double sustain, double release)
|
||||
{
|
||||
m_attackRate = attack;
|
||||
m_decayRate = decay;
|
||||
m_sustainLevel = sustain;
|
||||
m_releaseRate = release;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //B3_ADSR_H
|
||||
166
Engine/lib/bullet/examples/TinyAudio/b3AudioListener.cpp
Normal file
166
Engine/lib/bullet/examples/TinyAudio/b3AudioListener.cpp
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
#include "b3AudioListener.h"
|
||||
#include "b3SoundSource.h"
|
||||
#include "Bullet3Common/b3Logging.h"
|
||||
#include "b3WriteWavFile.h"
|
||||
#include <math.h>
|
||||
|
||||
template <class T>
|
||||
inline const T& MyMin(const T& a, const T& b)
|
||||
{
|
||||
return a < b ? a : b;
|
||||
}
|
||||
#define MAX_SOUND_SOURCES 128
|
||||
#define B3_SAMPLE_RATE 48000
|
||||
|
||||
struct b3AudioListenerInternalData
|
||||
{
|
||||
int m_numControlTicks;
|
||||
double m_sampleRate;
|
||||
|
||||
b3SoundSource* m_soundSources[MAX_SOUND_SOURCES];
|
||||
|
||||
b3WriteWavFile m_wavOut2;
|
||||
bool m_writeWavOut;
|
||||
|
||||
b3AudioListenerInternalData()
|
||||
: m_numControlTicks(64),
|
||||
m_sampleRate(B3_SAMPLE_RATE),
|
||||
m_writeWavOut(false)
|
||||
{
|
||||
for (int i = 0; i < MAX_SOUND_SOURCES; i++)
|
||||
{
|
||||
m_soundSources[i] = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
b3AudioListener::b3AudioListener()
|
||||
{
|
||||
m_data = new b3AudioListenerInternalData();
|
||||
if (m_data->m_writeWavOut)
|
||||
{
|
||||
m_data->m_wavOut2.setWavFile("bulletAudio2.wav", B3_SAMPLE_RATE, 2, false);
|
||||
}
|
||||
}
|
||||
|
||||
b3AudioListener::~b3AudioListener()
|
||||
{
|
||||
if (m_data->m_writeWavOut)
|
||||
{
|
||||
m_data->m_wavOut2.closeWavFile();
|
||||
}
|
||||
|
||||
delete m_data;
|
||||
}
|
||||
|
||||
int b3AudioListener::addSoundSource(b3SoundSource* source)
|
||||
{
|
||||
int soundIndex = -1;
|
||||
|
||||
for (int i = 0; i < MAX_SOUND_SOURCES; i++)
|
||||
{
|
||||
if (m_data->m_soundSources[i] == 0)
|
||||
{
|
||||
m_data->m_soundSources[i] = source;
|
||||
soundIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return soundIndex;
|
||||
}
|
||||
|
||||
void b3AudioListener::removeSoundSource(b3SoundSource* source)
|
||||
{
|
||||
for (int i = 0; i < MAX_SOUND_SOURCES; i++)
|
||||
{
|
||||
if (m_data->m_soundSources[i] == source)
|
||||
{
|
||||
m_data->m_soundSources[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b3AudioListenerInternalData* b3AudioListener::getTickData()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
const b3AudioListenerInternalData* b3AudioListener::getTickData() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
double b3AudioListener::getSampleRate() const
|
||||
{
|
||||
return m_data->m_sampleRate;
|
||||
}
|
||||
|
||||
void b3AudioListener::setSampleRate(double sampleRate)
|
||||
{
|
||||
m_data->m_sampleRate = sampleRate;
|
||||
}
|
||||
|
||||
int b3AudioListener::tick(void* outputBuffer, void* inputBuffer1, unsigned int nBufferFrames,
|
||||
double streamTime, unsigned int status, void* dataPointer)
|
||||
{
|
||||
B3_PROFILE("b3AudioListener::tick");
|
||||
|
||||
b3AudioListenerInternalData* data = (b3AudioListenerInternalData*)dataPointer;
|
||||
register double outs[2], *samples = (double*)outputBuffer;
|
||||
register double tempOuts[2];
|
||||
int counter, nTicks = (int)nBufferFrames;
|
||||
bool done = false;
|
||||
|
||||
int numSamples = 0;
|
||||
|
||||
while (nTicks > 0 && !done)
|
||||
{
|
||||
counter = MyMin(nTicks, data->m_numControlTicks);
|
||||
bool newsynth = true;
|
||||
if (newsynth)
|
||||
{
|
||||
for (int i = 0; i < counter; i++)
|
||||
{
|
||||
outs[0] = 0.;
|
||||
outs[1] = 0.;
|
||||
//make_sound_double(outs,1);
|
||||
float numActiveSources = 0;
|
||||
|
||||
for (int i = 0; i < MAX_SOUND_SOURCES; i++)
|
||||
{
|
||||
if (data->m_soundSources[i])
|
||||
{
|
||||
tempOuts[0] = 0;
|
||||
tempOuts[1] = 0;
|
||||
|
||||
if (data->m_soundSources[i]->computeSamples(tempOuts, 1, data->m_sampleRate))
|
||||
{
|
||||
numActiveSources++;
|
||||
//simple mixer
|
||||
outs[0] += tempOuts[0];
|
||||
outs[1] += tempOuts[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//soft-clipping of sounds
|
||||
outs[0] = tanh(outs[0]);
|
||||
outs[1] = tanh(outs[1]);
|
||||
|
||||
*samples++ = outs[0];
|
||||
*samples++ = outs[1];
|
||||
numSamples++;
|
||||
}
|
||||
nTicks -= counter;
|
||||
}
|
||||
if (nTicks == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
//logging to wav file
|
||||
if (data->m_writeWavOut && numSamples)
|
||||
{
|
||||
data->m_wavOut2.tick((double*)outputBuffer, numSamples);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
27
Engine/lib/bullet/examples/TinyAudio/b3AudioListener.h
Normal file
27
Engine/lib/bullet/examples/TinyAudio/b3AudioListener.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef B3_AUDIO_LISTENER_H
|
||||
#define B3_AUDIO_LISTENER_H
|
||||
|
||||
class b3SoundSource;
|
||||
|
||||
class b3AudioListener
|
||||
{
|
||||
struct b3AudioListenerInternalData* m_data;
|
||||
|
||||
public:
|
||||
b3AudioListener();
|
||||
virtual ~b3AudioListener();
|
||||
|
||||
static int tick(void* outputBuffer, void* inputBuffer1, unsigned int nBufferFrames,
|
||||
double streamTime, unsigned int status, void* dataPointer);
|
||||
|
||||
int addSoundSource(b3SoundSource* source);
|
||||
void removeSoundSource(b3SoundSource* source);
|
||||
|
||||
b3AudioListenerInternalData* getTickData();
|
||||
const b3AudioListenerInternalData* getTickData() const;
|
||||
|
||||
double getSampleRate() const;
|
||||
void setSampleRate(double sampleRate);
|
||||
};
|
||||
|
||||
#endif //B3_AUDIO_LISTENER_H
|
||||
492
Engine/lib/bullet/examples/TinyAudio/b3ReadWavFile.cpp
Normal file
492
Engine/lib/bullet/examples/TinyAudio/b3ReadWavFile.cpp
Normal file
|
|
@ -0,0 +1,492 @@
|
|||
|
||||
//b3ReadWavFile is implemented based on code from the STK toolkit
|
||||
//See https://github.com/thestk/stk
|
||||
//Some improvement: the ticking data (b3WavTicker) is separate from wav file,
|
||||
//This makes it possoble to play a single wav multiple times at the same time
|
||||
|
||||
#include "b3ReadWavFile.h"
|
||||
#include "b3SwapUtils.h"
|
||||
|
||||
const unsigned long B3_SINT8 = 0x1;
|
||||
const unsigned long B3_SINT16 = 0x2;
|
||||
const unsigned long B3_SINT24 = 0x4;
|
||||
const unsigned long B3_SINT32 = 0x8;
|
||||
const unsigned long B3_FLOAT32 = 0x10;
|
||||
const unsigned long B3_FLOAT64 = 0x20;
|
||||
|
||||
b3ReadWavFile::b3ReadWavFile()
|
||||
{
|
||||
fd_ = 0;
|
||||
m_machineIsLittleEndian = b3MachineIsLittleEndian();
|
||||
}
|
||||
b3ReadWavFile::~b3ReadWavFile()
|
||||
{
|
||||
if (fd_)
|
||||
fclose(fd_);
|
||||
}
|
||||
|
||||
void b3ReadWavFile::normalize(double peak)
|
||||
{
|
||||
int i;
|
||||
double max = 0.0;
|
||||
|
||||
for (i = 0; i < m_frames.size(); i++)
|
||||
{
|
||||
if (fabs(m_frames[i]) > max)
|
||||
max = (double)fabs((double)m_frames[i]);
|
||||
}
|
||||
|
||||
if (max > 0.0)
|
||||
{
|
||||
max = 1.0 / max;
|
||||
max *= peak;
|
||||
for (i = 0; i < m_frames.size(); i++)
|
||||
m_frames[i] *= max;
|
||||
}
|
||||
}
|
||||
|
||||
double b3ReadWavFile::interpolate(double frame, unsigned int channel) const
|
||||
{
|
||||
int iIndex = (int)frame; // integer part of index
|
||||
double output, alpha = frame - (double)iIndex; // fractional part of index
|
||||
|
||||
iIndex = iIndex * channels_ + channel;
|
||||
output = m_frames[iIndex];
|
||||
if (alpha > 0.0)
|
||||
output += (alpha * (m_frames[iIndex + channels_] - output));
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
double b3ReadWavFile::tick(unsigned int channel, b3WavTicker *ticker)
|
||||
{
|
||||
if (ticker->finished_) return 0.0;
|
||||
|
||||
if (ticker->time_ < 0.0 || ticker->time_ > (double)(this->m_numFrames - 1.0))
|
||||
{
|
||||
for (int i = 0; i < ticker->lastFrame_.size(); i++) ticker->lastFrame_[i] = 0.0;
|
||||
ticker->finished_ = true;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double tyme = ticker->time_;
|
||||
|
||||
bool interpolate_ = true; //for now
|
||||
|
||||
if (interpolate_)
|
||||
{
|
||||
for (int i = 0; i < ticker->lastFrame_.size(); i++)
|
||||
ticker->lastFrame_[i] = interpolate(tyme, i);
|
||||
}
|
||||
|
||||
// Increment time, which can be negative.
|
||||
ticker->time_ += ticker->rate_;
|
||||
return ticker->lastFrame_[channel];
|
||||
}
|
||||
|
||||
void b3ReadWavFile::resize()
|
||||
{
|
||||
m_frames.resize(channels_ * m_numFrames);
|
||||
}
|
||||
|
||||
b3WavTicker b3ReadWavFile::createWavTicker(double sampleRate)
|
||||
{
|
||||
b3WavTicker ticker;
|
||||
ticker.lastFrame_.resize(this->channels_);
|
||||
ticker.time_ = 0;
|
||||
ticker.finished_ = false;
|
||||
ticker.rate_ = fileDataRate_ / sampleRate;
|
||||
return ticker;
|
||||
}
|
||||
|
||||
bool b3ReadWavFile::getWavInfo(const char *fileName)
|
||||
{
|
||||
fd_ = fopen(fileName, "rb");
|
||||
if (fd_ == 0)
|
||||
return false;
|
||||
|
||||
char header[12];
|
||||
if (fread(&header, 4, 3, fd_) != 3)
|
||||
return false;
|
||||
bool res = false;
|
||||
|
||||
if (!strncmp(header, "RIFF", 4) &&
|
||||
!strncmp(&header[8], "WAVE", 4))
|
||||
res = true;
|
||||
//getWavInfo( fileName );
|
||||
|
||||
// Find "format" chunk ... it must come before the "data" chunk.
|
||||
char id[4];
|
||||
int chunkSize;
|
||||
if (fread(&id, 4, 1, fd_) != 1)
|
||||
return false;
|
||||
while (strncmp(id, "fmt ", 4))
|
||||
{
|
||||
if (fread(&chunkSize, 4, 1, fd_) != 1)
|
||||
return false;
|
||||
if (!m_machineIsLittleEndian)
|
||||
{
|
||||
b3Swap32((unsigned char *)&chunkSize);
|
||||
}
|
||||
if (fseek(fd_, chunkSize, SEEK_CUR) == -1)
|
||||
return false;
|
||||
if (fread(&id, 4, 1, fd_) != 1)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the data is not compressed.
|
||||
unsigned short format_tag;
|
||||
if (fread(&chunkSize, 4, 1, fd_) != 1)
|
||||
return false; // Read fmt chunk size.
|
||||
if (fread(&format_tag, 2, 1, fd_) != 1)
|
||||
return false;
|
||||
if (!m_machineIsLittleEndian)
|
||||
{
|
||||
b3Swap16((unsigned char *)&format_tag);
|
||||
b3Swap32((unsigned char *)&chunkSize);
|
||||
}
|
||||
if (format_tag == 0xFFFE)
|
||||
{ // WAVE_FORMAT_EXTENSIBLE
|
||||
dataOffset_ = ftell(fd_);
|
||||
if (fseek(fd_, 14, SEEK_CUR) == -1)
|
||||
return false;
|
||||
unsigned short extSize;
|
||||
if (fread(&extSize, 2, 1, fd_) != 1)
|
||||
return false;
|
||||
if (!m_machineIsLittleEndian)
|
||||
{
|
||||
b3Swap16((unsigned char *)&extSize);
|
||||
}
|
||||
if (extSize == 0)
|
||||
return false;
|
||||
if (fseek(fd_, 6, SEEK_CUR) == -1)
|
||||
return false;
|
||||
if (fread(&format_tag, 2, 1, fd_) != 1)
|
||||
return false;
|
||||
if (!m_machineIsLittleEndian)
|
||||
{
|
||||
b3Swap16((unsigned char *)&format_tag);
|
||||
}
|
||||
if (fseek(fd_, dataOffset_, SEEK_SET) == -1)
|
||||
return false;
|
||||
}
|
||||
if (format_tag != 1 && format_tag != 3)
|
||||
{ // PCM = 1, FLOAT = 3
|
||||
// oStream_ << "FileRead: "<< fileName << " contains an unsupported data format type (" << format_tag << ").";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get number of channels from the header.
|
||||
short int temp;
|
||||
if (fread(&temp, 2, 1, fd_) != 1)
|
||||
return false;
|
||||
if (!m_machineIsLittleEndian)
|
||||
{
|
||||
b3Swap16((unsigned char *)&temp);
|
||||
}
|
||||
channels_ = (unsigned int)temp;
|
||||
|
||||
// Get file sample rate from the header.
|
||||
int srate;
|
||||
if (fread(&srate, 4, 1, fd_) != 1)
|
||||
return false;
|
||||
if (!m_machineIsLittleEndian)
|
||||
{
|
||||
b3Swap32((unsigned char *)&srate);
|
||||
}
|
||||
fileDataRate_ = (double)srate;
|
||||
|
||||
// Determine the data type.
|
||||
dataType_ = 0;
|
||||
if (fseek(fd_, 6, SEEK_CUR) == -1)
|
||||
return false; // Locate bits_per_sample info.
|
||||
if (fread(&temp, 2, 1, fd_) != 1)
|
||||
return false;
|
||||
if (!m_machineIsLittleEndian)
|
||||
{
|
||||
b3Swap16((unsigned char *)&temp);
|
||||
}
|
||||
if (format_tag == 1)
|
||||
{
|
||||
if (temp == 8)
|
||||
dataType_ = B3_SINT8;
|
||||
else if (temp == 16)
|
||||
dataType_ = B3_SINT16;
|
||||
else if (temp == 24)
|
||||
dataType_ = B3_SINT24;
|
||||
else if (temp == 32)
|
||||
dataType_ = B3_SINT32;
|
||||
}
|
||||
else if (format_tag == 3)
|
||||
{
|
||||
if (temp == 32)
|
||||
dataType_ = B3_FLOAT32;
|
||||
else if (temp == 64)
|
||||
dataType_ = B3_FLOAT64;
|
||||
}
|
||||
if (dataType_ == 0)
|
||||
{
|
||||
// oStream_ << "FileRead: " << temp << " bits per sample with data format " << format_tag << " are not supported (" << fileName << ").";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Jump over any remaining part of the "fmt" chunk.
|
||||
if (fseek(fd_, chunkSize - 16, SEEK_CUR) == -1)
|
||||
return false;
|
||||
|
||||
// Find "data" chunk ... it must come after the "fmt" chunk.
|
||||
if (fread(&id, 4, 1, fd_) != 1)
|
||||
return false;
|
||||
|
||||
while (strncmp(id, "data", 4))
|
||||
{
|
||||
if (fread(&chunkSize, 4, 1, fd_) != 1)
|
||||
return false;
|
||||
if (!m_machineIsLittleEndian)
|
||||
{
|
||||
b3Swap32((unsigned char *)&chunkSize);
|
||||
}
|
||||
chunkSize += chunkSize % 2; // chunk sizes must be even
|
||||
if (fseek(fd_, chunkSize, SEEK_CUR) == -1)
|
||||
return false;
|
||||
if (fread(&id, 4, 1, fd_) != 1)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get length of data from the header.
|
||||
int bytes;
|
||||
if (fread(&bytes, 4, 1, fd_) != 1)
|
||||
return false;
|
||||
if (!m_machineIsLittleEndian)
|
||||
{
|
||||
b3Swap32((unsigned char *)&bytes);
|
||||
}
|
||||
m_numFrames = bytes / temp / channels_; // sample frames
|
||||
m_numFrames *= 8; // sample frames
|
||||
|
||||
dataOffset_ = ftell(fd_);
|
||||
byteswap_ = false;
|
||||
if (!m_machineIsLittleEndian)
|
||||
{
|
||||
byteswap_ = true;
|
||||
}
|
||||
wavFile_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool b3ReadWavFile::read(unsigned long startFrame, bool doNormalize)
|
||||
{
|
||||
// Make sure we have an open file.
|
||||
if (fd_ == 0)
|
||||
{
|
||||
// oStream_ << "FileRead::read: a file is not open!";
|
||||
// Stk::handleError( StkError::WARNING ); return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the m_frames size.
|
||||
unsigned long nFrames = this->m_numFrames; //m_frames.frames();
|
||||
if (nFrames == 0)
|
||||
{
|
||||
// oStream_ << "FileRead::read: StkFrames m_frames size is zero ... no data read!";
|
||||
// Stk::handleError( StkError::WARNING );
|
||||
return false;
|
||||
}
|
||||
|
||||
if (startFrame >= m_numFrames)
|
||||
{
|
||||
return false;
|
||||
//oStream_ << "FileRead::read: startFrame argument is greater than or equal to the file size!";
|
||||
//Stk::handleError( StkError::FUNCTION_ARGUMENT );
|
||||
}
|
||||
|
||||
// Check for file end.
|
||||
if (startFrame + nFrames > m_numFrames)
|
||||
nFrames = m_numFrames - startFrame;
|
||||
|
||||
long i, nSamples = (long)(nFrames * channels_);
|
||||
unsigned long offset = startFrame * channels_;
|
||||
|
||||
// Read samples into StkFrames data m_frames.
|
||||
if (dataType_ == B3_SINT16)
|
||||
{
|
||||
signed short int *buf = (signed short int *)&m_frames[0];
|
||||
if (fseek(fd_, dataOffset_ + (offset * 2), SEEK_SET) == -1)
|
||||
return false;
|
||||
if (fread(buf, nSamples * 2, 1, fd_) != 1)
|
||||
return false;
|
||||
if (byteswap_)
|
||||
{
|
||||
signed short int *ptr = buf;
|
||||
for (i = nSamples - 1; i >= 0; i--)
|
||||
b3Swap16((unsigned char *)ptr++);
|
||||
}
|
||||
if (doNormalize)
|
||||
{
|
||||
double gain = 1.0 / 32768.0;
|
||||
for (i = nSamples - 1; i >= 0; i--)
|
||||
m_frames[i] = buf[i] * gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = nSamples - 1; i >= 0; i--)
|
||||
m_frames[i] = buf[i];
|
||||
}
|
||||
}
|
||||
else if (dataType_ == B3_SINT32)
|
||||
{
|
||||
int *buf = (int *)&m_frames[0];
|
||||
if (fseek(fd_, dataOffset_ + (offset * 4), SEEK_SET) == -1)
|
||||
return false;
|
||||
if (fread(buf, nSamples * 4, 1, fd_) != 1)
|
||||
return false;
|
||||
if (byteswap_)
|
||||
{
|
||||
int *ptr = buf;
|
||||
for (i = nSamples - 1; i >= 0; i--)
|
||||
b3Swap32((unsigned char *)ptr++);
|
||||
}
|
||||
if (doNormalize)
|
||||
{
|
||||
double gain = 1.0 / 2147483648.0;
|
||||
for (i = nSamples - 1; i >= 0; i--)
|
||||
m_frames[i] = buf[i] * gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = nSamples - 1; i >= 0; i--)
|
||||
m_frames[i] = buf[i];
|
||||
}
|
||||
}
|
||||
else if (dataType_ == B3_FLOAT32)
|
||||
{
|
||||
float *buf = (float *)&m_frames[0];
|
||||
if (fseek(fd_, dataOffset_ + (offset * 4), SEEK_SET) == -1)
|
||||
return false;
|
||||
if (fread(buf, nSamples * 4, 1, fd_) != 1)
|
||||
return false;
|
||||
if (byteswap_)
|
||||
{
|
||||
float *ptr = buf;
|
||||
for (i = nSamples - 1; i >= 0; i--)
|
||||
b3Swap32((unsigned char *)ptr++);
|
||||
}
|
||||
for (i = nSamples - 1; i >= 0; i--)
|
||||
m_frames[i] = buf[i];
|
||||
}
|
||||
else if (dataType_ == B3_FLOAT64)
|
||||
{
|
||||
double *buf = (double *)&m_frames[0];
|
||||
if (fseek(fd_, dataOffset_ + (offset * 8), SEEK_SET) == -1)
|
||||
return false;
|
||||
if (fread(buf, nSamples * 8, 1, fd_) != 1)
|
||||
return false;
|
||||
if (byteswap_)
|
||||
{
|
||||
double *ptr = buf;
|
||||
for (i = nSamples - 1; i >= 0; i--)
|
||||
b3Swap64((unsigned char *)ptr++);
|
||||
}
|
||||
for (i = nSamples - 1; i >= 0; i--)
|
||||
m_frames[i] = buf[i];
|
||||
}
|
||||
else if (dataType_ == B3_SINT8 && wavFile_)
|
||||
{ // 8-bit WAV data is unsigned!
|
||||
unsigned char *buf = (unsigned char *)&m_frames[0];
|
||||
if (fseek(fd_, dataOffset_ + offset, SEEK_SET) == -1)
|
||||
return false;
|
||||
if (fread(buf, nSamples, 1, fd_) != 1)
|
||||
return false;
|
||||
if (doNormalize)
|
||||
{
|
||||
double gain = 1.0 / 128.0;
|
||||
for (i = nSamples - 1; i >= 0; i--)
|
||||
m_frames[i] = (buf[i] - 128) * gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = nSamples - 1; i >= 0; i--)
|
||||
m_frames[i] = buf[i] - 128.0;
|
||||
}
|
||||
}
|
||||
else if (dataType_ == B3_SINT8)
|
||||
{ // signed 8-bit data
|
||||
char *buf = (char *)&m_frames[0];
|
||||
if (fseek(fd_, dataOffset_ + offset, SEEK_SET) == -1)
|
||||
return false;
|
||||
if (fread(buf, nSamples, 1, fd_) != 1)
|
||||
return false;
|
||||
if (doNormalize)
|
||||
{
|
||||
double gain = 1.0 / 128.0;
|
||||
for (i = nSamples - 1; i >= 0; i--)
|
||||
m_frames[i] = buf[i] * gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = nSamples - 1; i >= 0; i--)
|
||||
m_frames[i] = buf[i];
|
||||
}
|
||||
}
|
||||
else if (dataType_ == B3_SINT24)
|
||||
{
|
||||
// 24-bit values are harder to import efficiently since there is
|
||||
// no native 24-bit type. The following routine works but is much
|
||||
// less efficient than that used for the other data types.
|
||||
int temp;
|
||||
unsigned char *ptr = (unsigned char *)&temp;
|
||||
double gain = 1.0 / 2147483648.0;
|
||||
if (fseek(fd_, dataOffset_ + (offset * 3), SEEK_SET) == -1)
|
||||
return false;
|
||||
for (i = 0; i < nSamples; i++)
|
||||
{
|
||||
if (m_machineIsLittleEndian)
|
||||
{
|
||||
if (byteswap_)
|
||||
{
|
||||
if (fread(ptr, 3, 1, fd_) != 1)
|
||||
return false;
|
||||
temp &= 0x00ffffff;
|
||||
b3Swap32((unsigned char *)ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fread(ptr + 1, 3, 1, fd_) != 1)
|
||||
return false;
|
||||
temp &= 0xffffff00;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (byteswap_)
|
||||
{
|
||||
if (fread(ptr + 1, 3, 1, fd_) != 1)
|
||||
return false;
|
||||
temp &= 0xffffff00;
|
||||
b3Swap32((unsigned char *)ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fread(ptr, 3, 1, fd_) != 1)
|
||||
return false;
|
||||
temp &= 0x00ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
if (doNormalize)
|
||||
{
|
||||
m_frames[i] = (double)temp * gain; // "gain" also includes 1 / 256 factor.
|
||||
}
|
||||
else
|
||||
m_frames[i] = (double)temp / 256; // right shift without affecting the sign bit
|
||||
}
|
||||
}
|
||||
|
||||
// m_frames.setDataRate( fileDataRate_ );
|
||||
|
||||
return true;
|
||||
|
||||
// error:
|
||||
// oStream_ << "FileRead: Error reading file data.";
|
||||
// handleError( StkError::FILE_ERROR);
|
||||
}
|
||||
53
Engine/lib/bullet/examples/TinyAudio/b3ReadWavFile.h
Normal file
53
Engine/lib/bullet/examples/TinyAudio/b3ReadWavFile.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#ifndef B3_READ_WAV_FILE_H
|
||||
#define B3_READ_WAV_FILE_H
|
||||
|
||||
#include "Bullet3Common/b3AlignedObjectArray.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
struct b3WavTicker
|
||||
{
|
||||
b3AlignedObjectArray<double> lastFrame_;
|
||||
bool finished_;
|
||||
double time_;
|
||||
double rate_;
|
||||
};
|
||||
|
||||
class b3ReadWavFile
|
||||
{
|
||||
bool byteswap_;
|
||||
bool wavFile_;
|
||||
unsigned long m_numFrames;
|
||||
unsigned long dataType_;
|
||||
double fileDataRate_;
|
||||
FILE *fd_;
|
||||
unsigned long dataOffset_;
|
||||
unsigned int channels_;
|
||||
bool m_machineIsLittleEndian;
|
||||
|
||||
public:
|
||||
b3ReadWavFile();
|
||||
virtual ~b3ReadWavFile();
|
||||
|
||||
b3AlignedObjectArray<double> m_frames;
|
||||
|
||||
bool getWavInfo(const char *fileName);
|
||||
|
||||
void normalize(double peak);
|
||||
|
||||
double interpolate(double frame, unsigned int channel) const;
|
||||
double tick(unsigned int channel, b3WavTicker *ticker);
|
||||
|
||||
void resize();
|
||||
|
||||
b3WavTicker createWavTicker(double sampleRate);
|
||||
|
||||
bool read(unsigned long startFrame, bool doNormalize);
|
||||
|
||||
int getNumFrames() const
|
||||
{
|
||||
return m_numFrames;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //B3_READ_WAV_FILE_H
|
||||
185
Engine/lib/bullet/examples/TinyAudio/b3SoundEngine.cpp
Normal file
185
Engine/lib/bullet/examples/TinyAudio/b3SoundEngine.cpp
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
#include "b3SoundEngine.h"
|
||||
|
||||
#include "RtAudio.h"
|
||||
|
||||
#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
|
||||
// larger buffer size may help. Larger buffer sizes, however, produce
|
||||
// more latency.
|
||||
//const unsigned int RT_BUFFER_SIZE = 1024;
|
||||
const unsigned int RT_BUFFER_SIZE = 256;
|
||||
|
||||
struct b3SoundEngineInternalData
|
||||
{
|
||||
b3AudioListener m_listener;
|
||||
RtAudio m_dac;
|
||||
bool m_useRealTimeDac;
|
||||
|
||||
b3AlignedObjectArray<b3SoundSource*> m_soundSources;
|
||||
b3HashMap<b3HashInt, b3ReadWavFile*> m_wavFiles;
|
||||
b3HashMap<b3HashString, int> m_name2wav;
|
||||
|
||||
int m_wavFileUidGenerator;
|
||||
|
||||
b3SoundEngineInternalData()
|
||||
: m_useRealTimeDac(false),
|
||||
m_wavFileUidGenerator(123)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
b3SoundEngine::b3SoundEngine()
|
||||
{
|
||||
m_data = new b3SoundEngineInternalData();
|
||||
}
|
||||
|
||||
b3SoundEngine::~b3SoundEngine()
|
||||
{
|
||||
exit();
|
||||
delete m_data;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
this->m_data->m_useRealTimeDac = useRealTimeDac;
|
||||
|
||||
if (useRealTimeDac)
|
||||
{
|
||||
RtAudioFormat format = (sizeof(double) == 8) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32;
|
||||
RtAudio::StreamParameters parameters;
|
||||
parameters.deviceId = m_data->m_dac.getDefaultOutputDevice();
|
||||
parameters.nChannels = 2;
|
||||
|
||||
unsigned int bufferFrames = RT_BUFFER_SIZE;
|
||||
double sampleRate = m_data->m_listener.getSampleRate();
|
||||
|
||||
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; 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();
|
||||
|
||||
for (int i = 0; i < m_data->m_wavFiles.size(); i++)
|
||||
{
|
||||
b3ReadWavFile** wavPtr = m_data->m_wavFiles.getAtIndex(i);
|
||||
if (wavPtr && *wavPtr)
|
||||
{
|
||||
b3ReadWavFile* wav = *wavPtr;
|
||||
delete wav;
|
||||
}
|
||||
}
|
||||
m_data->m_wavFiles.clear();
|
||||
m_data->m_name2wav.clear();
|
||||
}
|
||||
|
||||
int b3SoundEngine::getAvailableSoundSource()
|
||||
{
|
||||
for (int i = 0; i < m_data->m_soundSources.size(); i++)
|
||||
{
|
||||
if (m_data->m_soundSources[i]->isAvailable())
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void b3SoundEngine::startSound(int soundSourceIndex, b3SoundMessage msg)
|
||||
{
|
||||
b3SoundSource* soundSource = m_data->m_soundSources[soundSourceIndex];
|
||||
soundSource->setOscillatorAmplitude(0, msg.m_amplitude);
|
||||
soundSource->setOscillatorAmplitude(1, msg.m_amplitude);
|
||||
|
||||
soundSource->setADSR(msg.m_attackRate, msg.m_decayRate, msg.m_sustainLevel, msg.m_releaseRate);
|
||||
|
||||
switch (msg.m_type)
|
||||
{
|
||||
case B3_SOUND_SOURCE_SINE_OSCILLATOR:
|
||||
{
|
||||
soundSource->setOscillatorFrequency(0, msg.m_frequency);
|
||||
soundSource->setOscillatorFrequency(1, msg.m_frequency);
|
||||
|
||||
soundSource->startSound(msg.m_autoKeyOff);
|
||||
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(msg.m_autoKeyOff);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void b3SoundEngine::releaseSound(int soundSourceIndex)
|
||||
{
|
||||
b3SoundSource* soundSource = m_data->m_soundSources[soundSourceIndex];
|
||||
soundSource->stopSound();
|
||||
}
|
||||
|
||||
int b3SoundEngine::loadWavFile(const char* fileName)
|
||||
{
|
||||
int* wavUidPtr = m_data->m_name2wav[fileName];
|
||||
if (wavUidPtr)
|
||||
{
|
||||
return *wavUidPtr;
|
||||
}
|
||||
char resourcePath[1024];
|
||||
|
||||
if (b3ResourcePath::findResourcePath(fileName, resourcePath, 1024, 0))
|
||||
{
|
||||
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);
|
||||
m_data->m_name2wav.insert(fileName, wavUID);
|
||||
return wavUID;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
double b3SoundEngine::getSampleRate() const
|
||||
{
|
||||
return m_data->m_listener.getSampleRate();
|
||||
}
|
||||
55
Engine/lib/bullet/examples/TinyAudio/b3SoundEngine.h
Normal file
55
Engine/lib/bullet/examples/TinyAudio/b3SoundEngine.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#ifndef B3_SOUND_ENGINE_H
|
||||
#define B3_SOUND_ENGINE_H
|
||||
|
||||
#include "Bullet3Common/b3Scalar.h"
|
||||
#include "b3Sound_C_Api.h"
|
||||
|
||||
struct b3SoundMessage
|
||||
{
|
||||
int m_type; //B3_SOUND_SOURCE_TYPE
|
||||
double m_amplitude;
|
||||
|
||||
double m_frequency;
|
||||
int m_wavId;
|
||||
|
||||
double m_attackRate;
|
||||
double m_decayRate;
|
||||
double m_sustainLevel;
|
||||
double m_releaseRate;
|
||||
bool m_autoKeyOff;
|
||||
|
||||
b3SoundMessage()
|
||||
: m_type(B3_SOUND_SOURCE_SINE_OSCILLATOR),
|
||||
m_amplitude(0.5),
|
||||
m_frequency(440),
|
||||
m_wavId(-1),
|
||||
m_attackRate(0.001),
|
||||
m_decayRate(0.00001),
|
||||
m_sustainLevel(0.5),
|
||||
m_releaseRate(0.0005),
|
||||
m_autoKeyOff(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class b3SoundEngine
|
||||
{
|
||||
struct b3SoundEngineInternalData* m_data;
|
||||
|
||||
public:
|
||||
b3SoundEngine();
|
||||
virtual ~b3SoundEngine();
|
||||
|
||||
void init(int maxNumSoundSources, bool useRealTimeDac);
|
||||
void exit();
|
||||
|
||||
int getAvailableSoundSource();
|
||||
void startSound(int soundSourceIndex, b3SoundMessage msg);
|
||||
void releaseSound(int soundSourceIndex);
|
||||
|
||||
int loadWavFile(const char* fileName);
|
||||
|
||||
double getSampleRate() const;
|
||||
};
|
||||
|
||||
#endif //B3_SOUND_ENGINE_H
|
||||
233
Engine/lib/bullet/examples/TinyAudio/b3SoundSource.cpp
Normal file
233
Engine/lib/bullet/examples/TinyAudio/b3SoundSource.cpp
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
#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;
|
||||
}
|
||||
31
Engine/lib/bullet/examples/TinyAudio/b3SoundSource.h
Normal file
31
Engine/lib/bullet/examples/TinyAudio/b3SoundSource.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef B3_SOUND_SOURCE_H
|
||||
#define B3_SOUND_SOURCE_H
|
||||
|
||||
#include "b3Sound_C_Api.h"
|
||||
|
||||
class b3SoundSource
|
||||
{
|
||||
struct b3SoundSourceInternalData* m_data;
|
||||
|
||||
public:
|
||||
b3SoundSource();
|
||||
virtual ~b3SoundSource();
|
||||
|
||||
virtual bool computeSamples(double* sampleBuffer, int numSamples, double sampleRate);
|
||||
|
||||
int getNumOscillators() const;
|
||||
void setOscillatorType(int oscillatorIndex, int type);
|
||||
void setOscillatorFrequency(int oscillatorIndex, double frequency);
|
||||
void setOscillatorAmplitude(int oscillatorIndex, double amplitude);
|
||||
void setOscillatorPhase(int oscillatorIndex, double phase);
|
||||
void setADSR(double attackRate, double decayRate, double sustainLevel, double releaseRate);
|
||||
|
||||
bool setWavFile(int oscillatorIndex, class b3ReadWavFile* wavFilePtr, int sampleRate);
|
||||
|
||||
void startSound(bool autoKeyOff);
|
||||
void stopSound();
|
||||
|
||||
bool isAvailable() const;
|
||||
};
|
||||
|
||||
#endif //B3_SOUND_SOURCE_H
|
||||
29
Engine/lib/bullet/examples/TinyAudio/b3Sound_C_Api.h
Normal file
29
Engine/lib/bullet/examples/TinyAudio/b3Sound_C_Api.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#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
|
||||
58
Engine/lib/bullet/examples/TinyAudio/b3SwapUtils.h
Normal file
58
Engine/lib/bullet/examples/TinyAudio/b3SwapUtils.h
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#ifndef B3_SWAP_UTILS_H
|
||||
#define B3_SWAP_UTILS_H
|
||||
|
||||
inline void b3Swap16(unsigned char *ptr)
|
||||
{
|
||||
unsigned char val;
|
||||
|
||||
// Swap 1st and 2nd bytes
|
||||
val = *(ptr);
|
||||
*(ptr) = *(ptr + 1);
|
||||
*(ptr + 1) = val;
|
||||
}
|
||||
|
||||
inline void b3Swap32(unsigned char *ptr)
|
||||
{
|
||||
unsigned char val;
|
||||
|
||||
// Swap 1st and 4th bytes
|
||||
val = *(ptr);
|
||||
*(ptr) = *(ptr + 3);
|
||||
*(ptr + 3) = val;
|
||||
|
||||
//Swap 2nd and 3rd bytes
|
||||
ptr += 1;
|
||||
val = *(ptr);
|
||||
*(ptr) = *(ptr + 1);
|
||||
*(ptr + 1) = val;
|
||||
}
|
||||
|
||||
inline void b3Swap64(unsigned char *ptr)
|
||||
{
|
||||
unsigned char val;
|
||||
|
||||
// Swap 1st and 8th bytes
|
||||
val = *(ptr);
|
||||
*(ptr) = *(ptr + 7);
|
||||
*(ptr + 7) = val;
|
||||
|
||||
// Swap 2nd and 7th bytes
|
||||
ptr += 1;
|
||||
val = *(ptr);
|
||||
*(ptr) = *(ptr + 5);
|
||||
*(ptr + 5) = val;
|
||||
|
||||
// Swap 3rd and 6th bytes
|
||||
ptr += 1;
|
||||
val = *(ptr);
|
||||
*(ptr) = *(ptr + 3);
|
||||
*(ptr + 3) = val;
|
||||
|
||||
// Swap 4th and 5th bytes
|
||||
ptr += 1;
|
||||
val = *(ptr);
|
||||
*(ptr) = *(ptr + 1);
|
||||
*(ptr + 1) = val;
|
||||
}
|
||||
|
||||
#endif //B3_SWAP_UTILS_H
|
||||
276
Engine/lib/bullet/examples/TinyAudio/b3WriteWavFile.cpp
Normal file
276
Engine/lib/bullet/examples/TinyAudio/b3WriteWavFile.cpp
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
// b3WriteWavFile is copied from Stk::FileWvOut/FileWrite
|
||||
// See also https://github.com/thestk/stk
|
||||
// by Perry R. Cook and Gary P. Scavone, 1995--2014.
|
||||
|
||||
#include "b3WriteWavFile.h"
|
||||
#include "Bullet3Common/b3AlignedObjectArray.h"
|
||||
#include "b3SwapUtils.h"
|
||||
|
||||
#define B3_FLOAT32 32
|
||||
#define B3_FLOAT64 64
|
||||
|
||||
// WAV header structure. See
|
||||
// http://www-mmsp.ece.mcgill.ca/documents/audioformats/WAVE/Docs/rfc2361.txt
|
||||
// for information regarding format codes.
|
||||
struct b3WaveHeader
|
||||
{
|
||||
char riff[4]; // "RIFF"
|
||||
int fileSize; // in bytes
|
||||
char wave[4]; // "WAVE"
|
||||
char fmt[4]; // "fmt "
|
||||
int chunkSize; // in bytes (16 for PCM)
|
||||
union {
|
||||
signed short formatCode; // 1=PCM, 2=ADPCM, 3=IEEE float, 6=A-Law, 7=Mu-Law
|
||||
unsigned short uformatCode;
|
||||
};
|
||||
signed short nChannels; // 1=mono, 2=stereo
|
||||
int sampleRate;
|
||||
int bytesPerSecond;
|
||||
signed short bytesPerSample; // 2=16-bit mono, 4=16-bit stereo
|
||||
signed short bitsPerSample;
|
||||
signed short cbSize; // size of extension
|
||||
signed short validBits; // valid bits per sample
|
||||
int channelMask; // speaker position mask
|
||||
char subformat[16]; // format code and GUID
|
||||
char fact[4]; // "fact"
|
||||
int factSize; // fact chunk size
|
||||
int frames; // sample frames
|
||||
};
|
||||
|
||||
struct b3WriteWavFileInternalData
|
||||
{
|
||||
FILE *m_file;
|
||||
int m_numChannels;
|
||||
int m_sampleRate;
|
||||
int m_dataType; // single precision 32bit float, 64bit double
|
||||
bool m_byteswap;
|
||||
int m_frameCounter;
|
||||
int m_bufferIndex;
|
||||
int m_bufferSize;
|
||||
bool m_clipped;
|
||||
bool m_isMachineLittleEndian;
|
||||
|
||||
b3AlignedObjectArray<float> m_floatBuffer;
|
||||
b3AlignedObjectArray<double> m_doubleBuffer;
|
||||
|
||||
b3WriteWavFileInternalData()
|
||||
: m_file(0),
|
||||
m_numChannels(0),
|
||||
m_dataType(B3_FLOAT32),
|
||||
m_byteswap(false),
|
||||
m_frameCounter(0),
|
||||
m_bufferIndex(0),
|
||||
m_bufferSize(1024),
|
||||
m_clipped(false)
|
||||
{
|
||||
m_floatBuffer.reserve(m_bufferSize);
|
||||
m_doubleBuffer.reserve(m_bufferSize);
|
||||
m_isMachineLittleEndian = b3MachineIsLittleEndian();
|
||||
}
|
||||
};
|
||||
|
||||
b3WriteWavFile::b3WriteWavFile()
|
||||
{
|
||||
m_data = new b3WriteWavFileInternalData();
|
||||
}
|
||||
|
||||
b3WriteWavFile::~b3WriteWavFile()
|
||||
{
|
||||
closeWavFile();
|
||||
delete m_data;
|
||||
}
|
||||
|
||||
bool b3WriteWavFile::setWavFile(std::string fileName, int sampleRate, int numChannels, bool useDoublePrecision)
|
||||
{
|
||||
m_data->m_numChannels = numChannels;
|
||||
m_data->m_sampleRate = sampleRate;
|
||||
if (useDoublePrecision)
|
||||
{
|
||||
m_data->m_dataType = B3_FLOAT64;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_data->m_dataType = B3_FLOAT32;
|
||||
}
|
||||
|
||||
if (fileName.find(".wav") == std::string::npos)
|
||||
fileName += ".wav";
|
||||
|
||||
m_data->m_file = fopen(fileName.c_str(), "wb");
|
||||
if (!m_data->m_file)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
struct b3WaveHeader hdr = {{'R', 'I', 'F', 'F'}, 44, {'W', 'A', 'V', 'E'}, {'f', 'm', 't', ' '}, 16, 1, 1, sampleRate, 0, 2, 16, 0, 0, 0, {'\x01', '\x00', '\x00', '\x00', '\x00', '\x00', '\x10', '\x00', '\x80', '\x00', '\x00', '\xAA', '\x00', '\x38', '\x9B', '\x71'}, {'f', 'a', 'c', 't'}, 4, 0};
|
||||
hdr.nChannels = (signed short)m_data->m_numChannels;
|
||||
|
||||
if (m_data->m_dataType == B3_FLOAT32)
|
||||
{
|
||||
hdr.formatCode = 3;
|
||||
hdr.bitsPerSample = 32;
|
||||
}
|
||||
else if (m_data->m_dataType == B3_FLOAT64)
|
||||
{
|
||||
hdr.formatCode = 3;
|
||||
hdr.bitsPerSample = 64;
|
||||
}
|
||||
|
||||
hdr.bytesPerSample = (signed short)(m_data->m_numChannels * hdr.bitsPerSample / 8);
|
||||
hdr.bytesPerSecond = (int)(hdr.sampleRate * hdr.bytesPerSample);
|
||||
|
||||
unsigned int bytesToWrite = 36;
|
||||
if (m_data->m_numChannels > 2 || hdr.bitsPerSample > 16)
|
||||
{ // use extensible format
|
||||
bytesToWrite = 72;
|
||||
hdr.chunkSize += 24;
|
||||
hdr.uformatCode = 0xFFFE;
|
||||
hdr.cbSize = 22;
|
||||
hdr.validBits = hdr.bitsPerSample;
|
||||
signed short *subFormat = (signed short *)&hdr.subformat[0];
|
||||
if (m_data->m_dataType == B3_FLOAT32 || m_data->m_dataType == B3_FLOAT64)
|
||||
*subFormat = 3;
|
||||
else
|
||||
*subFormat = 1;
|
||||
}
|
||||
|
||||
m_data->m_byteswap = false;
|
||||
if (!m_data->m_isMachineLittleEndian)
|
||||
{
|
||||
m_data->m_byteswap = true;
|
||||
b3Swap32((unsigned char *)&hdr.chunkSize);
|
||||
b3Swap16((unsigned char *)&hdr.formatCode);
|
||||
b3Swap16((unsigned char *)&hdr.nChannels);
|
||||
b3Swap32((unsigned char *)&hdr.sampleRate);
|
||||
b3Swap32((unsigned char *)&hdr.bytesPerSecond);
|
||||
b3Swap16((unsigned char *)&hdr.bytesPerSample);
|
||||
b3Swap16((unsigned char *)&hdr.bitsPerSample);
|
||||
b3Swap16((unsigned char *)&hdr.cbSize);
|
||||
b3Swap16((unsigned char *)&hdr.validBits);
|
||||
b3Swap16((unsigned char *)&hdr.subformat[0]);
|
||||
b3Swap32((unsigned char *)&hdr.factSize);
|
||||
}
|
||||
|
||||
char data[4] = {'d', 'a', 't', 'a'};
|
||||
int dataSize = 0;
|
||||
if (fwrite(&hdr, 1, bytesToWrite, m_data->m_file) != bytesToWrite)
|
||||
return false;
|
||||
if (fwrite(&data, 4, 1, m_data->m_file) != 1)
|
||||
return false;
|
||||
if (fwrite(&dataSize, 4, 1, m_data->m_file) != 1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void b3WriteWavFile::closeWavFile()
|
||||
{
|
||||
if (m_data->m_file == 0)
|
||||
return;
|
||||
|
||||
flushData(1);
|
||||
|
||||
int bytesPerSample = 1;
|
||||
if (m_data->m_dataType == B3_FLOAT32)
|
||||
bytesPerSample = 4;
|
||||
else if (m_data->m_dataType == B3_FLOAT64)
|
||||
bytesPerSample = 8;
|
||||
|
||||
bool useExtensible = false;
|
||||
int dataLocation = 40;
|
||||
if (bytesPerSample > 2 || m_data->m_numChannels > 2)
|
||||
{
|
||||
useExtensible = true;
|
||||
dataLocation = 76;
|
||||
}
|
||||
|
||||
int bytes = (int)(m_data->m_frameCounter * m_data->m_numChannels * bytesPerSample);
|
||||
if (bytes % 2)
|
||||
{ // pad extra byte if odd
|
||||
signed char sample = 0;
|
||||
fwrite(&sample, 1, 1, m_data->m_file);
|
||||
}
|
||||
#ifndef __LITTLE_ENDIAN__
|
||||
b3Swap32((unsigned char *)&bytes);
|
||||
#endif
|
||||
fseek(m_data->m_file, dataLocation, SEEK_SET); // jump to data length
|
||||
fwrite(&bytes, 4, 1, m_data->m_file);
|
||||
|
||||
bytes = (int)(m_data->m_frameCounter * m_data->m_numChannels * bytesPerSample + 44);
|
||||
if (useExtensible) bytes += 36;
|
||||
#ifndef __LITTLE_ENDIAN__
|
||||
b3Swap32((unsigned char *)&bytes);
|
||||
#endif
|
||||
fseek(m_data->m_file, 4, SEEK_SET); // jump to file size
|
||||
fwrite(&bytes, 4, 1, m_data->m_file);
|
||||
|
||||
if (useExtensible)
|
||||
{ // fill in the "fact" chunk frames value
|
||||
bytes = (int)m_data->m_frameCounter;
|
||||
#ifndef __LITTLE_ENDIAN__
|
||||
b3Swap32((unsigned char *)&bytes);
|
||||
#endif
|
||||
fseek(m_data->m_file, 68, SEEK_SET);
|
||||
fwrite(&bytes, 4, 1, m_data->m_file);
|
||||
}
|
||||
|
||||
fclose(m_data->m_file);
|
||||
m_data->m_file = 0;
|
||||
}
|
||||
|
||||
void b3WriteWavFile::tick(double *frames, int numFrames)
|
||||
{
|
||||
int iFrames = 0;
|
||||
int j, nChannels = m_data->m_numChannels;
|
||||
|
||||
for (int i = 0; i < numFrames; i++)
|
||||
{
|
||||
for (j = 0; j < nChannels; j++)
|
||||
{
|
||||
double sample = frames[iFrames++];
|
||||
if (sample < -1.)
|
||||
{
|
||||
sample = -1.;
|
||||
m_data->m_clipped = true;
|
||||
}
|
||||
if (sample > 1)
|
||||
{
|
||||
sample = 1.;
|
||||
m_data->m_clipped = true;
|
||||
}
|
||||
|
||||
if (m_data->m_dataType == B3_FLOAT32)
|
||||
{
|
||||
m_data->m_floatBuffer.push_back((float)sample);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_data->m_doubleBuffer.push_back(sample);
|
||||
}
|
||||
|
||||
flushData(m_data->m_bufferSize);
|
||||
}
|
||||
|
||||
m_data->m_frameCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
void b3WriteWavFile::flushData(int bufferSize)
|
||||
{
|
||||
if (m_data->m_dataType == B3_FLOAT32)
|
||||
{
|
||||
if (m_data->m_floatBuffer.size() >= bufferSize)
|
||||
{
|
||||
fwrite(&m_data->m_floatBuffer[0], sizeof(float), m_data->m_floatBuffer.size(), m_data->m_file);
|
||||
m_data->m_floatBuffer.resize(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_data->m_doubleBuffer.size() >= bufferSize)
|
||||
{
|
||||
fwrite(&m_data->m_doubleBuffer[0], sizeof(double), m_data->m_doubleBuffer.size(), m_data->m_file);
|
||||
m_data->m_doubleBuffer.resize(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
29
Engine/lib/bullet/examples/TinyAudio/b3WriteWavFile.h
Normal file
29
Engine/lib/bullet/examples/TinyAudio/b3WriteWavFile.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef B3_WRITE_WAV_FILE_H
|
||||
#define B3_WRITE_WAV_FILE_H
|
||||
|
||||
// b3WriteWavFile is copied from Stk::FileWvOut/FileWrite
|
||||
// See also https://github.com/thestk/stk
|
||||
// by Perry R. Cook and Gary P. Scavone, 1995--2014.
|
||||
#include <string>
|
||||
|
||||
class b3WriteWavFile
|
||||
{
|
||||
void incrementFrame(void);
|
||||
void flush();
|
||||
|
||||
struct b3WriteWavFileInternalData* m_data;
|
||||
|
||||
void flushData(int bufferSize);
|
||||
|
||||
public:
|
||||
b3WriteWavFile();
|
||||
virtual ~b3WriteWavFile();
|
||||
|
||||
bool setWavFile(std::string fileName, int sampleRate, int numChannels, bool useDoublePrecision = true);
|
||||
|
||||
void closeWavFile();
|
||||
|
||||
void tick(double* values, int numValues);
|
||||
};
|
||||
|
||||
#endif //B3_WRITE_WAV_FILE_H
|
||||
41
Engine/lib/bullet/examples/TinyAudio/premake4.lua
Normal file
41
Engine/lib/bullet/examples/TinyAudio/premake4.lua
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
project "App_TinyAudioExample"
|
||||
|
||||
language "C++"
|
||||
|
||||
kind "ConsoleApp"
|
||||
|
||||
includedirs {
|
||||
".",
|
||||
"../../src",
|
||||
}
|
||||
|
||||
defines {"B3_USE_STANDALONE_EXAMPLE", "__STK_REALTIME__"}
|
||||
files {
|
||||
"**.cpp",
|
||||
"**.h",
|
||||
"../StandaloneMain/main_console_single_example.cpp",
|
||||
"../Utils/b3ResourcePath.cpp"
|
||||
}
|
||||
|
||||
links {"Bullet3Common"}
|
||||
|
||||
if os.is("Windows") then
|
||||
links {"winmm","Wsock32","dsound"}
|
||||
defines {"WIN32","__WINDOWS_MM__","__WINDOWS_DS__"}
|
||||
end
|
||||
|
||||
|
||||
if os.is("Linux") then initX11()
|
||||
defines {"__OS_LINUX__","__LINUX_ALSA__"}
|
||||
links {"asound","pthread"}
|
||||
end
|
||||
|
||||
|
||||
if os.is("MacOSX") then
|
||||
links{"Cocoa.framework"}
|
||||
links{"CoreAudio.framework", "coreMIDI.framework", "Cocoa.framework"}
|
||||
|
||||
defines {"__OS_MACOSX__", "__MACOSX_CORE__"}
|
||||
end
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue