foobar2000のwsh panel modのサンプルコードその4



そろそろ飽きてきました。
『ボリュームを作ろう』がメインテーマです。
愛ってよくわからないけど傷つく感じが素敵。
裏テーマとして図形関係も書きます。

sample5.txtをインポートしてください。

Image_computer/wsh_8.png



// ==PREPROCESSOR==
// @name "Sample5 Volume"
// @author "Junya Renno"
// ==/PREPROCESSOR==

var imgpath = fb.ProfilePath+"/skin/sample/";

function RGB(r, g, b) { return (0xff000000 | (r << 16) | (g << 8) | (b)); }


var g_drag = 0;
var bar_color = RGB(151,15,13);

function on_paint(gr){
var ww = window.Width;
var wh = window.Height;
var volume =fb.Volume;
var pos =Math.exp((-(-volume-100)*Math.log(100))/100);
var x = pos * ww /100;

var bg_img = gdi.Image(imgpath+"bg.png");
var knob_img = gdi.Image(imgpath+"knob2.png");

if(x<5){x=0;}
else if(x>(ww- 5)){x= ww - 11;}
else{x-=5;}

gr.DrawImage(bg_img,0,0,ww,10,0,0,bg_img.Width-10,10);
gr.FillSolidRect(0,2,x,8,bar_color);
gr.DrawImage(knob_img,x,0,11,11,0,0,11,11);

}

function on_mouse_lbtn_down(x,y){
g_drag = 1;
}

function on_mouse_lbtn_up(x,y){
on_mouse_move(x,y);
g_drag = 0;
}

function on_mouse_move(x,y){
if(g_drag){
var pos = x * 100 / window.Width;
var v =100-(Math.log(pos)*100)/Math.log(100);
v = (v<0) ? 0 : (v<100) ? v : 100;
v = -v;
if(fb.Volume != v)
fb.Volume = v;
}
}

function on_mouse_wheel(delta){
if(delta>0)
fb.VolumeUp();
else
fb.VolumeDown();
}
function on_volume_change(val){
window.Repaint();
}



基本的な考え方として、fb.volumeに現在の値が入っているので、それを取ってきて表示し、新しい値をfb.volumeに入れればOK。

問題はこの部分。

var volume =fb.Volume;
var pos =Math.exp((-(-volume-100)*Math.log(100))/100);
var x = pos * ww /100;


ボリュームの値をxに変換しているわけですが、Wsh Panel Modについてくるサンプル(Volbar.txt)はもっと単純です。

var x = window.Width * ((100 + volume) / 100);


xは0~パネルの横幅の値をとります。
volumeは-100~0の値です。
どう変換するかというのが肝ですね。
ところが一つ問題がありまして、『音量は単純に比例しているわけではない』という点です。
むしろ対数であって、単純な変換の場合には音量が極端に大きくなっていく問題があるわけです。
それでこういうちょっと面倒な式にしてあるわけですね。
実際私が考えた式ではなく、monolite plusにあった式を拝借しました。(にしては偉そうだな)
理屈については小学生でも分かるデシベル(dB)の話技術レポート db(デシベルとは)とかに詳しいです。
単純な計算式のサンプルをsample6.txt、Wsh Panel Modにはhuman hearing curve(人間の聞こえ方をグラフにしたもの)をもとにしたサンプルがあったので、それを使ってsample8.txtを比較用に書きました。
試してみるのも一興でしょう。


gr.DrawImage(bg_img,0,0,ww,10,0,0,bg_img.Width-10,10);
gr.FillSolidRect(0,2,x,8,bar_color);
gr.DrawImage(knob_img,x,0,11,11,0,0,11,11);


ボリュームの描写部分です。
背景の画像を敷いて、長方形を塗りつぶし、つまみの画像を表示しています。

描画関係の命令については他に

●DrawLine(x1, y1, x2, y2, line_width, color)
線を引く。
●DrawRect(x, y, w, h, line_width, color)
長方形を描く。
●DrawRoundRect(x, y, w, h, arc_width, arc_height, line_width, color)
角の丸い長方形を描く。
●DrawEllipse(x, y, w, h, line_width, color)
楕円を描く。
●DrawPolygon(color, line_width, points)
点を線でつないだ図形を描く。pointは[x1,y1,x2,y2,...]のようなペア同士の配列。


●FillSolidRect(x, y, w, h, color)
長方形の塗りつぶし。
●FillGradRect(x, y, w, h, angle, color1, color2, focus = 1.0)
長方形で、2色を使ったグラデーションの塗りつぶし。angle=0~360,focus=0~1.0
●FillRoundRect(x, y, w, h, arc_width, arc_height, color)
角の丸い長方形の塗りつぶし。
●FillEllipse(x, y, w, h, color)
楕円の塗りつぶし。
●FillPolygon(color, fillmode, points)
点を線でつないだ図形の塗りつぶし。

などがあります。
やろうと思えばつまみも背景も直接描けるんですが、そこはデザインとの絡みです。


on_mouse_move(x,y){}で、ドラッグしていれば計算してfb.volumeにセットします。
on_mouse_wheel(delta){}ではマウスホイールの動きを感知し、ボリュームを上下させています。

こんなもんですかねえ。
次は最終回、『ドロップダウン型のボタンを作る』です。
プレイリスト関連は私もイマイチわかってないんですが、まあがんばってみましょう。


では。


追記:

v = (v<0) ? 0 : (v<100) ? v : 100;

みたいなのは三項演算子といいまして、

if( v<0 ) v=0;
else if(v<100) v=v;
else v=100;

と同等。
なんとなくこれ使うと『俺プログラム書けるぜ!』みたいな雰囲気を出せるので、覚えておくと得かも。



 

>描画関係の命令

JScript panelをinstallしてbooklet(CD付属のものをocrでword fileにしたもの)を開くボタンを作成しました。(初心者なのでえらく時間が掛かりました。)
componentの中の例題fileをベースにしています。前置きが長くなりましたが、
ボタンの形をFillSolidRectからFillRoundRectにしたいのですが、うまくいきません。調べてみたところ、FillRoundRectはPREPROCESSORで参照しているpanel.jsに含まれていませんでした。panel.jsにFillRoundRectを導入するやり方をご教授ください。よろしくお願いします。

Posted by 杉木美喜雄 at 2021/07/11 (Sun) 08:24:38

>よくわかんないけど

要するにCDに入ってる冊子を自分でスキャンしてファイル化して、それをfoobar2000から開きたいってことでOK?
とりあえずJscript panelのバージョンと貴公の書いたソースコードみないとわからんのでそういうことで。単にパラメータ間違えてるってことはないですか?
panel.jsはあまり関係ない気がするな。

Posted by 連野 at 2021/07/11 (Sun) 17:52:46

>描画関係の命令

基礎的なことを勉強せずにいきなりの質問で混乱を招いたようです。
JavaとJScriptの違いも知りませんでした。JScriptは極めて自由な言語のように感じました。じっくりと勉強したうえでまた質問させてください。
bookletを呼び出すPanelは不完全なところがありつつなんとなく動いています。

Posted by 杉木美喜雄 at 2021/07/16 (Fri) 08:21:44

>まあよろしいかと存じます

まるで車のエンジンの不調をリモートで直せ(しかも画像は写さないぞ)っていわれてるようなものでして、実物を見るのが一番手っ取り早いというかそれしかできないし。

最近はSMPなる新参者も出てきて多少古くなっていますが「WSH Panel Mod(JScript Panel) の基本事項」(パソコン関連カテゴリの7ページめ)などもご参考になるかと思われます。

Posted by 連野 at 2021/07/16 (Fri) 23:20:05

>Jscriptからword file open

しばらくご無沙汰でした。
”Posted by 杉木美喜雄 at 2021/07/11 (Sun) 08:24:38”
の続きです。
下記のscriptでplay中の関連word fileを開くようにしました。
動作で不満な点があります。
曲をmouse pointしただけではword fileが開かず、
play buttonを押して演奏状態にしないとだめです。
この点を解消するにはどうしたらよいか、ご教授頂ければ
ありがたく思います。
screenshotが添付できるともっとわかりやすいと感じますが、
もし方法があれば教えてください。


*******以下script***********
// ==PREPROCESSOR==
// @name "Word_File_Open_2nd"
// @author "Mikio Sugiki 2021_07_10"
// @import "%fb2k_component_path%docs\Flags.txt"
// @import "%fb2k_component_path%docs\Helpers.txt"
// @import "%fb2k_component_path%samples\complete\js\lodash.min.js"
// @import "%fb2k_component_path%samples\complete\js\helpers.js"
// @import "%fb2k_component_path%samples\complete\js\panel.js"
// @import "%fb2k_component_path%samples\complete\js\text.js"
// Reference Sample : SimpleThemedButton.txt by "T.P Wang"
var panel = new _.panel();
var text = new _.text('text_reader', LM, TM, 0, 0);
// the variable to instantiate word application
var oWordApp;
// the variable that represents the document content
var oDoc;
var tfo = {
album : fb.TitleFormat('%album%'),
artist : fb.TitleFormat('%artist%'),
title : fb.TitleFormat('%title%'),
playback_time : fb.TitleFormat('%playback_time% '),
length : fb.TitleFormat(' %length%')
};
function SimpleButton(x, y, w, h, text, fonClick, state) {
this.state = state ? state : ButtonStates.normal;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.text = text;
this.fonClick = fonClick;

this.containXY = function (x, y) {
return (this.x <= x) && (x <= this.x + this.w) && (this.y <= y) && (y <= this.y + this.h);
}

this.changeState = function (state) {
var old = this.state;
this.state = state;
return old;
}

this.draw = function (gr) {
if (this.state == ButtonStates.hide) return;

switch (this.state) {
case ButtonStates.normal:
g_theme.SetPartAndStateId(1, 1);
break;

case ButtonStates.hover:
g_theme.SetPartAndStateId(1, 2);
break;

case ButtonStates.down:
g_theme.SetPartAndStateId(1, 3);
break;

case ButtonStates.hide:
return;
}

g_theme.DrawThemeBackground(gr, this.x, this.y, this.w, this.h);
// gr.FillRoundRect(0, 0, 160, 40, 20, 20, utils.GetSysColour(15));
gr.GdiDrawText(this.text, g_font, RGB(0,0,0), this.x, this.y, this.w, this.h, DT_CENTER| DT_VCENTER | DT_CALCRECT | DT_NOPREFIX);
}

this.onClick = function () {
this.fonClick && this.fonClick();
}
}

function drawAllButtons(gr) {
for (var i in buttons) {
buttons[i].draw(gr);
}
}

function chooseButton(x, y) {
for (var i in buttons) {
if (buttons[i].containXY(x, y) && buttons[i].state != ButtonStates.hide) return buttons[i];
}

return null;
}

function on_paint(gr) {
// gr.FillSolidRect(0, 0, window.Width, window.Height, utils.GetSysColour(10));
gr.FillSolidRect(0, 0, window.Width, window.Height, RGB(0, 0, 0));
gr.DrawRoundRect(5, 2, 170, 45, 15, 15, 3, RGB(5, 245, 235));
gr.DrawRoundRect(5, 57, 170, 45, 15, 15, 3, RGB(5, 245, 235));
// gr.FillRoundRect(0, 0, 160, 40, 20, 20, utils.GetSysColour(15))
// FillSolidRect(x, y, w, h, color)
// 長方形の塗りつぶし。
// FillGradRect(x, y, w, h, angle, color1, color2, focus = 1.0)
// 長方形で、2色を使ったグラデーションの塗りつぶし。angle=0~360,focus=0~1.0
// FillRoundRect(x, y, w, h, arc_width, arc_height, color)
// 角の丸い長方形の塗りつぶし。
// FillEllipse(x, y, w, h, color)
// 楕円の塗りつぶし。
// FillPolygon(color, fillmode, points)
// 点を線でつないだ図形の塗りつぶし。

drawAllButtons(gr);
}

function on_mouse_move(x, y) {
var old = cur_btn;
cur_btn = chooseButton(x, y);

if (old == cur_btn) {
if (g_down) return;
} else if (g_down && cur_btn && cur_btn.state != ButtonStates.down) {
cur_btn.changeState(ButtonStates.down);
window.Repaint();
return;
}

old && old.changeState(ButtonStates.normal);
cur_btn && cur_btn.changeState(ButtonStates.hover);
window.Repaint();
}

function on_mouse_leave() {
g_down = false;

if (cur_btn) {
cur_btn.changeState(ButtonStates.normal);
window.Repaint();
}
}

function on_mouse_lbtn_down(x, y) {
g_down = true;

if (cur_btn) {
cur_btn.changeState(ButtonStates.down);
window.Repaint();
}
}

function on_mouse_lbtn_up(x, y) {
g_down = false;

if (cur_btn) {
cur_btn.onClick();
cur_btn.changeState(ButtonStates.hover);
window.Repaint();
}
}

var cur_btn = null;
var g_theme = window.CreateThemeManager('Button');
var g_font = gdi.Font('Georgia', 15);
var g_down = false;

var ButtonStates = {
normal: 0,
hover: 1,
down: 2,
hide: 3
}

var buttons = {
FileOpen: new SimpleButton(10, 5, 160, 40, 'BookLet_File_OPEN', function () {
// gr.FillRoundRect(0, 0, 160, 40, 20, 20, utils.GetSysColour(15))
// gr.DrawRoundRect(0, 0, 180, 180, 25, 25, 3, RGB(245, 5, 235));
// var sample = text.header_text();
// console.log(text.header_text());
// var result = sample.indexOf('ID', 0);
// console.log(result);
// console.log(tfo.album.Eval());
var titleID='ID'+'0'+tfo.album.Eval().substr(2,4);
// console.log(titleID);
var doc_file_path = 'V:\\CDDB\\bk_new\\'+titleID+'.docx';
// console.log(doc_file_path);


// create instance of word
oWordApp = new ActiveXObject("Word.Application");
// make it visible on the client
oWordApp.Visible = "true";
// open the document
oDoc = oWordApp.Documents.Open( doc_file_path );
}),

FileClose: new SimpleButton(10, 60, 160, 40, 'CLOSE_BookLet_File', function () {
// ファイルを閉じる
oDoc.Close();
// ワードを終了する
oWordApp.Quit();
})
};

Posted by 杉木美喜雄 at 2021/08/17 (Tue) 09:41:24

>選択中の曲を取るなら fb.GetFocusItem()でしょう

こんな感じかな。
再生中はそのデータを取って、再生していないときは選択している曲のデータを取る(ただしプレイリストにあるものだけ)
underscoreは入ってるんですよね?
ワード持ってないのでそこら辺のチェックはしてません。個人的にはPDFファイルにしてビューアで見た方が軽いだろうとは思います。


// ==PREPROCESSOR==
// @name "Word_File_Open_2nd"
// @author "Mikio Sugiki 2021_07_10"
// @import "%fb2k_component_path%docs\Flags.txt"
// @import "%fb2k_component_path%docs\Helpers.txt"
// @import "%fb2k_component_path%samples\complete\js\lodash.min.js"
// @import "%fb2k_component_path%samples\complete\js\helpers.js"
// @import "%fb2k_component_path%samples\complete\js\panel.js"
// @import "%fb2k_component_path%samples\complete\js\text.js"
// Reference Sample : SimpleThemedButton.txt by "T.P Wang"
// ==/PREPROCESSOR==

var panel = new _.panel();
var text = new _.text('text_reader', LM, TM, 0, 0);
// the variable to instantiate word application
var oWordApp;
// the variable that represents the document content
var oDoc;
var tfo = {
album : fb.TitleFormat('%album%'),
artist : fb.TitleFormat('%artist%'),
title : fb.TitleFormat('%title%'),
playback_time : fb.TitleFormat('%playback_time% '),
length : fb.TitleFormat(' %length%')
};
function SimpleButton(x, y, w, h, text, fonClick, state) {
this.state = state ? state : ButtonStates.normal;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.text = text;
this.fonClick = fonClick;

this.containXY = function (x, y) {
return (this.x <= x) && (x <= this.x + this.w) && (this.y <= y) && (y <= this.y + this.h);
}

this.changeState = function (state) {
var old = this.state;
this.state = state;
return old;
}

this.draw = function (gr) {
if (this.state == ButtonStates.hide) return;

switch (this.state) {
case ButtonStates.normal:
g_theme.SetPartAndStateId(1, 1);
break;

case ButtonStates.hover:
g_theme.SetPartAndStateId(1, 2);
break;

case ButtonStates.down:
g_theme.SetPartAndStateId(1, 3);
break;

case ButtonStates.hide:
return;
}

g_theme.DrawThemeBackground(gr, this.x, this.y, this.w, this.h);
// gr.FillRoundRect(0, 0, 160, 40, 20, 20, utils.GetSysColour(15));
gr.GdiDrawText(this.text, g_font, RGB(0,0,0), this.x, this.y, this.w, this.h, DT_CENTER| DT_VCENTER | DT_CALCRECT | DT_NOPREFIX);
}

this.onClick = function () {
this.fonClick && this.fonClick();
}
}

function drawAllButtons(gr) {
for (var i in buttons) {
buttons[i].draw(gr);
}
}

function chooseButton(x, y) {
for (var i in buttons) {
if (buttons[i].containXY(x, y) && buttons[i].state != ButtonStates.hide) return buttons[i];
}

return null;
}

function on_paint(gr) {
// gr.FillSolidRect(0, 0, window.Width, window.Height, utils.GetSysColour(10));
gr.FillSolidRect(0, 0, window.Width, window.Height, RGB(0, 0, 0));
gr.DrawRoundRect(5, 2, 170, 45, 15, 15, 3, RGB(5, 245, 235));
gr.DrawRoundRect(5, 57, 170, 45, 15, 15, 3, RGB(5, 245, 235));
// gr.FillRoundRect(0, 0, 160, 40, 20, 20, utils.GetSysColour(15))
// FillSolidRect(x, y, w, h, color)
// 長方形の塗りつぶし。
// FillGradRect(x, y, w, h, angle, color1, color2, focus = 1.0)
// 長方形で、2色を使ったグラデーションの塗りつぶし。angle=0~360,focus=0~1.0
// FillRoundRect(x, y, w, h, arc_width, arc_height, color)
// 角の丸い長方形の塗りつぶし。
// FillEllipse(x, y, w, h, color)
// 楕円の塗りつぶし。
// FillPolygon(color, fillmode, points)
// 点を線でつないだ図形の塗りつぶし。

drawAllButtons(gr);
}

function on_mouse_move(x, y) {
var old = cur_btn;
cur_btn = chooseButton(x, y);

if (old == cur_btn) {
if (g_down) return;
} else if (g_down && cur_btn && cur_btn.state != ButtonStates.down) {
cur_btn.changeState(ButtonStates.down);
window.Repaint();
return;
}

old && old.changeState(ButtonStates.normal);
cur_btn && cur_btn.changeState(ButtonStates.hover);
window.Repaint();
}

function on_mouse_leave() {
g_down = false;

if (cur_btn) {
cur_btn.changeState(ButtonStates.normal);
window.Repaint();
}
}

function on_mouse_lbtn_down(x, y) {
g_down = true;

if (cur_btn) {
cur_btn.changeState(ButtonStates.down);
window.Repaint();
}
}

function on_mouse_lbtn_up(x, y) {
g_down = false;

if (cur_btn) {
cur_btn.onClick();
cur_btn.changeState(ButtonStates.hover);
window.Repaint();
}
}

var cur_btn = null;
var g_theme = window.CreateThemeManager('Button');
var g_font = gdi.Font('Georgia', 15);
var g_down = false;

var ButtonStates = {
normal: 0,
hover: 1,
down: 2,
hide: 3
}

var buttons = {
FileOpen: new SimpleButton(10, 5, 160, 40, 'BookLet_File_OPEN', function () {
// gr.FillRoundRect(0, 0, 160, 40, 20, 20, utils.GetSysColour(15))
// gr.DrawRoundRect(0, 0, 180, 180, 25, 25, 3, RGB(245, 5, 235));
// var sample = text.header_text();
// console.log(text.header_text());
// var result = sample.indexOf('ID', 0);
// console.log(result);
var handle;

if (fb.IsPlaying) {
handle = fb.GetNowPlaying();
} else {
handle = fb.GetFocusItem();
};
tfo = {
album : fb.TitleFormat('%album%').EvalWithMetadb(handle),
artist : fb.TitleFormat('%artist%').EvalWithMetadb(handle),
title : fb.TitleFormat('%title%').EvalWithMetadb(handle),
playback_time : fb.TitleFormat('%playback_time% ').EvalWithMetadb(handle),
length : fb.TitleFormat(' %length%').EvalWithMetadb(handle)
};

var titleID='ID'+'0'+tfo.album.substr(2,4);
console.log(tfo.album);
console.log(titleID);
var doc_file_path = 'V:\\CDDB\\bk_new\\'+titleID+'.docx';
// console.log(doc_file_path);


// create instance of word
oWordApp = new ActiveXObject("Word.Application");
// make it visible on the client
oWordApp.Visible = "true";
// open the document
oDoc = oWordApp.Documents.Open( doc_file_path );
}),

FileClose: new SimpleButton(10, 60, 160, 40, 'CLOSE_BookLet_File', function () {
// ファイルを閉じる
oDoc.Close();
// ワードを終了する
oWordApp.Quit();
})
};

Posted by 連野 at 2021/08/18 (Wed) 02:34:36

>Jscriptからword file open

早急な返信ありがとうございます。
完璧に動きました。
それからワードファイルですが、ご指摘のようにopenするのに一呼吸掛かります。
おすすめのPDFにしてみます。(ファイルの数が1000を超えていますが。。。)
これからも、ご指導よろしくお願いします。

Posted by 杉木美喜雄 at 2021/08/18 (Wed) 10:07:41

>1000以上あるんじゃ

無理して変えることないですよー。労力の方が大変でしょ。
そこまであるとは思わなかったです。

Posted by 連野 at 2021/08/18 (Wed) 20:52:10

コメントは日本語でお願いします。(URLは入力禁止:Do not URL writing.) :System message: コメントを受けつけています。