オシロスコープが買えなくて、身近なものを変わりにしようと思い立ちました。USBインターフェースを持ったPICマイコンでも使おうかと思ったのですが、パソコンのオーディオ入力を使用している方がいらっしゃったので、自分でもやってみようと思い開発を始めました。
まず、当面の目標は、オーディオデータを取得しグラフに表示する事としました。Macでオーディオ入力を取り扱うにはCoreAudioというフレームワークを使用します。もっともCoreAudio自体は、いろいろなフレームワークの集合なので、誤解無きよう。
前回も書いたのですが、入力データを取得しそれをグラフに表示するクラスを作成しました。その名も、SignalViewクラスです。このクラスはNSViewクラスを継承したクラスです。と言う訳で、ソースを示します。説明は次回。
//
// SignalView.h
//
// Created by 大野 英 on 11/07/16.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import
#import
#import
@interface SignalView : NSView {
AudioUnit InputUnit;
AudioDeviceID InputDevice;
NSArray *graphPath;
CGContextRef context;
IBOutlet NSButton *_start;
bool austatus;
}
-(OSStatus)createInputUnit;
-(OSStatus)enableIO;
-(OSStatus)setDefaultInputDeviceAsCurrent;
-(OSStatus)setAudioFormat;
-(OSStatus)setCallBackProc;
-(AudioBufferList *)allocateAudioBufferList:(UInt32)Channels Size:(UInt32)size;
-(void)drawSignal:(AudioBufferList *)list;
-(IBAction)start:(id)sender;
@end
//
// SignalView.m
//
// Created by 大野 英 on 11/07/16.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "SignalView.h"
@implementation SignalView
OSStatus InputProc( void * inRefCon,
AudioUnitRenderActionFlags * ioActionFlags,
const AudioTimeStamp * inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList * ioData
){
SignalView *SELF = (SignalView *)inRefCon;
AudioBufferList *bufferList = [SELF allocateAudioBufferList:1 Size:sizeof(SInt16)*inNumberFrames];
AudioUnitRender(SELF->InputUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, bufferList);
[SELF drawSignal:bufferList];
[SELF setNeedsDisplay:YES];
[SELF removeAudioBufferList:bufferList];
return noErr;
};
- (id)initWithFrame:(NSRect)frame {
OSStatus err;
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
err = [self createInputUnit];
[_start setTitle:@"START"];
if (err) {
return nil;
}
err = [self enableIO];
if (err) {
return nil;
}
err = [self setDefaultInputDeviceAsCurrent];
if (err) {
return nil;
}
err = [self setAudioFormat];
if (err) {
return nil;
}
err = [self setCallBackProc];
if (err) {
return nil;
}
AudioUnitInitialize(InputUnit);
}
return self;
}
-(OSStatus)createInputUnit{
AudioComponentDescription cd;
OSStatus err;
cd.componentType=kAudioUnitType_Output;
cd.componentSubType=kAudioUnitSubType_HALOutput;
cd.componentManufacturer=kAudioUnitManufacturer_Apple;
cd.componentFlags = 0;
cd.componentFlagsMask =0;
AudioComponent com=AudioComponentFindNext(NULL, &cd);
err = AudioComponentInstanceNew(com, &InputUnit);
//err = AudioUnitInitialize(InputUnit);
return err;
}
-(OSStatus)enableIO{
OSStatus err;
UInt32 enableIO=0;
enableIO = 1;
err = AudioUnitSetProperty(InputUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO));
if (err) {
NSLog(@"Failed Enable Input. ");
return err;
}
enableIO = 0;
err = AudioUnitSetProperty(InputUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO));
if (err) {
NSLog(@"Failed Disable Output. ");
return err;
}
return err;
}
-(OSStatus)setDefaultInputDeviceAsCurrent{
OSStatus err;
UInt32 size = sizeof(AudioDeviceID);
AudioObjectPropertyAddress address;
address.mElement=kAudioObjectPropertyElementMaster;
address.mScope=kAudioObjectPropertyScopeGlobal;
address.mSelector=kAudioHardwarePropertyDefaultInputDevice;
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &address, 0, NULL, &size, &InputDevice);
if (err) {
NSLog(@"Can't get default input device.\n");
return err;
}
err = AudioUnitSetProperty(InputUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &InputDevice, sizeof(InputDevice));
if (err) {
NSLog(@"Can't set default input device.\n");
return err;
}
return err;
}
-(OSStatus)setAudioFormat{
OSStatus err;
AudioStreamBasicDescription audioFormat;
audioFormat.mSampleRate = 44100.00;//44100.00;
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = 1;
audioFormat.mBitsPerChannel = 16;
audioFormat.mBytesPerPacket = 2;
audioFormat.mBytesPerFrame = 2;
err = AudioUnitSetProperty(InputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
1,
&audioFormat,
sizeof(audioFormat));
if (err) {
NSLog(@"Can not set output stream format.\n");
return err;
}
return err;
}
-(OSStatus)setCallBackProc{
OSStatus err;
AURenderCallbackStruct input;
input.inputProc = InputProc;
input.inputProcRefCon = self;
err = AudioUnitSetProperty(
InputUnit,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
0,
&input,
sizeof(input));
if (err) {
NSLog(@"Can not set Input callback proc.\n");
return err;
}
return err;
}
-(AudioBufferList *)allocateAudioBufferList:(UInt32)numChannels Size:(UInt32)size{
AudioBufferList *list;
UInt32 i;
list = (AudioBufferList*)calloc(1, sizeof(AudioBufferList)
+ numChannels * sizeof(AudioBuffer));
if (list == NULL) return NULL;
list->mNumberBuffers = numChannels;
for(i = 0; i < numChannels; ++i) {
list->mBuffers[i].mNumberChannels = 1;
list->mBuffers[i].mDataByteSize = size;
list->mBuffers[i].mData = malloc(size);
if(list->mBuffers[i].mData == NULL) {
[self removeAudioBufferList:list];
return NULL;
}
}
return list;
}
- (void)removeAudioBufferList:(AudioBufferList *)list
{
UInt32 i;
if(list) {
for(i = 0; i < list->mNumberBuffers; i++) {
if (list->mBuffers[i].mData) free(list->mBuffers[i].mData);
}
free(list);
}
}
-(void)drawSignal:(AudioBufferList *)list{
UInt32 frames,i,j;
SInt16 *input;
float signal=0;
NSMutableArray *temp = [NSMutableArray array];
if(graphPath==nil){
graphPath = [NSArray array];
}
for(i=0;i<=list->mNumberBuffers-1;i++){
frames = list->mBuffers[i].mDataByteSize/sizeof(SInt16);
input = list->mBuffers[i].mData;
for(j=0;j
signal = (float)input[j];
if (signal >= 0) {
signal=signal/32767;
}else {
signal=signal/32768;
}
NSNumber *sig = [NSNumber numberWithFloat:signal ];
[temp addObject:sig];
//[sig autorelease];
}
}
NSMutableArray *gP = [graphPath mutableCopyWithZone:nil];
[gP addObjectsFromArray:temp];
while ([gP count]>[self bounds].size.width) {
[gP removeObjectAtIndex:0];
}
graphPath = [gP copyWithZone:nil];
[gP release];
}
-(IBAction)start:(id)sender{
if (austatus==0) {
AudioOutputUnitStart(InputUnit);
[_start setTitle:@"STOP"];
austatus = 1;
}else {
AudioOutputUnitStop(InputUnit);
[_start setTitle:@"START"];
austatus = 0;
}
}
- (void)drawRect:(NSRect)dirtyRect {
// Drawing code here.
float signal;
if (context == NULL) {
context = [[NSGraphicsContext currentContext] graphicsPort];
}
CGContextSetRGBFillColor(context, 0, 0, 0, 1);
CGContextFillRect(context, CGRectMake(dirtyRect.origin.x, dirtyRect.origin.y, dirtyRect.size.width, dirtyRect.size.height));
CGContextBeginPath(context);
CGContextMoveToPoint(context, dirtyRect.origin.x, dirtyRect.size.height*0.5);
CGContextAddLineToPoint(context, dirtyRect.size.width, dirtyRect.size.height*0.5);
CGContextSetRGBFillColor(context, 0, 1, 0, 1);
CGContextStrokePath(context);
if (graphPath==nil) {
return;
}
int i=0;
CGContextBeginPath(context);
for(i=0;i<[graphPath count]-1;i++){
signal = [[graphPath objectAtIndex:i] floatValue];
//NSLog(@"%f",signal);
CGContextMoveToPoint(context, (float)i, (signal*0.5+0.5)*dirtyRect.size.height);
signal = [[graphPath objectAtIndex:i+1] floatValue];
//NSLog(@"%f",signal);
CGContextAddLineToPoint(context, (float)(i+1), (signal*0.5+0.5)*dirtyRect.size.height);
}
CGContextSetRGBStrokeColor(context, 0, 1, 0, 1);
CGContextStrokePath(context);
}
@end
最近のコメント