眠いしお腹すいたし(´・ω・`)

C#関連を主に書きます。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

ObservableVoiceCaptureをリリースしてみました。

この度

www.nuget.org

ObservableVoiceCaptureというライブラリをnugetにリリースしました。

現在のバージョンは0.0.2です。まだ破壊的な変更をするかと思います。

このライブラリは.NET4.5以上、Xamarin.Android、Xamarin.iOSで利用することができます。

github.com

にて管理を行っています。

どんなライブラリ?

音声をキャプチャしたり再生したりできます。

.NET45版ではNAudio

naudio.codeplex.com

を使用して音声データの入出力を行っています。

Xamarin.Android版ではNativeのAudioRecoed/AudioTrackを使っています。

Xamarin.iOS版ではInputAudioQueue/OutputAudioQueueを使っています。

PCMデータをbyteの形で取り扱う非常に低レベルなライブラリです。

特徴は?

インターフェースが全プラットフォームで共通になっています。

サウンドキャプチャはIObservable<byte>でサウンド再生はIObserver<byte[]>になっていて非常にシンプルです。

現時点ではXamairn Pluginの形で提供されていないのですが、近日中にXamarin Pluginに対応しようと思っています。

要するにXamarin Formsで低レベルなオーディオインターフェースをPCL上にて扱えるということになる予定です。

サンプルは?

すいません。間に合っていません。そのうちサンプルアプリを作ろうと思います。

コードのイメージはこんな感じです。

gist.github.com

キャプチャしたPCMデータを1000msecディレイさせてそのまま再生させる例です。

実際に動かすとむっちゃハウリングします(^◇^)

割と簡単にPCMデータのやり取りを行えるようにしているつもりです。

どんなことに使えるの?

これ単体ではあんまり使い道がないかもしれません。

このライブラリは私が今作ろうとしている物の一部をライブラリ化したものです。

PCMデータをエンコードしてネットワーク経由で送信して受信してデコードして再生するとVoIPになったりします。

要するに単体では使えん(^◇^)

ということです。

以上、すごく無駄なエントリーでした(^^)/

Netjsを使ってC#からTypeScriptへの変換をしてみた。

github.com

アイコンがイケメンです。素晴らしいライブラリです。間違いないです。

ではなくてC#をTypeScript/JavaScriptに変換してくれるライブラリです。

仕組み的にはC#で作成したクラスライブラリファイル(.dll)を読み込んで.ts/.jsファイルを作成するイメージになります。

使い方

  1. GitHubからプロジェクトをクローンしてビルドします。

  2. 引数に変換したいdllを指定してexeをコマンドラインから実行します。

  3. そのフォルダにファイルが作成されます。

  4. mscorlib.tsを作成されたtsファイルと同じ階層に置きます。

.NetFrameworkはどこまでカバーされているのか?

基本的にほとんど変換されません。

using Systemの範囲内は少しカバーされています。

具体的には

Netjs/mscorlib.ts at master · praeclarum/Netjs · GitHub

この範囲です。

ようするにNetjsは純粋にC#プログラムをTypeScriptに変換してくれます。

だから既存のC#資産をTypeScirptに。。。ぐへへぇ。みたいな事は出来ません。

私の使い方

既存のC#資産をTypeScriptに変換して使いました。

さっきと言っていることが違うのですが、C#で書いていても.NetFrameworkをあまり使っていない資産というものもあるということです。

NSpeex - Speex for .Net and Silverlight - Source Code

こういう類の資産です。

現在、私の知る限り

github.com

duoco.de

emscriptenでC変換されたjsは結構見かけます。

duocodeeが現時点で継続しているプロダクトとしては最新の環境になると思いますが

連載:「○○してみた」日記:C#→JavaScriptコード変換ってどうよ? DuoCodeを試してみた - @IT

出力されるコードを見て(´・ω・`)となってしまったため試しませんでした。

Netjsでは

SimpleVoiceDeliver/NSpeex.Decoder.ts at master · yuka1984/SimpleVoiceDeliver · GitHub

こんな感じのコードが出力されます。

これなら手を入れようかって気になるかなぁと思います。

使ってみて

元コードがかなり.NETを使っていないコードだったので、すんなり変換できましたが、.NETを使っている部分で足りない部分はmscorlib.tsに書き足しました。

たしかArray.CopyとRandomだったと思います。

ようするに足りなかったら自分で書けば良いんです。

Reference Source

ソースコードは載ってます。

変換精度ですが、一部(´・ω・`)な部分がありました。

IL詳しくないのでわかんないのですが、配列の値セットが array[index] = value;みたいに変換されたいのですがarray.set_item(index, value);という風に変換されることがあって、その部分は手で修正しました。

後は

  • byte[] short[]number[]に変換されるのでUint8Array、Int16Arrayに書き直した。

  • C#の2次元配列[,]に対応していないため、元コードをジャグ配列に直してからビルドした。

ぐらいです。

大体1日もかからず目的のtsファイルを作れたのですごく助かりました。

生かせる部分は限定的ですがアイディア次第では楽することができるかもしれません。

Xamarin.FormsでOnSleepとOnResumeの呼ばれるタイミングをAndroidとiOSで似た感じにしたい

Xamarin.FromsのPCLプロジェクトのApplicationクラスにはOnSleep/OnResumeという仮想関数が用意されています。

Working with the App Lifecycle - Xamarin

  • OnSleep - Called each time the application goes to the background.

  • OnResume - Called when the application is resumed, after being sent to the background.

となっているわけですが、AndroidiOSで呼ばれるタイミングが全然違ったりします。

Androidでは画面上部から設定画面をプルダウンしたりしてもOnSleepは呼ばれませんが、iOSでは画面上部、画面下部から設定画面を引っ張ったりするだけでOnSleepが呼ばれたりします。

なのでタイミングが違いすぎてあまり役に立たなかったりします。

iOSAndroidに合わせたい場合には以下のようにすると一応合わせることができます。

gist.github.com

一応と書いてあるのはAppDelegate.csのOnResignActivationの基底をコールしないことによる副作用が見えないためです。

現時点で副作用らしい副作用は出ていませんが不安である場合には各プラットフォームのMainActivity/AppDelegateにて動作を実装してOnSleep/OnResumeを使用しないほうが良いかもしれません。

MVVMっぽくXamarin.Formsアプリ作ってみました。その5でおしまい

最後です。

MVVMっぽくXamarin.Formsアプリ作ってみました。その1 - 眠いしお腹すいたし(´・ω・`)

MVVMっぽくXamarin.Formsアプリ作ってみました。その2 - 眠いしお腹すいたし(´・ω・`)

MVVMっぽくXamarin.Formsアプリ作ってみました。その3 - 眠いしお腹すいたし(´・ω・`)

MVVMっぽくXamarin.Formsアプリ作ってみました。その4 - 眠いしお腹すいたし(´・ω・`)

前回まででXamarin.Forms本体のプロジェクト、そこコアロジックであるコントローラインターフェースへの実装、そして特定動作のライブラリ作成に関しての説明を行いました。

最後にこれらのライブラリをどのように結合しているかに関して説明します。

DependencyInjection

Xamarin.FormsのPCLプロジェクトで使用しているコントローラインターフェースに実装を注入するためにUnityを使用しています。

unity.codeplex.com

参照設定

まずは参照設定に関して説明します。

Xamarin.FormsのPCLプロジェクト

f:id:tamafuyou:20160422183333p:plain

コントローラインターフェースプロジェクト(SwLib.PCL)のみを参照設定しています。

このプロジェクトからは直接KeyValueStoreは使用しないため、それらは参照設定していません。

Xamarin.Android/Xamarin.iOSプロジェクト

f:id:tamafuyou:20160422183655p:plain

コントローラインターフェースとその実装、KeyValueStoreのインターフェースとその実装を参照設定しています。

コンテナーの利用

まずはUnityコンテナーにインターフェースと実装の登録を行います。

XamarinFormsSampleStopWatch/MainActivity.cs at master · yuka1984/XamarinFormsSampleStopWatch · GitHub

XamarinFormsSampleStopWatch/AppDelegate.cs at master · yuka1984/XamarinFormsSampleStopWatch · GitHub

インターフェースに対する実装を登録いているだけです。 LifetimeManagerにContainerControlledLifetimeManagerを使用することでシングルトンとなります。 LifetimeManagerは割と簡単にカスタマイズできたり作ったりできたりするので拡張性がかなりあります。

そしてコンテナーをPCLプロジェクトに引き渡します。

PCLプロジェクトのApplicationクラスはIUnityContainerを引数としていて、コンストラクタにてContainerを使用してViewModelなどを作成します。

コンテナーを介してインスタンスを作成することで、ViewModelのコンストラクタ引数にコントローラの実装が注入されます。

XamarinFormsSampleStopWatch/App.cs at master · yuka1984/XamarinFormsSampleStopWatch · GitHub

プロジェクトの初期段階ではインターフェースしかなく実装がないため、UI担当者はインターフェースに対するモッククラスを作成しコンテナーに登録する形で作業を進めていました。

UIがある程度完成しコントローラに対する本物の実装も形になってきた段階で登録を置き換えることでコードの変更を殆ど行うことなくモックと本物の切り替えを行うことができます。

ちなみに、今回のサンプルではViewとViewModelの関連付けはコードで書いていますし、実際に作ったプロダクトでもコードで書いています。

これは規模が小さければ、そっちのほうが楽だと思ったからです。

同じViewModelのインスタンスを使ってViewを切り替えたりとか管理できる範囲だったらViewとViewModelをセットにしてしまうよりコートでゴリゴリしたほうが色々とできました。

最後に

結構中途半端な説明になってしまってしまいましたが、このような構成をベースにプロダクトを作成しました。

こういった解説に不慣れなためあまり良いコンテンツにできなかったのですが、自分の復習になった事、ページ遷移に関するネタなど色々と新たに学習できました。

Xamarin.Formsの実践投入においての、良くも悪くも一例として役に立ってくれればうれしいです。

以上

二つのObservableでタイムアウトをチェック

Rxのお勉強。

Rxのタイムアウトって一つのシーケンスの間隔でタイムアウトを管理するのですが、やりたい事は二つのObservableを組み合わせるパターン。

一つ目のObservableがOnNextされてから二つ目のObservableがOnNextされるまでの間にタイムアウトが発生したらErrorを流す。

リクエストとレスポンスを同期させてタイムアウトも管理したいイメージです。

public static IObservable<TResult> SyncTimeout<TSource, TResult>(this IObservable<TSource> first,
        IObservable<TSource> second, TimeSpan dueTime, Func<TSource, TSource, TimeSpan, TResult> selectorFunc)
    {
        var firstStamp = first.Select(x => Tuple.Create(x, DateTime.Now.Ticks));
        var secondStamp = second.Select(x => Tuple.Create(x, DateTime.Now.Ticks))
            .Merge(firstStamp.Delay(dueTime).Select(x => Tuple.Create(x.Item1, (long) 0)));
        return firstStamp
            .Zip(secondStamp, (f, s) => new {First = f.Item1, Second = s.Item1, Span = s.Item2 - f.Item2})
            .Take(1)
            .Repeat()
            .Select(x =>
            {
                if (x.Span < 0) throw new TimeoutException();
                return selectorFunc(x.First, x.Second, TimeSpan.FromMilliseconds(x.Span));
            });
    }

なんかもっとうまく書けないですかねぇ(´・ω・`)

アドバイスいただけたりするとうれしいです。

その後

アドバイスいただいてこんな感じにできました。

gist.github.com

MVVMっぽくXamarin.Formsアプリ作ってみました。その4

続きです。

MVVMっぽくXamarin.Formsアプリ作ってみました。その1 - 眠いしお腹すいたし(´・ω・`)

MVVMっぽくXamarin.Formsアプリ作ってみました。その2 - 眠いしお腹すいたし(´・ω・`)

MVVMっぽくXamarin.Formsアプリ作ってみました。その3 - 眠いしお腹すいたし(´・ω・`)

コントローラインターフェースの実装

f:id:tamafuyou:20160418224824p:plain

この画像の構成はPCLブロジェクトにて行われていますが、コントローラインターフェースの実装はPCLでは行っていません。

GitHub - yuka1984/XamarinFormsSampleStopWatch: Xamarin.Formsサンプルアプリ

今回のサンプルアプリではコントローラインターフェースはPCLプロジェクトのSwLib.Interfaceで作成していますが、インターフェースに対する実装はSwLib.Core、SwLib.Core.Droid、SwLib.Core.Touchで行っています。

SwLib.Coreは普通のクラスライブラリプロジェクト f:id:tamafuyou:20160421202205p:plain

SwLib.Core.DroidはAndroid用クラスライブラリプロジェクト f:id:tamafuyou:20160421201528p:plain

Swlib.Core.TouchはiOS用クラスライブラリプロジェクトになります。 f:id:tamafuyou:20160421201845p:plain

これから説明する方法はUWPプロジェクトへの展開を意識していません。

あくまでもAndroidiOSをターゲットとしたクラスライブラリの作成方法となります。

私は以下の手順でコントローラの実装を行っています。

  1. クラスライブラリプロジェクト(SwLib.Core)を作成してインターフェースを宣言しているPCL(SwLib.Interface)を参照設定する。
  2. インターフェースに対する実装クラスを作成する。
  3. 作成したクラスの単体テストSwLib.CoreTestsを作成して試験を行う。
  4. Androidクラスライブラリ(SwLib.Core.Droid)を作成してPCLを参照設定する。
  5. SwLib.Coreの.csファイルをリンクとしてSwLib.Core.Droidに追加する。 f:id:tamafuyou:20160421203624p:plain
  6. iOSクラスライブラリ(SwLib.Core.Touch)を作成してPCLを参照設定する。
  7. SwLib.Coreの.csファイルをリンクとしてSwLib.Core.Touchに追加する。

SwLib.Coreの実装では普通のライブラリプロジェクトとAndroidiOSのプロジェクトですべてファイルが一緒ですが、例えばコントローラインターフェースが複数あって一つのインターフェースに対してWindowsiOSは共通だけどAndroidに関してはJavaを使って実装するなどという場合には、そのクラスの実装はリンク追加せずAndroidプロジェクトで実装を行います。

このような実装手順とプロジェクト構成はコントローラインターフェースだけでなく、共通化クラスにも適応します。

例としてKeyValueStoreというライブラリ実装を用意しました。

KeyValueStoreの実装

KeyValueStoreと言っていますが、簡易のデータ保存ストアです。

プロジェクト構成は

KeyValueStore.PCLはIKeyValueStoreというインターフェースを持っています。

KeyValueStore.CoreはIKeyValueStoreの.NET46用実装です。

KeyValueStore.Core.DroidはIKeyValueStoreのAndroid実装です。

KeyValueStore.Core.TouchはIKeyValueStoreのiOS実装です。

このライブラリはNuGet Gallery | SQLite.Net PCL 3.1.1を使用してIKeyValueStoreに対する実装を行っています。

Windowsクラスライブラリのファイルは3つ

XamarinFormsSampleStopWatch/Kvs.cs at master · yuka1984/XamarinFormsSampleStopWatch · GitHub

SQLiteのテーブルのマッピング用クラスです。

XamarinFormsSampleStopWatch/KeyValueStoreBase.cs at master · yuka1984/XamarinFormsSampleStopWatch · GitHub

IKeyValueStoreに対するabstractの実装です。

各プラットフォームで差分がない部分を実装して差分をabstractとしています。

XamarinFormsSampleStopWatch/KeyValueStore.cs at master · yuka1984/XamarinFormsSampleStopWatch · GitHub

KeyValueStoreBaseのWindows用実装です。

Kvs.csとKeyValueStoreBaseはAndroid/iOSプロジェクトへリンク追加しますが、KeyValueStore.csはリンク追加せず、それぞれのプロジェクトで作成しています。

XamarinFormsSampleStopWatch/KeyValueStore.cs at master · yuka1984/XamarinFormsSampleStopWatch · GitHub

XamarinFormsSampleStopWatch/KeyValueStore.cs at master · yuka1984/XamarinFormsSampleStopWatch · GitHub

このプロジェクトではあまり実装に差分はありません。

DependencyInjectionを使用すれば吸収できる範囲だとは思います。

KeyValueStoreというプロジェクトを単独で切り離せる用にするために、このような形となっています。

今回のサンプルではXamarin.FormsプロジェクトとSwLib、KeyValueStoreを一つのソリューションで開発していますが、それぞれを別のソリューションで作成する事も可能な依存関係となっています。

ソリューションを分けた上でローカルnugetを使用した結合を行いXamarin.Formsアプリのソリューション自体を小さくすることができたりしますしロース管理のリポジトリを分けて開発することもできます。

今回は以上です。

次回、再度Xamarin.Formsプロジェクトに戻ってDIContainerに関して説明を行って終了にしたいと思います。

毎度細切れで申し訳ありません('ω')

Xamarin iOSのバインディングライブラリを組み込んだらエミュレータで動かせなかった

Xamarin iOSでネイティブのライブラリを使うためにバインディングライブラリを作ってプロジェクトに組み込んだりします。

作り方とかはこの辺を見たりします。

qiita.com

で組み込んでエミュレータで動かそうとビルドするわけですよ。エラーが発生するわけですよ。

アーキテクチャが対応してないよって。

.aファイルのサポートアーキテクチャがARMv7+ARM64だったりするわけで当然ですよね。

バインディングライブラリは参照に追加しているのでこのままではエミュレータで動かせなくなってしまう(´・ω・`)

Bindingライブラリを使っている機能は動かなくてよいからエミュレータで動作させたかったので以下のことをしました。

ちなみにバインディングライブラリは大体こんな感じで作ってます。

    public abstract class HogeBase : IHoge
    {
        public static class NativeMethods
        {
            [DllImport("__Internal")]
            public static extern void Init();

            [DllImport("__Internal")]
            public static extern void Start();

            [DllImport("__Internal")]
            public static extern void Stop();
        }

        public abstract void Start();
        public abstract void Stop();
    }

IHogeはAndroidとごにょごにょ・・・・

でXamarin iOSプロジェクトでは

    public class Hoge : HogeBase
    {
        private static Lazy<Hoge> _instance = new Lazy<Hoge>(() => new Hoge());
        public static Hoge GetInstance() => _instance.Value;
        private Hoge()
        {
            NativeMethods.Init();
        }

        public override void Start()
        {
            Debug.WriteLine("start");
            NativeMethods.Start();
        }

        public override void Stop()
        {
            Debug.WriteLine("stop");
            NativeMethods.Stop();
        }
    }

こんな感じで実装していました。

  1. 構成がDebugでプラットフォームがIPhoneSimulatorの時に__SIM__というコンパイルシンボルを追加。

  2. こんな風に書き換えちゃう

#if __SIM__
    public class Hoge : IHoge
    {
        private static Lazy<Hoge> _instance = new Lazy<Hoge>(() => new Hoge());
        public static Hoge GetInstance() => _instance.Value;
        private Hoge()
        {
            
        }
        public void Start()
        {
            Debug.WriteLine("start");            
        }
        public void Stop()
        {
            Debug.WriteLine("stop");
        }
    }
#else
    public class Hoge : HogeBase
    {
        private static Lazy<Hoge> _instance = new Lazy<Hoge>(() => new Hoge());
        public static Hoge GetInstance() => _instance.Value;
        private Hoge()
        {
            NativeMethods.Init();
        }

        public override void Start()
        {
            Debug.WriteLine("start");
            NativeMethods.Start();
        }

        public override void Stop()
        {
            Debug.WriteLine("stop");
            NativeMethods.Stop();
        }
    }
#endif

要するにIPhoneSimulatorで動かすときはバインディングライブラリを使わないっていうだけです。 参照でバインディングライブラリを追加していたとしても実際に使われなければビルドできます。

この問題ってどうやって解決すればよいか教えてください(´・ω・`)