今回も前回に引き続きProcessingによる音の可視化について探求していきます。今回は最終課題を見据えて、より実践的なプログラムをいくつか紹介していきます。
スライド資料
サンプルプログラム
本日使用したプログラムのサンプルは、以下からダウンロードしてください。
最終課題について
次回(24日)の講義で、最終課題を発表を希望する方は、以下から申し込みしてください。
最終課題: Processingを用いた音響の視覚化
Sonic Piを用いて生成した音響作品をProcessingで視覚化した、30秒から1分程度のオリジナルのオーディオ・ビジュアル作品を制作してください。
提出物:(重要!!)
- 音を生成したSonic Piのプログラムソース
- ビジュアルを生成するProcessingのプログラムソース
- Processingプログラムのスクリーンショット (一番かっこいい瞬間を捉える!)
締切
2017年7月31日
提出方法
3つの提出データ(Sonic Piソース、Processingソース、スクリーンショト)を圧縮せずにメールに添付して提出。その際に以下の情報もあわせて本文に入力してください。
- 作品タイトル
- 学籍番号
- 氏名
提出先
tadokoro+teu17@gmail.com
課題制作テンプレート
minimのFFTを使用した音響の可視化のプログラムのテンプレートです。課題制作に利用してください。
import ddf.minim.analysis.*;
import ddf.minim.*;
Minim minim;
AudioPlayer player;
FFT fft;
//FFTサイズ (2の冪乗で指定)
int fftSize = 512;
void setup() {
size(800, 600);
//minim初期化
minim = new Minim(this);
//サウンドファイル読み込み
player = minim.loadFile("recording.wav", fftSize);
//サウンドファイル再生
player.loop();
//FFTの初期化
fft = new FFT(player.bufferSize(), player.sampleRate());
}
void draw() {
background(0);
//左チャンネル
fft.forward(player.left);
for (int i = 0; i < fft.specSize(); i++) {
//スペクトラムデータは -> fft.getband(i);
}
//右チャンネル
fft.forward(player.right);
for (int i = 0; i < fft.specSize(); i++) {
//スペクトラムデータは -> fft.getband(i);
}
}

この講義では、前半にProcessingを使用したインタラクティブなアニメーションの基本、後半はSonic Piを使用したサウンドプログラミングを行ってきました。今回はいよいよこの両者を融合して、音と映像を同時に用いた表現に挑戦していきたいと思います。
まず始めにProcessingを用いて音を視覚化する方法を検討します。単純に感覚的手法で視覚化するのではなく、音を周波数成分に分解しその変化を視覚的に表現します。ここでは、FFT(高速フーリエ変換)という手法を用います。
次に、この視覚化の手法を用いて、Sonic Piで作成してレコーディングした音をProcessingで読み込み、視覚化する方法について解説します。サウンドの解析には、Processingのコアライブラリである、Soundライブラリーを使用します。
スライド資料
サンプルコード
サウンドファイルの再生
//Soundライブラリーの読み込み
import processing.sound.*;
//サウンドプレイヤー
SoundFile soundfile;
void setup() {
//サウンドファイルを読み込んでプレイヤーを初期化
soundfile = new SoundFile(this, "sound.aiff");
//ループ再生
soundfile.loop();
}
void draw(){
}
マウスで再生スピードと音量を変化
//Soundライブラリーの読み込み
import processing.sound.*;
//サウンドプレイヤー
SoundFile soundfile;
void setup() {
size(800, 600);
//サウンドファイルを読み込んでプレイヤーを初期化
soundfile = new SoundFile(this, "sound.aiff");
//ループ再生
soundfile.loop();
}
void draw() {
background(0);
//マウスのX座標で再生スピードを設定
float rate = map(mouseX, 0, width, 0.0, 2.0);
//マウスのY座標で音量を設定
float amp = map(mouseY, 0, height, 1.0, 0.0);
//再生スピードを適用
soundfile.rate(rate);
//音量を適用
soundfile.amp(amp);
}
音量を円の直径に反映
//Soundライブラリーの読み込み
import processing.sound.*;
//サウンドプレイヤー
SoundFile soundfile;
//音量解析
Amplitude rms;
void setup() {
size(800, 600);
fill(0, 127, 255);
noStroke();
//サウンドファイルを読み込んでプレイヤーを初期化
soundfile = new SoundFile(this, "sound.aiff");
//ループ再生
soundfile.loop();
//音量解析を初期化
rms = new Amplitude(this);
//音量解析の入力を設定
rms.input(soundfile);
}
void draw() {
background(0);
//音量を解析して値を調整
float diameter = map(rms.analyze(), 0.0, 1.0, 0.0, width);
//取得した音量で円を描く
ellipse(width/2, height/2, diameter, diameter);
}
FFTによるスペクトラム表示
//Soundライブラリーの読み込み
import processing.sound.*;
//サウンドプレイヤー
SoundFile soundfile;
//オーディオデバイス
AudioDevice device;
//FFT(高速フーリエ変換)
FFT fft;
//FFTサイズ
int bands = 1024;
//グラフの高さのスケールを設定
float scale = 20.0;
void setup() {
size(800, 600);
fill(0, 127, 255);
noStroke();
//オーディオバッファーの設定
device = new AudioDevice(this, 44000, bands);
//サウンドファイルを読み込んでプレイヤーを初期化
soundfile = new SoundFile(this, "sound.aiff");
//ループ再生
soundfile.loop();
//FFTの初期化
fft = new FFT(this, bands);
fft.input(soundfile);
}
void draw() {
background(0);
//FFT解析実行
fft.analyze();
noFill();
stroke(255);
//線分の描画開始
beginShape();
//FFTのバンドの数だけくりかえし
for (int i = 0; i < bands; i++) {
//FFTの解析結果を高さにグラフを描く
vertex(i * width/float(bands), height - fft.spectrum[i] * height * scale);
}
//線分の描画終了
endShape();
}
引き続きSonic Piを使った音楽プログラミングについて考えていきます。
今回は、Sonic Piにおけるプログラムの構造化とデータ構造に焦点を絞って解説していきます。構造化プログラミング言語における基本的なプログラム構造は「順次」「反復」「条件分岐」の3に代表されます。Sonic Piでもこの3つのプログラム構造を作成することが可能です。実際に音に出して確認しながらSonic Piにおけるプログラムの構造化を学びます。
もう1つデータ構造について考えます。Sonic Piでは「リスト」というデータ構造が多用されます。リストとはデータの一覧を構成するデータの型です。まず始めにリストの使用法を解説します。さらにSonic Piでは多くの和音(chord)と音階(scale)がリスト形式で定義されています。和音と音階の使用方、また和音や音階による即興について考えていきます。
スライド資料
課題: 和音、音階を利用して旋律を作る
- Sonic Piの和音(chord)もしくは音階(scale)を使用してループする旋律(メロディー)を作曲してください
課題の提出方法
- emailで提出
- To : tadokoro+teu17@gmail.com
- Subject: 課題7
- 作成したプログラムのソースコードを本文にカット&ペースト
- 本文に以下の内容を記入して提出
- 学籍番号
- 氏名
- 締切: 次回の講義開始時間 (7月10日 16:45) まで!
サンプルコード
# 回数を指定した反復
3.times do
play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
sleep 0.25
end
#反復のネスト(入れ子)
4.times do
sample :drum_heavy_kick
2.times do
sample :elec_blip2, rate: 2
sleep 0.25
end
sample :elec_snare
4.times do
sample :drum_tom_mid_soft
sleep 0.125
end
end
#条件分岐
i = 0
loop do
if i % 4 == 0 then
sample :drum_heavy_kick
else
sample :drum_cymbal_closed
end
sleep 0.125
i = i+1
end
#ifとone_in
loop do
if one_in(3)
sample :drum_heavy_kick
else
sample :drum_cymbal_closed
end
sleep 0.125
end
#ifとone_in2
loop do
if one_in(3)
sample :drum_heavy_kick
sleep 0.25
else
sample :drum_cymbal_closed
sleep 0.125
end
end
#複数のリズムの共存
live_loop :live do
sample :drum_heavy_kick
4.times do
sample :elec_blip2, rate: 2
sleep 1.0/8.0
end
sample :elec_snare
4.times do
sample :drum_tom_mid_soft
sleep 0.125
end
end
live_loop :live2 do
sample :drum_heavy_kick
3.times do
sample :elec_blip2, rate: 2
sleep 1.0/8.0
end
sample :elec_snare
4.times do
sample :drum_tom_mid_soft
sleep 0.125
end
end
#和音
play chord(:C4, :major)
sleep 1.0
play chord(:C4, :major7)
sleep 1.0
play chord(:C4, :minor)
sleep 1.0
play chord(:C4, :minor7)
sleep 1.0
play chord(:C4, :dim)
sleep 1.0
play chord(:C4, :dim7)
sleep 1.0
play chord(:C4, :sus2)
sleep 1.0
play chord(:C4, :sus4)
sleep 1.0
play chord(:C4, :augmented)
sleep 1.0
#アルペジオ
play_pattern_timed chord(:C4, :major), 0.25
sleep 0.5
play_pattern_timed chord(:C4, :major7), 0.25
sleep 0.5
play_pattern_timed chord(:C4, :minor), 0.25
sleep 0.5
play_pattern_timed chord(:C4, :minor7), 0.25
sleep 0.5
play_pattern_timed chord(:C4, :dim), 0.25
sleep 0.5
play_pattern_timed chord(:C4, :dim7), 0.25
sleep 0.5
play_pattern_timed chord(:C4, :add9), 0.25
sleep 0.5
#アルペジオ2
play_pattern_timed chord(:C4, :major), [0.25, 0.5]
sleep 0.5
play_pattern_timed chord(:C4, :major7), [0.25, 0.5]
sleep 0.5
play_pattern_timed chord(:C4, :minor), [0.25, 0.5]
sleep 0.5
play_pattern_timed chord(:C4, :minor7), [0.25, 0.5]
sleep 0.5
play_pattern_timed chord(:C4, :dim), [0.25, 0.5]
sleep 0.5
play_pattern_timed chord(:C4, :dim7), [0.25, 0.5]
sleep 0.5
play_pattern_timed chord(:C4, :add9), [0.25, 0.5]
sleep 0.5
#スケール(音階)
play_pattern_timed scale(:C4, :major), 0.25
sleep 0.5
play_pattern_timed scale(:C4, :minor), 0.25
sleep 0.5
play_pattern_timed scale(:C4, :aeolian), 0.25
sleep 0.5
play_pattern_timed scale(:C4, :ahirbhairav), 0.25
sleep 0.5
play_pattern_timed scale(:C4, :augmented), 0.25
sleep 0.5
play_pattern_timed scale(:C4, :augmented2), 0.25
sleep 0.5
play_pattern_timed scale(:C4, :bartok), 0.25
sleep 0.5
#スケールとchooseによる即興
use_synth :prophet
live_loop :live do
play choose(scale(:c3, :minor, num_octaves: 3)), cutoff: rrand(60, 100)
play choose(scale(:c4, :minor, num_octaves: 3)), cutoff: rrand(60, 100)
play choose(scale(:c5, :minor, num_octaves: 3)), cutoff: rrand(60, 100)
sleep 0.25
end
#ライヒ Piano Phase
notes = [:E4, :Fs4, :B4, :Cs5, :D5, :Fs4, :E4, :Cs5, :B4, :Fs4, :D5, :Cs5]
use_synth :prophet
with_fx :reverb do
live_loop :reich1 do
i = 0
12.times do
play (notes[i]), release: 0.4, pan: 0.8, cutoff: 80
sleep 0.15
i = i + 1
end
end
live_loop :reich2 do
i = 0
12.times do
play (notes[i])+12, release: 0.4, pan: -0.8, cutoff: 80
sleep 0.151
i = i + 1
end
end
end
Sonic Piでプログラミングによる音楽制作を行います。今回は、前回のシンセを使用した音の再生とは別の方法、サンプルを使用した音の再生を取り上げます。サンプルは、サウンドファイルを読み込んで指定したタイミングで音を再生します。前回のシンセがシンセサイザーとすると、サンプルはサンプラーに相当します。演奏するタイミングだけでなく、音量、定位、再生スピードなど様々なパラメータを設定可能です。また、Sonic Piにあらかじめ用意されたサンプルだけでなく、ファイルの場所を指定して外部ファイルを読み込むことも可能です。
今回はさらに、Sonic Piにおけるランダム (乱数) の生成とその使用方法について解説します。Sonic Piでは、単純な乱数だけでなく、指定した範囲の乱数 (rrand, rrand_i)、リストの中からランダムに選択(choose)、サイコロをふってその結果で選択(dice) など様々なランダム化の関数が用意されています。音楽への応用を考えながら、乱数を使用していきます。
スライド資料
課題: 乱数を利用して旋律を作る
- Sonic Piのランダムに関する機能(chooseを含む)を使用してループする旋律(メロディー)を作曲してください
課題の提出方法
- emailで提出
- To : tadokoro+teu17@gmail.com
- Subject: 課題6
- 作成したプログラムのソースコードを本文にカット&ペースト
- 本文に以下の内容を記入して提出
- 学籍番号
- 氏名
- 締切: 次回の講義開始時間 (7月3日 16:45) まで!

「メディア芸術の基礎」の前半では、主にProcessingを用いたビジュアルプログラミングを行ってきました。後半はまた新たな内容をとりあげていきます。
後半の講義では、音響や音楽をプログラミングを用いて処理し表現を行います。サウンドを扱うための開発環境として「Sonic Pi」を使用します。Sonic piは、教育現場でのプログラミングや音楽の授業をサポートするように設計された、ライブコーディング可能な無料のサウンドプログラミング開発環境です。「カノンからダブステップまで」というキャッチフレーズに代表されるように、古典〜現代の音楽を作曲できる、Mac OS XやWindows、さらにはRaspberry Piでも動かすことが可能で、柔軟なプログラミングが可能です。
今回は、Sonic Piの入門として、インストールから操作方法、そしてプログラミングの基本を学んでいきます。
スライド資料
課題: Sonic Piで旋律のループを作る
- Sonic Piを使用してループする旋律(メロディー)を作曲してください
- 和音やサンプル音など授業内で扱わなかったプログラミングの手法は自由に使用して構いません
課題の提出方法
- emailで提出
- To : tadokoro+teu17@gmail.com
- Subject: 課題5
- 作成したプログラムのソースコードを本文にカット&ペースト
- 本文に以下の内容を記入して提出
- 学籍番号
- 氏名
- 締切: 次回の講義開始時間 (6月26日 16:45) まで!

前回に引き続き、Processingにおけるオブジェクト指向プログラミング(OOP)の方法について解説していきます。今回はOOPの重要な概念の一つである「継承 (インヘリタンス) 」について考えていきます。継承とは、既存クラスの機能構造を共有する新たなクラス(サブクラス)を派生させることです。サブクラスからは親となるクラスのプロパティやメソッドをそのまま引き継ぐことが可能です。この継承の仕組みを効果的に活用することで、既存のクラスを再利用しながらそこに新たな機能を加えていくということが可能となります。
次回までにやってくること
次回からは、新たな内容に入ります。これまではProcessingを用いて主に視覚的な表現を扱ってきました。次回からは、Sonic Piという開発環境を用いて音や音楽を扱います。
次回までに以下のサイトからSonic Piの最新版を使用しているOSにあわせてダウンロードし、インストールしておいてください。

スライド資料
サンプルプログラム
上下左右に動きまわる円 (基本テンプレート)
// Spotクラスを宣言
Spot spot;
void setup() {
size(800, 600, P2D);
noStroke();
frameRate(60);
//位置ベクトルをランダムに生成
PVector loc = new PVector(width/2.0, height/2.0);
//速度ベクトルをランダムに生成
PVector vec = new PVector(random(-4, 4), random(-4, 4));
//インスタンス化して配列に格納
spot = new Spot(loc, vec, random(5, 30));
//背景を黒に
background(0);
}
void draw() {
// 画面をフェードさせる
blendMode(BLEND);
fill(0, 0, 0, 10);
rect(0, 0, width, height);
// 色を加算合成に
blendMode(ADD);
// 円の色を設定
fill(31, 127, 255, 127);
// Spotクラスのmove()メソッドを呼び出す
spot.move();
// Spotクラスのdraw()メソッドを呼び出す
spot.draw();
}
// Spotクラス
class Spot {
// プロパティ
PVector location; //位置 (ベクトル!)
PVector velocity; //速度 (ベクトル!)
float diameter; //直径
// コンストラクター
Spot(PVector _location, PVector _velocity, float _diameter) {
location = _location;
diameter = _diameter;
velocity = _velocity;
}
// 移動
void move() {
//位置ベクトル + 速度ベクトル = 次フレーム位置ベクトル
location.add(velocity);
//左右の壁でバウンドさせる
if (location.x < diameter / 2 || location.x > width - diameter / 2) {
location.x = constrain(location.x, diameter/2, width - diameter / 2);
velocity.x *= -1;
}
//上下の壁でバウンドさせる
if (location.y < diameter / 2 || location.y > height - diameter / 2) {
location.y = constrain(location.y, diameter/2, height - diameter / 2);
velocity.y *= -1;
}
}
// 描画
void draw() {
ellipse(location.x, location.y, diameter, diameter);
}
}
クラスの配列 – 大量の物体を同時に動かす
// 物体の数
int numSpots = 400;
// Spotクラスを配列として宣言
Spot[] spots = new Spot[numSpots];
void setup() {
size(800, 600, P2D);
noStroke();
frameRate(60);
// 配列の数だけSpotクラスをインスタンス化
for (int i = 0; i < spots.length; i++) {
//位置ベクトルをランダムに生成
PVector loc = new PVector(width/2.0, height/2.0);
//速度ベクトルをランダムに生成
PVector vec = new PVector(random(-4, 4), random(-4, 4));
//インスタンス化して配列に格納
spots[i] = new Spot(loc, vec, random(5, 30));
}
background(0);
}
void draw() {
// 画面をフェードさせる
blendMode(BLEND);
fill(0, 0, 0, 10);
rect(0, 0, width, height);
// 色を加算合成に
blendMode(ADD);
// 円の色を設定
fill(31, 127, 255, 63);
for (int i = 0; i < spots.length; i++) {
// Spotクラスのmove()メソッドを呼び出す
spots[i].move();
// Spotクラスのdraw()メソッドを呼び出す
spots[i].draw();
}
}
// Spotクラス
class Spot {
// プロパティ
PVector location; //位置 (ベクトル!)
PVector velocity; //速度 (ベクトル!)
float diameter; //直径
// コンストラクター
Spot(PVector _location, PVector _velocity, float _diameter) {
location = _location;
diameter = _diameter;
velocity = _velocity;
}
// 移動
void move() {
//位置ベクトル + 速度ベクトル = 次フレーム位置ベクトル
location.add(velocity);
//左右の壁でバウンドさせる
if (location.x < diameter / 2 || location.x > width - diameter / 2) {
location.x = constrain(location.x, diameter/2, width - diameter / 2);
velocity.x *= -1;
}
//上下の壁でバウンドさせる
if (location.y < diameter / 2 || location.y > height - diameter / 2) {
location.y = constrain(location.y, diameter/2, height - diameter / 2);
velocity.y *= -1;
}
}
// 描画
void draw() {
ellipse(location.x, location.y, diameter, diameter);
}
}
クラスの継承 – 重力を付加
// 物体の数
int numSpots = 400;
// Spotクラスを配列として宣言
GravitySpot[] spots = new GravitySpot[numSpots];
void setup() {
size(800, 600, P2D);
noStroke();
frameRate(60);
// 配列の数だけSpotクラスをインスタンス化
for (int i = 0; i < spots.length; i++) {
//位置ベクトルをランダムに生成
PVector loc = new PVector(width/2.0, height/2.0);
//速度ベクトルをランダムに生成
PVector vec = new PVector(random(-4, 4), random(-4, 4), 1.0);
//重力を0.1に設定
float gravity = 0.1;
//インスタンス化して配列に格納
spots[i] = new GravitySpot(loc, vec, random(5, 30), gravity);
}
background(0);
}
void draw() {
// 画面をフェードさせる
blendMode(BLEND);
fill(0, 0, 0, 10);
rect(0, 0, width, height);
// 色を加算合成に
blendMode(ADD);
// 円の色を設定
fill(31, 127, 255, 63);
for (int i = 0; i < spots.length; i++) {
// Spotクラスのmove()メソッドを呼び出す
spots[i].move();
// Spotクラスのdraw()メソッドを呼び出す
spots[i].draw();
}
}
// Spotクラス
class Spot {
// プロパティ
PVector location; //位置 (ベクトル!)
PVector velocity; //速度 (ベクトル!)
float diameter; //直径
// コンストラクター
Spot(PVector _location, PVector _velocity, float _diameter) {
location = _location;
diameter = _diameter;
velocity = _velocity;
}
// 移動
void move() {
//位置ベクトル + 速度ベクトル = 次フレーム位置ベクトル
location.add(velocity);
//左右の壁でバウンドさせる
if (location.x < diameter / 2 || location.x > width - diameter / 2) {
location.x = constrain(location.x, diameter/2, width - diameter / 2);
velocity.x *= -1;
}
//上下の壁でバウンドさせる
if (location.y < diameter / 2 || location.y > height - diameter / 2) {
location.y = constrain(location.y, diameter/2, height - diameter / 2);
velocity.y *= -1;
}
}
// 描画
void draw() {
ellipse(location.x, location.y, diameter, diameter);
}
}
//Spotを継承した、GravitySpot(重力を付加)
class GravitySpot extends Spot {
float gravity; //重力
//コンストラクター
GravitySpot(PVector _location, PVector _velocity, float _diameter, float _gravity) {
//Superクラス(Spotクラス)をインスタンス化
super(_location, _velocity, _diameter);
//重力を設定
gravity = _gravity;
}
void move() {
//速度に重力を付加
velocity.y += gravity;
//スーパークラスのmove()を呼び出し
super.move();
}
void draw() {
//スーパークラスのdraw()を呼び出し
super.draw();
}
}
クラスの継承 – 重力と摩擦力を付加
// 物体の数
int numSpots = 400;
// Spotクラスを配列として宣言
GravitySpot[] spots = new GravitySpot[numSpots];
void setup() {
size(800, 600);
noStroke();
frameRate(60);
// 配列の数だけSpotクラスをインスタンス化
for (int i = 0; i < spots.length; i++) {
//位置ベクトルをランダムに生成
PVector loc = new PVector(width/2.0, height/2.0);
//速度ベクトルをランダムに生成
PVector vec = new PVector(random(-4, 4), random(-4, 4), 1.0);
//円の大きさを5から30に
float diameter = random(5, 30);
//重力を0.1に設定
float gravity = 0.1;
//摩擦力を0.998に
float friction = 0.998;
//インスタンス化して配列に格納
spots[i] = new GravitySpot(loc, vec, diameter, gravity, friction);
}
background(0);
}
void draw() {
// 画面をフェードさせる
blendMode(BLEND);
fill(0, 0, 0, 10);
rect(0, 0, width, height);
// 色を加算合成に
blendMode(ADD);
// 円の色を設定
fill(31, 127, 255, 63);
for (int i = 0; i < spots.length; i++) {
// Spotクラスのmove()メソッドを呼び出す
spots[i].move();
// Spotクラスのdraw()メソッドを呼び出す
spots[i].draw();
}
}
// Spotクラス
class Spot {
// プロパティ
PVector location; //位置 (ベクトル!)
PVector velocity; //速度 (ベクトル!)
float diameter; //直径
// コンストラクター
Spot(PVector _location, PVector _velocity, float _diameter) {
location = _location;
diameter = _diameter;
velocity = _velocity;
}
// 移動
void move() {
//位置ベクトル + 速度ベクトル = 次フレーム位置ベクトル
location.add(velocity);
//左右の壁でバウンドさせる
if (location.x < diameter / 2 || location.x > width - diameter / 2) {
location.x = constrain(location.x, diameter/2, width - diameter / 2);
velocity.x *= -1;
}
//上下の壁でバウンドさせる
if (location.y < diameter / 2 || location.y > height - diameter / 2) {
location.y = constrain(location.y, diameter/2, height - diameter / 2);
velocity.y *= -1;
}
}
// 描画
void draw() {
ellipse(location.x, location.y, diameter, diameter);
}
}
//Spotを継承した、GravitySpot(重力と摩擦を付加)
class GravitySpot extends Spot {
float gravity; //重力
float friction; //摩擦力
//コンストラクター
GravitySpot(PVector _location,
PVector _velocity,
float _diameter,
float _gravity,
float _friction) {
//Superクラス(Spotクラス)をインスタンス化
super(_location, _velocity, _diameter);
//重力を設定
gravity = _gravity;
//摩擦力を設定
friction = _friction;
}
void move() {
//速度に摩擦力を掛ける
velocity.mult(friction);
//速度に重力を付加
velocity.y += gravity;
//スーパークラスのmove()を呼び出し
super.move();
}
void draw() {
//スーパークラスのdraw()を呼び出し
super.draw();
}
}

今回はProcessingで生成的(Generative)な形態を生みだすにはどうすればよいのか、試行錯誤しながら実験していきます。まず初めに、コードを用いた生成的な表現の実例をいくつか紹介した後、実際にProcessingでプログラミングしていきます。
まず始めに、完全にランダムな確率で動きまわる「ランダムウォーク」な動きをする点の動きをつくり、その軌跡を描いてみます。次にこのランダムな動きを増殖させていきます。増殖の際に今回は全てを一つのプログラムに書くのではなく、それぞれの点を細かなプログラムで実装し、その小さなプログラム達を組合せることで一つの機能を生みだすような設計にします。この小さなプログラムを「オブジェクト (Object)」と呼び、オブジェクトを構成単位にしてプログラムを作成していく手法を、オブジェクト指向プログラミング (OOP) と呼びます。このOOPの考え方は今後も重要な内容となってきますので、実例を通して確実に理解していきましょう。
スライド資料
サンプルプログラム
ランダムウォーク (クラス無し)
PVector location;
PVector velocity;
void setup() {
size(800, 600);
frameRate(60);
//位置と速度のベクトルを初期化
location = new PVector(width/2, height/2);
velocity = new PVector();
//背景は一度だけ (軌跡を残す)
background(0);
}
void draw() {
//描画設定
noStroke();
fill(255, 31);
//速度を設定
velocity.x = random(-1, 1);
velocity.y = random(-1, 1);
location.add(velocity);
//円を描く
ellipse(location.x, location.y, 2, 2);
}
ランダムウォーク10倍速 (クラス無し)
PVector location;
PVector velocity;
void setup() {
size(800, 600);
frameRate(60);
//位置と速度のベクトルを初期化
location = new PVector(width/2, height/2);
velocity = new PVector();
//背景は一度だけ (軌跡を残す)
background(0);
}
void draw() {
//描画設定
noStroke();
fill(255, 31);
for (int i = 0; i < 10; i++) {
//速度を設定
velocity.x = random(-1, 1);
velocity.y = random(-1, 1);
location.add(velocity);
//円を描く
ellipse(location.x, location.y, 2, 2);
}
}
Walkerクラスによるランダムウォーク
//Walkerクラスのオブジェクトの宣言
Walker walker;
void setup() {
size(800, 600);
frameRate(60);
//クラスをインスタンス化
walker = new Walker();
background(0);
}
void draw() {
//Walkerクラスのdraw()メソッドを実行
noStroke();
fill(255, 31);
walker.draw();
}
class Walker {
PVector location;
PVector velocity;
Walker() {
location = new PVector(width/2, height/2);
velocity = new PVector();
}
//ランダムウォークを描画
void draw() {
for (int i = 0; i < 10; i++) {
velocity.x = random(-1, 1);
velocity.y = random(-1, 1);
location.add(velocity);
ellipse(location.x, location.y, 2, 2);
}
}
}
Walkerクラスの配列 (10個)
//Walkerクラスのオブジェクトの宣言
int NUM = 10;
Walker[] walker = new Walker[NUM];
void setup() {
size(800, 600);
frameRate(60);
//配列の数だけインスタンス化
for (int i = 0; i < NUM; i++) {
walker[i] = new Walker();
}
background(0);
}
void draw() {
//色の設定
noStroke();
fill(255, 31);
//配列の数だけ描画
for (int i = 0; i < NUM; i++) {
walker[i].draw();
}
}
//Walkerクラス
class Walker {
PVector location;
PVector velocity;
Walker() {
location = new PVector(width/2, height/2);
velocity = new PVector();
}
//ランダムウォークを描画
void draw() {
for (int i = 0; i < 10; i++) {
velocity.x = random(-1, 1);
velocity.y = random(-1, 1);
location.add(velocity);
ellipse(location.x, location.y, 2, 2);
}
}
}
Walkerクラスの配列 (100個)
int NUM = 100;
Walker[] walker = new Walker[NUM];
void setup() {
size(1280, 720);
frameRate(60);
//Walker配列の初期化
for (int i = 0; i < NUM; i++) {
walker[i] = new Walker();
}
//背景
background(0);
}
void draw() {
//画面をフェードさせる
blendMode(BLEND);
fill(0, 5);
rect(0, 0, width, height);
//色の設定
noStroke();
blendMode(ADD);
fill(255, 31);
//Walker配列を描画
for (int i = 0; i < NUM; i++) {
walker[i].draw();
}
}
//Walkerクラス
class Walker {
PVector location;
PVector velocity;
Walker() {
location = new PVector(width/2, height/2);
velocity = new PVector();
}
void draw() {
for (int i = 0; i < 10; i++) {
velocity.x = random(-1, 1);
velocity.y = random(-1, 1);
location.add(velocity);
rect(location.x, location.y, 2, 2);
}
}
}
ProcessingのPImageクラスは、外部のビットマップ画像(Jpeg, GIF、PNGなど)をデータとしてプログラムに読み込むことができます。読み込んだ画像は単に画面に表示するだけでなく、色や明度やサイズを変更して表示することができます。さらには、画像に含まれる全てのピクセルの色情報を読み取り配列に格納することが可能です。そのデータをもとに別の画像を再生成することが可能となり、読み込んだ画像データの色情報をもとにした多彩な表現が可能となります。
今回はProcessingに画像を読み込んで、分析再合成することで、様々な表現の可能性について探っていきます。
スライド資料
サンプルファイル
課題: Proessingに読み込んだ画像ファイルのデータで表現する
- PImageに読み込んだ画像ファイルの情報から、新たなイメージを生成してください
課題の提出方法
- emailで提出
- To : tadokoro+teu17@gmail.com
- Subject: 課題4
- 作成したプログラムの実行結果をスクリーンキャプチャーしてメールに添付
- 本文に以下の内容を記入して提出
- 学籍番号
- 氏名
- 締切: 次回の講義開始時間 (6月5日 16:45) まで
今回は、より複雑なアニメーションに挑戦します。たくさんの図形を、一定の手続き(= アルゴリズム)にそって動かしてみます。動きのアルゴリズムはいろいろありますが、今回はその一例として、三角関数(sin, cos , tan)を使用した動きをとりあげます。三角関数の定義は、直角三角形の角度とそれを取り囲む辺の比率で定義されます。しかし、同時に円運動を座標に変換する仕組みとしても活用可能です。この仕組みを応用してアニメーションするとどうなるか、三角関数を用いたアニメーションで美しいパターン「ビジュアルハーモニー」を生成する手法を紹介します。
スライド資料
サンプルプログラム
課題
課題: 「ビジュアルハーモニー」を表現する
- sin関数による動きで、オリジナルの「ビジュアルハーモニー」を表現してみる
- 周波数を変化させてみる
- 動かす形を変えてみる
- 色を変化させてみる
- 3Dで表現してみる (上級編)
- …etc.
サンプルコード: 以下のサンプルが課題のベースになります。
int NUM = 256;
PVector[] location = new PVector[NUM];
float phase, velocity, radius;
void setup() {
size(800, 800, P3D);
frameRate(60);
background(0);
noFill();
phase = 0.0;
velocity = 2.0;
radius = height / 4.0;
for (int i = 0; i < NUM; i++) {
location[i] = new PVector(0, 0);
}
}
void draw() {
background(0);
translate(width / 2.0, height / 2.0);
for (int i = 0; i < NUM; i++) {
location[i].x = cos(radians(phase) / NUM * (i+1)) * radius;
location[i].y = sin(radians(phase) / NUM * (i+1)) * radius;
stroke(0, 127, 255);
float diameter = i * (height / 2) / float(NUM);
ellipse(location[i].x, location[i].y, diameter, diameter);
}
phase += velocity;
}
課題の提出方法
- emailで提出
- To : tadokoro+teu17@gmail.com
- Subject: 課題3
- 作成したプログラムの実行結果をスクリーンキャプチャーしてメールに添付
- 本文に以下の内容を記入して提出
- 学籍番号
- 氏名
- 締切: 次回の講義開始時間 (5月29日 16:45) まで
今回からいよいよ動きのある表現(= アニメーション)について扱っていきます。アニメーションを実現するには、まず時間を扱う基本構造を知る必要があります。Processingでは、setup(), draw() という2つのブロックにわけて、初期化と更新を行うことでアニメーションを実現しています。まず始めはこの基本構造について理解します。次に、これから動きを扱う際に、向きと大きさをもった「ベクトル」という概念を理解します。ベクトルを理解することで、位置や運動を整理して記述することが可能となります。最後に、この基本構造をベクトルを活用して簡単なアニメーションを作成します。
スライド資料
サンプルプログラム
本日とりあげたサンプルプログラムは以下からダウンロードしてください。
課題
今日とりあげた最後のサンプルを元に、以下のスケッチを作成してください。
- それぞれの円の色をランダムに
- それぞれの円の大きさをランダムに
以下のサンプルがベースになります。
int NUM = 100;
PVector[] location = new PVector[NUM];
PVector[] velocity = new PVector[NUM];
void setup() {
size(800, 600);
frameRate(60);
noStroke();
fill(0, 127, 255);
for (int i = 0; i < NUM; i++) {
location[i] = new PVector(random(width), random(height));
velocity[i] = new PVector(random(-4, 4), random(-4, 4));
}
}
void draw() {
background(15);
for (int i = 0; i < NUM; i++) {
ellipse(location[i].x, location[i].y, 20, 20);
location[i].add(velocity[i]);
if ((location[i].x > width) || (location[i].x < 0)) {
velocity[i].x *= -1;
}
if ((location[i].y > height) || (location[i].y < 0)) {
velocity[i].y *= -1;
}
}
}
課題の提出方法
- emailで提出
- To: tadokoro+teu17@gmail.com
- Subject: 課題2
- 本文に以下の内容を記入して提出
- 学籍番号
- 氏名
- 作成したソースコード (.pde) を本文にコピー&ペースト
- 締切: 次回の講義開始時間 (5月22日 16:45) まで