月別アーカイブ: 2008年7月

24ビット

24bitのオーディオデータを作ったり読みこもうとしてもObjective-C(というかC言語)では24bitの型がありませんので、符号付き32bit整数の上位24bit分を使うというのが簡単な方法ではないかと思います。

ただこの場合、32bitの下位8bitは単純に切り捨てられますので、音にこだわるのであればディザをかけたりしたほうが良いのかもしれませんが、とりあえず今回は無視します。

32bit領域のAlignedHighなデータとして渡したりするのであれば何もしなくてもいいですが、32bit領域でAlignedLowな24bitのデータにしなくちゃいけないなんて時があったりしたら、

SInt32 value;

という、あるオーディオのデータがあったとして、

value >>= 8;

としてやります。

処理系によって算術シフトになるか論理シフトが変わるらしいですが、Xcodeでgccを使っているのであればこの場合算術シフトで、valueがマイナスのときは上位8bitが1で埋め尽くされています。

ちなみに、IntelMac想定の検証コード。

#import <Foundation/Foundation.h>

void logBit (SInt32 value) {
    printf("MSB ");
    for (NSInteger i = 3; i >= 0; i--) {
        Byte *valuePtr = (Byte *)&value;
        for (NSInteger j = 7; j >= 0; j--) {
            SInt32 isBitOn = (valuePtr[i] & (1 << j)) ? YES : NO;
            printf("%d", isBitOn);
        }
        printf(" ");
    }
    printf(" LSB\n\n");
}

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    SInt32 value = INT32_MIN;
    printf("value = %d\n", value);
    logBit(value);
    
    SInt32 sValue = value;
    sValue >>= 8;
    printf("signed shift value = %d\n", sValue);
    logBit(sValue);
    
    UInt32 uValue = (UInt32)value;
    uValue >>= 8;
    printf("unsigned shift value = %u\n", uValue);
    logBit(uValue);
    
    [pool drain];
    return 0;
}

実行すると…

value = -2147483648
MSB 10000000 00000000 00000000 00000000  LSB

signed shift value = -8388608
MSB 11111111 10000000 00000000 00000000  LSB

unsigned shift value = 8388608
MSB 00000000 10000000 00000000 00000000  LSB

という感じになります。

リサージュ波形を表示する

オシロスコープで表示できるようなリサージュ波形を描画するには、

x = sin(右チャンネルのオーディオデータ);
y = sin(左チャンネルのオーディオデータ);

という感じでサンプルごとに座標を求めて、線で繋ぐ。

NSViewのサブクラスでこのようなインスタンス変数があるとして、

NSUInteger length; //オーディオデータのサンプル数
float *lPtr; //左チャンネルのオーディオデータ
float *rPtr; //右チャンネルのオーディオデータ

以下のようにdrawRectメソッドを記述する。(※2008/7/8 描画する位置をrectではなくboundsから求めるように変更しました)

- (void)drawRect:(NSRect)rect {
    
    NSRect viewRect = [self bounds];
    double halfWidth = viewRect.size.width / 2.0;
    double halfHeight = viewRect.size.height / 2.0;
    
    [[NSColor blackColor] set];
    NSRectFill(
        NSMakeRect(0, 0, viewRect.size.width, viewRect.size.height));
    
    NSBezierPath *path = [NSBezierPath bezierPath];
    [[NSColor greenColor] set];
    [path setLineWidth:1.0];
    
    for (NSUInteger i = 0; i < length; i++) {
        
        double x = sin(rPtr[i]);
        double y = sin(lPtr[i]);
        
        NSPoint point = 
            NSMakePoint(x * halfWidth + halfWidth , 
                y * halfHeight + halfHeight);
        if ([path isEmpty]) {
            [path moveToPoint:point];
        }
        
        [path lineToPoint:point];
    }
    
    [path stroke];
}