js-ctypes で音声読み上げ

nanto_vi (TOYAMA Nao), 2010-02-20

js-ctypes and so on

js-ctypes
JavaScript から外部ライブラリの関数を呼び出し。
AquesTalk2
テキスト (かな表記音声記号列) を音声として出力。
KAKASI
漢字かな交じり文をひらがなに変換。

3 つを組み合わせると……?

デモ

選択範囲の文字列を読み上げる。

userChrome.js + read-selection.uc.js

AquesTalk2

int AquesTalk2Da_PlaySync(const char *koe,
                          int iSpeed = 100,
                          void *phontDat = 0);
let at2da = ctypes.open('AquesTalk2Da.dll');
let playSync = at2da.declare('AquesTalk2Da_PlaySync',
                             ctypes.stdcall_abi,
                             ctypes.int32_t,
                             ctypes.string,
                             ctypes.int32_t,
                             ctypes.uint32_t);
let text = encode('はじめまして', 'Shift_JIS');
playSync(text, 100, 0);
at2da.close();


function encode(string, encoding) {
  let converter = Cc['@mozilla.org/intl/scriptableunicodeconverter'].
                  createInstance(Ci.nsIScriptableUnicodeConverter);
  converter.charset = encoding;
  return converter.ConvertFromUnicode(string);
}

KAKASI

int kakasi_getopt_argv(int argc, char **argv);
char *kakasi_do(char *str);
int kakasi_free(char *p);

どうすればいいんだ?

どうすれバインダー!?

ポインタの取得

js-ctypes のソースコードを見てみると……、
ctypes.ustring の場合は文字列データがそのまま渡される。

void *memchr(const void *buf, int c, size_t count);
function getPointer(blobString) {
  if (!blobString) return 0;
  let crt = ctypes.open('msvcr71');
  let memchr = crt.declare('memchr',
                           ctypes.stdcall_abi,
                           ctypes.uint32_t,
                           ctypes.ustring,
                           ctypes.int32_t,
                           ctypes.uint32_t);
  let firstByte = blobString.charCodeAt(0) & 0xff;
  let pointer = memchr(blobString, firstByte, 1);
  crt.close();
  return pointer;
}

ポインタから文字列の取得

size_t strlen(const char *str);
void *memcpy(void *dest, const void *src, size_t count);
function getCString(pointer) {
  let crt = ctypes.open('msvcr71');
  let strlen = crt.declare('strlen',
                           ctypes.stdcall_abi,
                           ctypes.uint32_t,
                           ctypes.uint32_t);
  let length = strlen(pointer);

  let memcpy = crt.declare('memcpy',
                           ctypes.stdcall_abi,
                           ctypes.uint32_t,
                           ctypes.ustring,
                           ctypes.uint32_t,
                           ctypes.uint32_t);
  let buffer = new Array(((length + 1) >> 1) + 1).join('\0');
  memcpy(buffer, pointer, length);

  let cString = buffer.replace(/[\s\S]/g, function (c) {
    let n = c.charCodeAt(0);
    return String.fromCharCode(n & 0xff, n >>> 8);
  }).substring(0, length);
  crt.close();
  return cString
}

安全性

Q. 文字列データを直接書き換えてるけど大丈夫?
A. わかりません。
Q. この手法はずっと使えるの?
A. わかりません。

まとめ

js-ctypes でポインタのポインタや構造体も無理やり扱えそうだけど、どんな副作用があるかわからないし、明日にも扱えなくなるかもしれないよ!