/dev/dspを用いた、FreeBSD(Linux)サウンドプログラミング。

研究で、どうしても、サウンド関連のプログラムをしなければならなくなったので、しょうがないので、webなどを検索してみましたが、意外に再生部分の解説はあるのですが、録音部分の解説はあまり見つかりませんでした。まぁ、出るか入るかの違いなので、そんなに、できる人には難しくないのですが、拙僧のように、猿真似しかできない人間にはつらかったので、簡単にどういう感じでやるかということに。ただ、勘違いしないでほしいのが、筆者は、音の専門家でもなく、またプロフェッショナルな理系でも無いため解説はいいかげんで、使えればいいやてきなところなでいくので....

■/dev/dspを確認せよ

とりあえず、/dev/dspがあること(あることはあるが動いてないかもしれない)を確認してください。cat /dev/sndstatあたりで、どのカードが認識されてるかなど、しることもできますが、最近のLinuxなどだと、自動的に設定されてるかもしれません。FreeBSDは最近の、PCIカードのサウンドカードだとdevice pcmをカーネルにくわえて再コンパイルすれば認識してくれます。と、ここらへんは他のページのほうがくわしいので、ここでは詳しくはのべません。というわけでほんとに動いてるかどうか確認するには、

cat /dev/dsp

とかして確認してみましょう、なんか文字がながれたらOK。ただし、空白な文字列などは、うまく設定されてない場合があるので注意が必要。また、/dev/dspWって場合もあるので、注意が必要。xmixerなどで、マイク入力が切られてる場合もあるので、確認が必要。確認と必要ばかりですが、それで確認が、できたなら、ヨーソロー。

/dev/dspからながれてくるのは、一体どういうデーターが流れているのでしょう?ためしに、マイク入力に何らかの曲をながして、 cat /dev/dspの出力を見てみましょう。出力を眺めると、xyzzuなと、なんか、けっこーアルファベットの順に成っています。つまり、波形の線形的性質を現してるといってもいいでしょう。サンプリングレートが十分たかければ、xからaなどというのは、あまりないっていうそういう話はどーでもいいということで....つまり、あとは、/dev/dspをオープンして、ふつーにファイルとしてよみとればいいです。

とりあえず、サンプルプログラムをのせておきます。ほんとのほんとに、サンプルプログラムなんで、あまり詳しくつっこまれると泣きます。

whisle.tar.gz(フーリエ変換も含む)
visual_sound.tar.gz(GUI,要open_gl Mesaフーリエ変換も含む)

一番簡単なサンプル

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/soundcard.h>


#define  DSP_IN    "/dev/dsp"
#define  DSP_OUT   "/dev/dsp"


main()
{
  int sound_card;
  unsigned  char s[50];
  unsigned int output_data;
  int parm,status,bits,sampling;

  if ((sound_card = open(DSP_IN, O_RDWR|O_NONBLOCK)) < 0) {
    perror(DSP_IN);
    exit(1);
  }

  /* set mono */
  parm = 0;           //0 is mono. 1 is streo.
  status = ioctl(sound_card , SNDCTL_DSP_STEREO, &parm);
  
  if (status < 0) {
    perror("error from sound device ioctl");
    exit(-1);
  }

  /*set 8bit sampling  */
  bits = 8;
  status = ioctl(sound_card, SOUND_PCM_WRITE_BITS, &bits);
  
  if (status < 0) {
    perror("error from sound device ioctl");  
    exit(1);
  }


  /* set 8000Hz(8khz) sampling rate */
  sampling = 8000;
  status = ioctl(sound_card , SOUND_PCM_WRITE_RATE, &sampling);
  if (status < 0) {
    perror("error from sound device ioctl");
  
    exit(1);
  }

  while(1)
    {
      int flg;
      memset( s, '\0', 50);

      do{
	flg = read( sound_card ,&s,1);
      }while((flg == 0)||(flg == -1));
      
      output_data =  (unsigned int)(s[0]);
      printf("%d\n", (int)(output_data-128)); //8bit data
    }

}

上記のプログラムは、かなり簡潔なプログラムです。流れとしては、

/dev/dspのオープン

モノラル出力にセット

8bitサンプリングにセット

8khzのサンプリグレートにセット

readにて、/dev/dspよりの読み込み

流れとしては、上のとおりです。。また、モノラル出力から、8khzのサンプリングレートの設定などは、ioctlをつかいデバイスに命令を与えているだけです。なので、ioctlに与える命令をかえれば、いろいろ出力をかえることができます。基本的にioctlで、デバイスに要求し、その処理をするのが、デバイスドライバの役割です。よって、サポートされてないサウンドカードにたいしては、直接たたく方式もあるのでしょうが、やったことないので、ここでは述べません。そのうち、USBをほげりたおす計画であるので、その時にしましょう。

■各種ioctrlの意味

ioctlについて、manでしらべてみると、
ioctl - デバイスを制御する

と、まんまですが、manによる解説には

ioctl() 関数は特殊ファイルの下層となっているデバイスのパラメータを操作し ます。特に、キャラクタ型特殊ファイル (たとえば、端末) の多数の操作属性は ioctl() で制御できます。引数 d は開いているファイル記述子である必要があり ます。

ま、つまり簡単にいうと、/dev/以下のキャラクタ端末ttyとかcuaa0とか、そういう関連のものを、操作できるわけです。ただし、やはりデバイスドライバがないと、どうにもならないので、サポートされてる場合は、涙をのむしかありません。では、以下の行についてみてみましょう。

/* set mono */
  parm = 0;           //0 is mono. 1 is streo.
  status = ioctl(sound_card , SNDCTL_DSP_STEREO, &parm);

上の分は、/dev/dspにたいして、出力はモノラルでお願いといっています。ちなみに、第一引数の、sound_cardは、/dev/dspでopenで帰されたファイル記述子。SNDCTRL_DSP_STEREOは、どういう要求か、parmは、変数です。書いてあるとおり、parmに0を入れると、モノラル、1だとステレオです。また、SNDCTRL_DSP_STEREOなど他にもいろいろな要求するための、キーワードなのですが、sys/soundcard.hにたっぷり書いてあります。locate soundcard.hとでもして探してください。また、これらが特に、詳しく解説されているページとして、

How to control PCM devices on FreeBSD-2.1.5---永尾 禎啓さん
(http://www.asahi-net.or.jp/~cj4t-ngo/pcmfuncs.html)

が、とても詳しく、参考になりました。ありがとうございます。

以下は440HZの正弦波を、ステレオで左右同位相でながしました。Gnuplotで作成したグラフです。モノラル出力の場合が左、ステレオ出力の場合が右です。
という、図よりステレオデーターはどうやら、右と左が交互に来てるようです。どっちが先かは確かめていません。(って、なんかオプションとかあるのかなー)

つぎに

/*set 8bit sampling  */
  bits = 8;
  status = ioctl(sound_card, SOUND_PCM_WRITE_BITS, &bits);
  
  if (status < 0) {
    perror("error from sound device ioctl");  
    exit(1);

の部分なのですが、この部分は量子化ビット数なのですが、ここでは8bitにセットしています。bits = 8 の部分をbits = 16としたならば、16bitになります。もちろん16bitのほうが音の再現性はよろしいですが、データー量もでかくなります。ここで注意したいのが、8bitで出力されてくるデーターはunsigned char で、16bitsの場合はshort intです。筆者はこれにはまり、はじめモノラルにセットしてるのに、ステレオに出てるぞーとか呆けたことをいってました。もちろん、中間値がちがいます。
最小 中間値 最大
8ビット 0 128 255
16ビット -32768 0 32767
となります。注意してください。ここらへんで、参考になったページといえば

□そこはかとなくLinux(C3Magazine)
http://www.c3.club.kyutech.ac.jp/c3mag2/LINUX/index.html

上のページはwaveも再生なども書いてあり、とても参考になりました。ありがとうございます。

  /* set 8000Hz(8khz) sampling rate */
  sampling = 8000;
  status = ioctl(sound_card , SOUND_PCM_WRITE_RATE, &sampling);

PCMであると、4000-44100HZまで設定できます。CD音質で録音しおうとするととんでもないデーター量になることがわかります(最近だとそうでもないですね2001年6月現在、256MBが4000円ぐらいなわけですから)。長時間録音の場合、うまく、データーをディスクなどに書き込むなどして録音する必要があります。そのうち、音などは、無限に録音できるぐらいのメモリー容量になりそうですが.....実際、現在でもHDDを使えば、無圧縮でも、1日ぐらいは録音できるレベルにある。というわけで、samplingに具体的に数字をいれれば、サンプリングレートが変わります。

do{
	flg = read( sound_card ,&s,1);
      }while((flg == 0)||(flg == -1));

あとは、こいつから読み出してやるだけ、flg == -1 のときはエラーなので、ちゃんと、エラー処理をしっかりすべきですね。

■cool Link

□そこはかとなくLinux(C3Magazine)
http://www.c3.club.kyutech.ac.jp/c3mag2/LINUX/index.html

□How to control PCM devices on FreeBSD-2.1.5---永尾 禎啓さん
(http://www.asahi-net.or.jp/~cj4t-ngo/pcmfuncs.html)

□Programing Tips-----Linux MoeMoe Project http://www2.pos.to/~rero2/tips/sound/sound_tips01.html

□Linux Sound Programming with OSS API-----Rio's Laboratory http://homepage3.nifty.com/rio_i/lab/oss/


[email protected]