xx0. 期末課題

以下は、2015年度の資料

部分的には、2016年度の期末課題のヒントになるかも。

課題提出締め切り:

1月xx日(x) まで。

レポートフォルダへ提出:

eclipse の projectのフォルダ や scala の projectのフォルダなど。

フォルダ名、に学籍番号を付けておくこと。

(参考2015)

import javax.sound.sampled._

/**

* @author kobashi.kazuhide

*/

object Test2 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 BaseFrequency = 440

// ド レ ミ ファ ソ ラ シ ド

val Fseq = List(1.0, 9.0/8, 5.0/4, 4.0/3, 3.0/2, 5.0/3, 15.0/8, 2.0/1)

// 音階再生用のデータ(1オクターブ分)音階 x 1秒分のデータの長さ

// 一つ一つの音の長さを変更すると、再生テンポ全体を変更できる

// 例 SAMPLE_RATE を SAMPLE_RATE*2 にする

val notes = Array.ofDim[Byte](Fseq.size,SAMPLE_RATE)

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

for ( (note, n) <- notes.zipWithIndex; i <- 0 until note.size) {

val r = i / (SAMPLE_RATE / BaseFrequency / Fseq(n) ).toInt

if (r % 2 == 0) { note(i) = 100 } else { note(i) = -100 }

}

// 再生

notes.foreach( note => line.write(note, 0, note.size) )

line.write(notes(0),0,notes(0).size)

line.write(notes(0),0,notes(0).size)

line.write(notes(0),0,notes(0).size)

line.drain() // 終了まで待機

}

(参考2015) 音を合成して再生する

chord2 は 音源データ2つを引数に取り、合成結果を音源として返す関数

chord3 は 音源データ3つを引数に取り、合成結果を音源として返す関数

※ オブジェクト指向的には、sound クラスを定義し、音響合成用のメソッドを定義するほうがスマートなプログラムです。

※ シンプルなコードだけで合成を実現するため、音源の数ごとに別々の関数として合成方法を定義しています。

※※ このスタイルでは、合成する音の数ごとに新たに関数定義が必要になるので、 引数として、合成する音源のListを渡して合成する関数を定義するべきでしょう。

// 2音の合成

def chord2(note1:Array[Byte],note2:Array[Byte]):Array[Byte] ={

note1.zip(note2).map {case (b1,b2) => ((b1+b2)/2).toByte}

}

line.write(chord2(notes(0),notes(2)) ,0, notes(0).size)

// 3音の合成

def chord3(note1:Array[Byte], note2:Array[Byte], note3:Array[Byte]):Array[Byte] ={

val tri_chord=note1.zip(note2).zip(note3)

tri_chord.map {case ((b1,b2),b3) => ((b1+b2+b3)/3).toByte}

}

line.write(chord3(notes(0),notes(2),notes(4)) ,0, notes(0).size)

(参考2015) 音を絞る(ADSR) エンベーロープのエフェクト と 利用

def effect2(note:Array[Byte]) = {

note.zipWithIndex.map {case (b,i) => (b*(note.size-i)/note.size).toByte}.toArray

}

line.write(effect2(notes(0)),0,notes(0).size)

(参考2015) より高い音の数値について

// ド レ ミ ファ ソ ラ シ ド 1オクターブ上の音は、元の数値の2倍の値で作成

val Fseq = List(1.0, 9.0/8, 5.0/4, 4.0/3, 3.0/2, 5.0/3, 15.0/8, 2*1.0 , 2*9.0/8, 2*5.0/4 )

(参考2015) for 文による繰り返し演奏の例

// エンベロープ 無しの音と 有りの音 を交互に3回再生

line.write(notes(0),0,notes(0).size) // エンベロープ無し(音は絞られない)

line.write(effect2(notes(0)),0,notes(0).size) // エンベロープ有り(音は絞られる)

line.write(notes(0),0,notes(0).size)

line.write(effect2(notes(0)),0,notes(0).size)

line.write(notes(0),0,notes(0).size)

line.write(effect2(notes(0)),0,notes(0).size)

// 2分音符でドレミ (1小節=1秒)

line.write(effect2(notes(0)),0,notes(0).size/2)

line.write(effect2(notes(1)),0,notes(0).size/2)

line.write(effect2(notes(2)),0,notes(0).size/2)

// 4分音符でドレミ (1小節=1秒)

line.write(effect2(notes(0)),0,notes(0).size/4)

line.write(effect2(notes(1)),0,notes(0).size/4)

line.write(effect2(notes(2)),0,notes(0).size/4)

// 16分音符で連続20回ドレミを再生

for(i <- 0 to 20) {

line.write(effect2(notes(0)),0,notes(0).size/16)

line.write(effect2(notes(1)),0,notes(0).size/16)

line.write(effect2(notes(2)),0,notes(0).size/16)

}

// 4分音符から16分音符へ、だんだんと早く演奏

for(i <- 4 to 16) {

line.write(effect2(notes(0)),0,notes(0).size/i)

line.write(effect2(notes(1)),0,notes(0).size/i)

line.write(effect2(notes(2)),0,notes(0).size/i)

}

(参考2015)音の列を1フレーズ登録して、2回再生

def MelodyA = {

line.write(effect2(notes(7)),0,notes(0).size/4)

line.write(effect2(notes(7)),0,notes(0).size/4)

line.write(effect2(notes(7)),0,notes(0).size/2)

line.write(effect2(notes(6)),0,notes(0).size/4)

line.write(effect2(notes(6)),0,notes(0).size/4)

line.write(effect2(notes(6)),0,notes(0).size/2)

line.write(effect2(notes(5)),0,notes(0).size/4)

line.write(effect2(notes(5)),0,notes(0).size/4)

line.write(effect2(notes(5)),0,notes(0).size/4)

line.write(effect2(notes(4)),0,notes(0).size/1)

}

MelodyA

MelodyA

(参考2015)音符を再生する関数を定義し、直接利用の例と、楽譜を登録して再生する例。

def play(note:(Int,Int)) = {

line.write(effect2(notes(note._1)),0,notes(note._1).size/note._2)

}

play(0,8)

play(1,8)

play(2,8)

play(3,8)

play(2,8)

play(1,8)

play(0,8)

val Music = List((4,4),(4,4),(4,4),(1,2), (3,4),(3,4),(3,4),(0,2))

Music.foreach(note => play(note))

(参考2014)

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()

// 三角波を高さと長さを変えて繰り返し再生。だんだん 高く 短く

for(k <- 1 to 5; j <- 1 to 10) line.write(Array.tabulate(SAMPLE_RATE)(i => (i*j).toByte) , 0 , SAMPLE_RATE/10/k)

// ブザー音

line.write(Array.tabulate(SAMPLE_RATE)(i => (i*1).toByte) , 0 , SAMPLE_RATE)

val C1 = 1.0;

val G1 = 3.0/2.0;

val C2 = 2.0;

val SEQ = List( (C1,2) , (G1, 1) , (C2, 1) );

def play(p:Double, len:Int) = line.write(Array.tabulate(SAMPLE_RATE/len)(i => (i*p).toByte) , 0 , SAMPLE_RATE/len);

play(C1,1)

play(G1,2)

for( note <- SEQ ) { play(note._1, note._2) }

楽譜を逆向きに演奏)

List の並べ替え メソッド reverse を利用する。

for( note <- SEQ.reverse ) { play(note._1, note._2) }

演奏のピッチ、テンポ の変更

元の2倍の高さで、3倍のスピードで演奏の例)

for( note <- SEQ ) { play(note._1 *2, note._2 *3) }

ランダム演奏

乱数オブジェクトの準備

import scala.util.Random

val r = new Random

乱数オブジェクトから、ランダムな Double 値 を生成した例

scala> r.nextDouble()

res4: Double = 0.8725661424461074

演奏例)

for( i <- 1 to 20) play(r.nextDouble()*8, 8)

その他)

上がって下がる。

def updown = {

for(k <- 1 to 15 by 1) line.write(Array.tabulate(SAMPLE_RATE)(i => (i*k).toByte) , 0 , SAMPLE_RATE/30);

for(k <- 15 to 1 by -1) line.write(Array.tabulate(SAMPLE_RATE)(i => (i*k).toByte) , 0 , SAMPLE_RATE/30);

}

updown