Hatena::ブログ(Diary)

モノヅクリブログ Twitter

2011-06-29 AVAudioPlayerを使った音楽ファイルの再生

AVAudioPlayerを使った音楽ファイルの再生

AVAudioPlayerを使って音楽を再生する方法です。再生時間をNSTimerを使って表示しています。NSRunLoopでも表示は可能かと思いますがNSTimerのほうがはるかに楽です。

.hファイル

#include <AVFoundation/AVFoundation.h>
@interface audioplayer : NSObject 
<AVAudioPlayerDelegate>
{
    NSTimer * timer;
    AVAudioPlayer * audio;
}
@end

.mファイル

// 音楽再生を開始
@implementation audioplayer
- (void)main 
{
    [super viewDidLoad];
    NSString * path = [[NSBundle mainBundle] pathForResource:@"soundfile" ofType:@"mp3"];
    NSURL * url = [NSURL fileURLWithPath:path];
    // errorを取得したいときはscheduledTimerWithTimeIntervalのerrorにNSError errorポインタ(&error)を投げます
    audio = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
    audio.delegate = self;
    [audio play];
    timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(playSequence) userInfo:nil repeats:YES];
    
}
// 再生時間を表示
- (void) playSequence{
    if(audio.playing){
        NSLog(@"%@", [NSString stringWithFormat:@"%f", audio.currentTime]);
    } else {
        [timer invalidate];
    }    
}
@end

ちなみにNSRunLoopを使うと下記のような記述になります。NSTimerのほうがいいですね。

    NSDate *now = [[NSDate alloc]init];
    NSTimer *timer = [[NSTimer alloc]initWithFireDate:now interval:0.1 target:self selector:@selector(playing) userInfo:nil repeats:YES];
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    [runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
    [runLoop run];

AVAudioPlayerでfadein/fadeout(フェードイン・フェードアウト)を実装したライブラリ

AVAudioPlayerを使ったライブラリを作りました。AVAudioPlayerの実例が少なかったからしっかりとまとめた。AudioQueueとかAudioUnitじゃないとできないんじゃないの?ということがAVAudioPlayerでできます。

AudioPlayerDelegateをdelegateして使いたいクラスに取り込んであげてください。

AudioPlayer.h

#import <Foundation/Foundation.h>
#include <AVFoundation/AVFoundation.h>

@protocol AudioPlayerDelegate
-(void) audioSequence:(double)currentTime;
-(void) audioFadein:(double)volume;
-(void) audioFadeout:(double)volume;
@optional
@end
@interface AudioPlayer : NSObject 
<AVAudioPlayerDelegate>
{
    NSTimer * timer;
    NSTimer * timerFade;
    AVAudioPlayer * audio;
    float power, currentVolume;
    double duration;
    BOOL fading, pausing;
    id<AudioPlayerDelegate> delegate;
}

@property (nonatomic, assign) id<AudioPlayerDelegate> delegate;
@property (nonatomic, retain) AVAudioPlayer * audio;
@property double duration;
@property float power;
@property float currentVolume;

-(id) initWithFilename:(NSString *)filename ofType:(NSString *)ofType delegate:(id)targetDelegate;
-(void)audioStart:(BOOL)fade currentTime:(double)currentTime volume:(float)volume;
-(void)audioStop:(BOOL)fade;
-(void)audioPause;
-(void)audioPlayThreaded:(id)threadAudio;
-(void)setVolume:(double)volume;
@end

AudioPlayer.m

#import "AudioPlayer.h"

@implementation AudioPlayer
@synthesize delegate, duration, power, currentVolume, audio;

#define FEDE_SEED 0.01f
#define VOLUME_SEED 0.005f
#define SEQUENCE_INTERVAL 0.5f
#define AVEPW4CH 0

-(id) initWithFilename:(NSString *)filename ofType:(NSString *)ofType delegate:(id)targetDelegate{
    NSString * path = [[NSBundle mainBundle] pathForResource:filename ofType:ofType];
    NSURL * url = [NSURL fileURLWithPath:path];
    audio = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
    [audio setMeteringEnabled:YES];
    audio.delegate = targetDelegate;
    self.delegate = targetDelegate;
    duration = audio.duration;
    fading = NO;
    currentVolume = 0.5f;
    return self;
}
- (void)audioSequence{
    if(audio == nil) return;
    if(audio.playing){
        [audio updateMeters];
        power = [audio averagePowerForChannel:AVEPW4CH]; // averagePowerForChannel OR peakPowerForChannel
        [self.delegate audioSequence:audio.currentTime];
	}
}
-(void)audioStart:(BOOL)fade currentTime:(double)currentTime volume:(float)volume{
    if(audio.playing) return;
    currentVolume = volume;
    if(fade){
        if(fading == NO){
            [audio setVolume:0.0f];
            timerFade = [NSTimer scheduledTimerWithTimeInterval:FEDE_SEED target:self selector:@selector(audioFadein) userInfo:nil repeats:YES];        
        }
    }
    audio.currentTime = currentTime;
    [NSThread detachNewThreadSelector:@selector(audioPlayThreaded:) toTarget:self withObject:audio];
    timer = [NSTimer scheduledTimerWithTimeInterval:SEQUENCE_INTERVAL target:self selector:@selector(audioSequence) userInfo:nil repeats:YES];
}
-(void)audioStop:(BOOL)fade{
    if(audio == nil) return;
    
    if(fade){
        if(fading == NO) timerFade = [NSTimer scheduledTimerWithTimeInterval:FEDE_SEED target:self selector:@selector(audioFadeout) userInfo:nil repeats:YES];
    } else {
        if(audio.playing){
            [audio prepareToPlay];
            [audio stop];
            [audio setCurrentTime:0.0f];
        }
    }
    
}
-(void)audioPause{
    if(audio == nil) return;
    if(audio.playing){
        [audio pause];
    }    
}
-(void)audioFadein{
    
    if(audio.playing){
        if((audio.volume+VOLUME_SEED) < currentVolume){
            fading = YES;
            [self.delegate audioFadein:audio.volume];
            [audio setVolume:audio.volume+VOLUME_SEED];
        } else {
            fading = NO;
            if([timerFade isValid]) [timerFade invalidate];
        }
    }
    
}
-(void)audioFadeout{
    
    if(audio.volume <= 0.0f){
        fading = NO;
        if([timerFade isValid]) [timerFade invalidate];
        [self audioStop:NO];
    } else {
        fading = YES;
        [self.delegate audioFadeout:audio.volume];
        [audio setVolume:audio.volume-VOLUME_SEED];
    }
    
}
-(void)audioPlayThreaded:(id)threadAudio{
    if(threadAudio == nil) return;
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    [(AVAudioPlayer *)threadAudio prepareToPlay];
    [(AVAudioPlayer *)threadAudio play];
    [pool release];
}
-(void)setVolume:(double)volume{
    [audio setVolume:volume];
}
@end

使い方は簡単。

-(void) main{
    ap = [[AudioPlayer alloc]initWithFilename:@"filename" ofType:@"mp3" delegate:self];
    [ap audioStart:YES currentTime:0.0f volume:0.5f]; // フェードで始まる。
    [ap audioStop:YES]; // フェードで終わる
}
-(void)audioSequence:(double)currentTime{
    // 再生中の処理
    NSLog(@"%f", currentTime); // 現在時間
    NSLog(@"%f", ap.power); // デシベル数 averagePowerForChannelで設定してある
}
// フェードイン中
-(void)audioFadein:(double)volume{
    NSLog(@"fadein = %f", volume);
}
// フェードアウト中
-(void)audioFadeout:(double)volume{
    NSLog(@"fadeout = %f", volume);
}