Guitar ScalerがIEに対応しました

Guitar Scaler
IEでは表示崩れが起きていましたが、弦とポジションマークの調整を行って、IEで見ても不自然にならないように修正しました。音符再生機能もIE6/7、Firefox1.5/2.0で利用可能です。配色とJavaScriptの実行速度面ではFirefoxの方がおすすめです。
OperaQuickTimeでの再生は上手くいくものの、prototype.js(1.5.0_rc1)のAjaxオブジェクト内でエラーになっているようで、対応できそうなら対応する予定です(音符再生機能以外は正常に機能します)。
あと、Macを所有していないため、Safariでの確認が全くできていないのが残念です。

CSSが適用される優先順位

#outside div {
color: red;
}
#inside {
color: blue;
}
<div id="outside">
<div id="inside">Hello World</div>
</div>

こんな場合にHello Worldは何色で表示されるか。これは赤色になる。外側で指定している「id=”outside”のDIV」という指定の方が優先されている。
さて、例えば実装の都合上、<div id=”outside”>を変更することが出来ない場合にHello Worldの色を変更したいならどうするか。

#outside div {
color: red;
}
.inside {
color: blue;
}
<div id="outside">
<div class="inside">Hello World</div>
</div>

クラス指定に変えてみる。けれど、これは上手くいかない。これは「div.inside」というようにタグを限定しても同じで、タグのオブジェクトを指定しているIDには勝てない。
多分、解決方法は二つあって一つ目はstyle属性で指定してしまう。

<div id="outside">
<div style="color:blue">Hello World</div>
</div>

これは当然青色になる。あともう一つはID指定箇所を追加して、そこからタグ名で辿る。

#outside div {
color: red;
}
#inside div {
color: blue;
}
<div id="outside">
<div id="inside">
<div>Hello World</div>
</div>
</div>

これで「id=”outside”のDIV」という指定に打ち勝って「id=”inside”のDIV」という指定が優先される。
CSSがこのように優先付けされるのは必要な挙動。ID内のタグ指定でスタイルを設定する場合はその中に同名のタグが出てきたときに動きにくくなるので注意が必要。

Guitar Scalerに音符再生機能を追加しました

Guitar Scaler
Guitar Scalerに現在表示しているスケールやコードの音を再生する機能を追加しました。コード名からどんな響きかイメージ出来ない場合などに非常に便利です。

Guitar Scaler
上のスクリーンショットの右下にある再生ボタンをクリックすると、現在表示しているスケール、もしくはコードの音をMIDIで再生します(QuickTimeが必要です)。
再生機能はFirefoxとIE6では確認できましたが、現状ではIE7での再生がされない不具合があります。QuickTimeがインストールされていないマシンで確認していたため(なんとおバカな)で、IE7でもちゃんと動きます。
先日追加した表示状態のURLを取得する機能を利用することで、他人にスケールやコードの響きを説明するような場合にも利用することができます。また、音が出ることによって、ギターを弾かない人でもコード音などに興味があれば楽しめるかもしれません。
どうぞご利用下さい。

学校でハモリを体験できるのは良いこと

音楽に興味のある人なら大体知っていることだと思うけれど、ピアノではハモリを実現することはできない。オクターブ以外は外れてしまう。これはピアノが平均律で調律されているためで、そのお陰でピアノはどのような調も演奏することができる。
ウィーン合唱楽団のCDを聴いていて思ったのだけれど、つくづくハモリ(共鳴)の素晴らしさを知っているということは音楽としての楽しさの幅が広がる重要な要素ではないかと思う。この辺が解らないと、オーケストラが音色として豊かなくらいにしか感じられないのではと懸念してしまう。そう、バイオリンなどの楽器はハモリが実現可能なのだ。
一番顕著なのは、やはり肉声だろう。ハモった音程になった瞬間に来る共鳴は独特だ。それも、たったの2音で実現可能だ(関係ないけれど、ドラクエも1のときは2和音だ)。そういう意味では学校の音楽の授業にある合唱というのはとても良い経験だと思う。少なくとも、各個人にピアノを触らせるよりは有意義だと思う。肉声のハーモニーの素晴らしさを感じることができれば、多分オーケストラや合唱のみの音楽を抵抗なく聴けるし、もっと言えばジャズなどの他のジャンルも聴けるだろう。
歌を歌うことが恥ずかしいと感じる時期とも衝突するだろうから難しいかもしれないけれど、そこに共鳴することの感動を覚えることができたなら、それで十二分に有意義な経験ができたと思って良いと思う。だから、少々恥ずかしくても、声を出して合唱してみると良いだろう。
一応補足しておくと、だからと言ってピアノが悪い楽器だとは全く思っていない。素晴らしい音色を奏でる最高の楽器の一つだと思う。ピアノは音を伸ばすことができないとか、音を揺らす(ビブラート)ことができないといったことを理由に駄目な楽器だなんて言う人は正直どうかしていると思う。楽器は何ができるとか何ができないとかで良し悪しを計るものではないだろう。人間が良いという音を求めるために楽器というのは存在するのだと思う。

Guitar Scalerに表示状態のURLを取得できる機能を追加しました

Guitar Scaler
Guitar Scalerに表示状態のURLを取得できる機能を追加しました。

Guitar Scaler
「表示状態のURLで開く」をクリックすると、現在表示しているスケールやチューニングなどの情報が設定されたURLに移動します。

Guitar Scaler
このURLでGuitar Scalerにアクセスすると初期表示でも、「表示状態のURLで開く」をクリックしたときの状態にスケールやチューニングがセッティングされます。
よく見るスケールをブックマークする場合や、他人に知らせたい場合に便利です。
今回の機能追加に伴って、ループの多いロジック箇所を見直してパフォーマンス改善を行いました。また、ブルーノートスケールに間違いがありましたので、修正しました。その他、若干スケールとコードが追加されています。
どうぞご利用下さい。

Guitar Scalerにコード表示機能を追加しました

Guitar Scaler
コードを表示できるようにしました(コードフォームではなく、コード音に対するポジションです)。コードストロークアルペジオ、スウィープなどのポジションを確認するときに役立つと思います。
スケールとコードを同時に表示することはできないので、表示したい方をラジオボタンで選択して下さい。あと、簡単ですがヘルプ書きました。

日付関数のPHPっぽい呼び方

PHP互換のJavaScript日付フォーマット関数をもっとPHPっぽくするとこんな感じか。

function date(pattern, time) {
var d = new Date();
if (time != null) {
d.setTime(time);
}
return DateFormatter.format(d, pattern);
}
alert( date("Y/m/d H:i:s") );

mktimeに当たるものも必要だから中途半端だけど。ついでにDateに追加するバージョンはこんな感じ。

Date.prototype.format = function(pattern) {
return DateFormatter.format(this, pattern);
}
var now = new Date();
alert( now.format("Y/m/d H:i:s") );

ギタースケールを表示するGuitar Scalerをリリースしました

http://guitarscaler.clonedoppelganger.net/

Guitar Scaler

ギターのスケールを視覚的に表示してくれるツールです。特徴としては、変則チューニング・27フレット・7弦ギターというニッチな需要に対応しています。また、各弦のチューニングが自由に行えるため、リンプ・ビズキットような7弦変則チューニング、KoЯnのような7弦の音下げ、スタンダードなドロップDなど、各種チューニングに対応します。


それでは使い方を簡単に説明します。

  • Key

スケールのルート音を選択します。

  • Scale

ポジションを表示したいスケールを選択します。

  • Strings

弦の本数(6弦 or 7弦)を選択します。

  • Tuning

各弦のチューニングを選択します。右にあるボタンは以下の通りです。
Guitar Scaler
左から「全てを半音下げ」「全てを半音上げ」「ドロップD」「レギュラーチューニング」に変更します。

  • Go

選択が終わったらGoボタンで下にポジションを表示します。赤色がルート音で緑色がスケール音です。また、上が1弦で下が6弦(7弦)です。


Guitar Scalerは、Firefoxのみ(IEでも動作しますが見た目が変です)動作します。おすすめスキンはTinseltownです。
どうぞご利用下さい。

PHP互換のJavaScript日付フォーマット関数をモジュール化した

http://clonedoppelganger.net/javascript/DateFormatter.html
使い方は

<script type="text/javascript" src="./DateFormatter.js"></script>

と読み込んでおいて

var now = new Date();
alert(
DateFormatter.format(now, "Y/m/d(J) H:i:s")
);

というようにやると

2006/12/03(日) 16:03:56

と出力することができます。

フォーマットパターンはソースコードにも書いていますが、以下のものが出力可能です。format文字を通常の文字として扱いたい場合は、直前に#を付けることでエスケープすることが可能です。

format 文字 説明
Y 年。4桁。
y 年。2桁。
m 月。前ゼロあり。
n 月。前ゼロなし。
F 月。フルスペル。
M 月。3文字形式。
O 月。旧暦日本語。
d 日。前ゼロあり。
j 日。前ゼロなし。
w 曜日。数値。
l 曜日。フルスペル。
D 曜日。3文字形式。
N 曜日。ISO-8601形式の数値。
J 曜日。日本語。
g 時。12時間単位。前ゼロなし。
G 時。24時間単位。前ゼロなし。
h 時。12時間単位。前ゼロあり。
H 時。24時間単位。前ゼロあり。
i 分。前ゼロあり。
s 秒。前ゼロあり。
a 午前または午後。(am/pm
A 午前または午後。(AM/PM
S 英語形式の序数を表すサフィックス。2文字。
z 年間の通算日。数字。(ゼロから開始)
t 指定した月の日数。
L 閏年であるかどうか。

DateFormatterという名前が長くて使いにくい場合や、別のGlobalオブジェクトと衝突する場合は適宜名称を変更して下さい。
利用・改変は自由です。どうぞご利用下さい。

PHP互換のJavaScript日付フォーマット関数

[追記3]
モジュール化したので、こちらがおすすめです。

[追記2]
id:spiritlooseさんの指摘を勘違いしてしまった私だけど、こいつはこのままにしておくことにする。一応、出来るだけfunctionを配列にしたり、クロージャを減らしてswitch文の中にロジックを書いたりして工夫はしてみたものの、ベンチとると現状の方が速いので(勿論、各メソッドをprototype化した方が高速だとは思うけれど)。
下のコードは一度に1000回以上ループするような局面ではあまり向いていないので、必要に応じて改変するのが良いでしょう。
[追記]
id:spiritlooseさんの指摘がもっともだと思ったので、Dateに引っ付けたバージョンに変更した。うん。こっちの方が無駄がない感じ。今回の使用例はこんな感じになる。

var now = new Date();
alert( Date.format(now, "Y/m/d(J) H:i:s") );

上記の結果は「2006/12/02(土) 16:20:40」となる。
コードは以下。利用・改変は自由なので気に入った方は利用してみて下さい。

/**
* Date formater
*
* @author clonedoppelganger(at)gmail.com
* @class Date
* @param {Date} target date object
* {String} pattern
* - Y 年。4桁。
* - y 年。2桁。
* - m 月。前ゼロあり。
* - n 月。前ゼロなし。
* - F 月。フルスペル。
* - M 月。3文字形式。
* - O 月。旧暦日本語。
* - d 日。前ゼロあり。
* - j 日。前ゼロなし。
* - w 曜日。数値。
* - l 曜日。フルスペル。
* - D 曜日。3文字形式。
* - N 曜日。ISO-8601形式の数値。
* - J 曜日。日本語。
* - g 時。12時間単位。前ゼロなし。
* - G 時。24時間単位。前ゼロなし。
* - h 時。12時間単位。前ゼロあり。
* - H 時。24時間単位。前ゼロあり。
* - i 分。前ゼロあり。
* - s 秒。前ゼロあり。
* - a 午前または午後。(am/pm)
* - A 午前または午後。(AM/PM)
* - S 英語形式の序数を表すサフィックス。2文字。
* - z 年間の通算日。数字。(ゼロから開始)
* - t 指定した月の日数。
* - L 閏年であるかどうか。
* 上記の予約パラメータを通常の文字としたい場合には直前に#を付けて下さい。
* @return {String} formatted date
*/
Date.format = function(d, pattern) {

if (typeof pattern != "string") return;

var dYear = d.getFullYear();
var dMonth = d.getMonth();
var dDate = d.getDate();
var dDay = d.getDay();
var dHours = d.getHours();
var dMinutes = d.getMinutes();
var dSeconds = d.getSeconds();

var preZero = function(value) {
return (parseInt(value) < 10) ? "0" + value : value
};
var from24to12 = function(hours) {
return (hours > 12) ? hours - 12 : hours;
}
var ampm = function() {
return (dHours < 12) ? "am" : "pm";
}
var isoDay = function() {
return (dDay == 0) ? "7" : dDay;
}
var weekFullEn = function() {
var week = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
return week[dDay];
}
var weekJp = function() {
var week = ["日","月","火","水","木","金","土"];
return week[dDay];
}
var monthFullEn = function() {
var month = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"];
return month[dMonth];
}
var monthOldJp = function() {
var month = ["睦月", "如月", "弥生", "卯月", "皐月", "水無月",
"文月", "葉月", "長月", "神無月", "霜月", "師走"];
return month[dMonth];
}
var lastDayOfMonth = function(dateObj) {
var tmp = new Date(dateObj.getFullYear(), dateObj.getMonth() + 1, 1);
tmp.setTime(tmp.getTime() - 1);
return tmp.getDate();
}
var isLeapYear = function() {
var tmp = new Date(dYear, 0, 1);
var sum = 0;
for (var i = 0; i < 12; i++) {
tmp.setMonth(i);
sum += lastDayOfMonth(tmp);
}
return (sum == 365) ? "0" : "1";
}
var dateCount = function() {
var tmp = new Date(dYear, 0, 1);
var sum = -1;
for (var i = 0; i < dMonth; i++) {
tmp.setMonth(i);
sum += lastDayOfMonth(tmp);
}
return sum + dDate;
}
var dateSuffix = function() {
var suffix = [
"st", "nd", "rd", "th", "th", "th", "th", "th", "th", "th",
"th", "th", "th", "th", "th", "th", "th", "th", "th", "th",
"st", "nd", "rd", "th", "th", "th", "th", "th", "th", "th", "st"
];
return suffix[dDate - 1];
}

var res = "";
for (var i = 0, len = pattern.length; i < len; i++) {
var c = pattern.charAt(i);
switch (c) {
case "#":
if (i == len - 1) break;
res += pattern.charAt(++i);
break;
case "Y": res += dYear; break;
case "y": res += dYear.toString().substr(2, 2); break;
case "m": res += preZero(dMonth + 1); break;
case "n": res += dMonth + 1; break;
case "d": res += preZero(dDate); break;
case "j": res += dDate; break;
case "w": res += dDay; break;
case "N": res += isoDay(); break
case "l": res += weekFullEn(); break;
case "D": res += weekFullEn().substr(0, 3); break;
case "J": res += weekJp(); break;
case "F": res += monthFullEn(); break;
case "O": res += monthOldJp(); break;
case "a": res += ampm(); break;
case "A": res += ampm().toUpperCase(); break;
case "H": res += preZero(dHours); break;
case "h": res += preZero(from24to12(dHours)); break;
case "g": res += from24to12(dHours); break;
case "G": res += dHours; break;
case "i": res += preZero(dMinutes); break;
case "s": res += preZero(dSeconds); break;
case "M": res += monthFullEn().substr(0, 3); break;
case "t": res += lastDayOfMonth(d); break;
case "L": res += isLeapYear(); break;
case "z": res += dateCount(); break;
case "S": res += dateSuffix(); break;
default : res += c; break;
}
}
return res;
}

そういえば、コードの配色はhttp://vimcolor.spiritloose.net/を利用してみた。ただ、日本語が化けてしまうので整形手作業でやってたり。あと、車輪の再開発的なところは、自分で自由利用宣言した方がペタペタ好き放題使えて気軽だよねってのと、知らない日付フォーマットを覚えるのが億劫だという後ろ向きな理由で、全然気にしていなかったりする。

あと、一応、追記前の内容も消さずに下に置いておきます。


以前に月の最終日を取得する関数を書いたけど、独り立ちしている関数だと実用的ではなかったので、結局こうなってしまった。タイトルに書いておいてなんだけど、PHP互換は言い過ぎでフォーマットの形式が同じという意味で捕らえて欲しい(PHPのdate関数が提供する機能を全て実装している訳ではないということ)。ただ、PHPに書き慣れた人だと、かなり使い勝手が良いのではと思う。
使用例はこんな感じになる。

alert(
new Date().format("Y月m月d日(J) H時i分s秒")
);

出力結果は

2006月12月02日(土) 00時43分59秒

となる。
PHPにない実装としては、日本語の曜日の出力ができるのと、日本語の旧暦月名(旧暦自体を計算している訳ではないので注意)が出力ができる。
予約語エスケープが\ではなく#になっているのは、\自体がJavaScriptエスケープ文字なので、\だらけになるのが嫌だったため。

利用や改変は自由なので、気に入った方は使ってみて下さい(バグがあった場合は教えてもらえると助かります)。

/**
* Date formater
*
* @author clonedoppelganger(at)gmail.com
* @class Date
* @param {String} pattern
* - Y 年。4桁。
* - y 年。2桁。
* - m 月。前ゼロあり。
* - n 月。前ゼロなし。
* - F 月。フルスペル。
* - M 月。3文字形式。
* - O 月。旧暦日本語。
* - d 日。前ゼロあり。
* - j 日。前ゼロなし。
* - w 曜日。数値。
* - l 曜日。フルスペル。
* - D 曜日。3文字形式。
* - N 曜日。ISO-8601形式の数値。
* - J 曜日。日本語。
* - g 時。12時間単位。前ゼロなし。
* - G 時。24時間単位。前ゼロなし。
* - h 時。12時間単位。前ゼロあり。
* - H 時。24時間単位。前ゼロあり。
* - i 分。前ゼロあり。
* - s 秒。前ゼロあり。
* - a 午前または午後。(am/pm)
* - A 午前または午後。(AM/PM)
* - S 英語形式の序数を表すサフィックス。2文字。
* - z 年間の通算日。数字。(ゼロから開始)
* - t 指定した月の日数。
* - L 閏年であるかどうか。
* 上記の予約パラメータを通常の文字としたい場合には直前に#を付けて下さい。
* @return {String} formatted date
*/
Date.prototype.format = function(pattern) {

if (typeof pattern != "string") return;

var d = new Date();
d.setTime(this.getTime());
var dYear = d.getFullYear();
var dMonth = d.getMonth();
var dDate = d.getDate();
var dDay = d.getDay();
var dHours = d.getHours();
var dMinutes = d.getMinutes();
var dSeconds = d.getSeconds();

var preZero = function(value) {
return (parseInt(value) < 10) ? "0" + value : value
};
var from24to12 = function(hours) {
return (hours > 12) ? hours - 12 : hours;
}
var ampm = function() {
return (dHours < 12) ? "am" : "pm";
}
var isoDay = function() {
return (dDay == 0) ? "7" : dDay;
}
var weekFullEn = function() {
var week = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
return week[dDay];
}
var weekJp = function() {
var week = ["日","月","火","水","木","金","土"];
return week[dDay];
}
var monthFullEn = function() {
var month = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"];
return month[dMonth];
}
var monthOldJp = function() {
var month = ["睦月", "如月", "弥生", "卯月", "皐月", "水無月",
"文月", "葉月", "長月", "神無月", "霜月", "師走"];
return month[dMonth];
}
var lastDayOfMonth = function(dateObj) {
var tmp = new Date(dateObj.getFullYear(), dateObj.getMonth() + 1, 1);
tmp.setTime(tmp.getTime() - 1);
return tmp.getDate();
}
var isLeapYear = function() {
var tmp = new Date(dYear, 0, 1);
var sum = 0;
for (var i = 0; i < 12; i++) {
tmp.setMonth(i);
sum += lastDayOfMonth(tmp);
}
return (sum == 365) ? "0" : "1";
}
var dateCount = function() {
var tmp = new Date(dYear, 0, 1);
var sum = -1;
for (var i = 0; i < dMonth; i++) {
tmp.setMonth(i);
sum += lastDayOfMonth(tmp);
}
return sum + dDate;
}
var dateSuffix = function() {
var suffix = [
"st", "nd", "rd", "th", "th", "th", "th", "th", "th", "th",
"th", "th", "th", "th", "th", "th", "th", "th", "th", "th",
"st", "nd", "rd", "th", "th", "th", "th", "th", "th", "th", "st"
];
return suffix[dDate - 1];
}

var res = "";
for (var i = 0, len = pattern.length; i < len; i++) {
var c = pattern.charAt(i);
switch (c) {
case "#":
if (i == len - 1) break;
res += pattern.charAt(++i);
break;
case "Y": res += dYear; break;
case "y": res += dYear.toString().substr(2, 2); break;
case "m": res += preZero(dMonth + 1); break;
case "n": res += dMonth + 1; break;
case "d": res += preZero(dDate); break;
case "j": res += dDate; break;
case "w": res += dDay; break;
case "N": res += isoDay(); break
case "l": res += weekFullEn(); break;
case "D": res += weekFullEn().substr(0, 3); break;
case "J": res += weekJp(); break;
case "F": res += monthFullEn(); break;
case "O": res += monthOldJp(); break;
case "a": res += ampm(); break;
case "A": res += ampm().toUpperCase(); break;
case "H": res += preZero(dHours); break;
case "h": res += preZero(from24to12(dHours)); break;
case "g": res += from24to12(dHours); break;
case "G": res += dHours; break;
case "i": res += preZero(dMinutes); break;
case "s": res += preZero(dSeconds); break;
case "M": res += monthFullEn().substr(0, 3); break;
case "t": res += lastDayOfMonth(d); break;
case "L": res += isLeapYear(); break;
case "z": res += dateCount(); break;
case "S": res += dateSuffix(); break;
default : res += c; break;
}
}
return res;
}