最近はopenFrameworksを用いてiPhoneアプリを創作しています。
音楽とインタラクションをテーマにしたアプリを作成していたところ、iPhoneのミュージックライブラリ内の曲を編集するようにする方法につまづいたため記録しておきます。
AVAudioPlayerでは複数曲の再生や音楽データの加工に対応していません。このため、openALを用いて音楽データを扱います。
ところが、openALではm4aやmp3のファイルに対応していないためcafファイルに変換して書き出す必要があります。
やりかたは
このサイトを参考にして実装しました。
基本的にはMPMediaPickerControllerで選択した曲に対して、AVAssetReaderとAVAssetWriterを用いてcaf形式に変換してます。
書き出したcaf形式の音楽データをopenALのインスタンスから読み込めば音楽データの加工できます。
ちなみに書き出す際のチャンネルは1つにしておかなければ距離に応じた音の減衰ができないので気をつけてください。
ここに数時間とられました。。。
// iPodの曲目のピッカーを表示
-(IBAction)showMediaPicker:(id)sender
{
MPMediaPickerController *picker = [[MPMediaPickerController alloc] initWithMediaTypes: MPMediaTypeMusic];
[picker setDelegate: self];
[picker setAllowsPickingMultipleItems: NO];
picker.prompt = NSLocalizedString (@"Add songs to play", "Prompt in media item picker");
// pickerをModalViewに表示
[self presentModalViewController: picker animated: YES];
[picker release];
}
//選択した曲に対して処理を行う
- (void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection
{
MPMediaItem *item = [mediaItemCollection.items lastObject];
NSLog(@"export succeeded?:%@", [self exportItem:item]? @"YES": @"NO");
[self dismissModalViewControllerAnimated:YES];
}
//cafファイルに変換してファイルの書き出し
- (BOOL)exportItem:(MPMediaItem *)item
{
NSError *error = nil;
NSDictionary *audioSetting = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat:44100.0],AVSampleRateKey,
[NSNumber numberWithInt:1],AVNumberOfChannelsKey, //チャネルを1にしておかないと音の減衰ができない
[NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,
[NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey,
[NSNumber numberWithBool:NO], AVLinearPCMIsFloatKey,
[NSNumber numberWithBool:0], AVLinearPCMIsBigEndianKey,
[NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved,
[NSData data], AVChannelLayoutKey, nil];
//読み込み側のセットアップ
NSURL *url = [item valueForProperty:MPMediaItemPropertyAssetURL];
AVURLAsset *URLAsset = [AVURLAsset URLAssetWithURL:url options:nil];
if (!URLAsset) return NO;
AVAssetReader *assetReader = [AVAssetReader assetReaderWithAsset:URLAsset error:&error];
if (error) return NO;
NSArray *tracks = [URLAsset tracksWithMediaType:AVMediaTypeAudio];
if (![tracks count]) return NO;
AVAssetReaderAudioMixOutput *audioMixOutput = [AVAssetReaderAudioMixOutput
assetReaderAudioMixOutputWithAudioTracks:tracks
audioSettings:audioSetting];
if (![assetReader canAddOutput:audioMixOutput]) return NO;
[assetReader addOutput:audioMixOutput];
if (![assetReader startReading]) return NO;
//書き込み側のセットアップ
NSString *title = [item valueForProperty:MPMediaItemPropertyTitle];
NSArray *docDirs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [docDirs objectAtIndex:0];
NSString *outPath = [[docDir stringByAppendingPathComponent:@"music1"]
stringByAppendingPathExtension:@"caf"];
NSURL *outURL = [NSURL fileURLWithPath:outPath];
AVAssetWriter *assetWriter = [AVAssetWriter assetWriterWithURL:outURL
fileType:AVFileTypeCoreAudioFormat
error:&error];
//ファイルが存在している場合は削除する
NSFileManager *manager = [NSFileManager defaultManager];
if([manager fileExistsAtPath:outPath]){
[manager removeItemAtPath:outPath error:&error];
}
if (error) return NO;
AVAssetWriterInput *assetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio
outputSettings:audioSetting];
assetWriterInput.expectsMediaDataInRealTime = NO;
if (![assetWriter canAddInput:assetWriterInput]) return NO;
[assetWriter addInput:assetWriterInput];
if (![assetWriter startWriting]) return NO;
//コピー処理
[assetReader retain];
[assetWriter retain];
[assetWriter startSessionAtSourceTime:kCMTimeZero];
dispatch_queue_t queue = dispatch_queue_create("assetWriterQueue", NULL);
//書き込み処理
[assetWriterInput requestMediaDataWhenReadyOnQueue:queue usingBlock:^{
NSLog(@"start");
while (1){
if ([assetWriterInput isReadyForMoreMediaData]) {
CMSampleBufferRef sampleBuffer = [audioMixOutput copyNextSampleBuffer];
if (sampleBuffer) {
[assetWriterInput appendSampleBuffer:sampleBuffer];
CFRelease(sampleBuffer);
}
else {
[assetWriterInput markAsFinished];
break;
}
}
}
[assetWriter finishWriting];
[assetReader release];
[assetWriter release];
NSLog(@"finish");
}];
dispatch_release(queue);
return YES;
}