作品15の2番目も、3部構成になっているが、今度はどちらも長調になっている。第3、4曲での極端なやり方に反省して、幻想的な雰囲気を壊さないように配慮したのかもしれない。
しかし、中間部の右手の多声進行は複雑だ。
エキエル版での謎の箇所として、まず出だし
そして中間部が終わった後の出だし。この2箇所でスラーのかかり方が微妙に違う。
これが意図したものなのか、単なる間違いなのか良く分からない。ちなみに全音だと、どの箇所も以下になっている。
みんな違うし ^^;
今回の演奏では、エキエル版の最初の出だしのスラーのかかり方を正としている。
楽譜引用は、エキエル版と全音版。
コメント・スパムが来ているが、コメントは承認が必要なので全部削除。ただ、削除も面倒なので何か対策ができるか試し中。
とりあえずIPのblacklistをnginxに入れてみたが効果無し。これまでコメントスパムって、WordPressとかの有名どころのアプリケーションを使っているところに、機械的に投げ込んでいるのだと思っていたのだけど、うちのようにフルスクラッチのところにも来るというのは、どういう仕組みなのだろう。formのURLがpost comment的なところを見つけて送っているんだろうか。まさか人力でもあるまいし。
というわけでコメント投稿のURLを変更してみた。Play!だとroutesを変えるだけで良いのでラクチンだ。
作品15も、9に引き続き3つで構成されている。作品15の最初の曲となるこの曲は、第3曲と同じで前後を穏かな長調、中間部を激しい短調のパッセージという形式になっている。ただ第3曲よりも洗練されて、全体に簡潔にまとめらている。
中間部、第2小節はペダルを踏む指示があるけど、スタカーティシモ。どのくらいペダルを踏み込むのかが演奏者に任されている感じ。
楽譜引用はエキエル版
Rustで、Multimap(1つのキーに複数の値を登録できるMap)を実装してみる。後でキーをソート順の取り出したいので、BtreeMapを使って、値にVecを入れる。
use std::collections::BTreeMap;
struct Bag {
map: BTreeMap<i32, Vec<i32>>
}
impl Bag {
fn add(&mut self, key: i32, value: i32) {
match self.map.get_mut(&key) {
None => {
self.map.insert(key, vec!(value));
},
Some(vec) => vec.push(value)
}
}
}
でも、これはコンパイルが通らない。
error[E0499]: cannot borrow `self.map` as mutable more than once at a time
--> /Users/shanai/.emacs.d/rust-playground/at-2017-01-22-230525/snippet.rs:32:17
|
30 | match self.map.get_mut(&key) {
| -------- first mutable borrow occurs here
31 | None => {
32 | self.map.insert(key, vec!(value));
| ^^^^^^^^ second mutable borrow occurs here
...
35 | }
| - first borrow ends here
mutable borrowが2回起きているという警告。まずここでself.mapに対するmutable参照を取得している。
match self.map.get_mut(&key) {
で、このmatchが終了する前に、ここでもう一度self.mapに対するmutable参照を取得している(insertは、mutableなselfが必要)。
None => {
self.map.insert(key, vec!(value));
},
つまりmutableな参照が、2つ存在してしまう瞬間がある。これはRustでは禁止されている。以下がエラーになるのと同じ。
let mut i = 0;
let pi = &mut i;
let pi2 = &mut i;
スコープを分けて、mutable参照が2つ同時に存在する瞬間を無くしてしまえば良い。
impl Bag {
fn add(&mut self, key: i32, value: i32) {
{
match self.map.get_mut(&key) {
None => {},
Some(vec) => {
vec.push(value);
return
}
}
}
self.map.insert(key, vec!(value));
}
}
でも、BtreeMapにはentry()という素敵なメソッドがあるので、これを使うのが簡単。
impl Bag {
fn add(&mut self, key: i32, value: i32) {
self.map.entry(key).or_insert(vec!()).push(value)
}
}
entryはキーを引数に取り、Entryというenumを返す。指定されたキーが無ければVacant、あればOccupiedが返る。or_insert()を呼び出すと、Vacantの場合は、引数に指定された値をmapに格納した上で、その値へのmutable参照を返す。Ocupiedの場合は、値へのmutable参照を返す。なので上のように書けば目的が達成できる。
fn main() {
let mut bag = Bag { map: BTreeMap::new() };
bag.add(1, 10);
bag.add(1, 20);
bag.add(2, 100);
println!("map = {:?}", bag.map);
}
map = {1: [10, 20], 2: [100]}
DrawingAreaは、デフォルトではマウスのイベントを拾えないようだ。発生するイベントの種類を増やすには、WidgetExt::add_event()というメソッドを使うらしい。
let drawingArea: gtk::DrawingArea = builder.get_object("drawingarea1").unwrap();
drawingArea.set_size_request(300, 300);
drawingArea.add_events(gdk_sys::GDK_BUTTON_PRESS_MASK.bits() as i32);
drawingArea.add_events(gdk_sys::GDK_BUTTON_RELEASE_MASK.bits() as i32);
JavaのSwingだと、clickedというイベントで同じ場所でマウスのボタンが押されて離されたという状態を取得できたが、どうやらGTK+には、そういうのが無いようだ。自分でPRESSの時の座標を覚えておいて、RELEASEの座標が同じだったらクリックと判定する必要がある模様。イベントハンドラの登録は、WidgetExt::connect_event()を使う。
drawingArea.connect_event(|_, e| {
let clone = e.clone();
match e.get_event_type() {
EventType::ButtonPress => {
let res: Result<EventButton, Event> = clone.downcast();
println!("pressed: {:?}", res.unwrap().get_position());
},
EventType::ButtonRelease => {
let res: Result<EventButton, Event> = clone.downcast();
println!("released: {:?}", res.unwrap().get_position());
},
_ => {}
}
return Inhibit(false);
});
Event型で来るため、特定のイベントであるかをEventTypeで判定して、ダウンキャストするというなんとも汚ないコード。なぜイベント自体をenumにしなかったのかな。GTK+との関係で難しいのだろうか。
クロージャの引数は&Eventで、downcastの引数は、&selfではなくてselfそのものなので、そのままイベントを渡すとmoveが起きてしまってコンパイルエラーになる。なので、一度clone()している。
コードサンプルは、こちら
作品9は3つのノクターンで構成されていて、第3曲が最後。ショパンとしては、この曲は意欲作だったのではないかと思う。ショパンのノクターンは3部構成になっているものが多いけど、この曲は、その中間部の雰囲気をがらりと変えて大きな効果を得ることを狙ったのだと思う。ただ、残念ながら一般には作品9の中での知名度は最も低くなってしまった。まぁ普通の人からすると、え、何、なんでノクターンなのに、こんなに激しいの? みたいな違和感が大きかったんだろうな。まぁ、こんな具合に作曲者が空回りしてしまうケースというのは多い。
この曲は、スタカーティシモが多用されている。面白いのはタイの後の音にスタカーティシモが付いているケースがあって、これ、どう表現するか悩みどころ。実際の演奏会なら大げさに右手を跳ね上げたりするんだろうか。
中間部。Agitatoでガンガン行く。ショパン的には「どうだ!」って感じだったのだろうな。
最後のアルペジオは、ものすごく綺麗だけど、これppで弾くのすごく難しそうで禿げそう。
楽譜引用はエキエル版
今回のお題は、2値(白黒)+alpha(透過付き)のgifファイルを読み込み、その中の黒を赤として描画したいというもの。要は「選択状態」の描画を赤色でしたい。だけど元の画像ファイルは2値(白黒)だよ。という状況。
Pixbufは、2値の画像読み込んだ場合でも、中ではRGBAそれぞれに8bitを割り当てているようだ。なので、ピクセル操作は思ったより簡単だった。
fn to_red(pixbuf: &Pixbuf) {
assert!(
pixbuf.get_colorspace() == gdk_pixbuf_sys::GDK_COLORSPACE_RGB,
"Unsupported color space: {}", pixbuf.get_colorspace()
);
assert!(pixbuf.get_has_alpha(), "This image does not have alpha channel");
let n_channels = pixbuf.get_n_channels();
let w = pixbuf.get_width();
let h = pixbuf.get_height();
let rowstride = pixbuf.get_rowstride();
let mut buf: &mut [u8] = unsafe {
pixbuf.get_pixels()
};
for y in 0..h {
for x in 0..w {
let offset = (y * rowstride + x * n_channels) as usize;
let r = buf[offset];
let g = buf[offset + 1];
let b = buf[offset + 2];
let a = buf[offset + 3];
if r == 0 && g == 0 && b == 0 {
buf[offset] = 255;
}
}
}
}
一応colorspaceと、alpha付きであることをチェック。
assert!(
pixbuf.get_colorspace() == gdk_pixbuf_sys::GDK_COLORSPACE_RGB,
"Unsupported color space: {}", pixbuf.get_colorspace()
);
assert!(pixbuf.get_has_alpha(), "This image does not have alpha channel");
あと、注意としてパディングされている可能性があるので、rowstrideにyを掛けてやる必要があるようだ。get_pixelsは、&mut [u8]が返ってくるので、大胆にもそのまま中をいじれてしまう。RGB全てが0(黒)なら、Rだけ255にしてやる。
for y in 0..h {
for x in 0..w {
let offset = (y * rowstride + x * n_channels) as usize;
let r = buf[offset];
let g = buf[offset + 1];
let b = buf[offset + 2];
let a = buf[offset + 3];
if r == 0 && g == 0 && b == 0 {
buf[offset] = 255;
}
}
}
コードは、GuiHubに置いておいた。
Rustで以下のようなJavaのenumを実現したい。
public enum SharpFlat {
SHARP(1), DOUBLE_SHARP(2), NATURAL(0), NULL(0), FLAT(-1), DOUBLE_FLAT(-2);
private final byte offset;
private SharpFlat(int offset) {
this.offset = (byte)offset;
}
public int getOffset() {
return offset;
}
}
RustのenumはJavaとかのenumとは考え方が違って直和型の定義のようだ。
structで定義してもいいけど、enumになっていればexhaustive checkされるので、やはりenumの方がありがたい。試してみると、enumもtraitを実装できるので、以下のようにすることで解決できた。
enum SharpFlat {
SHARP,
FLAT,
}
trait HasOffset {
fn offset(&self) -> i8;
}
impl HasOffset for SharpFlat {
fn offset(&self) -> i8 {
match *self {
SharpFlat::SHARP => 1,
SharpFlat::FLAT => -1,
}
}
}
fn foo(sharp_flat: Option<SharpFlat>) {
match sharp_flat {
None => {
println!("none");
},
Some(SharpFlat::SHARP) => {
println!("sharp {}", SharpFlat::SHARP.offset());
},
Some(SharpFlat::FLAT) => {
println!("flat");
},
}
}
fn main() {
foo(Some(SharpFlat::SHARP));
}
ショパンのノクターンの代名詞とも言える有名な曲。
構成は単純だけど、楽譜上、作曲者の指示が非常に多く、その1つ1つをどう表現するかが腕の見せどころとなる。第1小節目、左手にはスタカート指定。
続く小節にはスタカート無し。Sempreも無しなので、第1小節だけだ。
ただダンパーペダルを踏む指示もあるので、踏み込んでスタカートとなれば聴感上はスタカートあっても無くても変わらない。なので気にしないという考えもあるだろう。実際そう弾いているピアニストも多い。しかしわざわざ、このように楽譜上の表現を変えているのだから、やはりスタカートをきちんと生かすべきだろう。つまりスタカートの部分ではダンパーペダルは、踏まないかハーフペダルとしてスタカートを生かすべきだ。
最後のクライマックスにも同様なスタカーティシモ指定。
ここもペダルを踏み込んでスタカーティシモでは違いが分からないわけで、さりとて、全くペダルを踏まずにスタカーティシモでは興醒めなので、うまい具合にハーフペダリングして軽く残響を残しつつ、スタカーティシモを表現することになる。こんな微妙なダンパーペダルの制御をリアルなピアノでやろうとしたら、普段使っているピアノを持ち歩かないと無理だろう。
ところで、ここにStrettoがあるのは今まで知らなかった、確かにこの箇所の演奏は若干テンポを上げるケースが多いなとは思っていたが、これはちゃんと楽譜に指示があったのか。
楽譜引用はエキエル版