2022年12月19日月曜日

secile general app privacy policy.

 Thank you for using my app. The app do not collects any information

Secileが作成したアプリを利用いただきありがとうございます。このアプリはいかなる情報も収集しません。

2022年9月12日月曜日

TonightMoon Privacy Policy.


Thank you for using TonightMoon. The information this app collects and its purposes are as follows:
  • location information
    • It is used to display the current position on the earth.
    • I use it to display Google AdMob ads.


TonightMoonを利用いただきありがとうございます。このアプリが収集する情報とその目的は以下の通りです。

  • 位置情報
    • 地球上に、現在位置を表示するために使用しています。
    • Google AdMob広告を表示するために使用しています。


2018年9月1日土曜日

プライバシーポリシー

アプリ内に入力したテキストデータやカメラで撮影した画像データは端末内に保存する目的でのみ使用されます。それらのデータが外部に送信されることはありません。

2017年3月10日金曜日

「フォトモー」プライバシーポリシー

写真タッチで効果音&写真が動く 「フォトモー」
プライバシーポリシー

このアプリがマイクを利用して録音した音声は、作品中の効果音としてのみ利用されます。その他の用途で利用されることはありません。

2014年7月23日水曜日

MediaPlayerでoggファイルがループ再生されてしまう問題(2)

getDuration()で得た再生時間(ms)経過後にMediaPlayerを停止させる方法を行うと、タイミングにずれが発生し、少し早めに停止したり、逆に少し遅くなって一瞬ループして先頭に戻ったりします。

このズレがどうしても許せない場合はこの方法は使えません。別の方法を考えてみました。

ANDROID_LOOP=trueメタデータ付きのoggファイルを直接再生するのではなく、一旦別ファイルにコピーして再生します。さらにコピーの途中で 「ANDROID_LOOP=true」に一致する部分があれば、その部分を空白文字に置き換えてしまいます。

static public boolean copyWithAndroidLoopFalse(InputStream src, OutputStream dst) {
    byte[] android_loop = {0x41, 0x4E, 0x44, 0x52, 0x4F, 0x49, 0x44, 0x5F, 0x4C, 0x4F, 0x4F, 0x50, 0x3D, 0x74, 0x72, 0x75, 0x65};
    while(true) {
        byte[] raw_page = readPageBytes(src);
        if (raw_page == null) return false;
        if (raw_page.length == 0) break;
        int index = findBytePattern(raw_page, android_loop);
        if (index >= 0) { //ANDROID_LOOP=trueが見つかったら空白文字に置き換える
            for(int i=0; i<android_loop.length; i++) {
                raw_page[index + i] = 0x20;
            }
        }
        try {
            dst.write(raw_page);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }
    return true;
}

static private int findBytePattern(byte[] search_byte, byte[] search_pattern) {
    int byte_length = search_byte.length;
    int pattern_length = search_pattern.length;
    for(int i=0; i<=byte_length - pattern_length; i++) {
        for(int j=0; j<pattern_length; j++) {
            if (search_byte[i+j] == search_pattern[j]) {
                if (j == search_pattern.length - 1) {
                    return i; //search_pattern全て一致
                }
            } else {
                break;
            }
        }
    }
    return -1; //一致しなかった
}

こんなことして大丈夫なの?と思われるかもしれませんが、Vorbisの仕様ではユーザーコメントは長さとコメントの組み合わせで、長さが変わらなければ大丈夫と思います。それよりもむしろ、「ユーザーコメントは名前と値をイコールでつないだもの」とあるのですが、そっちを守っていないのが少し心配です。
フォーマットは以下のサイトを参考にしました
Mitsugu Oyama のソフトウェア倉庫


2014年7月12日土曜日

MediaPlayerでoggファイルがループ再生されてしまう問題

MediaPlayerでANDROID_LOOP=trueメタデータ付きのoggファイルを再生すると、setLooping(false)にもかかわらずループしてしまいます。この問題(というか仕様)への対処方法を検索してもいい方法が見つからなかったため、自分で調べてみました。

対策としては、ANDROID_LOOP=trueのoggファイルを再生するときには、getDuration()で得た再生時間(ms)経過後にMediaPlayerの再生を停止するしかないようです。

問題はどうやってANDROID_LOOP=trueのoggファイルか判定するかです。

まずANDROID_LOOP=trueメタデータ付きのoggファイルをバイナリエディタで開いてみました。すると文字列で"ANDROID_LOOP=true"を確認することができます。



ちょっと強引な方法ですが、oggファイルを読み込んでその中に文字列"ANDROID_LOOP=true"が含まれているか確認すれば判定できそうです。

いくつかのoggファイルを確認したところ、"ANDROID_LOOP=true"が存在する場所はファイルによって異なっていたため、読み込むバイト数はあらかじめきめられません。ただしいずれのoggファイルも先頭付近にありましたので、とりあえず256バイト読み込んで判定してみます。

static public boolean isAndroidLoop(InputStream stream) {
    try {
        byte[] data = new byte[256];
        stream.read(data);
        String text = new String(data);
        return text.contains("ANDROID_LOOP=true");
    } catch (IOException e) {
        e.printStackTrace();
        return false;
    }
}

手持ちのoggファイルをいくつか試してみましたが、これで一応、ANDROID_LOOP=trueかどうか判定することができました。でも本当にこれでいいんでしょうか。
  1. ANDROID_LOOP=trueが256バイト以降にあったら正しく判定できない
  2. oggファイルのサイズが256バイト以下の小さいファイルであったら正しく判定できない
  3. そもそもこのやり方って正しいの?
まじめにやるには、oggファイルのフォーマットを理解して、フォーマットに従ってファイルを読み込む必要があります。フォーマットの詳細についてすべて理解する必要はありませんが(私も理解していません)、簡単に説明すると
  • oggファイルは音声専用のファイルフォーマットではなく、movやaviのように動画や音声など複数のデータをまとめて格納するためのコンテナファイル
  • oggファイルはページという単位でデータを格納する
そこでフォーマットにしたがってoggファイルを読み込んでみます。フォーマットは以下のサイトを参考にしました
本家のogg仕様
Mitsugu Oyama のソフトウェア倉庫

まず1ページ分のbyte配列を読み込む関数を作成します。
public static byte[] readPageBytes(InputStream stream) {
    try {
        DataInputStream dis = new DataInputStream(stream);

        // 読み込めなかったらデータの終わりと判断する
        byte[] capture_pattern = new byte[4];
        if (dis.read(capture_pattern) == -1) return new byte[0];
        if (capture_pattern[0]!=0x4f || capture_pattern[1]!=0x67 || capture_pattern[2]!=0x67 || capture_pattern[3]!=0x53) return null;

        byte[] header = new byte[22];
        dis.read(header);

        byte page_segments = dis.readByte();

        int segment_data_bytes = 0;
        byte[] segment_table = new byte[page_segments];
        int segment_table_int[] = new int[page_segments];
        for(int i=0; i<page_segments; i++) {
            segment_table[i] = dis.readByte();
            segment_table_int[i] = segment_table[i] & 0xFF; //JavaではByteは符号付き
            segment_data_bytes += segment_table_int[i];
        }

        byte[][] segment_data = new byte[page_segments][];
        for(int i=0; i<page_segments; i++) {
            segment_data[i] = new byte[segment_table_int[i]];
            dis.read(segment_data[i]);
        }

        int result_index = 0;
        int result_bytes = 4 + 22 + 1 + page_segments + segment_data_bytes;
        byte[] result = new byte[result_bytes];
        System.arraycopy(capture_pattern, 0, result, result_index, 4); result_index += 4;
        System.arraycopy(header, 0, result, result_index, 22); result_index += 22;
        result[result_index] = page_segments; result_index += 1;
        System.arraycopy(segment_table, 0, result, result_index, page_segments); result_index += page_segments;
        for(int i = 0; i<page_segments; i++) {
            System.arraycopy(segment_data[i], 0, result, result_index, segment_table_int[i]); result_index += segment_table_int[i];
        }                    
                
        return result;
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}

この関数を以下のように 呼び出します。
static public boolean isAndroidLoop(InputStream stream) {
    while(true) {
        byte[] raw_page = readPageBytes(stream);
        if (raw_page == null) break;
        if (raw_page.length == 0) break;
        String text = new String(raw_page);
        if (text.contains("ANDROID_LOOP=true")) {
            return true;
        }
    }
    return false;
}

2014年2月21日金曜日

変身コスチューム

変身シリーズ第三弾。Androidアプリ「変身コスチューム」をリリースしました。今回のモチーフは「ハピネスチャージプリキュア」です。


アプリ自体はハピネスチャージプリキュア放送前から完成していましたが、女の子や服や靴の絵を準備するのに手間取り、ようやく公開となりました。

合わせて、変身コスチュームをカスタマイズするツール公開します。このツールをつかえば、このアプリにでてくる画像や音声をハピネスチャージプリキュアにカンタンに変更することができます。
ただしこちらはまだ開発中で、自分で画像や音声を登録する機能はありません。バンダイのプリキュアおもちゃサイトから画像や音声をダウンロードする機能だけです。お試しください。