我有以下卷积算法:
public class Spatializer : MonoBehaviour
{
const int MAX_TAPS = 128;
static float[,,] IRs = null;
// http://stackoverflow.com/questions/7237907/1d-fast-convolution-without-fft
public static float[] Spatialize( float[] srcMono, int toneme, bool loop )
{
if( IRs == null )
LoadIRs( );
int inSamps = srcMono.Length;
int outSamps = inSamps + ( loop ? 0 : MAX_TAPS-1 );
float [] L = new float[ outSamps ];
float [] R = new float[ outSamps ];
int i,j,k;
float x_i;
for ( i = 0; i < inSamps; i++)
{
x_i = srcMono[ i ];
for ( j = 0; j < MAX_TAPS; j++)
{
k = i + j;
if( k >= inSamps )
if( loop )
k %= inSamps;
L[ k ] += x_i * IRs[ toneme, 0, j ];
R[ k ] += x_i * IRs[ toneme, 1, j ];
}
}
float[] outputInterleaved = new float[ 2 * outSamps ];
int outPtr = 0;
for ( i = 0; i < outSamps; i++)
{
outputInterleaved[ outPtr++ ] = L[ i ];
outputInterleaved[ outPtr++ ] = R[ i ];
}
return outputInterleaved;
}
static void LoadIRs( )
{
IRs = new float[ 12, 2, MAX_TAPS ];
// !!! get wav from file
float [] wav = new float[ ... ];
float step = ...;
// de-interleave and resample
for( int toneme = 0; toneme < 12; toneme++ )
{
for( int tap=0; tap < MAX_TAPS; tap++ )
{
int n = (int)Mathf.RoundToInt( (float)tap * step );
IRs[ toneme, 0, tap ] = wav[ 2 * n ];
IRs[ toneme, 1, tap ] = wav[ 2 * n + 1 ];
}
}
}
}
我怀疑如果我只需要在卷积循环中访问一维数组就会快得多,因为从一维数组中访问一个元素所涉及的周期比从一个元素访问元素要少一些一个三维数组。
// extract array we want OUTSIDE loop
float [] IR_L = IRs[ toneme, 0 ]; // BAD SYNTAX <-- how to do this?
float [] IR_R = IRs[ toneme, 1 ]; // BAD SYNTAX <-- how to do this?
for ( i = 0; i < inSamps; i++)
{
x_i = srcMono[ i ];
for ( j = 0; j < MAX_TAPS; j++)
{
k = i + j;
if( k >= inSamps )
if( loop )
k %= inSamps;
L[ k ] += x_i * IR_L[ j ];
R[ k ] += x_i * IR_R[ j ];
}
}
以上是伪代码,因为我不知道如何实现它,甚至是否可能。
所以我的问题是:我可以提取
float [] IR_L = IRs[ toneme, 0 ]; // BAD SYNTAX <-- how to do this?
所以不要写
IRs[ toneme, 0, j ]
在内循环中,我可以写
IR_L[ j ]
答案 0 :(得分:0)
通过浏览Basilevs的优秀链接,我从内循环中移除了分支。
我找到了数组问题的答案:解决方案是使用锯齿状数组; [][] 而不是 [,]。常识是[] []执行得更快,而[,]允许更整洁和更简洁的代码。在我的情况下,我需要[] []。
这是最终的卷积算法 - 它的性能至少比原始算法快四倍:
const int MAX_TAPS = 128;
static float[,][] IRs = null;
public static float[] Spatialize( float[] srcMono, int toneme, bool loop )
{
if( IRs == null )
LoadIRs( );
int inSamps = srcMono.Length;
int convSamps = inSamps + MAX_TAPS;
float [] destL = new float[ convSamps ];
float [] destR = new float[ convSamps ];
float [] filtL = IRs[ toneme, 0 ];
float [] filtR = IRs[ toneme, 1 ];
int i,j,k;
float x_i;
for ( i = 0; i < inSamps; i++)
{
x_i = srcMono[ i ];
k = i;
for ( j = 0; j < MAX_TAPS; j++, k++ )
{
// think: k = i + j;
destL[ k ] += x_i * filtL[ j ];
destR[ k ] += x_i * filtR[ j ];
}
}
// circular convolution?
if( loop ) {
for ( j = 0; j < MAX_TAPS; j++ ) {
destL[ j ] += destL[ inSamps + j ];
destR[ j ] += destR[ inSamps + j ];
}
}
int outSamps = loop ? inSamps : convSamps;
float[] outputInterleaved = new float[ 2 * outSamps ];
int outPtr = 0;
for ( i = 0; i < outSamps; i++)
{
outputInterleaved[ outPtr++ ] = destL[ i ];
outputInterleaved[ outPtr++ ] = destR[ i ];
}
return outputInterleaved;
}
static void LoadIRs( )
{
IRs = new float[ 12, 2] [];
// !!! fill wav[] from file
for( int toneme = 0; toneme < 12; toneme++ )
{
// de-interleave and resample
float [] L = new float[ MAX_TAPS ];
float [] R = new float[ MAX_TAPS ];
for( int tap=0; tap < MAX_TAPS; tap++ )
{
int n = (int)Mathf.RoundToInt( (float)tap * step );
L[ tap ] = wav[ 2 * n ];
R[ tap ] = wav[ 2 * n + 1 ];
}
IRs[ toneme, 0 ] = L;
IRs[ toneme, 1 ] = R;
}