*ステレオ音声再生実験

はじめに

IEに備わっている音楽演奏・音声再生機能(BGSOUNDオブジェクト)の動作試験です。ページを開くとシラネーヨ(ならびにモナー)のステレオ音声が再生されます。ヘッドホンで聴くと,あたかもシラネーヨが目の前を通りすぎたり,突然近づいてきたりする‥‥ハズなのですが,まだ試行錯誤の段階なもので,そのような錯覚を覚えるほど完成度は高くありません。時折,音声に「プチプチ」っていう雑音が入って耳障りなのですが,今後改善できるかどうか分りません。

この動的コンテンツはInternet Explorerでなければ実行できません。

コンソール
音声選択
操作
バランス
自動
音量
自動

注意事項

  • 音声が出ないときは,音の鳴るアプリケーション(“Windows Media Player”など)が動作していないか確認してください。もし動作しているならそのアプリケーションを終了させて,このウェブページを開き直して(リロードして)ください。
  • ボタン操作のタイミングによって,以後マウス操作ができなくなることがあります。そんなときはバックスペースキーを押してください。
  • Windows2000のIE5.5で動作確認しております。
  • 長時間視聴すると“は○じん”になるかもしれません。お気をつけください。

コンソールの使い方

  • 音声選択: 再生する音声ファイルを選択します。
  • 操作:「連続再生」ボタンを押すと,音声を繰り返し再生します。「停止」ボタンを押すと停止します。
  • バランス: 音声のバランス(左右のスピーカのどちらに音声を寄せるか)を調整します。「左」,「中央」,「右」を押すと任意にバランスが切り替わります。「≪」,「≫」はバランスを微調整するボタンです。「自動」を選択しているときは,あらかじめ決められた周期でバランスが遷移します。
  • 音量:音量をセットします。「最小」は無音。「最大」は通常の音量です(スピーカが張り裂けんばかりの大音量にはなりません)。「自動」を選択しているときは,あらかじめ決められた周期で音量が遷移します。
  • メタ情報BGSOUNDオブジェクトの情報が表示されます。

音声ファイルの説明

  • [MP3] シラネーヨ
    「シラネーヨ」の声です。この音声は,私の声を元に音声処理装置で加工したものです。
  • [MP3] マターリしよーヨ
    上と同じ「シラネーヨ」の声です。「第2回音声伝送実験」で公開した音声ファイルと同一です。
  • [MP3] オマエモナー
    「モナー」の声です。ふしぎ少女風(なぞ)にしてみました。

※音声ファイルの容量はどれも30kバイト程度と小さいのですが,ネットワークの状態によってロードに時間が掛かったり,失敗したりすることがあるかもしれません。


技術資料

使用ツール:Microsoft Visual J++ 6.0

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Visual Studio 98">
<META HTTP-EQUIV="Content-Type" content="text/html">

<bgsound id=sound>

<script><!--
  function SoundDecorator(target) {
    this.target = target;
    this.src;
  }

  SoundDecorator.prototype.PAN_LEFT = -10000;
  SoundDecorator.prototype.PAN_CENTER = 0;
  SoundDecorator.prototype.PAN_RIGHT = +10000;
  SoundDecorator.prototype.VOLUME_MAX = 0;
  SoundDecorator.prototype.VOLUME_MIN = -10000;

  SoundDecorator.prototype.setPan = function (val) {
    switch (val) {
    case 'center':
      this.target.balance = this.PAN_CENTER;
      break;
    case 'right':
      this.target.balance = this.PAN_RIGHT;
      break;
    case 'left':
      this.target.balance = this.PAN_LEFT;
      break;
    default:
      if (typeof val == 'number') {
        this.target.balance = val;
      }
      break;
    }
  }
  SoundDecorator.prototype.getPan = function () {
    return this.target.balance;
  }
  SoundDecorator.prototype.setVolume = function (val) {
    this.target.volume = val;
  }
  SoundDecorator.prototype.getVolume = function () {
    return this.target.volume;
  }
  SoundDecorator.prototype.setSrc = function (src) {
    this.src = src;
  }
  SoundDecorator.prototype.getSrc = function () {
    return this.src;
  }
  SoundDecorator.prototype.play = function () {
    if (arguments.length == 1) {
      this.setSrc(arguments[0]);
    }
    this.target.loop = 1;
    this.target.src = this.src;
  }
  SoundDecorator.prototype.repeat = function () {
    if (arguments.length == 1) {
      this.setSrc(arguments[0]);
    }
    this.target.loop = -1;
    this.target.src = this.src;
  }
  SoundDecorator.prototype.stop = function () {
    this.target.src = null;
  }
  SoundDecorator.prototype.toString = function () {
    return "properties={"
    + "src=\"" + this.target.src + '"'
    + ","
    + "loop=" + this.target.loop
    + ","
    + "volume=" + this.target.volume
    + ","
    + "balance=" + this.target.balance
    + "}";
  }

  ////////

  var sd = new SoundDecorator(sound);
  var demo1ID = null, demo2ID = null;

  function setDemo1(op) {
    if (demo1ID != null) {
      clearInterval(demo1ID);
      demo1ID = null;
      sd.setPan(sd.PAN_CENTER);
      panPanel.disabled = false;
    }
    if (op) {
      var demo = new function (sdecorator) {
        this.sound = sdecorator;
        this.array = new Array();
        this.index = 0;

        var i = 0;
        var step = 500;

        for (var pan = -5000; pan <= 5000; pan += step) {
          this.array[i ++] = pan;
        }
        for (var pan = 5000; pan >= -5000; pan -= step) {
          this.array[i ++] = pan;
        }

        this.exec = function () {
          this.sound.setPan(this.array[this.index]);
          this.index = (this.index + 1) % this.array.length;
        }
      } (sd); // 無名オブジェクトのコンストラクタの引数

      var functor = function () {
        demo.exec();
      }
      demo1ID = setInterval(functor, 400);
      panPanel.disabled = true;
    }
  }

  function setDemo2(op) {
    if (demo2ID != null) {
      clearInterval(demo2ID);
      demo2ID = null;
      volumePanel.disabled = false;
    }
    if (op) {
      var demo = new function (sdecorator) {
        this.sound = sdecorator;
        this.array = new Array();
        this.index = 0;

        var i = 0;
        var step = 500;

        for (var pan = -5000; pan <= 0; pan += step) {
          this.array[i ++] = pan;
        }
        for (var pan = 0; pan >= -5000; pan -= step) {
          this.array[i ++] = pan;
        }

        this.exec = function () {
          this.sound.setVolume(this.array[this.index]);
          this.index = (this.index + 1) % this.array.length;
        }
      } (sd);

      var functor = function () {
        demo.exec();
      }
      demo2ID = setInterval(functor, 400);
      volumePanel.disabled = true;
    }
  }

  function selectSource(filename) {
    sd.setSrc(filename);
    sd.repeat();
  }

  ////////

  function update() {

    function updatePanIndicator() {
      var currentPan = sd.getPan();
      var leftValue, rightValue;

      if (currentPan == sd.PAN_CENTER) {
        leftValue = 0;
        rightValue = 0;
      } else {
        if (currentPan < sd.PAN_CENTER) {
          leftValue = 0;
          rightValue = -currentPan / 100;
        } else {
          leftValue = currentPan / 100;
          rightValue = 0;
        }
      }

      console.panIndicator.value = ""
        +"L = -" +leftValue + "db"
        +","
        +"R = -" +rightValue + "db";

      var isLeft = (currentPan <= sd.PAN_LEFT);
      var isRight = (currentPan >= sd.PAN_RIGHT);
      var isCenter = (currentPan == sd.PAN_CENTER);

      console.leftPanButton.disabled = isLeft;
      console.leftShiftPanButton.disabled = isLeft;
      console.centerPanButton.disabled = isCenter;
      console.rightShiftPanButton.disabled = isRight;
      console.rightPanButton.disabled = isRight;
    }

    function updateVolumeIndicator() {
      var currentVolume = sd.getVolume();
      console.volumeIndicator.value = currentVolume / 100 + "db";
    
      var isMaximum = (currentVolume >= sd.VOLUME_MAX);
      var isMinimum = (currentVolume <= sd.VOLUME_MIN);

      console.decVolumeButton.disabled = isMinimum;
      console.incVolumeButton.disabled = isMaximum;
      console.minimizeVolumeButton.disabled = isMinimum;
      console.maximizeVolumeButton.disabled = isMaximum;
    }

    updatePanIndicator();
    updateVolumeIndicator();
    console.detailField.value = sd;
  }

  function init() {
    console.demo1switch.checked = true;
    console.demo2switch.checked = true;
    setDemo1(console.demo1switch.checked);
    setDemo2(console.demo2switch.checked);

    selectSource(console.fileSelector.value);
    ////
    setInterval(function () {update();}, 250);
  }

  window.onload = init;
//--></script>

</HEAD>
<BODY>

<form id=console style="border:solid;border-color:#c60;background-color:#fc9;padding-left:20px">
  <div style="color:white;margin-left:-20px;background-color:#f90;font-weight:bold">コンソール</div>
  <div style="margin-top:10px">
    音声選択
    <select id=fileSelector onchange="selectSource(this.value)">
      <option value="au-shiraneyo-9788364805.mp3" selected>( ´ー`) シラネーヨ 
      <option value="au-mattari-9779724812.mp3">( ´ー`) マターリしよーヨ
      <option value="au-omaemona-9788364795.mp3">( ´∀`) オマエモナー
    </select>
  </div>

  <div style="margin-top:10px">
    操作
    <input type=button value="連続再生" id=playButton onclick="sd.repeat()">
    <input type=button value="停止" id=stopButton onclick="sd.stop()">
  </div>

  <div style="margin-top:10px">
    バランス<br>
    <input type=checkbox id=demo1switch onclick="setDemo1(this.checked)">自動<br>
    <div id=panPanel style="margin-left:5px">
      <input type=button value="左" id=leftPanButton onclick="sd.setPan(sd.PAN_LEFT)">
      <input type=button value="中央" id=centerPanButton onclick="sd.setPan(sd.PAN_CENTER)">
      <input type=button value="右" id=rightPanButton onclick="sd.setPan(sd.PAN_RIGHT)">
      <input type=button value="≪" id=leftShiftPanButton onclick="sd.setPan(sd.getPan() - 250)">
      <input type=button value="≫" id=rightShiftPanButton onclick="sd.setPan(sd.getPan() + 250)">
      <input type=text id=panIndicator disabled size=25>
    </div>
  </div>

  <div style="margin-top:10px">
    音量<br>
    <input type=checkbox id=demo2switch onclick="setDemo2(this.checked)">自動<br>
    <div id=volumePanel style="margin-left:5px">
      <input type=button value="最小" id=minimizeVolumeButton onclick="sd.setVolume(sd.VOLUME_MIN)">
      <input type=button value="最大" id=maximizeVolumeButton onclick="sd.setVolume(sd.VOLUME_MAX)">
      <input type=button value=" - " id=decVolumeButton onclick="sd.setVolume(sd.getVolume() - 250)">
      <input type=button value=" + " id=incVolumeButton onclick="sd.setVolume(sd.getVolume() + 250)">
      <input type=text id=volumeIndicator disabled>
    </div>
  </div>

  <div style="margin-top:10px">
    メタ情報<br>
    <textarea cols=60 rows=4 id=detailField wrap=virtual disabled></textarea>
  </div>
</form>

</BODY>
</HTML>
yumi-ii/shiraneyo AAアプリ研究室, Copyright (c) MURONO 2001-2002, 2004-2005, murono@mtc.biglobe.ne.jp