<!DOCTYPE HTML>
<HTML LANG="ja">
<HEAD>
<META CHARSET="Shift_JIS">
<TITLE>ルーレット</TITLE>
</HEAD>
<BODY STYLE="background-color:#CCFFFF">
<DIV ALIGN=CENTER>
<BR>
<FORM>
<TABLE><TR>
<TD ALIGN=CENTER VALIGN=TOP>
<DIV STYLE="position:relative">
<CANVAS ID="img" WIDTH=240 HEIGHT=240></CANVAS>
<CANVAS ID="det" WIDTH=242 HEIGHT=242 STYLE="position:absolute; left:-1px; top:-1px; visibility:hidden"></CANVAS>
<IMG SRC="images/pointer.png" WIDTH=16 HEIGHT=12 STYLE="position:absolute; left:234px; top:114px">
</DIV>
<BR>
<INPUT TYPE=BUTTON ID="start" VALUE="スタート" onClick="start_stop()" STYLE="font-size:large">
</TD>
<TD WIDTH=30></TD>
<TD ALIGN=LEFT VALIGN=TOP>
<INPUT TYPE=BUTTON ID="load" VALUE="選択肢" onClick="load_sel()"><BR>
<DIV STYLE="width:12em; overflow:hidden; background-color:white; border:solid 1px; padding:2px">
<PRE ID="sel" STYLE="margin:0; cursor:default; -moz-user-select:none"></PRE>
</DIV><BR>
<LABEL><INPUT ID="div_1" TYPE=RADIO NAME="div" onClick="sel_div(1)">分割なし</LABEL>
<LABEL><INPUT ID="div_2" TYPE=RADIO NAME="div" onClick="sel_div(2)">2分割</LABEL>
<LABEL><INPUT ID="div_3" TYPE=RADIO NAME="div" CHECKED onClick="sel_div(3)">3分割</LABEL>
</TD>
</TR></TABLE>
</FORM>
</DIV>
<PRE ID="mea" STYLE="float:left; margin:0; visibility:hidden"></PRE><!-- テキスト サイズ計測用 -->
<SCRIPT TYPE="text/javascript">
<!--
// ファイル読み込み
function load_sel() {
elem_file = document.createElement("INPUT");
elem_file.type = "FILE";
elem_file.onchange = file_sel;
elem_file.click();
}
function file_sel() {
file_reader = new FileReader();
file_reader.onload = file_sel2;
file_reader.readAsText(elem_file.files.item(0), "Shift_JIS");
}
function file_sel2() {
sel_str = file_reader.result.split(/\r\n|\n|\r/);
file_reader = undefined;
if(!sel_str[sel_str.length - 1].length)
sel_str.length--;
var cnt = 12 - sel_str.length;
if(cnt < 0)
sel_str.length = 12;
var str = sel_str.join("\u200b\n");
for(; cnt > 0; cnt--)
str += "\u200b\n";
elem_sel.textContent = str + "\u200b";
if(!sel_str.length) { // 選択肢なし
elem_det.style.visibility = "hidden";
with(ctx_img) {
clearRect(0, 0, 240, 240);
beginPath();
arc(120, 120, 119.5, 0, PI2, false);
fillStyle = "white";
fill();
stroke();
}
n_sel = 0;
elem_start.disabled = true;
return;
}
elem_start.disabled = false;
wheel(); // 盤面作成
}
// 分割数選択
function sel_div(n) {
n_div = n;
if(n_sel) // 選択肢あり
wheel(); // 盤面作成
}
// スタート/ストップ
function start_stop() {
if(speed) { // 回転中
// 曲を停止
elem_aud.pause();
elem_aud.volume = 0;
elem_aud = new Audio();
elem_aud.mozSetup(1, 44100);
clearTimeout(tune_id);
elem_start.disabled = true;
decel = 0.996 + Math.random() * 0.002; // 減速割合
// 減速中の音 作成
var i, j;
pcm[0] = pcm[24] = 0;
pcm[12] = 0.3;
pcm[36] = -0.3;
for(i = 1, j = 64; i < 12; i++, j += 64)
pcm[24 + i] = pcm[48 - i] = - (pcm[i] = pcm[24 - i] = sin[j]);
for(i = 48; i < 3840; i++)
pcm[i] = pcm[i - 48];
}
else {
elem_det.style.visibility = "hidden";
elem_start.value = "ストップ";
elem_load.disabled = elem_div_1.disabled = elem_div_2.disabled = elem_div_3.disabled = true;
speed = Math.PI / 90;
decel = 1;
i_tune = Math.floor(Math.random() * tunes.length);
i_note = 0;
note_len = 0;
// 曲を開始
elem_aud = new Audio();
elem_aud.mozSetup(1, 44100);
samples_written = 0;
tune();
int_id = setInterval(rotate, 16);
}
}
// 回転
function rotate() {
var prev_theta = theta;
var n;
speed *= decel;
if((theta += speed) > PI2) {
theta -= PI2;
}
else {
if(decel < 1) { // 減速中
if((n = Math.floor((theta + angle_2) / angle)) != Math.floor((prev_theta + angle_2) / angle)) {
// 選択肢の境を越えた
// 減速中の音
for(var pcm_off = 0;
(pcm_off += elem_aud.mozWriteAudio(pcm.subarray(pcm_off, 3840 - pcm_off))) < 3840; )
;
}
}
}
draw_wheel(); // 盤面を描画する
if(speed < 0.0002) {
// 停止
clearInterval(int_id);
speed = 0;
// 確定の表示
with(ctx_det) {
clearRect(0, 0, 242, 242);
beginPath();
if(n_pie == 1) {
arc(121, 121, 119.5, 0, PI2, false);
}
else {
moveTo(121, 121);
var a = theta - n * angle;
arc(121, 121, 119.5, - angle_2 + a, angle_2 + a, false);
closePath();
}
stroke();
}
// 確定時の音 作成
var i, j;
pcm[0] = pcm[16] = 0;
pcm[8] = 0.3;
pcm[24] = -0.3;
for(i = 1, j = 96; i < 8; i++, j += 96)
pcm[16 + i] = pcm[32 - i] = - (pcm[i] = pcm[16 - i] = sin[j]);
for(i = 32; i < 3584; i++)
pcm[i] = pcm[i - 32];
det_cnt = 9;
int_id = setInterval(stopped, 80);
}
}
// 確定表示
function stopped() {
if(det_cnt-- & 0x1) {
elem_det.style.visibility = "visible";
// 確定時の音
for(var pcm_off = 0;
(pcm_off += elem_aud.mozWriteAudio(pcm.subarray(pcm_off, 3584 - pcm_off))) < 3584; )
;
if(!det_cnt) { // 確定表示終了
clearInterval(int_id);
elem_aud = undefined;
elem_start.value = "スタート";
elem_start.disabled
= elem_load.disabled = elem_div_1.disabled = elem_div_2.disabled = elem_div_3.disabled = false;
}
}
else {
elem_det.style.visibility = "hidden";
}
}
// 盤面を作成する
function wheel() {
elem_det.style.visibility = "hidden";
n_sel = sel_str.length; // 選択肢数
if(n_sel > 12 / n_div)
n_sel = 12 / n_div;
n_pie = n_sel * n_div; // 盤の分割数
angle = (angle_2 = Math.PI / n_pie) * 2; // 扇形の角度とその 1/2
if(n_pie > 2) {
tan = Math.tan(angle_2);
var y_2 = 121 * tan;
}
var elem_mea = document.getElementById("mea");
elem_mea.style.font = elem_pie[0].getContext("2d").font;
for(var i = 0; i < n_sel; i++) {
with(elem_pie[i].getContext("2d")) {
// 背景
fillStyle = colors[i];
if(n_pie > 2) {
clearRect(0, 0, 120, 240);
beginPath();
moveTo(0, 120);
lineTo(121, 120 - y_2);
lineTo(121, 120 + y_2);
fill();
}
else {
fillRect(0, 0, 120, 240);
}
// テキスト
// サイズ調整(最小は 8 ピクセルとする)
elem_mea.textContent = sel_str[i];
for(size = 14; size >= 8; size--) {
elem_mea.style.fontSize = String(size) + "px";
var h_2 = elem_mea.offsetHeight / 2;
var x1 = (n_pie > 2) ? h_2 / tan : 0;
var w = x1 + elem_mea.offsetWidth;
if(w * w + h_2 * h_2 <= 14161/* 119*119 */)
break;
}
var x2 = Math.sqrt(14161/* 119*119 */ - h_2 * h_2);
var x;
if(size == 7) { // 8 ピクセルでも収まりきらない
x = x1;
save();
beginPath();
rect(0, 0, x2, 240);
clip();
}
else {
x = (x1 + x2 - elem_mea.offsetWidth) / 2;
}
fillStyle = "black";
font = elem_mea.style.font;
fillText(sel_str[i], x, 120);
if(size == 7)
restore();
}
}
theta = 0;
draw_wheel(); // 盤面を描画する
}
// 盤面を描画する
function draw_wheel() {
with(ctx_img) {
clearRect(0, 0, 240, 240);
beginPath();
arc(120, 120, 119.5, 0, PI2, false);
// 各扇形
save();
clip();
fillStyle = (n_sel == 1) ? colors[0] : "white";
fillRect(0, 0, 240, 240);
var i_pie = 0;
for(var cnt = n_div; cnt; cnt--) {
for(var i = 0; i < n_sel; i++) {
save();
translate(120, 120);
rotate(theta - i_pie * angle);
drawImage(elem_pie[i], 0, -120);
restore();
i_pie++;
}
}
restore();
// 周囲
stroke();
}
}
// 曲
function tune() {
while(samples_written - elem_aud.mozCurrentSampleOffset() < 22050/* 500ms */) {
for(var i_pcm = 0; i_pcm < 3840; i_pcm++) {
var i;
if(!note_len) { // 1 音符/休符 終わり
for(; ; ) {
if(!i_note) { // 曲終わり
// 次の曲を選択
if((i = Math.floor(Math.random() * (tunes.length - 1))) >= i_tune)
i++;
notes = tunes[i_tune = i];
note_oct = 4;
}
var c = notes.charAt(i_note); // 楽譜から 1 文字取り出し
if(++i_note == notes.length)
i_note = 0;
if(c == "*") {
// オクターブの設定
if(!i_note)
continue;
c = notes.charAt(i_note); // 次の 1 文字取り出し
if(++i_note == notes.length)
i_note = 0;
if(c >= "0" && c <= "8")
note_oct = c.charCodeAt() - "0".charCodeAt();
}
else {
if(c >= "A" && c <= "G" || c == "_") {
if(c == "_") { // 休符
note_a1 = 0;
}
else {
if(!i_note)
continue;
i = note_oct * 12 + 3 + n_i[c.charCodeAt() - "A".charCodeAt()];
switch(notes.charAt(i_note)) { // 次の 1 文字を調べる
case "+": // ♯
i++;
if(++i_note == notes.length)
i_note = 0;
break;
case "-": // ♭
i--;
if(++i_note == notes.length)
i_note = 0;
break;
}
if(i >= 0 && i < 88)
note_a1 = freq_a[i];
}
// 音符/休符の長さ
var n = "";
while(i_note) {
c = notes.charAt(i_note); // 次の 1 文字を調べる
if(c < "0" || c > "9")
break;
n += c;
if(++i_note == notes.length)
i_note = 0;
}
if(n.length) {
if((note_len = parseInt(n, 10) * UNIT_LEN)) {
note_cnt = 0;
break;
}
}
}
}
}
}
if(note_a1 == 0) { // 休符
val = 0;
}
else {
i = Math.round(note_cnt++ * note_a1) % 3072;
if (i < 768)
val = sin[i];
else if(i < 1536)
val = sin[1536 - i];
else if(i < 2304)
val = - sin[i - 1536];
else
val = - sin[3072 - i];
if(note_len < 20)
val *= note_len / 20;
}
pcm[i_pcm] = val;
note_len--;
}
for(var pcm_off = 0; ; ) {
var written = elem_aud.mozWriteAudio(pcm.subarray(pcm_off, 3840 - pcm_off));
samples_written += written;
if((pcm_off += written) == 3840)
break;
}
}
tune_id = setTimeout(tune, 100);
}
colors = ["#FF9999", "#99FF99", "#FFFF99", "#9999FF", "#FF99FF", "#99FFFF", // 背景色
"#FFCC99", "#99CCFF", "#FF99CC", "#99FFCC", "#CC99FF", "#CCFF99"];
sel_str = ["カツ丼", "カレーライス", "ラーメン", "ハンバーガー"]; // 選択肢文字列
tunes = [ // 曲テーブル
"*4G3_1G4G4E4G4A4G4E7_1E4D11_1E4D7_1G3_1G4G4E4G4A4G4E7_1D8E4D4C11_1"
+ "G3_1G4G4E4G4A4G4E7_1E4D11_1E4D7_1G3_1G4G4E4G4A4G4E7_1D8E4D4C11_1",
"*4C2D2E4G4G6A2G4E4C6D2E4E4D4C4D11_1C2D2E4G4G6A2G4E4C6D2E4E4D4D4C11_1"
+ "C2D2E4G4G6A2G4E4C6D2E4E4D4C4D11_1C2D2E4G4G6A2G4E4C6D2E4E4D4D4C11_1",
"*4G2G2G2_2G2G2G2_2G2G2*5C2_2D2_2E2_2*4G2G2G2_2G2G2*5C2_2E2_2D2_2*4B2_2G2_2"
+ "G2G2G2_2G2G2G2_2G2G2*5C2_2D2_2E2_2C2E2G10F2E2D2C2_2E2_2C2_2"
+ "*4G2G2G2_2G2G2G2_2G2G2*5C2_2D2_2E2_2*4G2G2G2_2G2G2*5C2_2E2_2D2_2*4B2_2G2_2"
+ "G2G2G2_2G2G2G2_2G2G2*5C2_2D2_2E2_2C2E2G10F2E2D2C2_2E2_2C2_2",
"*4A2G2C2_2*5C2_2C2_2*4A2G2C2_2*5C2_2C2_2*4A2G2C2_2*5C2_2*3A2_2*5C2_2*3G2_2*4B2_2B2_2"
+ "A2G2*3G2_2*4B2_2B2_2A2G2*3G2_2*4B2_2B2_2A2G2*3G2_2*4B2_2*3A2_2*4B2_2C2_2*5C2_2C2_2"
+ "*4A2G2*5E2_2C2_2C2_2*4A2G2*5E2_2C2_2C2_2*4A2G2*5E2_2C2_2G2_2C2_2A2_2*4B2_2B2_2"
+ "A2G2*5A2_2*4B2_2B2_2A2G2*5A2_2*4B2_2B2_2A2G2*5A2_2*4B2_2*5G2_2*4B2_2*5E2_2C2_2C2_2"
];
n_i = [9, 11, 0, 2, 4, 5, 7]; // 音名 - 周波数インデックス 変換
UNIT_LEN = 2400;
// 盤面表示用 Canvas
(ctx_img = (elem_img = document.getElementById("img")).getContext("2d")).strokeStyle = "black";
// 作業用 Canvas
elem_pie = new Array();
elem_pie.length = 12;
for(i = 0; i < 12; i++) {
with(elem_pie[i] = document.createElement("CANVAS")) {
width = 120;
height = 240;
with(getContext("2d")) {
textAlign = "left";
textBaseline = "middle";
}
}
}
// 確定表示用 Canvas
with(ctx_det = (elem_det = document.getElementById("det")).getContext("2d")) {
strokeStyle = "red";
lineWidth = 2;
}
// 選択肢
(elem_sel = document.getElementById("sel"))
.textContent = sel_str.join("\n")
+ "\n\u200b\n\u200b\n\u200b\n\u200b\n\u200b\n\u200b\n\u200b\n\u200b"; // 選択肢サンプル
n_div = 3; // 分割数
PI2 = 2 * Math.PI;
wheel(); // 盤面作成
speed = 0;
// 音階周波数テーブル作成
// [48] が 440Hz
// sin テーブルのインデックスに対応する値に換算して格納する
freq_a = new Float32Array(88);
a = 440 / 44100 * 3072;
for(i = 0; i < 88; i++)
freq_a[i] = a * Math.pow(2, (i - 48) / 12);
// sin テーブル作成
sin = new Float32Array(769);
sin[0] = 0;
sin[768] = 0.3;
for(i = 1; i < 768; i++)
sin[i] = Math.sin(i * Math.PI / 1536) * 0.3;
pcm = new Float32Array(3840); // PCM データ
elem_start = document.getElementById("start");
elem_load = document.getElementById("load");
elem_div_1 = document.getElementById("div_1");
elem_div_2 = document.getElementById("div_2");
elem_div_3 = document.getElementById("div_3");
// ページを再ロードしたときのため
document.forms[0].reset();
elem_start.disabled
= elem_load.disabled = elem_div_1.disabled = elem_div_2.disabled = elem_div_3.disabled = false;
//-->
</SCRIPT>
</BODY>
</HTML>
|