トップ «前の日(11-04) 最新 次の日(11-06)» 追記

本 日 の h o g e

hogeとはワイルドカードのようなものです。日々起こった、さまざまなこと −すなわちワイルドカード− を取り上げて日記を書く、という意味で名付けたのかというとそうでもありません。適当に決めたらこんな理由が浮かんできました。

更新情報の取得には rdflirs を使ってもらえると嬉しいです.


11/05/2002 JavaScript

tDiary 12日目

男脳・女脳チェックのスクリプトを久しぶりに覗いてみたら汚いことこの上なし。見るに堪えなかったので適当に直す。あぁ、あとどーにも非効率的な処理をしていたのでそこも適当に改善。しっかし、過去の自分はこんな簡単な効率問題を理解出来なかったのかと思うと情けない。変数は減らせるしループ回数も押えられるのに、なぜこの方法でやらなかったんだろう?

まぁ、ここは自分が成長したということで素直に喜んでおこうか。むう。

オムライス

オムライスの上手な食べ方を誰か教えてください。卵と米がいつも分離してしまいます。これじゃ意味ないじゃないですか。

Apache

っていうかさー…何で、でびやーんのApacheってsuEXECがデフォルトで有効になってるのさー。イヤ、セキュリティ的に、使った方が良いってのは分かるけど、結構制約受けるから知らないユーザが困るじゃんよー。…つーかエラーメッセージが分かりづらすぎ。Premature end of script headersとか言われても分からんわ。

…てなわけで、こんなエラーメッセージを見たらsuexecバイナリのsuビットを落としましょう。それで解決。

追記

うーん、ポエムはともかくとして、日記は結構面白いものかも知れんですぞ。まぁ、何を書いてあるかにもよるでしょうが…。
本日のツッコミ(全1件) [ツッコミを入れる]

yosh2/3 [昔書いたプログラム、日記、ポエムは読みたくないものですな 後者2つは書いた事ないけど… ]


11/05/2004

tDiary 743日目

[小ネタ] 頭文字Dキャラクター別占い

RYoの右脳 から.

atzmさんは 秋山 渉 です!!

●「秋山 渉」系のあなたは、物事を前向きに考えられて、更にはとても優しい人といえます。が、切れるときはもうとまらない、どうにも止まりません。そんなときは妹のピンタが良くききます。(※ロリコンでは無いです)それでかつマジメな試合となると体中のアドレナリンがどっぱどぱ状態になります。凄いと認めた人に対しては褒め称えます。持ち前のキレをなくしさえすれば良い兄弟重いな人となれるでしょう。良き友でありライバルであるような人が貴方の周りにはたくさん居ることでしょう。

ラッキーアイテムはアドレナリンです(アイテムなのか?)

●atzmさんの開運ポーズは、三点倒立です!

えーっと……誰だ.レース用エンジンの時の人?

[小ネタ] ラブ・ファミコン 〜 ブラウザでファミコンしよう 〜

TokuLog! から.

むおー,熱い.何て熱い blog なんだ.

本日のツッコミ(全5件) [ツッコミを入れる]

ati [86レビンの人ですな < 秋山 渉 ]

atzm [うーんそんな人もいたようないなかったような……読み直してきます(^^; ]

ati [あぁぁぁぁ,タイミング的には「レース用エンジンの時の人」で間違いないです ^^; ]

RYo [三点倒立に期待してますw ]

atzm [エンジン見て「バカにしてんのか」って言った人…でしたっけ. とりあえず三点倒立に期待されても困る今日この頃です. ..]


11/05/2005 うーん

tDiary 1108日目

[Py][戯言] HostIP とか mimetools とか RFC とか

import urllib
import mimetools

class HostIP(dict):
    _URL_BASE = 'http://api.hostip.info/rough.php?position=true&ip=%s'
    _GOOGLEMAPS_BASE = 'http://maps.google.com/?q=%sN+%sE(%s)'
    def __init__(self, ipaddr):
        url = self._URL_BASE % ipaddr
        #
        fp = urllib.urlopen(url)
        headers = mimetools.Message(fp, 0)
        fp.close()
        #
        dict.__init__(self, headers.dict)
        self['ipaddr'] = ipaddr
        self['url'] = url
        self['googlemaps'] = self._GOOGLEMAPS_BASE % (self['latitude'], self['longitude'], ipaddr)

>>> test = HostIP('210.156.41.55')
>>> for k, v in test.items():
...     print '%s: %s' % (k, v)
... 
city: Morioka
guessed: true
url: http://api.hostip.info/rough.php?position=true&ip=210.156.41.55
googlemaps: http://maps.google.com/?q=39.7N+141.15E(210.156.41.55)
latitude: 39.7
country: JAPAN
ipaddr: 210.156.41.55
country code: JP
longitude: 141.15

一応うまく行くには行くんだがね.

mimetools は 2.3 以降で撤廃 (互換としては残る) らしい.email.Parser.HeaderParser を使うと,こいつは RFC2822 に完璧に準拠してるらしく,ヘッダフィールド名に空白が混じるとダメなんで Country Code が取れなくなる.てか mimetools.Message が RFC に完全準拠してないのがおもろい.利便性を取ったのかな.

しかしやっぱ横着せずに XML の方を解析すんのが正統派なんかなー.めんどくせ.

てか

このサーバ岩手公園内にあるのかよ(わら

でもいいところまでいってる.

[Gentoo][Perl] app-portage/g-cpan 便利かも

Perl 離れが進んでたので g-cpan を使う機会がなかなかなかったけど,今日初めて使ってみた.これ便利だわ.

$ sudo g-cpan -i Imager::Fill

これで CPAN から Imager::Fill を自動的に探してきて ebuild を自動生成してインストールまでしてくれる.すげー.

[日記] 飯日記

  • 12時頃:豚丼
  • 20時頃:カップ麺
本日のツッコミ(全7件) [ツッコミを入れる]

しんどう [g-cpanは便利って言えば、便利。 中で何がどーなっているかはまったくわかりませんけど(笑) ]

atzm [いやー,中が全く分からないのは Perl だかうわなにをするやめrくぁwせdrftgyふじこ ]

しんどう [atzmさん、なんか変な薬やりました?(w ]

usata [g-ctan も作ったんですけどねえー ]

atzm [うーん強いて挙げれば百薬の長と呼ばれる妙薬ですかねぇ ;) てか CTAN はいいですねえ.バリバリの TeX 使..]

mget * [ぐーぐる先生のローカルは、なかなか素敵。とっ都庁周辺がぁぁぁ!ほんぎゃdじゃせdrffgyふじこ ]

atzm [コンビニだらけですな. ]


11/05/2006 むう

tDiary 1473日目

[日記] 飯日記

  • 12時頃:豚めし,サラダ
  • 17時頃〜:飲み

大学の同期と飲み.


11/05/2007 ふむ

tDiary 1838日目

[日記] 飯日記

  • 11時頃: サラダ巻き
  • 23時頃: おでん,サラダ

11/05/2009 むー

tDiary 2569日目

[日記] 眠れんので何か書く

季節性インフルエンザ予防接種の問診にて.

俺「よろしくおねがいしまーす」
(問診票を手渡す)
医「あらー良いお名前ねー」
俺「はっ?」
医「よく言われない? 何て言うか,こう,皆を集めて一緒になるっていうか」
(目を閉じて何かを抱きかかえるようなジェスチャー)
俺「はあ,どうもありがとうございます...」
医「鉄腕アトムみたい」
俺「えっ」

問診してくれ.

[日記] 眠れんので何か書く2

帰りの電車で立ってたら,可愛げな女性が入ってきて隣に.

そしたら屁が出た.

オチはない.

[日記] てか

予防接種した痕が一日半経っても赤く腫れてる.

やっぱ免疫力落ちてるんかなー.

本日のツッコミ(全2件) [ツッコミを入れる]

smbd [俺も水曜に予防接種したけど、まだ腫れて熱もってるよ ]

atzm [んー,去年はそんなことなかった気がするんだけど... トシか :) ]


11/05/2011 ふむ

tDiary 3299日目

[小ネタ][JS] Web Audio API

うんこを流す計画その 3.

ひょんなことから Web Audio API に興味を持ったので,Web Audio API を使った 変なもの を作ってみた (きっと chrome とかでしか動かないと思う).

色々思うところはあるのだけれど,何というかコンテキストが「Web Audio API」というよりもはや「いかにして波形を作り合成するか」になってしまい,もう疲れたので一旦区切りを付けた感じ.

まあ何のことはない,右上部のテキストエリアに書かれた JSON データを 1 つの RIFF WAVE データに変換して Web Audio API で鳴らすというもの. JSON は全て配列で表現されていて,ルールは以下のようになっている. 休符はない.相手は死ぬ.

トップ:
  [旋律1, 旋律2, ...]

旋律:
  [音1, 音2, ...]

音:
  [長さ(秒), 周波数群]

周波数群:
  [周波数1, 周波数2, ...]

複数の周波数は同時に鳴るので,これで和音を表現する. 複数の旋律も同時に鳴る.

MML とかじゃねーのかよ,という件については,僕が MML を知らないという理由以外にも理由があって,そもそもの動機がデータを音にしたいだけだったからです. テキストエリアにデフォルトで入っているデータを見ると,IPv4 アドレスっぽいものが入ってたりする. そう,こういうそこら中にある雑多なデータを数値化して音にするとどういう感じになるのかなーという. まあ結果は「意味わからんww」でしたけど :-P

音を作る

まず,周波数と長さというただの数値を音に変えるにはどうすれば良いか. 音というのは波なので,周波数,サンプリングレート,長さから正弦波なり矩形波なりを生成すれば良い.

なお,ただの正弦波だとだいぶのっぺりした音になる. 今回はこの正弦波に 2 次ベジェ曲線のカーブ具合を係数に加えて,徐々に音が弱まるような感じにしてある (これのせいで結構変な音が出たりするのだけれど...).

コードを抜粋すると,大体以下のような感じ.

this.duration;   // 長さ
this.frequency;  // 周波数
this.maxamp;     // 最大振幅
this.hz;         // サンプリングレート

this.signals = new Array(Math.round(this.duration * this.hz));
                 // サンプリングレート x 長さ分の配列を生成する

var len = this.signals.length;
var f   = this.frequency * 2.0 * Math.PI / this.hz;
var p   = 0;

// 2 次ベジェ曲線用
var pt1 = {x: 0,   y: 0};
var pt2 = {x: len, y: this.maxamp};
var pt3 = {x: len, y: 0};

for (var i = 0; i < len; i++) {
    // ベジェの計算
    var t  = i / len;
    var tp = 1 - t;
    var x  = Math.round(tp * tp * pt1.x + 2 * t * tp * pt2.x + t * t * pt3.x);
    var y  = Math.round(tp * tp * pt1.y + 2 * t * tp * pt2.y + t * t * pt3.y);
    var d  = (len - x) / len * y;

    // sin にカーブ具合を係数として与えて保存
    this.signals[i] = Math.round(Math.sin(p) * d);

    p += f;
}

複数の旋律を同時に鳴らす

音を合成するには,その波同士を足し合わせれば良いらしい (これを加算合成というらしい) のだけれど,問題は 8bit データの加算合成.

16bit の wav データは signed で -32768 〜 32767 を取るのでそうそう溢れるようなことはないのだけれど,8bit の wav データは unsigned で 0 〜 255 を取る. 8bit のデータ同士を単に加算合成してしまうと,すぐに溢れてしまうどころか,無音を示すのが 128 なので,無音同士を合成すると音が鳴るという大変なことになる.

これについてはあまり良い方法を思いつかなかったので,今回は 8bit データも内部的には signed で持っておいて合成をやりやすくしておいた上で,最終的にバイナリに変換する際に +128 した.

RIFF WAVE データ形式に変換する

作った配列をバイナリに変換してヘッダを付加してやる.

どうも JavaScript はバイナリを扱うのが得意ではないようで,例えば Python で言う struct モジュールみたいなものはないようだ.

数値をバイナリにパックするには,String.fromCharCode() を使うことができる.このメソッドは 16bit unsigned 列を受け取って,対応する文字列を返してくれるらしい.

が,ヘッダを 8bit データとして付加する関係上,データを 16bit unsigned にしてパックしてしまうと,後で Web Audio API に食わせる際に障害となるので,8bit unsigned としてパックしてやる. 16bit のデータを 8bit のデータに分解することは,ビットシフト/マスクを駆使すればできるのだけれど,今回は typed array というものを使ってみることにする.

var buf = new ArrayBuffer(2);
var u16 = new Uint16Array(buf);
var u8  = new Uint8Array(buf);
u16[0] = 65535;  // 65535 を入れると,
[u8[0], u8[1]];  // [255, 255] に分解される

後は wav ファイルフォーマット を参考にさせて貰いつつパックしていく.

データを Web Audio API に食わせる

実際に音を鳴らすには webkitAudioContext を使う. webkitAudioContext の使い方は 仕様書 を見るとして,問題は,先ほどまでに作った音データをどうやって食わせるのか.

音データは AudioBuffer というもので表現されるらしく, webkitAudioContext の持つ createBuffer() メソッドで作成することができる. この AudioBuffer を音源ノードに乗せて,webkitAudioContext の destination に繋いでやれば良いというわけ.

ただしこの AudioBuffer には先ほど作ったバイナリを直接乗せることはできないようで,どうも ArrayBuffer として渡してやらないといけないようだ.

つまり以下のような感じになる.

var data;  // 作ったバイナリ

// ArrayBuffer に変換
// ここでバイナリがヘッダ 8bit,データ 16bit だとおかしなことになってしまう
var arrbuf = new ArrayBuffer(data.length);
var bytes  = new Uint8Array(arrbuf);
for (var i = 0; i < data.length; i++)
    bytes[i] = data.charCodeAt(i);

// webkitAudioContext を調整
var audioCtx = new webkitAudioContext();
var gainNode = audioCtx.createGainNode();
gainNode.gain.value = 1.0;
gainNode.connect(audioCtx.destination);

// 音源ノードを作る
var voice = audioCtx.createBufferSource();

// 音データを乗せる
voice.buffer = audioCtx.createBuffer(arrbuf, false);

// 音源ノードを繋ぐ
voice.connect(gainNode);

// 鳴らす!!
voice.noteOn(0);

(追記) ローパスフィルタを通す

Web Audio API には各種フィルタも実装されているようだ. 例えばローパスフィルタを通したい場合は,ノードの繋ぎを変えて,フィルタを間に挟んでやれば良い.

var audioCtx = new webkitAudioContext();

var lowpassFilter = audioCtx.createBiquadFilter();
lowpassFilter.type = lowpassFilter.LOWPASS;        // ローパスに設定
lowpassFilter.frequency.value = 440;               // 440hz より上を遮断
lowpassFilter.connect(audioCtx.destination);

var gainNode = audioCtx.createGainNode();
gainNode.gain.value = 1.0;
gainNode.connect(lowpassFilter);

(追記2) 音色 (を生成する関数) を textarea からいじれるようにした

この文章を書いてからまた少しいじっていたのだけれど,いかんせんとにかく面倒臭いので textarea から動的に関数を生成することにした. textarea の中身を変更すると音色がもにょっと変わります.

正弦波なら↓という感じにすれば良いし,

return Math.round(Math.sin(count * delta) * wmaker.maxamp);

矩形波なら↓という感じにすれば良い.

var sig  = Math.round(Math.sin(count * delta) * wmaker.maxamp);
var base = Math.floor(wmaker.maxamp / 2);
return sig < base ? -base : base;

しかし JavaScript ってすごいね,↓で関数作れるとか... eval 並に邪悪なコードをいくらでも書けそうだぜ!

new Function("引数名1", "引数名2", ..., "関数の中身");

(追記3) アホなミス & 勘違いをしていた

Web Audio API を色々いじって遊んでいた際に playbackrate (再生速度) を 0.5 に設定していたのを忘れていた. 音が正常 (?) に鳴った時に「ウムこれでいいのだ」とか思っていたのだけれど,実際は全然良くなかった.

ヘッダ 8bit, データ 16bit でパックしたものを Uint8Array で ArrayBuffer 化したため,謎のデータになっていたみたい. charCodeAt() での変換時に 16bit unsigned が 8bit unsigned に丸められてたのだと思う (ただこれで倍速とはいえ音程がちゃんとする理由はイマイチよく理解できていない).

なのでそこら辺を修正して,日記の内容も編集した (「RIFF WAVE データ形式に変換する」項). ついでに lowpass を 0 にすることでフィルタを切れるようにもしておいた.

ところでこの辺のデバッグ作業をやっていて思ったのが,やはりどうにもデバッグがやりづらいということ. というのも,生成したデータを wav ファイルに落として別プレイヤーで再生してみる,ということができないんですな. バイナリなので console.log() に出してもダメ.

しょうがないので base64 encode して console.log() に出して,それを保存した上で base64 -d file.txt | aplay とかちまちま打っていたのでした.

雑感

とにかく全てが分からなかったのでもう何が何だか.

音がなんたるかが分からない. そもそも音って何だっけ? 合成って何それおいしいの? という感じ. 音の強さ辺りとか,多分今も理解できてないと思う.

JavaScript が分からない. バイナリの扱いどころか配列の扱いすらおぼつかない. けどこれはまあだいぶ慣れたかな.

Web Audio API が分からない. 概念やその使い方など,いまだに自分の理解が合ってるかどうかすら分からない.

とりあえず,やっぱり自分は物理/数学的な感性を持ち合わせていないんだなということだけは分かりました. 波形を操作するにあたって数式を色々もにょもにょしてた時間が一番長かったと思う...