IronPythonでインターフェースのイベントを実装する

Pythonにはイベントを定義する構文がないから困った。

そんなときはadd_イベント名(self, handler)、remove_イベント名(self, handler) を実装する。

例えば、INotifyPropertyChangedインターフェースのPropertyChangedイベントだとこう。

class NotifyObject(INotifyPropertyChanged):
	def add_PropertyChanged(self, handler):
		if not hasattr(self, "_propertyChangedHandlers"):
			self._propertyChangedHandlers = [handler]
		else:
			self._propertyChangedHandlers.append(handler)

	def remove_PropertyChanged(self, handler):
		if not hasattr(self, "_propertyChangedHandlers"):
			self._propertyChangedHandlers = []
		else:
			try:
				self._propertyChangedHandlers.remove(handler)
			except ValueError:
				pass

	def RaisePropertyChanged(self, propertyName):
		if not hasattr(self, "_propertyChangedHandlers"):
			self._propertyChangedHandlers = []
		else:
			for handler in self._propertyChangedHandlers:
				handler(self, PropertyChangedEventArgs(propertyName))

 

こんな感じでイベントハンドラの管理も自分でやる。

ハリオ カフェプレス・スリム

ハリオ カフェプレス・スリム ブラック CPS-2TBを買った。味がブレにくい抽出器具が欲しくて、初めはサイフォンを検討していたのだけど、安くてペーパードリップでもサイフォンでも得られない味わいを体験させてくれそうなフレンチプレスを購入。

書籍とかネットとかで見た抽出方法を色々試してみたんだけど、なんだかんだ言って外箱に書いてあるハリオ公式(?)の淹れ方、粗挽き、熱湯、2分抽出が美味しかった。

これは手軽に美味しいコーヒーが飲める良いものです。

ハリオのサーバーとドリッパーを購入

今までメーカー不詳のサーバーを使っていたのだけど、目盛が二杯目からしか入ってなくて使いにくかった。そこでハリオ V60コーヒーサーバー 700 VCS-02Bを買ってきた。早速試そうと思ったら問題発覚。カリタの101ドリッパー(1~2杯用のね)が嵌らない!(2~4杯用のは問題ない)

f:id:anis774:20110321183434j:image

しかしガッカリすることは何もない。だって、これを買うときに、同じ棚にあったハリオ V60透過ドリッパー クリア VD-01Tも一緒に衝動買いしちゃってたんですもの。

早速使ってみましたよ。カリタは注ぐ湯量が多いと豆が湯に完全に漬かってしまって雑味が出てしまうのだけど、ハリオのは注いだ湯がそのまま落ちて、結果抽出時間が短くなってスッキリした味になる。その分、ハリオの方が抽出時間が短かい方向への自由度は高いと感じた。カリタは湯が落ちる速度に合わせて湯を注ぐことになるから味が安定する。そのかわり失敗すると凄く不味い。ハリオは自由度が高い分、狙った味から外れることはあっても失敗は少ない。

その味なんだけど、最初の一口目では角が丸くてスッキリした印象を持った。ただ、少し冷めてくると、甘味が良く出てるのに気付いた。”ネルドリップの味わい”って宣伝文句はこれのことなんだなと理解。渋みやエグ味は出にくい。

これ、お勧めですよ。どの程度まで味に幅を持たせられるのか、これから使い込んでいくのが楽しみです。

TwitterizerでUserStreamを利用する

TwitterのAPIライブラリ、Twitterizer Ver 2.3.1を使ってUserStreamを利用しようと以下のコードを書いたんです。

using System;
using System.Net;
using System.Reflection;

namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			string consumerKey = "hoge";
			string consumerSecret = "hogehoge";
			Twitterizer.OAuthTokenResponse reqToken = Twitterizer.OAuthUtility.GetRequestToken(consumerKey, consumerSecret, "oob");
			Uri uri = Twitterizer.OAuthUtility.BuildAuthorizationUri(reqToken.Token);
			System.Diagnostics.Process.Start(uri.ToString());
			Console.WriteLine("Pinコードを入力しる");
			string pinCode = Console.ReadLine();
			Twitterizer.OAuthTokenResponse accToken = Twitterizer.OAuthUtility.GetAccessToken(consumerKey, consumerSecret, reqToken.Token, pinCode);
			Twitterizer.OAuthTokens token = new Twitterizer.OAuthTokens
			{
				ConsumerKey = consumerKey,
				ConsumerSecret = consumerSecret,
				AccessToken = accToken.Token,
				AccessTokenSecret = accToken.TokenSecret
			};
			using (var twitterStream = new Twitterizer.Streaming.TwitterStream(token))
			{
				twitterStream.OnStatusReceived += (Twitterizer.TwitterStatus status) =>
				{
					Console.WriteLine(string.Format("{0}/({1})\r\n\t{2}",
						status.User.Name,
						status.User.ScreenName,
						status.Text));
				};
				twitterStream.StartUserStream();
				while (true) { }
			}
		}
	}
}

何度試しても404が返ってくる。実はこれ、ベータ版のアドレスにアクセスしてるんですよ。これがソースコード

no title

がっつりとハードコーディングされちゃっててどうにもなりません。自家ビルドして使う?嫌ですよ、そんなの。面倒じゃないですか。幸い、このStartUserStreamメソッドは短いです。じゃあStartUserStreamメソッドを呼び出してる部分のコードをURLだけ置き換えた同じ内容のコードに書き換えればいいじゃない!

以下の3行までは問題ないです。

WebRequestBuilder builder = new WebRequestBuilder(new Uri("http://betastream.twitter.com/2b/user.json"), HTTPVerb.GET, this.Tokens);
HttpWebRequest request = builder.PrepareRequest();
request.KeepAlive = true;

URLを置き換えるだけです。WebRequestBuilderクラスはpublicなので問題なく利用出来ます。引数に渡しているthis.Tokensはコンストラクタに渡したインスタンスそのものなので、こちらのスコープで保持しているものに置き換えればいいです。

問題は最後の行ですね

request.BeginGetResponse(StreamCallback, request);

引数に渡しているStreamCallbackっての、これprivateなメソッドなんですよ。これはリフレクションで取ってくることにしました。

MethodInfo method = twitterStream.GetType().GetMethod("StreamCallback",
    BindingFlags.Instance |
    BindingFlags.NonPublic);
AsyncCallback callback = (_) =>
{
    method.Invoke(twitterStream, new[] { _ });
};

リフレクションで取ってきたメソッドをInvokeするコードをAsyncCallbackデリゲートで包んであげればOKじゃんって寸法です。

で、最終的なコードがこんな感じ

using System;
using System.Net;
using System.Reflection;

namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			string consumerKey = "hoge";
			string consumerSecret = "hogehoge";
			Twitterizer.OAuthTokenResponse reqToken = Twitterizer.OAuthUtility.GetRequestToken(consumerKey, consumerSecret, "oob");
			Uri uri = Twitterizer.OAuthUtility.BuildAuthorizationUri(reqToken.Token);
			System.Diagnostics.Process.Start(uri.ToString());
			Console.WriteLine("Pinコードを入力しる");
			string pinCode = Console.ReadLine();
			Twitterizer.OAuthTokenResponse accToken =Twitterizer.OAuthUtility.GetAccessToken(consumerKey, consumerSecret, reqToken.Token, pinCode);
			Twitterizer.OAuthTokens token = new Twitterizer.OAuthTokens
			{
				ConsumerKey = consumerKey,
				ConsumerSecret = consumerSecret,
				AccessToken = accToken.Token,
				AccessTokenSecret = accToken.TokenSecret
			};
			using (var twitterStream = new Twitterizer.Streaming.TwitterStream(token))
			{
				twitterStream.OnStatusReceived += (Twitterizer.TwitterStatus status) =>
				{
					Console.WriteLine(string.Format("{0}/({1})\r\n\t{2}",
					status.User.Name,
					status.User.ScreenName,
					status.Text));
				};
				//twitterStream.StartUserStream();
				//StartUserStreamメソッドがベータ版のURLに繋いでエラーになるから
				//下記ソースの動作をURLだけ変えて実行
				//http://pm.twitterizer.net/projects/twitterizer/repository/entry/tags/twitterizer-2.3.1/Twitterizer2.Streaming/TwitterStream.cs#L138
				var builder = new Twitterizer.WebRequestBuilder(new Uri("https://userstream.twitter.com/2/user.json"), Twitterizer.HTTPVerb.GET, twitterStream.Tokens);
				HttpWebRequest request = builder.PrepareRequest();
				request.KeepAlive = true;
				MethodInfo method = twitterStream.GetType().GetMethod("StreamCallback",
					BindingFlags.Instance |
					BindingFlags.NonPublic);
				AsyncCallback callback = (_) =>
				{
					method.Invoke(twitterStream, new[] { _ });
				};
				request.BeginGetResponse(callback, request);
				while (true) { }
			}
		}
	}
}

 

これでめでたくUserStreamが使えるようになりました。ちなみにtrunkの最新のソースコードでもURLはベータ版のものがベタ書きのままでした。中の人忙しいのかな…。

IronPythonでメソッドをオーバーライドして基底クラスに投げる。

IronPythonに限ったことじゃないけど、IronPythonでWinforms触ってたら避けられない問題なので。組込み関数のsuperを使う。

import clr
clr.AddReference("system.windows.forms")
from System.Windows.Forms import *
class HogeForm(Form):
	WM_LBUTTONDBLCLK = 0x0203
	count = 0

	def WndProc(self, m):
		if m.Msg == self.WM_LBUTTONDBLCLK:
			self.Text = self.count.ToString()
			self.count += 1
		super(HogeForm, self).WndProc(m)

if __name__ == "__main__":
	Application.Run(HogeForm())

 

Form.WndProc(self, m)

でも動くけど推奨されていないみたい。

or_tender宅にお邪魔してきました

僕はPCオーディオというものを初めて目にした。

自作PCのOSはWindowsXP、ASIO対応のサウンドボードから外付けのD/AコンバータへUSB接続、ジェフローランドのパワーアンプへ抵抗のみを介して直結、KEFの同軸2WAYのバスレフ型スピーカーを駆動する。ボリュームはfoober2000のソフトウェアボリュームで調整している。

その配置は特異で、デスク上にPC本体、スピーカー、ディスプレイ、キーボード、マウスが置かれている。スピーカーはコンクリートブロックの上に置かれているのだが、その悪影響を考慮してか間には厚めの合板が挟まれている。所謂デスクトップオーディオというものなのだが、メインシステムと呼べるだけの規模のそれは見たことがなかった。

彼のPCに入っている各種音源や、僕が持参した課題曲のCDを聴く中で面白いものを聴かせて貰った。PCで音楽を再生しながら、タスクマネージャでプロセスを切って行くのだ。その中にはヤバそうなものもいくつかあったと思う。その度に音に実体感が増していって、とどめにexplorer.exeを切るとガラっと再生音の表情が変わるのだ。壁紙を切っただけで音のクオリティが増したりするのを目の当たりにした。僕も10年近くオーディオ趣味の世界にいて、何をしても音は変わるものだということは知っているが、まさかそんなことで音が変わるとは信じられなかった。とにかく僕の目の前でPCが奏でる音が変わったのだ。

そうして本気モードになったPCの外付けCD-ROMドライブに課題曲のCDを入れて消化していった。

まもなく気付いたのが、ボリュームを絞ると情報量が減ること。PCの音楽プレーヤーソフトのボリュームの仕組みのよるものだと思う。なので、ボリュームは常に最大に近い部分で聴くことにした。パワーアンプの前段に入っている抵抗の値が適切なのか、音量が上がり過ぎることはない。非オーディオマニアが普通に聴くような音量なのではないかと思う。

その音質なのだが、とにかく物足りない。初っ端からX JAPANを聴いたのだが、スネアのアタックが全く効いていないし、シンバルが鈴のような上品さなのだ。アタックがピークまで立ち上がらない。國府田マリ子のライブ音源で顕著に感じられた傾向なのだが、語りかけるようなヴォーカルはしっかりと鳴らすが、声を張るヴォーカルでは女声が本来持っている耳を刺すような五月蝿さが出ない。生の音が見せる刺激的な一面を一切出さないのだ。彼女の声よりも観衆のオタク男子共の男臭い歓声の方が際立った程である。

村治佳織のCDなんてのは全くもってダメだった。弦のアタックが全くない。クラシックギターは大きな音が出ない楽器であるから、それ相応の小さな音量で楽しめないと嘘なのだ。しかし、とにかく物足りないので、どこまでも音量を上げたくなってしまう。

しかし、高木綾子のフルートで見直すことになる。僕は彼女のフルートの暖く豊潤な音色が大好きなのだが、これをかなり良い線で鳴らして見せた。鋭い立ち上がりを要求されなければ良い音を出すのだ。これには物足りなさを感じることなく楽しませて貰った。

もうひとつ秀逸なのは、その音場である。狭いスペースにスピーカーが設置されているのでそう広い音場ではないのだが、至近距離で聴くのでスケール感は充分だ。楽器の配置を奥行も含めてはっきりと再現してみせる。普通のブックシェルフ型を至近距離で聴くとヴォーカルの口が大きくなりがちになったりするが、狭いスピーカーの間隔が作る音場の広さとバランスの取れたサイズで歌ってくれている。この音像を作る為の試行錯誤は相当なものだったのではないかと思う。

こうして書いていくと、僕がこの音を低く評価しているかのように感じられると思うが、そんなことはない。このシステムが光るフィールドがあるのだ。最近の良くない録音のアニソン達だ。こういった音源は僕の家では全く鳴らない。粗が全部出てしまう。しかし、このシステムはそういった粗を、粗”だけ”を包み隠すのだ。他の魅力はしっかりと描く。ここまでに書いたこのシステムの欠点は、非常に高い次元でのトレードオフの結果なのだ。とにかくどんなに酷い録音でも上手くまとまった音で鳴る。録音の善し悪しを意識させずに音楽だけを聴かせてくれる。最高のプラチナ(坂本真綾)を聴かせて貰ってとても幸せな気持になった。良い音楽体験をありがとう。

 

僕も最新のアニソンを楽しめるサブシステムが欲しいな~。超電磁砲聴きたいよ超電磁砲聴きたい。球アンプに現代スピーカーの組み合わせが良い線行くんじゃないかなーとか思ったり。

 

【課題曲】

  • X JAPAN BEST ~FAN’S SELECTION~より
  • 國府田マリ子 / Happy!Happy!Happy!より
    • どうしよう
    • ~メッセージ~
    • Happy!Happy!Happy!
  • 國府田マリ子 / My Best Friendより
    • まちぶせ
    • Pure -Liveバージョン-
    • Twin memories -Liveバージョン-
  • 椎名恵 / シングル・コレクション2より
    • いつか空に届いて
    • LOVE IS ALL ★LIVE VERSION
  • JUDY AND MARY / FRESHより
    • POWER OF LOVE
    • DAYDREAM
    • そばかす
    • Brand New Wave Upper Ground
  • 高木綾子 / シシリエンヌ ~フルート名曲集より
    • カヴァティーナ
  • 宮村優子 / Best Collection めっちゃベストより
    • タマシー
    • 根性戦隊ガッツマン
    • HELLO,STRANGE DAYS
  • 村治佳織 / アランフェス協奏曲より
    • タンゴ・アン・スカイ
  • 村治佳織 / アランフェス協奏曲 XRCD2盤より
    • タンゴ・アン・スカイ
  • 村治佳織 / transformationsより
    • オーバー・ザ・レインボー
    • ロンドンデリーの歌

 

3日乗らないただの人 http://cytender.exblog.jp/11199928/

蓄音機すげぇマジすげぇ

第172回 蓄音機で聴く SPレコード鑑賞会

http://gramophone.jp/

行ってきた。ピュアで、鮮烈で、空気がそのまま入った音。90年前の電気を一切使わない録音、ラッパに向って演奏すると、その音のエネルギーがそのまま溝になる。

これを、やはり電気を一切使わずに、溝を針でなぞる。その振動をラッパで増幅する。

そうして再生された音。何も足さない音。生に最も近い音楽。90年前の演奏。

胸が熱くなりませんか?

or_tender氏を我が家に招いてオーディオオフの巻

or_tenderさんが我が家の音を評価して下さってます。

あにす宅へお邪魔しました。 : 3日乗らないただの人

僕は招いた側なのでこれと言って書くこともない訳でして。強いて言うなら、坂本真綾いいね!幸せな気持になりました。それと、汚部屋で本当にごめんなさい。

言い訳をさせて貰うとですね、当日の午前中に近所のホームセンターとか電気屋とか梯子したんですよ、3件くらい、掃除機のゴミパックを求めて。音だけじゃなく、お部屋も綺麗にしてお招きしたかったのです。でもね、売ってないの。掃除機の機種が微妙にマイナーなのか古いのか、対応したゴミパックが売ってないの!

ファブリーズ買って帰りましたとさ。お猫様いるからね、住んでる僕にはわからない臭いもあろうかと。大丈夫だったかな…。