<?php

/* openal test */


class SoundGenerator {
    var $freq = 44100; // [Hz]
    var $half_tone_diff;
    var $tone_list;
    function __construct() {
	$half_tone_diff = pow(2, 1/12);
	// A が 440 の 2の12乗根の階乗列をつくる (平均室)
	$this->tone_list = array(
	    'C' => 440 / pow($half_tone_diff, 9), // ド
	    'D' => 440 / pow($half_tone_diff, 7), // レ
	    'E' => 440 / pow($half_tone_diff, 5), // ミ
	    'F' => 440 / pow($half_tone_diff, 4), // ファ
	    'G' => 440 / pow($half_tone_diff, 2), // ソ
	    'A' => 440,                           // ラ
	    'B' => 440 * pow($half_tone_diff, 2), // シ
	);
    }
    // OpenAL の音声出力
    function output($data) {
	// openal setting
	$dev = openal_device_open();
	$con = openal_context_create($dev);
	openal_context_current($con);

	$buff = openal_buffer_create();
	openal_buffer_data($buff, AL_FORMAT_MONO8, $data, $this->freq);
	$src = openal_source_create();
	openal_source_set($src, AL_BUFFER, $buff);

	// play sound
	openal_source_play($src);

	$time = strlen($data) / $this->freq;
	sleep($time + 1);

	// destroy
	openal_context_destroy($con);
	openal_device_close($dev);
    }
    // 音程から音声データを生成
    function toneData($tone, $period) {
	if ($tone == 'R') {
	    $tone_hz = 0;
	} else {
	    $tone_hz = $this->tone_list[$tone];
	}
	$volume = 127;
	$data = '';
        for ($i = 0 ; $i < $this->freq * $period ; $i++) {
            $value = sin(2 * M_PI * $tone_hz * $i / $this->freq) * $volume + 127;
//	    if ($value < 127) $value /= 2;
            $data .= chr($value);
	}
	return $data;
    }
    // MML を解釈して音声データを生成
    function playData($text) {
	$data = '';
	$text = str_replace(' ', '', $text);
        preg_match_all('/([C-GABR])([0-9]+)/i', $text, $matches);
	foreach ($matches[1] as $idx => $tone) {
	    $note_div = $matches[2][$idx];
	    $period = 4 * 0.5 / $note_div; // 0.5 = T120
	    $data .= $this->toneData($tone, $period);
	}
	return $data;
    }
    function play($texts) { // score
	if (is_array($texts) === false) {
	    $texts = array($texts);
	}
	$datas = array();
	$data_lens = array();
	foreach($texts as $text) {
       	    $data = $this->playData($text);
       	    $datas []= $data;
	    $data_lens []= strlen($data);
	}
	$data_maxlen = max($data_lens);
	// 音声データの長さを最大値に揃える (BUG now!)
	foreach($texts as $idx => $text) {
	    $texts[$idx] .= str_pad(chr(127), $data_maxlen - $data_lens[$idx]);
	}
	$data = '';
	// 複数の音を合成 (和音用)
        for ($i = 0 ; $i < $data_maxlen ; $i++) {
	    $datum = 0;
	    foreach($texts as $idx => $text) {
		$datum += ord($datas[$idx][$i]);
	    }
	    $data  .= chr($datum / count($texts));
	}
	$this->output($data);
    }
}

$sg = new SoundGenerator();

// テスト
$sg->play("C4 D4 E4 F4 G4 A4 B4");
// 適用な曲
// $sg->play(array("E8E16D16C8D8 E16R16 E16R16 E16R16 R8 D16R16 D16R16 D16R16"));
// 和音
// $sg->play(array("C1C1", "R2E2E1", "R2R2G1"));


echo "OK\n";
exit(0);
