14. 総合演習(1/2)
総合演習課題 2018年度版
締切り 2018年7月31日(火)
内容
今回と次回(最終回)は、期末の課題の制作期間です。
残り2回+〆切の期間で、以下の課題に取り組んで下さい。
テーマを以下のA~Dの中から1つ選んで提出する。
テーマA
・SNAP! によるCG
要件:
オブジェクト指向のスタイルでコードを作成する。
ブロック定義機能を利用していること。
CGに再帰構造を用いる。
提出方法:
1.Fileアイコンから、「プロジェクトを書き出す」を選ぶ。
2.ブラウザのダウンロード領域にXMLファイルとして保存される。
ファイル名: 名称未設定.xml
保存されたファイルをWebClassの総合演習にアップロードする。
テーマB
・Java による演奏アプリ
要件:
プロジェクト名前 Final
オブジェクト指向のスタイルでコードを作成する。
複数のパートと複数の小節を演奏する。
曲の構造を反映したコードを作成する。 例)小節の繰り返し、カノン を演奏データをパターン化して利用する。
提出方法:
プロジェクト Final を zip にまとめ、WebClassの総合演習にアップロードする。
テーマC
・Java によるGUIデモ
要件:
プロジェクト名前 Final
オブジェクト指向のスタイルでコードを作成する。
デザインパターンを意識したコードであること。
提出方法:
プロジェクト Final を zip にまとめ、WebClassの総合演習にアップロードする。
テーマD
・Java のStream APIのデモ
要件:
プロジェクト名前 Final
関数型プログラミングのスタイルでコードを作成する。
例)
文字列の変換処理をStream スタイルで作成する。
あきすとぜねこ 占いを作成してみる。
カナからローマ字への変換は、以下のリンク先を参考にしてヘボン式で変換することとする。
http://park.itc.u-tokyo.ac.jp/eigo/UT-Komaba-Nihongo-no-romaji-hyoki-v1.pdf
平仮名からローマ字に変換するテキスト処理は、こちらを参照するとよい。
http://syunpon-java.com/programing/java/sample/hiragana2roma.shtml
提出方法:
プロジェクト Final を zip にまとめ、WebClassの総合演習にアップロードする。
評価方法:
各A~Dのテーマごとに指定された要件についてプログラム言語の機能の活用状況を考慮して採点する。
30点満点
補足: 関数スタイルのプログラム例 Scalaでアナグラム検索
※参照 https://qiita.com/kumazo/items/0876c5b251ecc131c960 Java 8 Stream API にテキストを流してみる
テーマAのヒント
・総合課題(2/2)にパターン生成(模様作成)のCGの例を掲載しているので参考にしてよい。
・ファイル→ライブラリインポートから、拡張機能を読み込んで利用してみてもOK。
※以下の画像の 赤点 で示したライブラリが楽しそう。
・SNAP!に12音階の音源をADSR付で追加する。参照 サンプルコードのDL
テーマBのヒント
・SoundSystem クラスの音源を切り替え可能に修正する。(サイン波・ブザー音など)
・ホワイトノイズの音源を追加する。
class SoundSystem に音源バッファを追加する。
ホワイトノイズ用バッファ:
static byte[][] rand16 = new byte[8][44100*2];
バッファの初期化用コード(16bit音源):
Random rnd = new Random();
for (int j = 0; j < rand16.length; j++) {
for (int i = 0; i < rand16[0].length; i += 2) {
if(i>rand16[0].length/8) continue;
rand16[j][i + 0] = (byte) (rnd.nextInt(255));
rand16[j][i + 1] = (byte) (rnd.nextInt(255));
}
}
class SoundSystem のインナークラス SSPlayer を使用する音源フィールドを追加。
byte[][] buf =SoundSystem.sin16;
interface Player に音源切り替え用メソッドを定義:
void setBuffer(byte[][] buf);
SSPlayer インナークラスに音源替え用メソッドを実装:
public void setBuffer(byte[][] buf) {
this.buf = buf;
}
SSPlayer インナークラスの演奏用メソッド play() で再生するバッファをPlayerにセットされた音源に変更:
public void play(int[][] score) {
for (int i = 0; i < score.length; i++) {
line.write(buf[score[i][0]], 0, SoundSystem.buf[0].length / score[i][1]);
}
line.drain();
}
main メソッドで Playerオブジェクトの使用する音源バッファを切り替える。
p2.setBuffer(SoundSystem.rand16);
総合演習課題 2017年度版
締切り 2018年1月19日(金) 日程表
内容
今回と次回(最終回)は、期末の課題の制作期間です。
残り2回+〆切の期間で、以下の課題に取り組んで下さい。
テーマを以下のA~Dの中から1つ選んで提出する。
テーマA
・SNAP! によるCG
要件:
オブジェクト指向のスタイルでコードを作成する。
CGに再帰構造を用いる。
提出方法:
1.Fileアイコンから、「プロジェクトを書き出す」を選ぶ。
2.ブラウザのダウンロード領域にXMLファイルとして保存される。
ファイル名: 名称未設定.xml
保存されたファイルをWebClassの総合演習にアップロードする。
テーマB
・Java による演奏アプリ
要件:
プロジェクト名前 Final
オブジェクト指向のスタイルでコードを作成する。
複数のパートと複数の小節を演奏する。
曲の構造を反映したコードを作成する。 例)小節の繰り返し、カノン を演奏データをパターン化して利用する。
提出方法:
プロジェクト Final を zip にまとめ、WebClassの総合演習にアップロードする。
テーマC
・Java によるGUIデモ
要件:
プロジェクト名前 Final
オブジェクト指向のスタイルでコードを作成する。
デザインパターンを意識したコードであること。
提出方法:
プロジェクト Final を zip にまとめ、WebClassの総合演習にアップロードする。
テーマD
・Java のStream APIのデモ
要件:
プロジェクト名前 Final
関数型プログラミングのスタイルでコードを作成する。
提出方法:
プロジェクト Final を zip にまとめ、WebClassの総合演習にアップロードする。
テーマBのヒント
複数の小節 bar を集めて 楽譜 score を構成するようにコードに修正を加える。
例) bar1 bar2 bar3 の3小節を bar1 bar2 bar3 bar1 の順に合計4個並べた score4 を構成する。
int[][] bar1 = { { 0, 2 }, { 1, 2 }, { 2, 2 }, { 1, 2 }, { 2, 2 }, { 3, 2 }, { 0, 4 }, { 1, 4 }, { 2, 4 },
{ 3, 4 }, { 4, 4 }, { 5, 4 }, { 6, 4 }, { 7, 4 } };
int[][] bar2 = { { 8, 2 }, { 8, 2 }, { 8, 2 }, { 8, 2 }, { 0, 2 }, { 1, 2 }, { 2, 2 }, { 1, 2 }, { 2, 2 },
{ 3, 2 }, { 0, 4 }, { 1, 4 }, { 2, 4 }, { 3, 4 }, { 4, 4 }, { 5, 4 }, { 6, 4 }, { 7, 4 } };
int[][] bar3 = { { 8, 2 }, { 8, 2 }, { 8, 2 }, { 8, 2 }, { 8, 2 }, { 8, 2 }, { 8, 2 }, { 8, 2 }, { 0, 2 },
{ 1, 2 }, { 2, 2 }, { 1, 2 }, { 2, 2 }, { 3, 2 }, { 0, 4 }, { 1, 4 }, { 2, 4 }, { 3, 4 }, { 4, 4 },
{ 5, 4 }, { 6, 4 }, { 7, 4 } };
List<int[][]> score4 = new ArrayList<>(Arrays.asList(bar1, bar2, bar3, bar1));
クラス Player の修正例)
フィールド部分 score の型を変更
class Player implements Runnable {
byte[][] sample;
List<int[][]> score;
メソッド部分 run() のサウンド出力ループを修正
score のループ と bar のループの 2重ループに変更。
for (int[][] bar : score) {
for (int[] is : bar) {
line.write(sample[is[0]], 0, sample[0].length / is[1]);
}
}
上記のコードで使用したライブラリに不足があれば、importして対応する。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
応用例
音源をブザー音から、正弦波に変更する。
byte[][] buf = new byte[9][44100];
int[][] rate = { { 1, 2 }, { 9, 16 }, { 5, 8 }, { 2, 3 }, { 3, 4 }, { 5, 6 }, { 15, 16 }, { 1, 1 } , { 0, 1 } };
int i = 0;
for (byte[] bs : buf) {
int[] rs = rate[i++];
int f = 880*rs[0]/rs[1];
for (int j = 0; j < bs.length; j++) {
bs[j] = (byte) (127*Math.sin(2*Math.PI*f*j/bs.length));
}
}
伴奏用の音源を用意する。
音程用の比率 rate2 は、最低音 が シ で始まる、1オクターブ低い音源。 分母を *2 倍してある。1オクターブ上げるには、分子を *2 倍する。
byte[][] buf2 = new byte[9][44100];
int[][] rate2 = {{ 15, 16*2*2 }, { 1, 2*2 }, { 9, 16*2 }, { 5, 8*2 }, { 2, 3*2 }, { 3, 4*2 }, { 5, 6*2 }, { 15, 16*2 } , { 0, 1*2 } };
i = 0;
for (byte[] bs : buf2) {
int[] rs = rate2[i++];
int f = 880*rs[0]/rs[1];
for (int j = 0; j < bs.length; j++) {
bs[j] = (byte) (127*Math.sin(2*Math.PI*f*j/bs.length));
}
}
童謡 チョウチョウ の楽譜
/*
* 『チョウチョウ』
*
* ソミミー /ファレレー /ドレミファ /ソソソー
* ソミミミ /ファレレレ /ドミソソ /ミミミー
* レレレレ /レミファー /ミミミミ /ミファソー
* ソミミミ /ファレレー /ドミソソ /ミミミー
*
*/
// 音程 ド 0 レ 1 ミ 2 ファ 3 ソ 4 ラ 5 シ 6 ド 7 休符 0
// 音長 1秒 1 1/2秒 2
int[][] bar1 = { { 4, 2 }, { 2, 2 }, { 2, 1 }}; //ソミミー
int[][] bar2 = { { 3, 2 }, { 1, 2 }, { 1, 1 }}; //ファレレー
int[][] bar3 = { { 0, 2 }, { 1, 2 }, { 2, 2 },{ 3, 2 }}; //ドレミファ
int[][] bar4 = { { 4, 2 }, { 4, 2 }, { 4, 1 }}; //ソソソー
int[][] bar1a = { { 4, 2 }, { 2, 2 }, { 2, 2 }, { 2, 2 }}; //ソミミミ
int[][] bar2a = { { 3, 2 }, { 1, 2 }, { 1, 2 }, { 1, 2 }}; //ファレレレ
int[][] bar5 = { { 0, 2 }, { 2, 2 }, { 4, 2 }, { 4, 2 }}; //ドミソソ
int[][] bar6 = { { 2, 2 }, { 2, 2 }, { 2, 1 }}; //ミミミー
int[][] bar7 = { { 1, 2 }, { 1, 2 }, { 1, 2 }, { 1, 2 }}; //レレレレ
int[][] bar8 = { { 1, 2 }, { 2, 2 }, { 3, 1 }}; //レミファー
int[][] bar6a = { { 2, 2 }, { 2, 2 }, { 2, 2 }, { 2, 2 }}; //ミミミミ
int[][] bar9 = { { 2, 2 }, { 3, 2 }, { 4, 1 }}; //ミファソー
List<int[][]> score1 = new ArrayList<>(Arrays.asList(
bar1, bar2, bar3, bar4,
bar1a, bar2a, bar5, bar6,
bar7, bar8, bar6a, bar9,
bar1a, bar2, bar5, bar6));
//伴奏 シ 0 ド 1 レ 2 ミ 3 ファ 4 ソ 5 ラ 6 シ 7 休符 0
int[][] bbar1 = { { 1, 1 }, { 5, 1 }};
int[][] bbar2 = { { 0, 1 }, { 5, 1 }};
int[][] bbar3 = { { 5, 2 }, { 5, 2 }, { 5, 2 }, { 5, 2 }};
List<int[][]> score2 = new ArrayList<>(Arrays.asList(
bbar1, bbar2, bbar1, bbar1,
bbar1, bbar2, bbar1, bbar1,
bbar3, bbar3, bbar3, bbar3,
bbar1, bbar2, bbar1, bbar1
));
上記のコードを一通り組み込んだサンプルを SoundTest4.java としてこのページに添付しておくので、参考にしてよい。
以下、2016年度の内容
今回からは、期末の課題の制作期間です。
残り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 )