14. 総合演習(1/2)

今回からは、期末の課題の制作期間です。

残り2回+〆切(1月末を予定)の期間で、以下の課題に取り組んで下さい。

テーマを以下の3つの中から選んで提出する。

テーマA

・JavaAppletによるキャラクターアニメーション

eclipse で Javaアプレットを作成する。

デザインパターンの解説で使用した、キャラクターを表現するクラスを拡張する。

要件:

オブジェクト指向のスタイルでコードを作成する。

オブジェクトの種類、振る舞い、アプレット画面に配置するオブジェクト数を 増加 させること。

テーマB

・Scala による音響合成プログラム

↓の資料で、音響合成のコードを紹介するので、これを拡張したプログラムを作成する。

要件:

音色の追加、音程・長さの指定、シーケンサー などの機能をプログラムして、演奏できるようにする。

テーマC

・Scala によるストリーム処理を利用したプログラム

テキスト処理を題材とする。

例)

HTMLファイルを入力とし、不要なデータを削除、または必要なデータを抽出など。

期末課題用のプロジェクトの名前は 学籍番号Final としてください。

提出はプロジェクトフォルダをレポートフォルダにコピーして提出です。

import javax.sound.sampled._
// オーディオ形式を指定
val SAMPLE_RATE = 44100; // 44.1KHz
val audio_format = new AudioFormat(SAMPLE_RATE, 8, 1, true, true)
val line = AudioSystem.getSourceDataLine(audio_format)
line.open()
line.start()
// パターン作成
val a:Stream[Byte] = 100.toByte #:: -100.toByte #:: a
// パターン伸長
def repeat(xs:Stream[Byte], n:Int) = xs.flatMap( e => Array.fill(n)(e) )
// 音源作成
val n1 = repeat(a,100).take(44100).toArray
val n2 = repeat(a,50).take(44100/2).toArray
// 再生コマンド
def play(xs:Array[Byte]) = line.write(xs, 0, xs.length)
// 単音再生
play(n1)
// 連続再生
List(n1, n2, n1, n2, n1).foreach(play)

コードで音源データとなる数列を作成:

音階の設定は、純正律ピタゴラス音律平均律 を参照。

SoundTest.scala の作成例)

import javax.sound.sampled._
object SoundTest extends App {
  val SAMPLE_RATE = 44100; // 44.1KHz
  val audio_format = new AudioFormat(SAMPLE_RATE, 8, 1, true, true)
  val line = AudioSystem.getSourceDataLine(audio_format)
  line.open()
  line.start()
  // パターン作成(ブザー音)
  val a: Stream[Byte] = 100.toByte #:: -100.toByte #:: a
  // 無音(休符パターン)
  val m: Stream[Byte] = 0.toByte #:: m
  
  // サイン波
  val b:Stream[Byte] = Array.tabulate(128)(i => (127*Math.sin(2*Math.PI*i/128)).toByte ).toStream #::: b
  // 一オクターブ上のサイン波(周波数2倍)
  val b2:Stream[Byte] = Array.tabulate(128)(i => (127*Math.sin(2*Math.PI*i/128 *2)).toByte ).toStream #::: b2
  // パターン伸長
  def repeat(xs: Stream[Byte], n: Int) = xs.flatMap(e => Array.fill(n)(e))
  // 再生コマンド
  def play(xs: Array[Byte]) = line.write(xs, 0, xs.length)
  val m1 = m.take(44100).toArray
  val C4 = repeat(a, 100).take(44100 / 4).toArray
  val D4 = repeat(a, 100 * 8 / 9).take(44100 / 4).toArray
  val E4 = repeat(a, 100 * 4 / 5).take(44100 / 4).toArray
  val F4 = repeat(a, 100 * 3 / 4).take(44100 / 4).toArray
  val G4 = repeat(a, 100 * 2 / 3).take(44100 / 4).toArray
  
  val c4 = b.take(44100 / 4).toArray
  val cc4 = b2.take(44100 / 4).toArray
  
  List(c4, cc4, c4, cc4).foreach(play)
  
  List(c4, C4,D4,E4,F4,G4,F4,E4,D4,C4).foreach(play)
  
  line.drain() // 終了まで待機
}

2015年度以前の旧資料

Scala の REPL コマンド http://www.ne.jp/asahi/hishidama/home/tech/scala/scala.html

ファイル読み込みコマンド

:load ファイル名

scala のプログラムファイルを読み込む。

試しに、先回の内容から、Scalaのプログラムをコピーして、3112789Finel.txt ファイルを作成し、読み込んで実行してみる。

読み込むファイルは、scala のコマンドと同じフォルダに置いておくと、パスを指定せずに済むので入力が楽。

または、 H: ドライブ(ユーザホーム)に保存して、 H:\ファイル名 で読み込む。

例)

import javax.sound.sampled._
import scala.util.Random
// オーディオ形式を指定
val SAMPLE_RATE = 44100; // 44.1KHz
val audio_format = new AudioFormat(SAMPLE_RATE, 8, 1, true, true)
val line = AudioSystem.getSourceDataLine(audio_format)
line.open()
line.start()
// 基準となる音の高さ
val frequency = 440
// 1秒間分のデータ
val b1 = new Array[Byte](SAMPLE_RATE)

// バイト列に適当な矩形波を作成

for (i <- 0 to b1.size -1) {
    val r = i / (SAMPLE_RATE / frequency)
    if( r % 2 == 0) { b1(i) = 100 } else { b1(i) = -100 }
}
// 再生
line.write(b1, 0, b1.length);
line.drain() // 終了まで待機

コードで音源データとなる数列を作成:

音階の設定は、純正律ピタゴラス音律平均律 を参照。

例)

// ホワイトノイズ
// ランダムな数値 -100 ~ +100 を並べる
val b2 = List.fill(SAMPLE_RATE)((Random.nextInt(200)-100).toByte).toArray
// 基準より1オクターブ高い音
val b3 = new Array[Byte](SAMPLE_RATE)
for (i <- 0 to b3.size -1) {
    val r = i / (SAMPLE_RATE / frequency/2)
    if( r % 2 == 0) { b3(i) = 100 } else { b3(i) = -100 }
}
// 基準を ド として ソ の高さの音
val b4 = new Array[Byte](SAMPLE_RATE)
for (i <- 0 to b4.size -1) {
    val r = i / (SAMPLE_RATE / frequency/3*2)
    if( r % 2 == 0) { b4(i) = 100 } else { b4(i) = -100 }
}

// 基準を ド として ミ の高さの音

val b5 = new Array[Byte](SAMPLE_RATE)
for (i <- 0 to b5.size -1) {
    val r = i / (SAMPLE_RATE / frequency/5*4)
    if( r % 2 == 0) { b5(i) = 100 } else { b5(i) = -100 }
}
// ドミソ の和音を合成
val b154 = new Array[Byte](SAMPLE_RATE)
for (i <- 0 to b154.size -1) {

b154(i) = (( b1(i) + b5(i) + b4(i) )/3).toByte

}

// 1/4秒再生

line.write(b1, 0, b1.size/ 4)

// 1秒再生

line.write(b2, 0, b2.size)
line.write(b3, 0, b3.size / 4)
line.write(b5, 0, b5.size / 4)
line.write(b4, 0, b4.size / 4)
line.write(b154, 0, b154.size )

// 1秒分の無音データを 全て 0 で用意する。

val b6:Array[Byte] = Array.fill(SAMPLE_RATE)(0)
// 三角波 -128 -127 ... -2 -1 0 1 2 3 ... +126 +127 のループ 
val b7:Array[Byte] = Array.tabulate(SAMPLE_RATE)(i => (i % 256 -128).toByte)
// 三角波 -128 -126 ... -2 0 2 4 ... +124 +126 のループ。1オクターブ上の音 
val b8:Array[Byte] = Array.tabulate(SAMPLE_RATE)(i => (i*2 % 256 -128).toByte)
// 基準音の2倍の高さのサイン波
val b9:Array[Byte] = Array.tabulate(SAMPLE_RATE)(i => (128*Math.sin(2*Math.PI *i/(SAMPLE_RATE/frequency/2) )).toByte)
line.write(b6, 0, b6.size )
line.write(b7, 0, b7.size )
line.write(b8, 0, b8.size )
line.write(b9, 0, b9.size )