Cocoa練習帳 -15ページ目

[Android]リスト表示(2)

iOSのUITableViewControllerの様に、画面全体にリストを表示するアクティビティが用意されている。




前回のコードを以下の内容に変更する。




public class HelloWorldActivity extends ListActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        String[] lines = { "Line01", "Line02", "Line03", "Line04" };
        ArrayAdapter arrayAdapter = new ArrayAdapter(
       
       
       
        setListAdapter(arrayAdapter);
    }
}



もし、アクティビティで使っているListViewが必要になれば、getListView()メソッドで取得できる。




関連情報
Android Developers

初めてのAndroid



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

初めてのAndroid 第3版/オライリージャパン
¥2,415
Amazon.co.jp

[Android]リスト表示

Android開発環境の状況は変化してきているようで、従来はEclipseに自分でADT (Android Developer Tools)を組み込むのが主流だったが、他の開発でEclipseを使っていない人向けにADT組み込み済みのEclipseが入手できるようになった。

Get the Android SDK

これは便利になったと思っていたところに、先日のGoogle I/O 2013でIntelliJベースのAndroid Studioが発表された。

Getting Started with Android Studio

ただし、まだ、Early Access Preview版ということなので、本件ではADT組み込み済みのExlipseを利用する事にした。




以前、開発環境の雛形から、以下のような簡単なアプリケーションを作成していた。




package demo.hello;
 
import android.app.Activity;
import android.os.Bundle;
 
public class HelloWorldActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}



これに、リストを表示させてみようと思う。iOSでいうところのTableViewのように。




onCreate()メソッドの内容を以下に書き換える。




public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        String[] lines = { "Line01", "Line02", "Line03", "Line04" };
        ListView listView = new ListView(this);
        setContentView(listView);
        ArrayAdapter arrayAdapter = new ArrayAdapter(
       
       
       
        listView.setAdapter(arrayAdapter);
    }



これを実行。実機だと画面ダンプの撮り方が分からなかったので、エミュレータで実行結果だ。




リスト表示




リスト表示するのはListViewクラス。これにlines変数の内容を表示させたいのだが、データとビューの中間で関連付けさせるのがArrayAdapterの役割だ。


setContentView()でlistViewを画面に配置する。




関連情報
Android Developers

初めてのAndroid




【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)
初めてのAndroid 第3版/オライリージャパン
¥2,415
Amazon.co.jp

関東第59回Cocoa勉強会

今回は松戸で開催。


前回勉強会は発表されたキーが2個のキーボードのリマッパーと耳コピ用アプリケーション、設定より規約、FontPanel、デバッグTips、VMware等についての発表があった。

キーのリマッパーについては、以前、開催されたUSB分科会のように、ドライバ関連について何かやりたいという提案があった。自分も興味があるので、次回の勉強会で何か発表できたらと考えている。




【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

[iOS]耳コピ用AudioPlayer(その6)

iPodアプリケーションのOn-The-GoインタフェースのArtistsでは、まず、Artistsのリストが表示され、あるArtistを選択するとAlbumsリストが表示され、あるAlbumを選択するのSongsリストが表示されるが、その為で情報を取得する方法を試行錯誤してみたので、それを紹介する。




- (void)viewDidLoad
{
    [super viewDidLoad];
    
    /* Artists一覧の取得 */
    MPMediaQuery    *artistsQuery = [MPMediaQuery artistsQuery];
    NSArray         *artistsArray = [artistsQuery collections];
    for (MPMediaItemCollection *mediaItemCollection in artistsArray) {
        MPMediaItem *mediaItem = [mediaItemCollection representativeItem];
        NSURL   *artistName = (NSURL*)[mediaItem valueForProperty:MPMediaItemPropertyArtist];
        NSLog(@"artist:%@", artistName);
        
        /* Albums一覧の取得 */
        MPMediaQuery    *albumsQuery = [[MPMediaQuery alloc] init];
        [albumsQuery addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:artistName
                                                                        forProperty:MPMediaItemPropertyArtist]];
        [albumsQuery setGroupingType:MPMediaGroupingAlbum];
        NSArray *albums = [albumsQuery collections];
        for (MPMediaItemCollection *album in albums) {
            MPMediaItem *representativeItem = [album representativeItem];
            NSString *albumTitle = [representativeItem valueForProperty:MPMediaItemPropertyAlbumTitle];
            NSLog(@" album:%@", albumTitle);
            
            /* Songs一覧の取得 */
            NSArray *songs = [album items];
            for (MPMediaItem *song in songs) {
                NSString *songTitle = [song valueForProperty: MPMediaItemPropertyTitle];
                NSLog(@"  song:%@", songTitle);
            }
        }
    }
}



はじめArtists一覧は木構造になっていて、Albums、Songsと辿れると予想していたのだが、そうではなくて、得られた情報から検索条件を作って取得する事になる。




ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/AudioPlayer - GitHub



関連情報
iPhone Core Audioプログラミング

Technical Q&A QA1668Playing media while in the background using AV Foundation

iPod Library Access Programming Guide



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)
iPhone Core Audioプログラミング/ソフトバンククリエイティブ
¥4,200
Amazon.co.jp

[iOS]耳コピ用AudioPlayer(その5)

耳コピ用の最低限の機能は実装した。まだ、iPad対応やSongs以外の一覧対応、指定範囲の繰り返し再生等が未実装だが。






本来はオーディオセッションは、アプリケーションの単位で管理する物だと思うが、まだ、仮なので再生画面での管理とした。




- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
    [[AVAudioSession sharedInstance] setActive:YES error:nil];
}
 
- (void)viewDidUnload
{
    [[AVAudioSession sharedInstance] setActive:NO error:nil];
    self.dict = nil;
    self.playerItem = nil;
    self.player = nil;
    [super viewDidUnload];
}



また、viewDidUnloadはメモリ不足時のView解放の為に呼ばれるので、位置としては適切ではないと思うが、アプリケーション単位での管理に変更する際に見直す予定だ。




再生位置をスライダで表示/変更できるようにしたのだが、再生中のスライダの更新をコールバックのハンドラで行う事にした。




- (void)viewWillAppear:(BOOL)animated
{
    DBGMSG(@"%s, dict:%@", __func__, self.dict);
    [super viewWillAppear:animated];
    
    /* 選択された曲 */
    NSURL       *url = [self.dict objectForKey:@"URL"];
    self.playerItem = [AVPlayerItem playerItemWithURL:url];
    self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
    
    /* 再生位置(先頭) */
    self.currentTimeSlider.minimumValue = 0.0;
    self.currentTimeSlider.maximumValue = CMTimeGetSeconds(self.playerItem.duration);
    self.currentTimeSlider.value = 0.0;
    
    /* 生成速度(停止) */
    self.rateSlider.minimumValue = 0.0;
    self.rateSlider.maximumValue = 2.0;
    self.rateSlider.value = 0.0;
    
    /* 再生位置の更新 */
    const double interval = (0.5f * self.currentTimeSlider.maximumValue)
                            / self.currentTimeSlider.bounds.size.width;
    const CMTime time     = CMTimeMakeWithSeconds(interval, NSEC_PER_SEC);
    __block DetailViewController * __weak blockWeakSelf = self;
    self.playerTimeObserver = [self.player addPeriodicTimeObserverForInterval:time
                                                                        queue:NULL
                                                                   usingBlock:^( CMTime time ) {
                                                                       DetailViewController *tempSelf = blockWeakSelf;
                                                                       if (! tempSelf) return;
                                                                       [tempSelf _updateCurrentTimeSlider];
                                                                   }];
}



また、末尾まで再生したら通知を受け取るようにした。これらについては、破棄の処理も必要だ。




- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(_playerDidPlayToEndTime:)
                                                 name:AVPlayerItemDidPlayToEndTimeNotification
                                               object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(_playerTimeJumped:)
                                                 name:AVPlayerItemTimeJumpedNotification
                                               object:nil];
}
 
- (void)viewWillDisappear:(BOOL)animated
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    if (self.playerTimeObserver) {
        [self.player removeTimeObserver:self.playerTimeObserver];
        self.playerTimeObserver = nil;
    }
    
    [super viewWillDisappear:animated];
}



再生とは再生速度を1.0にする事。停止とは再生速度を0.0にする事になるため、再生速度を自由に変更できるアプリケーションでは、再生と停止のボタンは無くても構わないが、用意した。再生ボタンを押下されると、停止状態なら再生速度を1.0に設定している。




- (IBAction)play:(id)sender
{
    if (0.0 == self.rateSlider.value) {
        self.player.rate = 1.0;
        [self _updateRateSlider];
    }
}
 
- (IBAction)stop:(id)sender
{
    self.player.rate = 0.0;
    [self _updateRateSlider];
}



再生位置と再生速度のスライダで値が変更されたら、再生位置と再生速度を変更するメソッドだ。




- (IBAction)currentTimeSliderDidChanged:(id)sender
{
    [self.player seekToTime:CMTimeMakeWithSeconds(self.currentTimeSlider.value, NSEC_PER_SEC)];
}
 
- (IBAction)rateSliderDidChanged:(id)sender
{
    self.player.rate = self.rateSlider.value;
}



逆に、再生位置と再生速度をスライダに反映するメソッドだ。




- (void)_updateCurrentTimeSlider
{
    const double duration = CMTimeGetSeconds( [self.player.currentItem duration] );
    const double time     = CMTimeGetSeconds([self.player currentTime]);
    const float  value    = (self.currentTimeSlider.maximumValue - self.currentTimeSlider.minimumValue )
                            * time / duration + self.currentTimeSlider.minimumValue;
    
    [self.currentTimeSlider setValue:value];
}
 
- (void)_updateRateSlider
{
    self.rateSlider.value = self.player.rate;
}



最後まで再生したら、再生位置を先頭に戻している。




- (void)_playerDidPlayToEndTime:(NSNotification *)notification
{
    [self.player seekToTime:CMTimeMakeWithSeconds(0.0, NSEC_PER_SEC)];
    self.player.rate = 0.0;
    [self currentTimeSliderDidChanged:nil];
}



これが再生画面。

再生画面








ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/AudioPlayer - GitHub



関連情報
iPhone Core Audioプログラミング

Technical Q&A QA1668Playing media while in the background using AV Foundation



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

iPhone Core Audioプログラミング/ソフトバンククリエイティブ
¥4,200
Amazon.co.jp

[iOS]耳コピ用AudioPlayer(その4)

まだ、試作レベルだが個人利用の範囲では、十分、耳コピアプリケーションとして利用できるので活用しているのだが、スクリーンがロックされると、再生が止まるという課題があったので、改善してみた。




まだ、試作という事で、詳細画面のViewControllerでの対応としたが、AVAudioSessionクラスを使って、再生機能を利用するプロパティを設定した。




- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
    [[AVAudioSession sharedInstance] setActive:YES error:nil];
}



そして、プロパティリストAudioPlayer-Info.plistにバックグラウンドに回っても、再生を続ける設定をした。

plist








ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/AudioPlayer - GitHub



関連情報
iPhone Core Audioプログラミング

Technical Q&A QA1668Playing media while in the background using AV Foundation



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)
iPhone Core Audioプログラミング/ソフトバンククリエイティブ
¥4,200
Amazon.co.jp

[iOS]耳コピ用AudioPlayer(その3)

アプリケーションとして完成させる為、あまり深く調べなかった事について、調査してみる。




曲の閲覧は、Musicアプリケーションを真似る事にした。画面下部のタブバーにプレイリストとアーティスト、曲、アルバムのボタンが並んでいて、それを選択すると対応したテーブルが表示される。

これは、iPodライブラリから曲情報を抽出する際に、対応したフィルタをかけて事になると予想されるが、MPMediaQueryクラスには、目的にあったメソッドが用意されている。













アーティストの一覧を取得する場合は以下のとおり。




MPMediaQuery    *artistsQuery = [MPMediaQuery artistsQuery];
NSArray         *artistsArray = [artistsQuery collections];
for (MPMediaItemCollection *mediaItemCollection in artistsArray) {
    MPMediaItem *mediaItem = [mediaItemCollection representativeItem];
    NSURL   *title = (NSURL*)[mediaItem valueForProperty:MPMediaItemPropertyArtist];
    NSLog(@"mediaItem:%@", title);
}



曲の一覧を取得する場合は以下のとおり。




MPMediaQuery    *songsQuery = [MPMediaQuery songsQuery];
NSArray         *mediaItems = [songsQuery items];
for (MPMediaItem *mediaItem in mediaItems) {
    NSString    *title = (NSString*)[mediaItem valueForProperty:MPMediaItemPropertyTitle];
    NSLog(@"mediaItem:%@", title);
}



アルバムの一覧を取得する場合は以下のとおり。




MPMediaQuery    *albumsQuery = [MPMediaQuery albumsQuery];
NSArray         *albumsArray = [albumsQuery collections];
for (MPMediaItemCollection *mediaItemCollection in albumsArray) {
    MPMediaItem *mediaItem = [mediaItemCollection representativeItem];
    NSURL   *title = (NSURL*)[mediaItem valueForProperty:MPMediaItemPropertyAlbumTitle];
    NSLog(@"mediaItem:%@", title);
}



ただし、プレイリストの一覧の取得方法がよく分からなかった。以下だと駄目みたい。




MPMediaQuery    *playlistsQuery = [MPMediaQuery playlistsQuery];
NSArray         *playlistsArray = [playlistsQuery collections];
for (MPMediaItemCollection *mediaItemCollection in playlistsArray) {
    MPMediaItem *mediaItem = [mediaItemCollection representativeItem];
    NSURL   *title = (NSURL*)[mediaItem valueForProperty:MPMediaItemPropertyTitle];
    NSLog(@"mediaItem:%@", title);
}



これが結果。




2013-04-25 23:50:33.531 AudioPlayer[21637:907] mediaItem:やくしまるえつこ みんなのクリスマスセッション - Dec 25, 2012
2013-04-25 23:50:33.543 AudioPlayer[21637:907] mediaItem:Age Of Consent
2013-04-25 23:50:33.548 AudioPlayer[21637:907] mediaItem:A Child's Christmas in Wales
2013-04-25 23:50:33.554 AudioPlayer[21637:907] mediaItem:Istanbul
2013-04-25 23:50:33.560 AudioPlayer[21637:907] mediaItem:Question Everything
2013-04-25 23:50:33.576 AudioPlayer[21637:907] mediaItem:Out of Control
2013-04-25 23:50:33.582 AudioPlayer[21637:907] mediaItem:サウンドストリート・アーカイブス - Mar 28, 2009



プロパティとしてMPMediaPlaylistPropertyNameを使い方法を紹介している方がいたが、上手くいかなかった。何故だ王。




サンプルコードをみると、この処理は時間がかかるので、GCDやブロックを使って、非同期に行っていた。




ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/AudioPlayer - GitHub



関連情報
iPhone Core Audioプログラミング



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)
iPhone Core Audioプログラミング/ソフトバンククリエイティブ
¥4,200
Amazon.co.jp

[iOS]耳コピ用AudioPlayer(その2)

本日は、調査途中のグダグダな内容となってしまった。申し訳ない。とりあえずの動作確認の報告とさせていただきたい。




前回の続き。得られたURLからAVPlayerのインスタンスを生成する。




NSURL       *url = [self.dict objectForKey:@"URL"];
AVPlayerItem    *playerItem = [AVPlayerItem playerItemWithURL:url];
self.player = [AVPlayer playerWithPlayerItem:playerItem];



それのplayメソッドを読んだあげれば再生を開始する。




[self.player play];



AVPlayerでは、playメソッドはrateプロパティを1.0に設定するという意味だ。なので、playメソッドを呼ばず、rateプロパティに値を設定すれば再生する。

例えば、0.5を設定すると、1/2の速度で再生する。




self.player.rate = 0.5;



いちおう、これで、耳コピ用の再生機能が実現できる事が分かる。




ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/AudioPlayer - GitHub



関連情報
iPhone Core Audioプログラミング



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

iPhone Core Audioプログラミング/ソフトバンククリエイティブ
¥4,200
Amazon.co.jp

[iOS]耳コピ用AudioPlayer

楽器の練習用にiOSのiPod Libraryに格納されている曲をスロー再生するアプリケーションが欲しくなり自作する事にした。

はるか昔、放送局向けの録音再生アプリケーションを製作した事があって、その時は、再生速度を上げる場合、音声データを間引いた事があったのだが、最新のCoreAudioのAVFoundationでは、自由に再生速度が変更できるようになっているようで、せっかくなので、それを利用しない手はない。

iOSのオーディオ関連のフレームワークは多数あり、かつ、変化も激しく、そして、似た名前の物があるので、一つ一つ、確認しながら作業を進める事にする。




いきなり、主要な機能の曲の再生に取りかかりたいのが人情だが、アプリケーションとしては、曲の選択のUIも重量な機能なので、どんな事が出来るのか、Appleのサンプルコードで確認する事にする。




AVPlayerDemo



このサンプルコードから、曲のリストを表示できるようにしてみた。




曲の一覧を取得し、それ表題を配列に格納する。




- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.songsList = [[NSMutableArray alloc] init];
    self.musicPlayerController = [MPMusicPlayerController iPodMusicPlayer];
    
    MPMediaQuery    *songsQuery = [MPMediaQuery songsQuery];
    NSArray         *mediaItems = [songsQuery items];
    for (MPMediaItem *mediaItem in mediaItems) {
        NSURL   *URL = (NSURL*)[mediaItem valueForProperty:MPMediaItemPropertyAssetURL];
        if (URL) {
            NSString    *title = (NSString*)[mediaItem valueForProperty:MPMediaItemPropertyTitle];
            [self.songsList addObject:title];
        }
    }
}



配列に格納した情報をテーブルに表示する。




- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.songsList.count;
}
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"SongsCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    
    NSString    *title = [self.songsList objectAtIndex:indexPath.row];
    cell.textLabel.text = title;
    
    return cell;
}



一覧が表示できた。




songslists





ソースコード
GitHubからどうぞ。

https://github.com/murakami/workbook/tree/master/ios/AudioPlayer - GitHub



関連情報
iPhone Core Audioプログラミング



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

iPhone Core Audioプログラミング/ソフトバンククリエイティブ
¥4,200
Amazon.co.jp

[OSX][iOS]合成フィルタ

『Core Animation for Max OS X and the iPhone』の『6.4 Compositing Filters』について説明する。




サンプルコードのFilteredViewは、アウトレットのスペルが間違っている。なので、スペル間違いを我慢して、FilteredView.mのメソッド名を変更するか、FilteredView.hのアウトレットをただしいスペルに変更し、MainMenu.nibのアウトレットの繋がりを張り直す必要がある。ちなみに、著者は後者を選択した。




合成フィルタ (Compositing Filters)は、異なる画像を合成するフィルターで、書籍ではブレンドモードフィルタ (Blend Mode Filters)と呼ばれる、重ねた画像を描画するフィルタのサンプルを説明している。




フィルタの定義は以下のとおり。




- (void) applyFilter

  CIFilter *filter = [CIFilter filterWithName:@"CIColorBurnBlendMode" 
                                keysAndValues:nil];
  [[controls animator] setCompositingFilter:filter];
}



灰色で描画しているビューで、




- (void)drawRect:(NSRect)rect
{
  [[NSColor lightGrayColor] set];
  NSRectFill(rect);
}



子ビューに対してフィルタを適用すると




- (IBAction)addFilter:(id)sender
{
  if (nil == [controls compositingFilter]) {
    [self applyFilter];
  }
}



子ビューが灰色になるということのようだ。




関連情報
Core Animation for Max OS X and the iPhone

Core Animation for Max OS X and the iPhone

合成フィルタ (Compositing Filters)

ブレンドモードフィルタ (Blend Mode Filters)



【Cocoa練習帳】
http://www.bitz.co.jp/weblog/

http://ameblo.jp/bitz/(ミラー・サイト)

Core Animation for Max OS X and the iPhone: Cre.../Pragmatic Bookshelf
¥3,480
Amazon.co.jp