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