「Xcode」カテゴリーアーカイブ

【Objective-C】iOSで音声認識サンプル(UIDictationController)

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

音声認識機能を調べていて、OpenEarsは日本語には今ひとつなので、非公式クラスらしけど、UIDictationControllerを使ってテストしてみました。

【参考】
iOS 5.1 の音声入力を使ってアプリケーションを操作してみる – 24/7 twenty-four seven
【iOS】【JPlayer】再生/停止の切り替えで音声認識をスタートして自動でストップする – The jonki

何故か1つ目のリンクのサンプルはうまく動かなくて、2つ目のリンクの方は音楽プレーヤーに組み込まれているので、音声認識だけを切り出してサンプルを作ってみました。

参考にしたリンクによれば、音声認識はキーボード経由でのみ行われ・・・

1.キーボードを表示。
2.音声入力ボタンを押す。
3.話す。
4.完了ボタンを押す。

という手順を踏んだ後、録音された音声がサーバ経由で文字列変換されて戻ってくるらしいです。

で、上記2.と4.を自動化するメソッドが、UIDictationControllerという非公開クラスに含まれています。

※キーボードは、UITextInputを紐付けた不可視のUIViewを用意して、becomeFirstResponderで表示する。
※UITextInputを使うので、必要なメソッドが定義されていないと、かなりの数のエラーがでます。警告だけど。

このサンプルでは、開始ボタンを押すと音声認識が開始(startDictation)され、timer(4.75秒)もしくは停止ボタンで音声認識が終了(stopDictation)するようになっています。画面はこんな感じ。

IMG_7121

認識率はさすがにいいです。コレ、早く公開APIにしてくれたらいろいろ使えそうなのになあ。iOS5.1からずっと非公開ってことは何かあるんですかね、問題が。

音声認識を使ったアプリのアイデアはあるんですが、OpenEarsで試してみるか、この方式で作っておいて公開APIになるまで寝かしておくか、悩みどころです。

一応、ソースをGitHubに上げてみました。初めてのGitHub(笑)。ご参考までに。

【Objective-C】ランダムな数値を取得する方法

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

配列からランダムな数を取得するというのは、どんなプログラムでもよくやります。

for (int i=[iconArray count]-1; i>=0; i--) {
	int j = rand() % (i+1);
	[iconArray exchangeObjectAtIndex:i withObjectAtIndex:j];
}

一見よさそうだけど、なんとコレ、毎回同じ数値しか返ってきません。このrand()という関数は、毎回初期化しないと同じ値を返すらしい。たとえばこの記事なんかは、初期化をすっ飛ばして書いてあるので、実際に使ってみるとハマるわけです(笑)。

※上の処理だと、初期化がまったく行われていないので、ビルドしたアプリを起動し直しても、ビルドし直しても毎回必ず同じ値になります。

で、初期化を加えたのがコレ。

srand(time(NULL));
for (int i=[iconArray count]-1; i>=0; i--) {
	int j = rand() % (i+1);
	[iconArray exchangeObjectAtIndex:i withObjectAtIndex:j];
}

srand()はrand()の初期化用関数で、毎回違う値を返すことで、返す値が変わることになるらしい。

【参考】
指定した範囲内で乱数を発生させる | Objective-Cでのアプリ開発記

で、こっちのarc4random()は初期化込みの関数で、初期化を意識せずに使える。

for (int i=[iconArray count]-1; i>=0; i--) {
	int j = arc4random() % (i+1);
	[iconArray exchangeObjectAtIndex:i withObjectAtIndex:j];
}

この方法が、初期化ミスもなく一番いいのではないかという結論になるわけですが。

【参考】
[Xcode]ランダム数値の生成 | C++ | alperithm
iPhone-Labo: ランダムな数値を取得する – rand() arc4random()

ググってみるとだいたい「arc4random()使えば問題ないよね」って書いてあるんだけど、そもそもsrand()とrand()が別々に実装されている理由って何なんですかね?そっちの方がすごく気になる。知ってる人がいたら教えてください。

ちなみにココで書かれている、配列を後ろから参照して入れ替える方法をFisher–Yatesアルゴリズムというらしい。初めて聞きました。

【参考】
Fisher–Yatesアルゴリズムがすごかったです。: PandaNoir
Fisher-Yates Shuffle – Faith and Brave – C++で遊ぼう

【Objective-C】アニメーションの終了イベント

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

とりあえずこんな感じで。delegateを設定して、終了時に呼ばれる関数をsetAnimationDidStopSelector:@selectorで指定する。

//アニメーション:設定
[UIView setAnimationsEnabled:YES];
[UIView beginAnimations:@"Flip" context:nil];
[UIView setAnimationDelegate : self ];
[UIView setAnimationDuration:0.5f];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:card cache:YES];
[UIView setAnimationDidStopSelector:@selector(compareCard)];

//アニメーション:開始
[UIView commitAnimations];

※card:アニメーションするUIView、compareCard:アニメーション終了時に呼ばれる関数

【参考】
あじもんすたー日記,iOSアニメーションでのミス

【Objective-C】再生リストからiCloudとDRM付きの曲を外す

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

前回書いたように、AVPlayer(AVQeuePlayer)でiPodライブラリの音楽を再生する場合、DRM付きの曲が再生できない。表示されているのに再生できないのはアプリとしてマズいので、再生リストから外す方法を考えてみました。

#import "SelectViewController.h"

@interface SelectViewController ()

@end

const int CATEGORY_ALBUM = 0;
const int CATEGORY_PLAYLIST = 1;
const int CATEGORY_ARTIST = 2;

@implementation SelectViewController
{
    NSDictionary *dataSource;
    NSMutableArray *key;

    int current_category;
}

//(中略)

-(void)setMusicCategory
{
    MPMediaQuery *query;
    switch (current_category) {
        case CATEGORY_ALBUM:
            query = [MPMediaQuery albumsQuery];
            break;
        case CATEGORY_PLAYLIST:
            query = [MPMediaQuery playlistsQuery];
            break;
        case CATEGORY_ARTIST:
            query = [MPMediaQuery artistsQuery];
            break;

        default:
            break;
    }

    //iCloudデータをフィルタリングで除外
    [query addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:[NSNumber numberWithBool:NO] forProperty:MPMediaItemPropertyIsCloudItem]];

    NSArray *collectionList = query.collections;
    key = [[NSMutableArray alloc] init];
    NSMutableArray *albumList = [[NSMutableArray alloc] init];

    for (MPMediaItemCollection *collection in collectionList) {
        MPMediaItem *representativeItem = [collection representativeItem];
        NSString *albumtTitle = [representativeItem valueForProperty:MPMediaItemPropertyAlbumTitle];

        //DRMチェック(AVPlayerで再生できないため/DRM対象曲のみを弾く)
        int drm_count = 0;

        NSMutableArray *itemList = [[NSMutableArray alloc] init];

        for (MPMediaItem *item in collection.items) {
            NSURL *url_item = [item valueForProperty:MPMediaItemPropertyAssetURL];
            if (url_item == NULL) {
                drm_count++;
            } else {
                [itemList addObject:item];
            }
        }

        if (itemList.count > 0) {
            MPMediaItemCollection *collection_new = [MPMediaItemCollection collectionWithItems:itemList];

            [albumList addObject:collection_new];

            NSString *keyword;
            switch (current_category) {
                case CATEGORY_ALBUM:
                    keyword = albumtTitle;
                    break;
                case CATEGORY_PLAYLIST:
                    keyword = [collection valueForProperty:MPMediaPlaylistPropertyName];
                    break;
                case CATEGORY_ARTIST:
                    keyword = [representativeItem valueForProperty:MPMediaItemPropertyArtist];
                    break;

                default:
                    break;
            }

            [key addObject:[NSString stringWithFormat:@"%@",keyword]];
        }
	}

    dataSource = [NSDictionary dictionaryWithObjects:albumList forKeys:key];
}

※ SelectViewControllerは、UITableViewControllerを継承したカスタムクラスです。

上に書いた関数「setMusicCategory」は、UITableViewに選択させる再生リストを表示するためのdataSourceを作成するための関数で、ボタンなどによって再生リストの種類(プレイリスト/アルバム/アーティスト)を切り換えたときに・・・

current_category = CATEGORY_ALBUM;
[self setMusicCategory];
[self.myTableView reloadData];

※self.myTableViewは、UITableViewControllerに含まれるUITableView。

みたいな感じで使います。

・iCloudのデータを除外する
これはフィルタ使って弾けるので、上のソースのこの部分。

//iCloudデータをフィルタリングで除外
[query addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:[NSNumber numberWithBool:NO] forProperty:MPMediaItemPropertyIsCloudItem]];

・DRM付きの曲を除外する
1つのアルバムで、DRMつきとDRMなしが混在することはないと思われるけど、プレイリストは任意に作れるので、引っ張ってきたMPMediaItemCollectionから、MPMediaItemのMPMediaItemPropertyAssetURLをひとつずつチェックしていくしかなさそうです。

for (MPMediaItem *item in collection.items) {
    NSURL *url_item = [item valueForProperty:MPMediaItemPropertyAssetURL];
    if (url_item == NULL) {
	drm_count++;
    } else {
	[itemList addObject:item];
    }
}

一応、コレで問題なさそうです。もっと効率的な方法があったら教えてください。

【Objective-C】AVQeuePlayerでMPMediaItemPropertyAssetURLがnullになる

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

AVPlayer(AVQeuePlayer)でiPodライブラリの音楽を再生するは、MPMediaItemからMPMediaItemPropertyAssetURLを取りだして、AVPlayerItemを生成する必要がある。

MPMediaItemPropertyAssetURL
A URL pointing to the media item, from which an AVAsset object (or other URL-based AV Foundation object) can be created, with any options as desired. Value is an NSURL object.

The URL has the custom scheme of ipod-library. For example, a URL might look like this:

ipod-library://item/item.m4a?id=12345
Usage of the URL outside of the AV Foundation framework is not supported.

Available in iOS 4.0 and later.

要はAV Foundation frameworkであるAVPlayer(AVQeuePlayer)で使える形に変換する必要があるってことですね。実際にはこんな感じ。

NSMutableArray *qItems;

for (MPMediaItem *mpItem in collection.items) {
	NSURL *url = [mpItem valueForProperty:MPMediaItemPropertyAssetURL];
	AVPlayerItem *api = [[AVPlayerItem alloc] initWithURL:url];
	[qItems addObject:api];
}

※collectionは、MPMediaItemCollection

ただこれでテストしていたら再生できない曲があって、いろいろ調べてみたら、MPMediaItemPropertyAssetURLがnullだった。

・昔、iTunesで購入したDRM付きの曲。
・iCloudにあって、iPhoneにダウンロードされていない曲。

この2つはURLがNULLになるらしい。

【参考】
blog.polikeiji.net: MPMediaItemPropertyAssetURLがnullのとき
iphone – How to detect if an MPMediaItem represents a DRM-protected audio track on iOS – Stack Overflow
ios4 – MPMediaItemPropertyAssetURL becomes null when Using MPMediaItems to play songs – Stack Overflow

で、こんな記事を見つけたので、再ダウンロードしてみたけど、DRMが外れない曲がある。まあ提供元が対応してなければ、そのままだよね。仮にDRMを外すことができたとしても、その行為をユーザにお願いするのはどうか?という問題もあるし。

iTunes Storeで購入したDRM楽曲は、再ダウンロードするとDRMフリーになる? | iPod love

ということで、この2種類は再生リストから外す方向で処理を考えてみます(続く)。

【Objective-C】AVPlayer関連リンク

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

とりあえずiPodライブラリを再生する音楽プレーヤー的なものを考えていたら、音の再生には3種類あるらしい。

・AVAudioPlayerクラス
・MPMusicPlayerControllerクラス
・AVPlayerクラス

【参考】Slow Days » Blog Archive » Core Audioざっくり覚え書き

今回考えているのは・・・

・バックグラウンド再生が可能(標準のミュージックプレイヤーと同じ)
・iPodライブラリ(自分がiPhoneに入れている音楽)の再生が可能

という前提なので、これだと「AVPlayerクラス」一択になるらしいので、関連リンクをメモしておきます。
(厳密には今回は、AVPlayerのサブクラスであるAVQueuePlayerを使っています)

Background Modes in iOS Tutorial
(これのPlaying Audioの箇所が役に立ちました)

かゆいところに手が届きそうなAVFoundationとMediaPlayerによる音楽再生アプリ作成メモ | Qiita
(ロック画面へアプリから情報表示など、知らなかったことばかり)

ios – Possible to insert item at top of queue using AVQueuePlayer? – Stack Overflow
(AVQueuePlayerで巻き戻しメソッドがないけどどうするの?キュー(Queue)なので、再生終わると配列から削除されます。で、原始的だけど、1つ前のAVPlayerItemを配列に入れて、順序を操作することで対応出来ます。ただ後ろに追加しかできないので、ちょっと面倒。)

AVPlayer Class Reference

AVQueuePlayer Class Reference

AVPlayerItem Class Reference

Objective-CのKVO(キー値監視)の使い方 | blog.nambo.jp

【2014.11.2 追記】
iOS の動画再生を試す | アカベコマイリ
(「AVPlayer の初期化」あたりは音楽でも同じ)

既にいくつか試してるのですが、その辺の記事は追々。

【Xcode】iTunes connectへのアプリ申請

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

昨日、無事アプリがリリースされました。パチパチ。「アイコン顔カメラ/icon face camera」というiPhoneアプリです。無料なので是非試してみて下さい。

アプリ申請でいくつか躓いたことがあったので、今後のためにメモ。
過去にもiOSアプリ制作に携わったことはあるのですが、リリース作業は今回が初めてでした。

・iTunes Connectは、iOS8リリースに合わせて更新された。
以前のと見た目が全然違っているので、ハッキリいって2014.8月以前の情報は無視した方がいいです。混乱します(笑)。

【参考】Xcode – 新しくなったiTunes Connectでのアプリの申請方法について – Qiita

・iAdはスクリーンショットに表示しない。
一週間経ってやっとレビューが始まったかと思ったら、数時間後にリジェクト(笑)。で、内容を確認してみると・・・

We found that your app and/or its metadata does not appear to include final content, which is not in compliance with the App Store Review Guidelines.

Specifically, your screenshots display empty iAd banners. It would be appropriate to remove the empty iAd banners from your screenshots.

で、訳すとこんな感じです。

私たちは、あなたのアプリケーションおよび/またはそのメタデータがApp Storeのレビューガイドラインを遵守していない最終的なコンテンツを含むように表示されないことがわかった。

具体的には、あなたのスクリーンショットは、空のiAdバナーを表示します。それはあなたのスクリーンショットから、空のiAdバナーを削除することが適切であろう。

iAd使ってるのにスクリーンショットに表示がないのはマズいと思って、わざわざ入れたのに完全に裏目でした。アプリ内の使っている画像もチェックして修正しました。

・アプリのバイナリのアップロードでエラー。
アーカイブを作って、Organizerからアップロードしたらエラーが出ました。

ERROR ITMS-9000: “Redundant Binary Upload. There already exists a binary upload with build version ‘1.0’ for train ‘1.0’”

調べてみたら、ビルド番号はアップロードごとに変更しないといけないらしいです。設定場所は、Xcodeの右カラムでプロジェクトを選んで、General>Identity>Buildです。デフォルトではVersionとBuildが同じになっていますが、Buildの番号を1.0.1などで変更すればいいらしいです。

【参考】ERROR ITMS-9000: “Redundant Binary Upload. There already exists a binary upload with build version ‘1.0’ for train ‘1.0’”のエラー対応方法

・iTunes connectでのビルド更新
新しくアップしたアプリ(バイナリ)は、マイAppのビルド欄に出てくるのかと思ってたら、マイAppのプレリリースタブの中に表示されるんですね。

・マイAppのビルド欄で「-」ボタン押して古いビルドを削除する。
・マイAppのビルド欄で「+」ボタン押して新しいビルドを選択する。

これで新しいビルドで再申請できました。

【参考】【アプリ申請】iTunes Connectが新しくなった!! 申請取り下げバイナリの再アップする方法

ちなみにiAdのスクリーンショット以外は問題(リジェクト)なかったのですが、申請からレビュー完了までかなり時間かかりました。iOS8のリリースと時期的に重なって、審査が混んでいたのかもしれませんが、参考までに書いておきます。

※申請時はリリース日付を2014.11.1に設定。尚、下記日時は米国時間です。

・September 28, 2014 22:34(c-geru)Prepare for Upload
・September 29, 2014 03:46(c-ger)Waiting For Review
・October 09, 2014 12:37(Apple)In Review
・October 09, 2014 19:09(Apple)Metadata Rejected >> iAdのスクリーンショットでリジェクト
・October 10, 2014 03:12(c-geru)Waiting For Review
・October 19, 2014 12:21(Apple)In Review
・October 19, 2014 13:02(Apple)Pending Developer Release >> レビュー完了
・October 20, 2014 09:25(c-geru)Processing for App Store >> リリース日付を10/21に変更
・October 20, 2014 09:31(Apple)Ready for Sale

【Xcode】iOS用のOCRライブラリ

あるんですね。有名なのは、この二つらしいです。

tesseract-ocr
nhocr

ググってみると、tesseract-ocrを使ったサンプルが多いようです。

iOSで日本語OCR!サンプルアプリ構築編〜iOS SDK 6.1 + tesseract-ocr 3.02〜 | Developers.IO
tesseract-orc で 自作プログラムに画像の文字を読ませてみた – Kikuchy’s Second Memory

一応、メモとして。

【Objective-C】MPMediaQueryで取得される曲情報にはiCloudも含まれる

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

MPMusicPlayerControllerでミュージックライブラリの再生をしていて、一覧に出てくるのに再生できない曲があって「何だろう?プログラムの問題?」といろいろ調べていたら・・・

MPMediaQueryで取得される曲情報にはiCloudも含まれる

ということらしい。リストにはあるけど、曲データがないので再生できない。というか、[MPMusicPlayerController play] ではエラーにはならずに再生した途端に「MPMusicPlayerControllerPlaybackStateDidChangeNotification」が返ってきて、MPMediaItem == NULL で再生終了になる。

※問題の出たアルバムは1曲しか含まれていなかったので。

具体的には、MPMediaItem に MPMediaItemPropertyIsCloudItem というプロパティがあります。これでiCloudにあるのかどうか判定できます。

・MPMediaItemPropertyIsCloudItem
A Boolean value indicating whether the media item is an iCloud item (YES), or not (NO). A media item is considered an iCloud item if it is available via iTunes Match and is not already stored on the device. Value is an NSNumber object representing a BOOL data type.

Available in iOS 6.0 and later.

・元々のコードはこんな感じ。

MPMediaQuery *query = [MPMediaQuery albumsQuery];

NSArray *albumlists = query.collections;

for (MPMediaItemCollection *albumlist in albumlists) {
MPMediaItem *representativeItem = [albumlist representativeItem];
NSString *albumtTitle = [representativeItem valueForProperty:MPMediaItemPropertyAlbumTitle];
}

・で、調べてみたら、iCloudのデータを除外するには [MPMediaQuery addFilterPredicate:] ですればOK。

MPMediaQuery *query = [MPMediaQuery albumsQuery];

[query addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:[NSNumber numberWithBool:NO] forProperty:MPMediaItemPropertyIsCloudItem]];

NSArray *albumlists = query.collections;

for (MPMediaItemCollection *albumlist in albumlists) {
MPMediaItem *representativeItem = [albumlist representativeItem];
NSString *albumtTitle = [representativeItem valueForProperty:MPMediaItemPropertyAlbumTitle];
}

問題の出た曲データが特定できなくて、iOS純正のミュージックプレイヤーと自作アプリの曲アルバム一覧を比較していて、やっと気づきました。

【参考】
MPMediaItem Class Reference | iOS Developer Library
MPMediaQuary albumsQuery結果からiCloudのみにあるものを除く | 響雲
articles of samekard: iPod library access クラウド音源への対応
MPMediaQuery – ignore songs on Cloud (i.e., iTunes Match)

【Objective-C】音楽再生関連のまとめ

諸々調べているのでメモ。

MPMusicPlayerControllerクラス | Second Flush
MPMediaQuery Class Reference | iOS Developer Library — Pre-Release
iOSで、ミュージックライブラリにアクセスして音楽を再生する | nackpan Blog
音楽プレイヤのメソッド(1) | Second Flush
Objective-C – ARC環境下でAVAudioPlayerを使い柔軟かつシンプルにサウンドを再生する方法 – Qiita
プロフェッショナルプログラマー: iOS 音声再生(AVAudioPlayer)
MPMediaEntity Class Reference | iOS Developer Library
iOS – iTunesみたいに再生中の曲のアートワーク(ジャケット写真)に合わせて背景色と文字色を変える – Qiita
Media Player Framework Reference | iOS Developer Library
AddMusic | iOS Developer Library
AVPlayerDemo | iOS Developer Library
[iOS]AVPlayerを使う | nackpan Blog