deargui-vpl/ref/virtools/Samples/Behaviors/Sound/behaviors src/GetSpectrum.cpp

404 lines
8.8 KiB
C++

/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
//
// GetSpectrum
//
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
#include "ckall.h"
#ifdef PSP
#define __max(a,b) (((a) > (b)) ? (a) : (b))
#define __min(a,b) (((a) < (b)) ? (a) : (b))
#endif
short FFT(short int dir,long m,float *x,float *y)
{
long n,i,i1,j,k,i2,l,l1,l2;
float c1,c2,tx,ty,t1,t2,u1,u2,z;
/* Calculate the number of points */
n = 1;
for (i=0;i<m;i++)
n *= 2;
/* Do the bit reversal */
i2 = n >> 1;
j = 0;
for (i=0;i<n-1;i++) {
if (i < j) {
tx = x[i];
ty = y[i];
x[i] = x[j];
y[i] = y[j];
x[j] = tx;
y[j] = ty;
}
k = i2;
while (k <= j) {
j -= k;
k >>= 1;
}
j += k;
}
/* Compute the FFT */
c1 = -1.0;
c2 = 0.0;
l2 = 1;
for (l=0;l<m;l++) {
l1 = l2;
l2 <<= 1;
u1 = 1.0;
u2 = 0.0;
for (j=0;j<l1;j++) {
for (i=j;i<n;i+=l2) {
i1 = i + l1;
t1 = u1 * x[i1] - u2 * y[i1];
t2 = u1 * y[i1] + u2 * x[i1];
x[i1] = x[i] - t1;
y[i1] = y[i] - t2;
x[i] += t1;
y[i] += t2;
}
z = u1 * c1 - u2 * c2;
u2 = u1 * c2 + u2 * c1;
u1 = z;
}
c2 = sqrtf((1.0f - c1) / 2.0f);
if (dir == 1)
c2 = -c2;
c1 = sqrtf((1.0f + c1) / 2.0f);
}
/* Scaling for forward transform */
if (dir == 1) {
for (i=0;i<n;i++) {
x[i] /= n;
y[i] /= n;
}
}
return(TRUE);
}
CKERROR CreateGetSpectrumBehaviorProto(CKBehaviorPrototype **pproto);
int GetSpectrum(const CKBehaviorContext& behcontext);
CKObjectDeclaration *FillBehaviorGetSpectrumDecl()
{
CKObjectDeclaration *od = CreateCKObjectDeclaration("Get Sound Spectrum");
od->SetDescription("Performs sound spectrum analysis.");
/* rem:
<SPAN CLASS=in>In: </SPAN>triggers the process<BR>
<SPAN CLASS=out>Out: </SPAN>is activated when the process is completed.<BR>
<BR>
<SPAN CLASS=pin>Start Frequency: </SPAN> Start of the requested frequency range.<BR>
<SPAN CLASS=pin>End Frequency: </SPAN> End of the requested frequency range.<BR>
<BR>
<SPAN CLASS=pout>Value: </SPAN>. Average sound volume for frequency range<BR>
<BR>
<SPAN CLASS=setting>Band Count: </SPAN>The number of bands used to perform spectrum analysis.<BR>
<BR>
Use this behavior building block to perform frequency analysis on a digital sound.
<BR>
*/
od->SetCategory("Sounds/Processing");
od->SetType(CKDLL_BEHAVIORPROTOTYPE);
od->SetGuid(CKGUID(0x47082a75,0x112b7c1b));
od->SetAuthorGuid(VIRTOOLS_GUID);
od->SetAuthorName("Virtools");
od->SetVersion(0x00010000);
od->SetCreationFunction(CreateGetSpectrumBehaviorProto);
od->SetCompatibleClassId(CKCID_WAVESOUND);
od->NeedManager(SOUND_MANAGER_GUID);
return od;
}
#define IN_START_FREQUENCY 0
#define IN_END_FREQUENCY 1
#define OUT_VALUE 0
#define SETTINGS_BAND_COUNT 0
#define LOCAL_TICK 1
#define LOCAL_BANDS 2
#define BITS 10
CKERROR CreateGetSpectrumBehaviorProto(CKBehaviorPrototype **pproto)
{
CKBehaviorPrototype *proto = CreateCKBehaviorPrototype("Get Sound Spectrum");
if(!proto) return CKERR_OUTOFMEMORY;
proto->DeclareInput("In");
proto->DeclareOutput("Out");
proto->DeclareInParameter("Start Frequency",CKPGUID_INT,"0");
proto->DeclareInParameter("End Frequency",CKPGUID_INT,"44100");
proto->DeclareOutParameter("Value",CKPGUID_PERCENTAGE,"0%");
proto->DeclareSetting("Band Count",CKPGUID_INT,"128");
proto->DeclareLocalParameter("Tick",CKPGUID_INT,"0");
proto->DeclareLocalParameter("Band",CKPGUID_VOIDBUF,NULL);
proto->SetBehaviorFlags(CKBEHAVIOR_TARGETABLE);
proto->SetFlags(CK_BEHAVIORPROTOTYPE_NORMAL);
proto->SetFunction(GetSpectrum);
*pproto = proto;
return CK_OK;
}
int GetSpectrum(const CKBehaviorContext& behcontext)
{
CKBehavior* beh = behcontext.Behavior;
beh->ActivateInput(0,FALSE);
beh->ActivateOutput(0);
CKWaveSound* wave=(CKWaveSound*)beh->GetTarget();
if( !wave) return CKBR_OK;
int nb_bands = 128;
beh->GetLocalParameterValue(SETTINGS_BAND_COUNT,&nb_bands);
nb_bands = 1 << BITS;
int startfreq = 0;
int endfreq = 44100;
beh->GetInputParameterValue(IN_START_FREQUENCY,&startfreq);
beh->GetInputParameterValue(IN_END_FREQUENCY,&endfreq);
if(startfreq >= endfreq)
return CKBR_OK;
CKWaveFormat Format;
wave->GetSoundFormat(Format);
CKTimeManager *tm = behcontext.Context->GetTimeManager();
unsigned int lasttick = 0;
beh->GetLocalParameterValue(LOCAL_TICK,&lasttick);
unsigned int currenttick = tm->GetMainTickCount();
if(currenttick!= lasttick)
{
beh->SetLocalParameterValue(LOCAL_TICK,&currenttick);
int pos = wave->GetPlayPosition();
int i,res = 0;
void *buf1 = NULL,*buf2 =NULL;
CKDWORD sz1=0,sz2=0;
int size = nb_bands*Format.nChannels*(Format.wBitsPerSample/8);
wave->Lock(pos,size,&buf1,&sz1,&buf2,&sz2,(CK_WAVESOUND_LOCKMODE)0);
int bytepersample = Format.wBitsPerSample / 8;
switch(Format.nChannels){
case 1: // Mono
{
VxScratch pool(((sz1+sz2)/bytepersample)*sizeof(float));
float *mem = (float *) pool.Mem();
float *it = mem;
VxScratch bandpool(nb_bands);
int isz1 = sz1;
int isz2 = sz2;
switch(Format.wBitsPerSample){
case 8: // 8 bits Mono
{
CKBYTE *ibuf1 = (CKBYTE*) buf1;
CKBYTE *ibuf2 = (CKBYTE*) buf2;
while(isz1>0){
*it = (float)(((int)(*ibuf1) + 0x7f));
ibuf1++;
isz1--;
it++;
}
while(isz2>0){
*it = ((float)((int)(*ibuf2) + 0x7f));
ibuf2++;
isz2--;
it++;
}
}
break;
case 16: // 16 bits Mono
{
signed short *ibuf1 = (signed short*) buf1;
signed short *ibuf2 = (signed short*) buf2;
while(isz1>0){
*it = ((float)(*ibuf1))/255.0f;
ibuf1++;
isz1-=2;
it++;
}
while(isz2>0){
*it = ((float)(*ibuf2))/255.0f;
ibuf2++;
isz2-=2;
it++;
}
}
break;
}
VxScratch im(nb_bands*sizeof(float));
memset(im.Mem(),0,nb_bands*sizeof(float));
FFT(1,BITS,mem,(float*) im.Mem());
VxScratch tmpBands(nb_bands );
unsigned char *bands = (unsigned char*) tmpBands.Mem();
for(i=0;i<nb_bands;i++){
bands[i] = (unsigned char) (unsigned int) (255.0f*XAbs(((float*)mem)[i]));
}
beh->SetLocalParameterValue(LOCAL_BANDS,bands,nb_bands);
}
break;
case 2:
{
VxScratch pool(((sz1+sz2)/bytepersample)*sizeof(float));
float *mem = (float *) pool.Mem();
float *it = mem;
VxScratch bandpool(nb_bands);
// Offset
int offset = (((sz1+sz2)/bytepersample))/2;
int isz1 = sz1/bytepersample;
int isz2 = sz2/bytepersample;
switch(Format.wBitsPerSample){
case 8:
{
CKBYTE *ibuf1 = (CKBYTE*) buf1;
CKBYTE *ibuf2 = (CKBYTE*) buf2;
while(isz1>0){
*it = (float)(((int)(*ibuf1) + 0x7f));
ibuf1++;
*(it+offset) = (float)(((int)(*ibuf1) + 0x7f));
ibuf1++;
isz1-=2;
it++;
}
while(isz2>0){
*it = (float)(((int)(*ibuf2)+ 0x7f));
ibuf2++;
*(it+offset) = (float)(((int)(*ibuf2)+ 0x7f));
ibuf2++;
isz2-=2;
it++;
}
}
break;
case 16:
{
signed short *ibuf1 = (signed short*) buf1;
signed short *ibuf2 = (signed short*) buf2;
while(isz1>0){
*it = (float)(*ibuf1)/255.0f;
ibuf1++;
*(it+offset) = (float)(*ibuf1)/255.0f;
ibuf1++;
isz1-=2;
it++;
}
while(isz2>0){
*(it) = (float)(*ibuf2)/255.0f;
ibuf2++;
*(it+offset) = (float)(*ibuf2)/255.0f;
ibuf2++;
isz2-=2;
it++;
}
}
break;
}
VxScratch im(nb_bands*sizeof(float));
memset(im.Mem(),0,nb_bands*sizeof(float));
FFT(1,BITS,mem,(float*) im.Mem());
memset(im.Mem(),0,nb_bands*sizeof(float));
FFT(1,BITS,mem+offset,(float*) im.Mem());
VxScratch tmpBands(nb_bands );
unsigned char *bands = (unsigned char*) tmpBands.Mem();
for(i=0;i<nb_bands;i++){
bands[i] = (unsigned char) (unsigned int) (128.0f*(XAbs(mem[i])+XAbs(mem[nb_bands+i])));
}
beh->SetLocalParameterValue(LOCAL_BANDS,bands,nb_bands);
}
break;
}
wave->Unlock(buf1,sz1,buf2,sz2);
}
float inv = 1.0f/255.0f;
float sum = 0;
unsigned char * bands = (unsigned char * ) beh->GetLocalParameterReadDataPtr(LOCAL_BANDS);
nb_bands >>= 1;
int startband = 1+(int) ((float)nb_bands*(float)startfreq / (0.5f*(float)Format.nSamplesPerSec));
int endband = 1+(int) ((float)nb_bands*(float)endfreq / (0.5f*(float)Format.nSamplesPerSec));
if(startband == endband)
endband = startband+1;
int end = __min(endband,nb_bands);
for(int i=startband;i<end;i++){
sum += inv*(float)bands[i];
}
sum /= endband-startband;
beh->SetOutputParameterValue(OUT_VALUE,&sum);
return CKBR_OK;
}