ステレオ素材をモノにする場合、ヒルベルト変換というもので片チャンの位相を90度ずらしてやるとバランスが崩れず良いらしいという事で、vDSPのFFTを使ってやってみました。
変換したいオーディオデータを実部にいれて、虚部は0のままにしておきます。FFTして、データのナイキストレート以下のレベルを2倍に(DC成分はそのまま)、ナイキストレート以上を0でクリアして、逆FFTすると虚部に90度位相のずれたオーディオデータが返ってきます。
以下が、その処理をするクラスのサンプルです。とりあえずこのサンプルではただFFTしてるだけなので、実際は窓関数かけてオーバーラップさせる感じですかね。
//
// Hilbert.h
//
#import <Cocoa/Cocoa.h>
#import <Accelerate/Accelerate.h>
@interface Hilbert : NSObject {
vDSP_Length fftLength;
vDSP_Length fftSize;
FFTSetup fftSetup;
}
- (id)initWithLog2n:(NSUInteger)log2n;
- (void)setFFTWithLog2n:(NSInteger)log2n;
- (void)processWithReal:(float *)realPtr imag:(float *)imagPtr;
- (void)fftWithReal:(float *)realPtr
imag:(float *)imagPtr inverse:(BOOL)isInv;
@end
//
// Hilbert.m
//
#import "Hilbert.h"
@implementation Hilbert
- (id)initWithLog2n:(NSUInteger)log2n
{
self = [super init];
if (self != nil) {
[self setFFTWithLog2n:log2n];
}
return self;
}
- (void)setFFTWithLog2n:(NSInteger)log2n
{
fftSize = 1 << log2n;
fftLength = log2n;
if (fftSetup != NULL) destroy_fftsetup(fftSetup);
fftSetup = create_fftsetup(fftLength, FFT_RADIX2);
}
- (void) dealloc
{
if (fftSetup != NULL) destroy_fftsetup(fftSetup);
[super dealloc];
}
- (void)processWithReal:(float *)realPtr imag:(float *)imagPtr
{
//FFTを行う
[self fftWithReal:realPtr imag:imagPtr inverse:NO];
//ナイキスト・レート以下を2倍にする
float tScale = 2.0;
UInt32 tHalfLength = fftSize / 2;
for (NSUInteger j = 1; j < tHalfLength; j++) {
realPtr[j] *= tScale;
imagPtr[j] *= tScale;
}
//ナイキストレート以上を0にする
memset(&(realPtr[tHalfLength]), 0, tHalfLength * sizeof(float));
memset(&(imagPtr[tHalfLength]), 0, tHalfLength * sizeof(float));
//逆FFTを行う
[self fftWithReal:realPtr imag:imagPtr inverse:YES];
}
- (void)fftWithReal:(float *)realPtr
imag:(float *)imagPtr inverse:(BOOL)isInv
{
FFTDirection direction = isInv ? FFT_INVERSE : FFT_FORWARD;
vDSP_Stride stride = 1;
COMPLEX_SPLIT splitComplex;
splitComplex.realp = realPtr;
splitComplex.imagp = imagPtr;
fft_zip(fftSetup, &splitComplex, stride, fftLength, direction);
if (isInv) {
float tScale = (float)1.0 / fftSize;
vsmul(splitComplex.realp, 1,
&tScale, splitComplex.realp, 1, fftSize);
vsmul(splitComplex.imagp, 1,
&tScale, splitComplex.imagp, 1, fftSize);
}
}
@end
コメントする