mirror of
https://github.com/bulletphysics/bullet3
synced 2024-12-14 05:40:05 +00:00
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:
parent
8cc1f51862
commit
943dd16e78
@ -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"),
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
@ -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( ¶meters, 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;
|
||||
}
|
||||
|
||||
|
102
examples/TinyAudio/b3ADSR.cpp
Normal file
102
examples/TinyAudio/b3ADSR.cpp
Normal 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");
|
||||
|
||||
}
|
26
examples/TinyAudio/b3ADSR.h
Normal file
26
examples/TinyAudio/b3ADSR.h
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
476
examples/TinyAudio/b3ReadWavFile.cpp
Normal file
476
examples/TinyAudio/b3ReadWavFile.cpp
Normal 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);
|
||||
}
|
57
examples/TinyAudio/b3ReadWavFile.h
Normal file
57
examples/TinyAudio/b3ReadWavFile.h
Normal 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
|
87
examples/TinyAudio/b3SoundEngine.cpp
Normal file
87
examples/TinyAudio/b3SoundEngine.cpp
Normal 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( ¶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();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
29
examples/TinyAudio/b3SoundEngine.h
Normal file
29
examples/TinyAudio/b3SoundEngine.h
Normal 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
|
@ -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;
|
||||
}
|
@ -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
|
||||
|
59
examples/TinyAudio/b3SwapUtils.h
Normal file
59
examples/TinyAudio/b3SwapUtils.h
Normal 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
|
276
examples/TinyAudio/b3WriteWavFile.cpp
Normal file
276
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) 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);
|
||||
}
|
||||
}
|
||||
}
|
32
examples/TinyAudio/b3WriteWavFile.h
Normal file
32
examples/TinyAudio/b3WriteWavFile.h
Normal 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
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user