07. Javaでオブジェクト指向(2/x)

2018年度資料

eclipseでコードを作成するときに便利なショートカットキー

入力候補テンプレート表示 Ctrl + Space

単語補完 Alt + /

クイックフィックス Ctrl + 1 (問題点の解決方法を提供)

コメントON・OFF Ctrl + /

インデント修正 Ctrl + I

1行削除 Ctrl + D

ソースコードのフォーマット Ctrl + Shift + F

補完入力中にキャメルケースの大文字部分の入力で絞込み 例) SytemHogeFuga は SHF で候補絞り込み

new SomeClass();と入力後、「Shift + Alt + L」で左辺側を補完

syso + Ctrl + Space で System.out.println()

Demo

SNAP! でサウンド処理。

JavaScript ブロックに WebAudioAPIによる音声合成処理を JavaScriptでプログラムした。

基準音 440Hz から順に半音ずつ上げて12音階で再生している。

JavaScriptブロックの 周波数 freq は、12平均律 で計算している。

このページに、以下のスクリプトのファイル(WebAudioAPITest.xml)を添付した。実行して動作を確かめるとよい。

※ ↑のJavaScriptは、こちらのサイトを参考にした。

コメント(2018.12.12):

・12平均律の音階の周波数の計算式は、(2^(1/12))^b の代わりに 2^(b/12) を使うと e^とln 1つずつ減らせられる

・エンベロープの減衰が無しで音を出したままで周波数を変更するとクリックノイズが発生する。周波数変更後に位相が0にリセットされるからか?要対応。

・ゲインにエンベロープをかけるのと同様に、周波数のAudioParamをsetValueAtTimeで変更したらクリックノイズが出ないのかも。要検証。

Javaの配列とサウンド処理

サウンドデータの生成と再生の例)

eclipse で、 プロジェクト java1 を新規作成する。

新規 java クラス SoundTest0 を作成する。

以下のコードを丸ごと、mainメソッドのブロック内にコピペで配置する。

多数のエラーが出るが、 マウスで 行番号の ×アイコン や エラーの波線部分 をクリックして 自動修正 で対応する。

※修正方法は授業中に指示する

※例外処理の追加 try catch ブロック については、例外処理に必要コードを try ブロック内に移動する

buf = new byte[44100]; // 音源データ用のメモリ(配列)の確保

// 音源データ初期化

for (int i = 0; i < buf.length; i++) {

buf[i] = i % 256 -128;

}

// 音源フォーマットの指定。オーディオシステムの初期化と起動

SourceDataLine line;

line = AudioSystem.getSourceDataLine(new AudioFormat(44100, 8, 1, true, true));

line.open();

line.start();

// 音源データをオーディオシステムのラインに書き込む(再生する)

line.write(buf, 0, buf.length);

line.drain();

※ 再生する音源データのサイズが小さい場合、オーディオシステムがある程度まとまった音源データが届くまで再生を開始しない。

※これを回避するため、line.drain(); でlineに送ったデータを即時再生させる。

エラーの修正がすんだら、

Ctrl + Shift + F

を押してコードを整形しておく。

このコードを実行して動作を確認する。

ブザー音が再生される筈。

つづけて配列処理の練習課題として、以下の応用に取り組む。

応用:

サウンドデータ生成部分の修正

例)

・音声データバッファ buf 配列のサイズを変更 44100 → 22050 再生時間は半分になる。

・音声データバッファ buf 配列のサイズを変更 44100 → 44100*2 再生時間は倍になる。

・データ出力サイズの変更 line.write(buf, 0, buf.length/2) に変更する。再生時間は半分になる。

・データ出力サイズの変更 line.write(buf, 0, buf.length*2) に変更する。プログラムを実行すると ArrayIndexOutOfBoundsException 実行時エラーが発生する。理由は?

・データ生成式の変更

データ生成式の変更例)

i % 256 -128

この式は、ノコギリ波になる。

% は剰余計算記号。

モノラルの8bitサウンドフォーマットにするため、データは +127~-128 のバイト値に収まるように int型変数 i の値を式で調整している

確認1)for ループの i が 0 1 2 と変化していくのに対して、この式の値の変化を 手で計算して 波形を確認する。

この計算式を以下のように修正して、再生音を確認する。

i/2 % 256 -128

にすると、1オクターブ下の音になる。

確認2)for ループの i が 0 1 2 と変化していくのに対して、この式の値の変化を 手で計算して 波形を元の波形と比較する。

i*2 % 256 -128

にすると、1オクターブ上の音になる。

確認3)for ループの i が 0 1 2 と変化していくのに対して、この式の値の変化を 手で計算して 波形を元の波形と比較する。

i*3/2 % 256 -128

にすると、ソの音(+5度。ド→レ→ミ→ファ→ソ)になる。

i*2/3 % 256 -128

にすると、低いファの音(ー5度。ド→シ→ラ→ソ→ファ)になる。

数学 と 音楽 の実験:

/2*2 の範囲 (1/2倍 ~ 2倍の範囲)で、5/4(+3度) と 3/2 と(+5度)2/3 (ー5度)の比率を組み合わせて、音階を構成することができる。

低いド *1/2 と 高いド *1 が2倍の関係になっているように、各音程は 低いレ *9/16 の2倍が高いレ *9/8 の関係になる。

*1 ()基準

*9/8 = *3/2 * 3/2 *1/2 (↑ドレミファソ ↑ソラシドレ ↓レドシラソファミ)

*5/4 (↑ドレ)

*4/3 = *2/3 * 2 (↓ドシラソファ ↑ファソラシドレミファ)

*3/2 (↑ドレミファ)

*5/3 = *2/3 * 5/4 *2 (↓ドシラソファ ↑ファソラ ↑ラシドレミファソ)

*15/8 = *3/2 * 5/4 (↑ドレミファソ ↑ソラ)

*2 (↑ドレミファソラシ )

このように基準音を単純な比率で調整してド ミ ファ ソ などの音程を作成することができる。

純正律 (ドレミファソラシド の音程用の比率) について調べて利用してみよう。

参照)純正率と平均律の違いを体感しよう

課題1

音楽演奏プログラムの作成1:

SoundTest1クラスを新規作成。

前述のSoundTest0 クラスから、サウンド再生に必要な部分をコピペしてSoundTest1を作成する。

※ mainメソッド内の該当コードを貼り付けると 必要に応じて import 文が追加される。

・ドレミファソラシド の8音のサウンドデータを初期化し、順番に再生する。

8個の音階用サウンドバッファを2次元配列で用意する。

byte[][] buf = new byte[8][44100];

サウンドバッファの初期化部分の変更。ドレミファソラシド 初期化でする。

for (int i = 0; i < buf[0].length; i++) {

buf[0][i] = (byte) (i*1/2 % 256 - 128);

buf[1][i] = (byte) (i*9/16 % 256 - 128);

//残り6行は、自分で考えてみよう。

}

演奏する順番に line.write へ配列を書き込むことで音階を再生する。さらに再生時間を徐々に短くする。

line.write(buf[0], 0, buf[0].length);

line.write(buf[1], 0, buf[1].length/2);

line.write(buf[2], 0, buf[2].length/3);

line.write(buf[3], 0, buf[3].length/4);

line.write(buf[4], 0, buf[4].length/5);

line.write(buf[5], 0, buf[5].length/6);

line.write(buf[6], 0, buf[6].length/7);

line.write(buf[7], 0, buf[7].length/8);


line.drain();

※再生データが多くなり、lineにデータを書き込んでも、一度に再生されなくなる場合がある。

※対策として、 line.drain() を使用し、バッファに残っている全てのデータを強制出力する。

音楽演奏プログラムの作成2:

SoundTest1クラスを パッケージエクスプローラでコピペして SoundTest2クラス を作成する。

※ファイル名やクラス名は自動的に修正される。

修正内容:

・2重ループと調律用の比率の配列 rate を用いて、8音階を初期化する。

※初期化部分に 追加

int[][] rate = { { 1, 2 }, { 9, 16 }, { 5, 8 }, { 2, 3 }, { 3, 4 }, { 5, 6 }, { 15, 16 }, { 1, 1 } };

for (int j = 0; j < buf.length; j++) {

for (int i = 0; i < buf[0].length; i++) {

buf[j][i] = (byte) (i * rate[j][0] / rate[j][1] % 256 - 128);

}

}

・演奏用データを配列 score で用意する。scoreは2次元配列で、音符(2つの数値をペアにした配列)を要素に持つ。

音符の数値ペアは、先頭が 音程 2番目が 長さ を表している。

※初期化部分に 追加

int[][] score = { { 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 }};

・メロディー配列を参照してサウンドバッファから音声データを選択し、line に書き込むループ処理をする。

※再生部分の修正

for (int i = 0; i < score.length; i++) {

line.write( buf[ score[i][0] ] , 0, buf[0].length / score[i][1]);

}

余技1: ドレミファソラシドを徐々に短く再生し、テンポアップしながら繰り返し再生する。

添付ファイル SoundTest3.java

for (int j = 0; j < 8; j++) {

for (int i = 0; i < 8; i++) {

line.write(buf[i], 0, buf[0].length /(i + 1) /(j+1));

}

}

余技2: 高速トレモロ奏法で疑似和音を再生する。

添付ファイル SoundTest4.java

for (int i = 0; i < 30; i++) { // ドを30回連打(24分音符で)

line.write(buf[0], 0, buf[0].length / 24);

}

for (int i = 0; i < 15; i++) { // ド→ミを15回連打

line.write(buf[0], 0, buf[0].length / 24);

line.write(buf[2], 0, buf[0].length / 24);

}

for (int i = 0; i < 10; i++) { // ド→ミ→ソを15回連打

line.write(buf[0], 0, buf[0].length / 24);

line.write(buf[2], 0, buf[0].length / 24);

line.write(buf[4], 0, buf[0].length / 24);

}

課題提出について:

eclipseのワークプレイスのフォルダを zip 圧縮して、後日、他の回の課題と一緒にまとめて webclass に課題提出。

時間が余ったら、↓の2016年度課題にも取り組む。

以下、2016年度用資料

・出席はWebClass で登録。

作業メモ: 課題1,2,3の例を作成して添付する。

先週の続き)

Maze3の解説。

課題3の解説。 第6回 L6b と L6_1b

キーワード:

クラス継承、抽象化(アブストラクト)、インターフェース

講義資料:

(参考1) WebClass の 全学共通コース → Java入門 → 第6章(クラス)第7章(クラス継承)

↑ Javaの文法的な部分についてはこちらのテキストで確認できます(Webにも多数の解説サイトあり)。 オブジェクト指向としての例はちょっと微妙(Webにもなかなか良い例示が少ない)。

(参考2) 応用プログラミング1 の 第8回 オブジェクト指向2 を参照。

※資料を置いているサーバーが老朽化して、ところどこリンク切れになっています。

http://kaz.cyteen.nagoya-bunri.a.jp/ の部分を http://www.nagoya-bunri.ac.jp/~kobashi/ に置き換えると表示できます。

(参考3) ScratchでJava (クラス・インスタンス・メソッドの部分)

プロジェクト名 Project4

以下の問題1~問題7は、 ファイル L7_1.java に指定したクラスやメソッドを追加修正して作成するとよい。

※プロジェクト内のファイルは一括してコンパイルされる。同一のプロジェクト内で、別々のファイルで同一のclass名が存在すると、コンパイル時にエラーになる。

問題1

コンソールアプリケーションで、メッセージ(文字列)でキャラクターの行動を表現する

例)

public class L7_1 { public static void main(String[] args) { Cat mycat = new Cat(); // インスタンス生成 mycat.name = "クロ"; // インスタンス変数への書き込み mycat.walk(100); // インスタンスメソッドの呼び出し mycat.walk(10); mycat.walk(1); mycat.run(10); Cat.sound(); // スタティックメソッドの呼び出し // スタティック変数へのアクセス System.out.println( Cat.limb + "本足"); } } class Cat { String name; static int limb = 4; //足の数 static void sound() { System.out.println("みゃ~"); } void walk(int step ) { // ねこが step 歩く System.out.println(name + "が" + step + "歩移動した"); } void run(int step ) { // ねこが step 歩く System.out.println("ねこが" + step*10 + "歩 走る"); } }

↑のプログラムに コンストラクタ を追加して、name に初期値を与える様にする。 mainも修正する。

Cat(String name) {

this.name = name;

}

default コンストラクタを修正して、 new Cat() でインスタンスを生成した際には、name を ”名無し”となる様にする。

Cat() {

//名前が "名無し" になるようなコードをここに書く

}

↑のプログラムに ねこ の現在の座標を管理する インスタンス変数 x y を追加して、移動するたびに値を更新するようにする。 mainも修正する。

int x;

int y;

値の更新の例) x += 10;

注) walk と run の両方とも座標の修正に対応すること。

↑のプログラムに report() メソッドを追加し、現在の座標を報告(コンソールに出力)するようにする。 mainも修正する。

void report() {

System.out.println( インスタンス変数 + "は X=" + インスタンス変数 + "にいる");

}

x,y 座標の報告でもよいし、 x 座標のみの報告でもよい。

↑のプログラムで、 sound(String str) のようにsoundメソッドをオーバーロードして追加し、 引数 str でコンソールで台詞を喋らせる。 mainも修正する。

(略)

問題2

2匹の猫が行動するように、 mainメソッドを修正する。適当に新しい猫の名前を決めて、コンソールに登場させる。

Cat yourCat = new Cat();

yourCat.name = "シロ";

yourCat.walk(50);

mycat.walk(10);

問題3

Cat クラスを元にして、より抽象度の高い Animal クラスを作成する。

Cat クラスを Animal クラスのサブクラスに修正する。

Animal クラスでは、動物のサイズ 大・中・小 と 名前 をフィールドに持ち、サブクラスに継承させる。

サイズの指定には、enum 列挙型 を使用する。 参照: http://www.atmarkit.co.jp/ait/articles/1103/03/news107_2.html

enum Size {

Big, Medium, Small

}

抽象メソッド move() を定義する。

abstract void move();

具体的な移動方法については、サブクラスで実装する。

コンストラクタを定義し、

abstract class Animal {

String name;

Size size;

abstract void move();

Animal(String name, Size size) {

初期化のコードを書く

}

}

注) super()super(String name, Size size) を使用して、Catのコンストラクタの修正が必要。

問題4

猫に鈴などのアイテムを付けるインターフェースを実装する。猫はアイテムを1つだけ装備できるとする。

インターフェース

interface ItemOwner {

void attachItem(String item); // アイテムの装備

void dropItem(); // アイテムを捨てる

void useItem(); // アイテムを使う

}

ItemOwner を実装する側のクラスにフィールド String item を追加する。

この item 変数にアイテムを保持するためのコードをインターフェースのattachItemメソッドに書く。

他の2つのメソッドでは、○○を捨てました、○○を使いましたと、メッセージ表示するコードを書く。

各メソッドの実行内容は、それらしいメッセージを表示できればOK。 特に useItem についてはどのようなアイテムを持っているか判定して

使い方を変える必要はない。(次回 オブジェクト指向的 な解決方法を示す)

問題5

Crow クラスを、猫同様に設計する。カラスらしいメソッドを考え定義すること。

Crowクラスも、 ItemOwner クラスを実装すること。

問題6

Cat クラスに ランダムに移動する randomWalk() メソッド ランダムな歩数 x 座標 y 座標を移動。

ItemOwner インターフェースに、randomUse() メソッド アイテムの使用メッセージをランダム選択

を追加して、mainクラスにも修正を加える。

randomUse() のコードは難しそう。パスしてもOK。

アイテムを複数登録して利用するためには ↓

String item フィールドにはアイテムを文字列として、 1つだけしか登録できない。

アイテムを複数登録するためには、 String[] items のように複数のアイテムを登録するためのデータ構造を使う必要がある。

ArrayList<String> items の利用を推奨する。 ArrayList の利用方法を検索して調べてみよう。

問題7

Cat クラスの run を walk を利用するコードに変更する。mainクラスにも修正を加える。

例) walk を run の引数の回数実行するように変更する

応用:

・Animal の name フィールドを private に設定し、 setName(String name) など、アクセサー・メソッド を利用する様に修正する。

・ setName(int id_number) や setName(Object obj) など、メソッドを多重定義して、利用例を示す。

・setName( obj.toString() ) のように、オブジェクトの toString() メソッドを利用する例として、適当に自作 class と その toString メソッドの実装を考えて、使用例を示す。

例) Class RBGColor RGBの8bitカラーをインスタンス変数に保持する。 toString は、RGBカラーの HTMLでも文字表現 #808080 などを String で返すものとする。

次回解説予定。

・ItemOwer インターフェースの配列を作成し、幾つかの要素を持たせる。

拡張 for で全要素に同じアイテムを持たせ、使用させ、捨てさせる。

次回解説予定。

・猫がアイテムを持っていない場合、既に持っている場合についてメッセージを表示する様に、実装を修正。

インターフェースのメソッドの戻り値が void であるのを、 boolean に変更し、アイテム操作に成功した場合 true 失敗した場合 false を返すように変更する。

・猫が複数のアイテムを持てるようにインターフェースを設計

(説明略) 次回解説予定。

・Itemの使い方を、Itemの種類に応じて変えられるように Item クラスを作成して対応する。

(説明略) 次回解説予定。

・タートルグラフィックスシステムの制作

Java Applet 画面上に、タートルグラフィックスを描く ねこクラスを作成する。

グラフィックスの描画には、アニメーション処理 は付けなくてよい。

例えば、猫が、直進 100 右回転90 直進 100 右回転90 直進 100 右回転90 直進 100 右回転90 と動いた場合、画面には サイズ100の正方形が表示されるようにすること。

(ヒント)

ねこ クラス に命令された内容を記憶する commands クラス 配列を持たせる。

ねこ クラス に commands クラス配列 の内容に応じでグラフィックスを描画する、 execute() メソッドを作成する。