Category Archives: プログラミング

いつの間にかChormeのgetUserMediaの挙動が変わっていた件

Comiket86/87で頒布した「いつもの人発見装置の作成」という本で作った装置は、マイコンとスマホとのデータ通信に、マイコンでDBPSK変調 → オーディオケーブル → スマホ(タブレット)のブラウザ上のJavaScriptで復調という手段をとってます。

で、Comiket86で作った装置を、Comiket87でもデモしようと思って、久しぶりに火を入れてみたら、うまく動かない。

タブレットにオーディオケーブルを指して、しばらくは、うまく通信できているのに、しばらくするとまったく通信できなくなる。

ブラウザ側のJavaScriptは getUserMedia というAPIでオーディオ入力データを取得しているのですが、どうやらComiket 86からComiket 87までの半年間でブラウザとして使っていたChormeのgetUserMediaの挙動が変わったようです。

いつの間にか AGC (Auto Gain Control) やら、エコー除去処理やら、ノイズ除去処理やらがデフォルトで動作するようになった模様。

いつもの人発見装置からブラウザに送り込んでいるオーディオデータは人間が聞くような音ではないので、新たに動くようになった機能によって、次第に、getUserMediaで得られるデータからDBPSKで変調された部分がフィルタで取り除かれてしまったようです。

これらの新機能を動かさないようにするには・・・・
http://d-rissoku.net/WifiDetector/ のデモのソースコード ( http://d-rissoku.net/WifiDetector/WifiDetector.js )でいえば、

  navigator.getUserMedia(
    {audio : true},
    function(s) {
      AudioComm.startReceive(audioCtx, s, WifiDetector.recvByteHandler);
    },
    function(e) {
      console.log(e);
    });

となっているのを、

  navigator.getUserMedia(
    {audio: {
      "mandatory": {
        "googEchoCancellation": false,
        "googAutoGainControl": false,
        "googNoiseSuppression": false,
        "googHighpassFilter": false
        },
      "optional": []
      }},
    function(s) {
      AudioComm.startReceive(audioCtx, s, WifiDetector.recvByteHandler);
    },
    function(e) {
      console.log(e);
    });

な塩梅にすればOKなようです。

Makefile 別ディレクトリに中間ファイル & 自動依存関係設定

普段、自分でMakefileを書くことはほとんど無い。あったとしても、ちょっとしたプログラムでソースファイルとして数個のCソースがあるようなもの。なので、Makefileの知識は、ターゲットと依存関係を : で区切って書くとか、変数が使えて、その参照は$(~)でやるとか、そういうぐらいしかなかった。

ところが、ある程度の規模があるプログラムを作成することになり、ちょっと自分流Makefileというものを、ちゃんと作っておこうと思い立つ。一回作っておけば、使いまわしできるからね。

こんなことができれば・・・・という自分の要求は、以下のとおり。

  1. 中間ファイル(オブジェクトファイル)は、ソースとは別のディレクトリに吐き出させたい。ただし、ここで、元となるソースは複数のディレクトリ内に散在している。
  2. ソースからインクルードしているヘッダなどの依存関係も、ちゃんとなんとかしてほしい
  3. ビルドは、最速である必要はないが、モッサリしているのは嫌
  4. 新しいソースを追加したとき、いろんなところを書き換えるのは嫌

適当にググレば、そんなMakefileのサンプルがわんさか出てきて、コピペで終了と思ったが、甘かった。1.と2.を同時に実現する方法がなかなか見つからない・・・。

ということで、Makefileの仕様を勉強しながら、自分流Makefileをつくる。

例題

例として次のようなディレクトリ・ファイル構成を考える。

 / 
 | Makefile / main.c / ValDef.h / Funcs.h
 |- SubDir1
 |   |   func.c
 |   |- SubSub
 |         func.c
 |- SubDir2
 |   func.c
 |- Build
     中間ファイルはここに吐き出させたい

作業スペースのルートに Makefile と Cコード(main.c)とヘッダファイル。そしてサブのディレクトリに、複数のCコード(func.c)が存在している状態。複数のディレクトリに同じファイル名のCファイルがあるのが曲者だ。既存のソースの流用などをしたいとき、同じファイル名のcファイルが複数存在することはありうる。

こういうMakefileを作った

SRCS = \
	main.c \
	SubDir1/func.c \
	SubDir1/SubSub/func.c \
	SubDir2/func.c

INC_DIR = .

BUILD_DIR = Build

OBJS=$(addprefix $(BUILD_DIR)/,$(patsubst %.c,%.o,$(SRCS)))
DEPS=$(patsubst %.o,%.d, $(OBJS))

CC = gcc
TARGET = TestProg.exe
CFLAGS += $(addprefix -I,$(INC_DIR))
LDFLAGS += 

all: $(BUILD_DIR) $(TARGET)

$(BUILD_DIR):
	mkdir -p $(BUILD_DIR)

$(TARGET) : $(OBJS)
	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^

$(BUILD_DIR)/%.o : %.c
	mkdir -p $(dir $@); \
	$(CC) -c $(CFLAGS) -o $@ $<

$(BUILD_DIR)/%.d : %.c
	mkdir -p $(dir $@); \
	$(CC) -MM $(CFLAGS) $< \
	| sed 's/$(notdir $*).o/$(subst /,\/,$(patsubst %.d,%.o,$@) $@)/' > $@ ; \
	[ -s $@ ] || rm -f $@

clean:
	rm -rf $(BUILD_DIR) $(TARGET)

.PHONY: all clean
-include $(DEPS)

上記のMakefileを、Buildディレクトリがまだできていない状態から実行したときに何が行われるのか、ちゃんと理解しようとすると、以下のとおり。

  1. makeは41行目のinclude $(DEPS)にしたがって、Build/main.d や、Build/SubDir1/func.d などのファイルをincludeしようとするが、これらファイルが 存在していないことに気づく。ゆえに、まずは ~.d ファイルがターゲットとなっているルールを見つける。
  2. ~.dを生成するルールは31~35行目にある。
    例えば、Build/SubDir1/func.d は、$(BUILD_DIR)/%.d にマッチする。このとき、%の部分(stemと言うらしい)は、SubDir1/func なので、make は、Build/SubDir1/func.d が依存するファイルが、 SubDir1/func.c であると認識する。
  3. fund.dを吐き出すための Build/SubDir1 ディレクトリを作成する。(32行目)
  4. gcc -MM オプションを使って func.c がincludeしているヘッダの一覧を標準出力に出力させる。ここで、gccの出力は、以下のフォーマットになる。
    func.o: SubDir1/func.c Funcs.h

    このままでは、次の理由でNGである。

    • .oファイルの相対パスが書かれていない。これでは、Func.h を書き換えても、Build/SubDir1/func.o を新たに生成する必要があると、makeは思わない。
    • .h ファイルに新しいincludeを追加したときに、.dファイルが新しく生成されない。

    そこでsedを使って、gccの出力を以下のようになるように書き換える。

    Build/SubDir1/func.o Build/SubDir1/func.d: SubDir1/func.c Funcs.h

    さらに、gcc -MMが失敗して、空の .d ファイルができてしまった場合は、その .d ファイルを削除するようにした。

  5. .dが出来上がるとincludeが成功するので、次にmakeは$(TARGET)の依存ファイルである各種.oファイルを生成しようとする
  6. そのルールは27-30行目に書いてあるので、それに従い、.oを作成
  7. .oができると24行目のルールで最終生成物 TARGET が出来上がる

・・・なんとか、できました。