404 lines
8.8 KiB
C++
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,¤ttick);
|
|
|
|
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;
|
|
}
|
|
|
|
|