disable keyboard repeat on Windows

add some TinyAudio classes to play wav, mostly from Stk (http://github.com/thestk/stk)
This commit is contained in:
Erwin Coumans 2017-04-26 21:31:01 -07:00
parent 8cc1f51862
commit 943dd16e78
18 changed files with 1452 additions and 57 deletions

View File

@ -52,6 +52,10 @@
#include "../RoboticsLearning/GripperGraspExample.h"
#include "../InverseKinematics/InverseKinematicsExample.h"
#ifdef B3_ENABLE_TINY_AUDIO
#include "../TinyAudio/TinyAudioExample.h"
#endif //B3_ENABLE_TINY_AUDIO
#ifdef ENABLE_LUA
#include "../LuaDemo/LuaPhysicsSetup.h"
#endif
@ -100,7 +104,6 @@ struct ExampleEntry
static ExampleEntry gDefaultExamples[]=
{
ExampleEntry(0,"API"),
ExampleEntry(1,"Basic Example","Create some rigid bodies using box collision shapes. This is a good example to familiarize with the basic initialization of Bullet. The Basic Example can also be compiled without graphical user interface, as a console application. Press W for wireframe, A to show AABBs, I to suspend/restart physics simulation. Press D to toggle auto-deactivation of the simulation. ", BasicExampleCreateFunc),
@ -307,7 +310,11 @@ static ExampleEntry gDefaultExamples[]=
ExampleEntry(1,"TinyRenderer", "Very small software renderer.", TinyRendererCreateFunc),
ExampleEntry(1,"Dynamic Texture", "Dynamic updated textured applied to a cube.", DynamicTexturedCubeDemoCreateFunc),
#ifdef B3_ENABLE_TINY_AUDIO
ExampleEntry(0,"Audio"),
ExampleEntry(1,"Simple Audio","Play some sound", TinyAudioExampleCreateFunc),
#endif
//Extended Tutorials Added by Mobeen
ExampleEntry(0,"Extended Tutorials"),

View File

@ -35,6 +35,28 @@ project "App_BulletExampleBrowser"
}
end
if _OPTIONS["audio"] then
files {"../TinyAudio/*.cpp"}
defines {"B3_ENABLE_TINY_AUDIO"}
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__"}
end
end
if _OPTIONS["lua"] then
includedirs{"../ThirdPartyLibs/lua-5.2.3/src"}
links {"lua-5.2.3"}
@ -184,6 +206,8 @@ project "BulletExampleBrowserLib"
files {"../LuaDemo/LuaPhysicsSetup.cpp"}
end
initOpenGL()
initGlew()

View File

@ -258,7 +258,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
break;
};
}
if (keycode>=0 && sData && sData->m_keyboardCallback)// && ((HIWORD(lParam) & KF_REPEAT) == 0))
if (keycode>=0 && sData && sData->m_keyboardCallback && ((HIWORD(lParam) & KF_REPEAT) == 0))
{
int state = 1;
(*sData->m_keyboardCallback)(keycode,state);

View File

@ -2,21 +2,27 @@
#include "../CommonInterfaces/CommonExampleInterface.h"
#include "../CommonInterfaces/CommonGUIHelperInterface.h"
#include "RtAudio.h"
#include "b3AudioListener.h"
#include "b3SoundEngine.h"
#include "b3SoundSource.h"
class TinyAudioExample : public CommonExampleInterface
{
b3AudioListener m_listener;
b3SoundSource m_soundA;
RtAudio m_dac;
GUIHelperInterface* m_guiHelper;
int m_soundIndexA;
b3SoundEngine m_soundEngine;
b3SoundSource* m_soundSource;
public:
TinyAudioExample(struct GUIHelperInterface* helper)
:m_guiHelper(helper)
:m_guiHelper(helper),
m_soundSource(0)
{
}
@ -26,35 +32,21 @@ public:
virtual void initPhysics()
{
m_soundIndexA = m_listener.addSoundSource(&m_soundA);
RtAudioFormat format = ( sizeof(double) == 8 ) ? RTAUDIO_FLOAT64 : RTAUDIO_FLOAT32;
RtAudio::StreamParameters parameters;
parameters.deviceId = 1;// dac.getDefaultOutputDevice();
parameters.nChannels = 2;
m_soundEngine.init();
int sampleRate = m_soundEngine.getSampleRate();
// 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 = 512;
unsigned int bufferFrames = RT_BUFFER_SIZE;
int sampleRate = m_listener.getSampleRate();
m_dac.openStream( &parameters, NULL, format, (unsigned int)sampleRate, &bufferFrames, &b3AudioListener::tick,
(void *)m_listener.getTickData());
// Install an interrupt handler function.
// (void) signal( SIGINT, finish );
m_dac.startStream();
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);
}
virtual void exitPhysics()
{
m_dac.closeStream();
m_listener.removeSoundSource(m_soundIndexA);
m_soundEngine.exit();
}
virtual void renderScene()
@ -78,6 +70,37 @@ public:
}
virtual bool keyboardCallback(int key, int state)
{
if (key=='v' || key=='b')
{
if (state)
{
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();
}
else
{
m_soundSource->stopSound();
}
}
return false;
}

View File

@ -0,0 +1,102 @@
#include "b3ADSR.h"
//ADSR mostly copied/reimplemented from Stk, see
//http://github.com/thestk/stk
//! ADSR envelope states.
#include <stdio.h>
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.005;
m_sustainLevel = 0.5;
m_state = ADSR_IDLE;
}
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;
printf("ADSR_ATTACK->ADSR_DECAY\n");
}
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;
printf("ADSR_DECAY->ADSR_SUSTAIN\n");
}
}
else
{
m_value += m_decayRate; // attack target < sustain level
if (m_value >= m_sustainLevel)
{
m_value = m_sustainLevel;
m_state = ADSR_SUSTAIN;
printf("ADSR_DECAY->ADSR_SUSTAIN\n");
}
}
break;
case ADSR_RELEASE:
m_value -= m_releaseRate;
if (m_value <= 0.0)
{
m_value = 0.0;
m_state = ADSR_IDLE;
printf("ADSR_RELEASE->ADSR_IDLE\n");
}
}
return m_value;
}
bool b3ADSR::isIdle() const
{
return true;
}
void b3ADSR::keyOn()
{
if (m_target <= 0.0)
m_target = 1.0;
m_state = ADSR_ATTACK;
printf("keyOn::ADSR_ATTACK\n");
}
void b3ADSR::keyOff()
{
m_target = 0.0;
m_state = ADSR_RELEASE;
printf("keyOff::ADSR_RELEASE\n");
}

View File

@ -0,0 +1,26 @@
#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;
public:
b3ADSR();
virtual ~b3ADSR();
double tick();
bool isIdle() const;
void keyOn();
void keyOff();
};
#endif //B3_ADSR_H

View File

@ -1,5 +1,7 @@
#include "b3AudioListener.h"
#include "b3SoundSource.h"
#include "Bullet3Common/b3Logging.h"
#include "b3WriteWavFile.h"
template <class T>
inline const T& MyMin(const T& a, const T& b)
@ -7,6 +9,7 @@ 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
{
@ -15,10 +18,14 @@ struct b3AudioListenerInternalData
b3SoundSource* m_soundSources[MAX_SOUND_SOURCES];
b3WriteWavFile m_wavOut2;
b3AudioListenerInternalData()
:m_numControlTicks(64),
m_sampleRate(48000)
m_sampleRate(B3_SAMPLE_RATE)
{
for (int i=0;i<MAX_SOUND_SOURCES;i++)
{
m_soundSources[i] = 0;
@ -29,10 +36,14 @@ struct b3AudioListenerInternalData
b3AudioListener::b3AudioListener()
{
m_data = new b3AudioListenerInternalData();
m_data->m_wavOut2.setWavFile("bulletAudio2.wav",B3_SAMPLE_RATE,2,false);
}
b3AudioListener::~b3AudioListener()
{
m_data->m_wavOut2.closeWavFile();
delete m_data;
}
@ -52,11 +63,14 @@ int b3AudioListener::addSoundSource(b3SoundSource* source)
return soundIndex;
}
void b3AudioListener::removeSoundSource(int soundSourceIndex)
void b3AudioListener::removeSoundSource(b3SoundSource* source)
{
if (soundSourceIndex >=0 && soundSourceIndex<MAX_SOUND_SOURCES)
for (int i=0;i<MAX_SOUND_SOURCES;i++)
{
m_data->m_soundSources[soundSourceIndex] = 0;
if (m_data->m_soundSources[i]==source)
{
m_data->m_soundSources[i] = 0;
}
}
}
@ -75,16 +89,24 @@ 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)
{
@ -103,6 +125,9 @@ int b3AudioListener::tick(void *outputBuffer,void *inputBuffer1,unsigned int nBu
{
if (data->m_soundSources[i])
{
tempOuts[0] = 0;
tempOuts[1] = 0;
if (data->m_soundSources[i]->computeSamples(tempOuts,1, data->m_sampleRate))
{
numActiveSources++;
@ -116,17 +141,25 @@ int b3AudioListener::tick(void *outputBuffer,void *inputBuffer1,unsigned int nBu
//simple mixer
if (numActiveSources)
{
outs[0] *= 1./numActiveSources;
outs[1] *= 1./numActiveSources;
outs[0] *= .3/numActiveSources;
outs[1] *= .3/numActiveSources;
}
*samples++ = outs[0];
*samples++ = outs[1];
numSamples++;
}
nTicks -= counter;
}
if(nTicks == 0)
break;
}
//logging to wav file
if (numSamples)
{
data->m_wavOut2.tick( (double *)outputBuffer,numSamples);
}
return 0;
}

View File

@ -16,12 +16,13 @@ public:
double streamTime, unsigned int status, void *dataPointer);
int addSoundSource(b3SoundSource* source);
void removeSoundSource(int soundSourceIndex);
void removeSoundSource(b3SoundSource* source);
b3AudioListenerInternalData* getTickData();
const b3AudioListenerInternalData* getTickData() const;
double getSampleRate() const;
void setSampleRate(double sampleRate);
};

View File

@ -0,0 +1,476 @@
//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()
{
m_machineIsLittleEndian = b3MachineIsLittleEndian();
}
b3ReadWavFile::~b3ReadWavFile()
{
}
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) goto error;
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) goto error;
while (strncmp(id, "fmt ", 4))
{
if (fread(&chunkSize, 4, 1, fd_) != 1) goto error;
if (!m_machineIsLittleEndian)
{
b3Swap32((unsigned char *)&chunkSize);
}
if (fseek(fd_, chunkSize, SEEK_CUR) == -1) goto error;
if (fread(&id, 4, 1, fd_) != 1) goto error;
}
// Check that the data is not compressed.
unsigned short format_tag;
if (fread(&chunkSize, 4, 1, fd_) != 1) goto error; // Read fmt chunk size.
if (fread(&format_tag, 2, 1, fd_) != 1) goto error;
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) goto error;
unsigned short extSize;
if (fread(&extSize, 2, 1, fd_) != 1) goto error;
if (!m_machineIsLittleEndian)
{
b3Swap16((unsigned char *)&extSize);
}
if (extSize == 0) goto error;
if (fseek(fd_, 6, SEEK_CUR) == -1) goto error;
if (fread(&format_tag, 2, 1, fd_) != 1) goto error;
if (!m_machineIsLittleEndian)
{
b3Swap16((unsigned char *)&format_tag);
}
if (fseek(fd_, dataOffset_, SEEK_SET) == -1) goto error;
}
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) goto error;
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) goto error;
if (!m_machineIsLittleEndian)
{
b3Swap32((unsigned char *)&srate);
}
fileDataRate_ = (double)srate;
// Determine the data type.
dataType_ = 0;
if (fseek(fd_, 6, SEEK_CUR) == -1) goto error; // Locate bits_per_sample info.
if (fread(&temp, 2, 1, fd_) != 1) goto error;
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) goto error;
// Find "data" chunk ... it must come after the "fmt" chunk.
if (fread(&id, 4, 1, fd_) != 1) goto error;
while (strncmp(id, "data", 4))
{
if (fread(&chunkSize, 4, 1, fd_) != 1) goto error;
if (!m_machineIsLittleEndian)
{
b3Swap32((unsigned char *)&chunkSize);
}
chunkSize += chunkSize % 2; // chunk sizes must be even
if (fseek(fd_, chunkSize, SEEK_CUR) == -1) goto error;
if (fread(&id, 4, 1, fd_) != 1) goto error;
}
// Get length of data from the header.
int bytes;
if (fread(&bytes, 4, 1, fd_) != 1) goto error;
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;
error:
if (fd_)
fclose(fd_);
// oStream_ << "FileRead: error reading WAV file (" << fileName << ").";
return false;
}
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);
}

View File

@ -0,0 +1,57 @@
#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_ = 0;
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

View File

@ -0,0 +1,87 @@
#include "b3SoundEngine.h"
#include "RtAudio.h"
#include "b3AudioListener.h"
#include "b3SoundSource.h"
#include "Bullet3Common/b3AlignedObjectArray.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;
b3AlignedObjectArray<b3SoundSource*> m_soundSources;
};
b3SoundEngine::b3SoundEngine()
{
m_data = new b3SoundEngineInternalData();
}
b3SoundEngine::~b3SoundEngine()
{
exit();
delete m_data;
}
void b3SoundEngine::init()
{
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( &parameters, NULL, format, (unsigned int)sampleRate, &bufferFrames, &b3AudioListener::tick,
(void *)m_data->m_listener.getTickData());
m_data->m_dac.startStream();
}
void b3SoundEngine::exit()
{
m_data->m_dac.closeStream();
for (int i=0;i<m_data->m_soundSources.size();i++)
{
m_data->m_listener.removeSoundSource(m_data->m_soundSources[i]);
delete m_data->m_soundSources[i];
}
m_data->m_soundSources.clear();
}
void b3SoundEngine::addSoundSource(b3SoundSource* source)
{
m_data->m_soundSources.push_back(source);
m_data->m_listener.addSoundSource(source);
}
void b3SoundEngine::removeSoundSource(b3SoundSource* source)
{
m_data->m_soundSources.remove(source);
}
int b3SoundEngine::loadWavFile(const char* fileName)
{
return 0;
}
double b3SoundEngine::getSampleRate() const
{
return m_data->m_listener.getSampleRate();
}

View File

@ -0,0 +1,29 @@
#ifndef B3_SOUND_ENGINE_H
#define B3_SOUND_ENGINE_H
#include "Bullet3Common/b3Scalar.h"
class b3SoundEngine
{
struct b3SoundEngineInternalData* m_data;
public:
b3SoundEngine();
virtual ~b3SoundEngine();
void init();
void exit();
//int createListener();
void addSoundSource(class b3SoundSource* source);
void removeSoundSource(class b3SoundSource* source);
int loadWavFile(const char* fileName);
double getSampleRate() const;
};
#endif //B3_SOUND_ENGINE_H

View File

@ -3,37 +3,66 @@
#define MY2PI (2.*3.14159265)
#include <math.h>
#include "../Utils/b3ResourcePath.h"
#include "Bullet3Common/b3FileUtils.h"
#include "b3ReadWavFile.h"
#include "b3ADSR.h"
struct b3SoundOscillator
{
int m_type;
double m_frequency;
double m_amplitude;
double m_phase;
double m_frequency;
b3WavTicker m_wavTicker;
double sampleWaveForm(double sampleRate)
double sampleSineWaveForm(double sampleRate)
{
while (m_phase >= MY2PI)
m_phase -= MY2PI;
double z = sinf(m_phase);
double z = sinf(m_phase);
double sample = m_amplitude*z;
m_phase += MY2PI * (1./sampleRate) * m_frequency;
return sample;
}
b3SoundOscillator()
:m_phase(0),
m_amplitude(0.8),
m_frequency(442.)
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_oscillator;
b3SoundOscillator m_oscillators[MAX_OSCILLATORS];
b3ADSR m_envelope;
b3ReadWavFile m_wavFile;
};
b3SoundSource::b3SoundSource()
@ -48,19 +77,138 @@ b3SoundSource::~b3SoundSource()
bool b3SoundSource::computeSamples(double* sampleBuffer, int numSamples, double sampleRate)
{
double* outputSamples = sampleBuffer;
for (int i=0;i<numSamples;i++)
{
double sample = m_data->m_oscillator.sampleWaveForm(sampleRate);
double sampleLeft = sample;
double sampleRight = sample;
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);
}
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];
*outputSamples++ = sampleLeft;
*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 true;
// 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;
}
void b3SoundSource::startSound()
{
if (m_data->m_envelope.isIdle())
{
for (int osc=0;osc<MAX_OSCILLATORS;osc++)
{
static int reset = 0;
printf("reset %d!\n",reset++);
if (m_data->m_oscillators[osc].m_wavTicker.finished_)
{
m_data->m_oscillators[osc].reset();
//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_wavFile.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();
}
void b3SoundSource::stopSound()
{
m_data->m_envelope.keyOff();
}
bool b3SoundSource::setWavFile(int oscillatorIndex, const char* fileName, 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);
// 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;
}

View File

@ -11,6 +11,18 @@ public:
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);
bool setWavFile(int oscillatorIndex, const char* fileName, int sampleRate);
void startSound();
void stopSound();
};
#endif //B3_SOUND_SOURCE_H

View File

@ -0,0 +1,59 @@
#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

View 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) goto error;
if (fwrite(&data, 4, 1, m_data->m_file) != 1) goto error;
if (fwrite(&dataSize, 4, 1, m_data->m_file) != 1) goto error;
return true;
error:
return false;
}
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);
}
}
}

View File

@ -0,0 +1,32 @@
#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 );
void tick( float* values, int numValues );
};
#endif //B3_WRITE_WAV_FILE_H

View File

@ -15,16 +15,19 @@
"**.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__","__LITTLE_ENDIAN__","__WINDOWS_DS__"}
defines {"WIN32","__WINDOWS_MM__","__WINDOWS_DS__"}
end
if os.is("Linux") then initX11()
defines {"__OS_LINUX__","__LINUX_ALSA__","__LITTLE_ENDIAN__"}
defines {"__OS_LINUX__","__LINUX_ALSA__"}
links {"asound","pthread"}
end
@ -33,6 +36,6 @@ if os.is("MacOSX") then
links{"Cocoa.framework"}
links{"CoreAudio.framework", "coreMIDI.framework", "Cocoa.framework"}
defines {"__OS_MACOSX__","__LITTLE_ENDIAN__"}
defines {"__OS_MACOSX__"}
end