【iPhone】自作iPod/AVAudioPlayerを使って複数の曲を扱う

前回のブログで「今度は複数の曲を扱えるようにする」とかいっときながら一ヶ月以上書いてなかったね。
弱小エントリだし、そんな急いで書かなくていいかなーと思ってたんだけど
リアル知人の何人かから「お前が書いたの読んでるよ(ニヤニヤ)」みたいな感じで言われてしまったので
サボらず書こうと思います。

機能自体はもうとうの昔に作ってたので、自分のコード覚えているか心配w

今日の目標はこれです。

<<前回作った自作iPodで、複数の曲を扱えるようにする>>
1:下準備
2:「次の曲」「前の曲」に移行するボタンを作る/ついでに曲の再生が終わったら次の曲を再生する

以上です。それでは早速。

<<1:下準備>>具体的にボタンとかの機能を実装する前に、下準備します。
今回AVAudioPlayerで複数の曲を扱うにあたっては
「どの曲が選択されているかを知る『タグ』のようなものを作って、それに応じて作成するAVAudioPlayerの内容を変える」
っていうシステムにします。
何言ってるかわかりづらいと思うので、コードで説明します。
どの曲が選択されているかを知る『タグ』はこれです。

int senkyoku;

そして、このintに応じて作成するAVAudioPlayerを変えるというアプローチになります。

-(void)chooseMusic{

if (audio.playing==YES) {
[audiostop];
[audiorelease];
}
switch (senkyoku) {
case1:{
NSString *path=[[NSBundlemainBundle]pathForResource:@"music1"ofType:@"mp3"];
NSURL *url=[NSURLfileURLWithPath:path];
audio = [[AVAudioPlayeralloc] initWithContentsOfURL:url error:nil];
kyokumei.text=@"曲名1";
break;}
case2:{
/*
*省略
*/
default:
break;
}
timeslider.maximumValue=audio.duration;
}

また、曲数は定数を作っておくと後々すごく便利です。

#define MusicNumber 10 //←扱う曲数

これで下準備は完了です。このchooseMusicを然るべきところで呼んであげれば、複数曲を扱うことが可能です。
・・・しかし動くことは動くんですが、問題点もあるので記しておきます。
1:全体的に名前がイケてないイケてるエンジニアのみなさんは、間違ってもint senkyokuとかやんないですよねwww
メソッド名とかもデザインパターンを全く意識できてなかったですね。恥ずかしいですが戒めの意味をこめてそのまま載せます。
2:膨大な曲数を扱えない今回は10曲ぐらいを想定してswitchで場合分けしていますが、この構造のままだと曲数が膨大になったときに困ります。
じゃあ
for(int i=0;i<=MusicNumber;i++){
NSString *path=[[NSBundlemainBundle] pathForResource:@"music%d",i ofType:@"mp3"];
NSURL *url=[NSURLfileURLWithPath:path];
audio = [[AVAudioPlayeralloc] initWithContentsOfURL:url error:nil];
}
とかでいいじゃん、って話ですが、これじゃダメなんです。てゆーか僕も最初はこれで書いてました。
なんでかっていうとforで作ってるpathとurlが同じ名前なので、最終的にどの曲を選んでもsenkyoku=MusicNumbebrのもの(最後の曲のもの)が再生されてしまうんです。
じゃあその辺の名前を動的にすればいいじゃんって話で、それはその通りです。releaseが若干面倒になりますが。
viewDidLoadのタイミングで曲数分のAVAudioPlayerを作成しておき、呼ぶAVAudioPlayerをその都度決めればいいですね。
ユーザーが曲の再生途中で「次の曲」に送ってしまった場合にちゃんとreleseするとか、
releaseする対象はその都度変わるからめんど臭いとかいう点に気をつければいけると思います。
その修正は今度しておこう。すいませんここまで完全にメモ。

<<2:「次の曲」「前の曲」に移行するボタンを作る>>ようやく目に見える部分を作ります。一応イメージとして画像を。

ボタンの実装は下準備してあるので簡単です。

-(IBAction)nextMusic{
senkyoku++;
[selfchooseMusic];
[audio.play];
}
-(IBAction)aheadMusic{
senkyoku=senkyoku-1;
[selfchooseMusic];
[audio.play];
}

ついでに曲の再生が終わったら「次の曲」ボタンを押したときの処理と同じことをしてあげるといいです。
再生終了時に次の曲を再生してくれるので。

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
// Music completed
if (flag) {
//nextMusixボタンが押された時と同じ処理をする
senkyoku++;
[selfchooseMusic];

[audio.play];
[audio setDelegate:self];
}
}

要はint senkyokuの値を変えてあげれば中身が変わるのでその処理を割当てます。
ただ、senkyokuが「範囲外」になってしまうとだめなので以下を追記します。

-(void)chooseMusic{
//↓追記
if (audio.playing==YES) {
[audiostop];
[audiorelease];
}
if(senkyoku==0){
senkyoku=MusicNumber;
}
if(senkyoku==MusicNumber+1){
senkyoku=1;

}

以上で目標達成です。(多分)

次はこんな機能を追加してあげるといいですね。
・シャッフル再生できるようにする(乱数作ってint senkyokuに突っ込むだけ。楽勝。)
iPhoneがスリープ状態でも音楽の再生ができるようにする(AVAudioSessionの設定をいじくるだけ。楽勝。)
・こんどこそたくさんの曲を扱っても破綻しない構造にする

感想としてはAVAudoPlayerをreleaseするタイミングがほんとにめんどくさい。ObjectiveCの宿命なんだろうけど。

あと破滅的に見づらいのではてぶ記法に慣れたい←