ホーム 主筆 その他ソフト その他情報 Syuhitu.org English

Windows関連

スクリーンセーバー作成法

半透明ウインドウの性能

bootfont.bin

キャビネット形式

ウインドウスタイルをいじる

Java製ソフトをServiceに登録する

イベントログにメッセージを出力する

コントロールパネルにアイコンを追加する

スクリプトによる拡張1

スクリプトによる拡張2

ガジェットの作成

大容量メモリ

メモリ搭載量の下限に挑む

スパースファイルにする

表示されるアイコンの種類を調べてみた

メモリマップIOとエラー処理

ファイルを作る順番と速度の関係

Cryptography API: Next Generationを使う

Windows 10のアクセントカラー

iSCSIディスクにバックアップを取る

サーバプロセスを分離して実装する

サーバプロセスを分離して実装する - F#

レジストリに大量に書き込む

Solaris関連

OpenGL

Solaris設定

ディレクトリの読み込み

主筆プラグイン開発

マルチスレッドでの開発

door

音を出す

Blade100の正しい虐め方

パッケージの作成

画像入出力

BMPファイル

ICOファイル

ANIファイル

JPEGファイル

減色アルゴリズム

減色アルゴリズムの並列化

その他アルゴリズムなど

自由軸回転

Base64

文字列操作

CPU利用率の取得

正規表現ライブラリ

メタボールを作る

メタボールを作る2

正規表現とNFA・DFA

C言語の構文解析

液晶ディスプレイを解体してみた

iSCSIの理論と実装

単一フォルダにファイルを沢山作る

USB-HUBのカスケード接続

SafeIntの性能

VHDファイルのフォーマット

USBメモリに書き込み続けてみた

Solarisで音を出す

2006年2月29日公開

Sunのマシン、とりあえずBlade100で音声を再生することについて考えてみる。

参考文献

文献も何も、/dev/audioのmanページを見れば、必要なことは全部書かれている。

だが英語である。

訳してみる

訳してみた。

それでもやっぱり判りにくいから、使い方について簡単に説明してみる。

デバイスファイルのオープン

まずは/dev/audioをopen()システムコールで開く必要がある。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
  int fd;

  // 開く
  fd = open( "/dev/audio", O_RDWR );
  if ( fd < 0 ) {
    // 失敗した
    fprintf( stderr, "It failed to open /dev/audio\n" );
    return 0;
  }

  // 閉じる
  close( fd );
  return 0;
}

マシンに直接繋いだキーボードとディスプレイで操作している限り、特に何も考えることなく一般ユーザ権限で/dev/audioを開くことができるはずである。

ただし、telnetで接続して操作している場合は、アクセス権が無くて開くことができない可能性がある。すなわち、/dev/audioを開くのにroot権限が必要になる。どうやら、リモートで接続されて盗聴などのよからぬ事をされるのを防止するという理由があるらしい。

どうしてもtelnetで接続した状態で一般ユーザから/dev/audioを使えるようにしたければ、デバイスファイルのパーミッションを変更する必要がある。

俺の環境では「/dev/audio」は「/dev/sound/0」へのシンボリックリンクになっており、「/dev/sound/0」は「/devices/pci@1f,0/sound@8:sound,audio」へのシンボリックリンクになっている。でもって、この最後の「/devices/pci@1f,0/sound@8:sound,audio」のパーミッションが「crw-------」となっているために、一般ユーザからは使えないようになっている。

だから、この「/devices/pci@1f,0/sound@8:sound,audio」というファイルのパーミッションを変更してやればいいということになる。とりあえず、全開にしておけば間違いはない。

デバイスの状態を取得する

デバイスファイルを開いたら、次はデバイスの状態を取得してみる。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

// audio_prinfo構造体の中身を表示する
void foo( const struct audio_prinfo *p )
{
  printf( "sample_rate = %d\n", p->sample_rate );
  printf( "channels = %d\n", p->channels );
  printf( "precision = %d\n", p->precision );
  printf( "encoding = %d : ", p->encoding );
  switch ( p->encoding ) {
  case AUDIO_ENCODING_ULAW:
    printf( "AUDIO_ENCODING_ULAW" );
    break;
  case AUDIO_ENCODING_ALAW:
    printf( "AUDIO_ENCODING_ALAW" );
    break;
  case AUDIO_ENCODING_LINEAR:
    printf( "AUDIO_ENCODING_LINEAR" );
    break;
  }
  printf( "\n" );
  printf( "gain = %d\n", p->gain );
  printf( "port = %d\n", p->port );
  printf( "buffer_size = %d\n", p->buffer_size );

  // 後は省略
}

// デバイスの状態を読み込み表示する
struct audio_info read_info( int fd )
{
  struct audio_info info;

  // 情報を取得する
  if ( ioctl( fd, AUDIO_GETINFO, &info ) < 0 ) {
    // 失敗
    fprintf( stderr, "It failed to get audio info\n" );
    return info;
  }

  printf( "Succseed to get info\n" );
  printf( "--- record ---\n" );
  foo( &info.record );
  printf( "--- play   ---\n" );
  foo( &info.play );
  printf( "\n" );
  printf( "monitor_gain = %d\n", info.monitor_gain );
  printf( "output_muted = %d\n", info.output_muted );

  return info;
}

int main( int argc, char *argv[] )
{
  int fd;

  // 開く
  fd = open( "/dev/audio", O_RDWR );
  if ( fd < 0 ) {
    // 失敗した
    fprintf( stderr, "It failed to open /dev/audio\n" );
    return 0;
  }

  // デバイスの情報を表示する
  read_info( fd );

  // 閉じる
  close( fd );
  return 0;
}

デバイスの状態の取得は、上記プログラムの真ん中ぐらいにある、ioctl()関数で行っている。その上にあるfooという関数は、単に構造体の中身を整理して画面に出力する処理を行っているだけである。また、main関数では/dev/audioを開いてread_info関数を呼び出しているだけである。

つまり、デバイスの設定を取得するには、AUDIO_GETINFOのioctlを行えばいいということになる。引数はaudio_info構造体の変数のアドレスである。

呼び出しに成功すると、引数に指定した構造体にデバイスの現在の設定が取得される。

Succseed to get info
--- record ---
sample_rate = 8000
channels = 1
precision = 8
encoding = 1 : AUDIO_ENCODING_ULAW
gain = 127
port = 1
buffer_size = 8192
--- play   ---
sample_rate = 8000
channels = 1
precision = 8
encoding = 1 : AUDIO_ENCODING_ULAW
gain = 191
port = 3
buffer_size = 0

monitor_gain = 0
output_muted = 0

当然、上記の値は実行したときの環境によって変わってくる。

デバイスの状態を変更する

今度はデバイスの状態を変更してみる。例えば、音声を再生するときの音量を変更してみる。また、再生する音声のエンコード方式をPCMに変更する。

// <<< read_info関数は上のプログラムと同じ >>>

int main( int argc, char *argv[] )
{
  int fd;
  struct audio_info info;

  // 開く
  fd = open( "/dev/audio", O_RDWR );
  if ( fd < 0 ) {
    // 失敗した
    fprintf( stderr, "It failed to open /dev/audio\n" );
    return 0;
  }

  // デバイスの情報を表示する
  info = read_info( fd );

  // 44.1KHz・16bit・PCM・ステレオに設定する
  info.play.sample_rate = 44100;
  info.play.channels = 2;
  info.play.precision = 16;
  info.play.encoding = AUDIO_ENCODING_LINEAR;
  info.play.gain = 10;
	
  if ( ioctl( fd, AUDIO_SETINFO, &info ) < 0 )
    fprintf( stderr, "It failed to set audio info\n" );

  // 閉じる
  close( fd );
  return 0;
}

デバイスの設定を変更するには、AUDIO_SETINFOのioctlを行えばいい。audio_info構造体に適当な値を設定してioctlしてやるだけである。

なお、AUDIO_SETINFOの場合でも引数に指定した構造体の値は一部変更される可能性がある。

音声の再生

デバイスの状態を変更し、とりあえずは音声を再生する環境を整えることができたので、この辺で音を出してみることにする。

音を出すには、とりあえず/dev/audioにwrite()システムコールでデータを書き込めばいいのだが、問題はどんなデータを書き込むのかということである。

上で設定した「PCM・44.1KHz・16bit・ステレオ」なら、下記のようになる。

  • 一つの「サンプル」は符号付き16bitの値
  • 「サンプル」がチャンネル数分集まって「サンプルフレーム」を形成する。左から右の順にデータが格納される。
  • 「サンプルフレーム」が「サンプリング周波数」個集まって、一秒分のデータになる。

以上を踏まえて、左側から400Hz、右側から1000Hzの音を出してみる。

#include <stdio.h>
#include <sys/audio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stropts.h>
#include <math.h>

void play( int fd )
{
  // 符号付き16bit
  // 44.1KHz・ステレオで一秒分のバッファ
  short buf[ 44100 * 2 ];
  int i;

  // バッファに再生するデータを格納する
  for ( i = 0; i < 44100; i++ ) {
    double w = (double)i / 44100.0 * 6.28318;
    // 左側
    buf[i*2+0] = (short)( sin(w*400.0)*30000.0 );
    // 右側
    buf[i*2+1] = (short)( sin(w*1000.0)*30000.0 );
  }

  // 3秒分バッファに書き込む
  for ( i = 0; i < 3; i++ )
    write( fd, buf, sizeof( buf ) );
}

int main()
{
  int fd;
  struct audio_info info;

  // デバイスファイルを開く
  fd = open( "/dev/audio", O_RDWR );
  if ( fd < 0 ) {
    // 失敗
    fprintf( stderr, "Failed to open file\n" );
    return 0;
  }

  // 現在の状態を取得
  if ( ioctl( fd, AUDIO_GETINFO, &info ) < 0 ) {
    // 失敗
    fprintf( stderr, "Failed to get audio info\n" );
    goto ERR_EXIT;
  }

  // 44.1KHz・16bit・ステレオ・PCMに設定する
  info.play.sample_rate = 44100;
  info.play.channels = 2;
  info.play.precision = 16;
  info.play.encoding = AUDIO_ENCODING_LINEAR;
  info.play.gain = 10;
  info.play.port = AUDIO_HEADPHONE;
  if ( ioctl( fd, AUDIO_SETINFO, &info ) < 0 ) {
    fprintf( stderr, "Failed to set audio info\n" );
    goto ERR_EXIT;
  }

  // 音を出す
  play( fd );

ERR_EXIT:
  // 閉じる
  close ( fd );

  return 0;
}

Blade100の内蔵スピーカーはモノラルで、左右の音の違いが確認できないから、一応ヘッドフォンに出力するようにしている。

AUファイル

話は変わるが、一応Solarisでは音声を記録するためのファイルフォーマットが決まっている。

それについてもmanページがあるが、例のごとく英語だから、非合法に和訳してみた

なお詳しくは知らんが、どうやら適当な.auファイルを/dev/audioにリダイレクトしてやれば、それだけで音声を再生することはできるとか、できないとか、聞いたことがあるような気がする。もっとも、俺はやったことはないが。

 


連絡先 - サイトマップ - 更新履歴
Copyright (C)  2000 - 2016 nabiki_t All Rights Reserved.