17
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

音の大きさをステレオミニプラグから値として取得する

Last updated at Posted at 2016-02-21

レベルメーター的な物を作りたいのです…!

ステレオミニプラグには交流が流れている

ステレオミニプラグ(フォーンプラグ)から、音の大きさの大小を取得したい。3極のステレオミニプラグは手前からGND、R、Lとして取得できる。まずは信号の取得のために適当なオーディオジャック変換基板で手軽に扱えるようにする。

続いて一定の波形を出力するため、Mac で 1kHz のサイン波を出力し、音量最大でステレオミニプラグに入力する。今回はAudioTest のソフトウェア(シェアウェア)を利用した。

以下はオシロスコープによるMacからの出力の波形。1メモリが左下にあるとおり500mVなので、だいたいピーク値が±1.9Vほどの交流で出力されている。また、iPhone 6s でアプリを使って同様に 1kHzのサイン波を流したところ、ピーク値が±1.4V。機器によって結構違うようだ。

sound_ac.png

つまるところ、音の大きさを読み取るには、交流 ±2.0V ほどの交流の電圧値をうまく Arduino 等から読み取れれば良いが、A/Dコンバータが交流を読めないので、値を読めるようにする必要がある。

単相半波で整流して値を取得する

まずは単純に机の上にあったスイッチングダイオード(1N4148)一本で単相半波で整流直流にしてみた。

rectification diode by hotchpotch b96c464bb5aa40ab - Upverter

sound_1n4148.png

最大値が1.2Vほどと、600mVほど下がってしまってる。1N4148のVF(順方向電圧)はスペック的には700mVほどなのでだいたい正しい。また0.6V以下の電圧だとそもそも0になってしまい、小さめの音はほぼ0になってしまう。

こんなときはVFが低いショットキーバリアダイオードを、ということで手元にある 1N5819 に変更したところ、だいたい400mVほど降下しているがだいぶましに。もっとVFが低いショットキーバリアダイオードのほうが良さそう。

sound_1n5819.png

あとはこの単相半波の値を Arduino で読み込んでそれっぽくする。なお通常の曲ではピーク値でも1Vも超えることがほぼ無いため、analogRead() の ADC の基準値を、標準の 5V から 1.1V へと analogReference(INTERNAL) で変更する。少しの期間をサンプリングして適当に平均を返す。わりとこれでもそれっぽく音の大小をとれる。

#define LEFT A2
#define SAMPLE 100

void setup() {
  Serial.begin(9600);
  analogReference(INTERNAL);
  pinMode(LEFT, INPUT);
}

void loop() {
  Serial.println(getLevel(LEFT));
}

uint8_t getLevel(uint8_t pin) {
  float avg = 0;
  float raw;
  for (int sample = 0; sample < SAMPLE; sample++) {
    raw = analogRead(pin);
    avg += raw;
  }
  return avg/SAMPLE;
}

ちなみに全波で整流すると、VF*2の電圧下降が起こるのでちょっと厳しそう。

追記・静かな曲だと 400mV の電圧降下でもほぼ値をとれなくなってしまう…。

レベルシフトで持ち上げる (Arduino の 5V を使う)

ピーク値が交流±1.8Vなら、それをレベルシフトして読み取れる範囲にする方法。

にあるとおり、レベルシフトで Arduino の出力している直流 5V を抵抗分圧で 2.5V にしてを加えることによって、信号を2.5Vの±1.8Vにすることで、 ADC で読み取れる 0V~5V の範囲に収める。

2.5V level shift by hotchpotch 1be660bfa85b15e7 - Upverter

sound_levelshift_5v.png

10uFのコンデンサは直流をカットして交流成分だけ流す用途、だと思う…。(違ったら教えてください…。)

なお交流からの入力が何も無い場合、2.5Vを期待する(analogRead()で512になってほしい)が、実際は抵抗分圧時の誤差があるので、少しずれる(カーボン抵抗の5%誤差から、金属皮膜抵抗の0.5%や1%誤差だとだいぶ緩和する)ので、中央値を512から変える必要が出てくる。

#define LEFT A2
#define SAMPLE 100
#define CENTER 512

void setup() {
  Serial.begin(9600);
  pinMode(LEFT, INPUT);
}

void loop() {
  Serial.println(getLevel(LEFT));
}

uint8_t getLevel(uint8_t pin) {
  float avg = 0;
  float raw;
  for (int sample = 0; sample < SAMPLE; sample++) {
    raw = analogRead(pin);
    avg += abs(raw - CENTER);
  }
  return avg/SAMPLE;
}

OPアンプを使う

非反転アンプ単電源用 交流結合型の方法で、+2.5Vに増幅の基準電圧をあわせることにより、0~5V の値として取得できる。ググっても出てくるけど、書籍 回路の素101 の項目007の「非反転アンプ単電源用 交流結合型」が大変解りやすかった。

その回路のオペアンプを LM358N に変えて 1000Hz のサイン波のオーディオ入力を2倍の出力に増幅してみた。最初C1(0.1u)を入れずに全く出力が出なくてうーん、と思ったんだけど、コンデンサを入れて直流成分のカットが必要なことが解った。(直流成分のカットが具体的に何かはまだ解っていない…)

zouhuku.png

まとめ

音量がコンスタントに大きいなら、単相短波なら1ショットキーと1抵抗の整流でも割とそれっぽい値がとれる。そうじゃないならダイオードによる電圧降下が馬鹿にならないので、レベルシフトなりオペアンプを使った方が良さそう。

17
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?