昭和エンジニアの四谷です。
自作プログラムと打ち込み譜面で、スマホの着メロを作りたいと思います!
タイトルに「3和音」と書きましたが、「携帯 3和音」でググると、出るわ出るわ、なつかしいiモード。イメージ的にはあんな感じ。ビープ音による3和音に挑戦してみます。
こちらを参考にさせていただきました。
おおまかな手順
独自の定数表現で楽譜を打ち込んで音楽を作成し、できた音源をスマホ(iPhone)に着信音として登録します。
プログラムで音符打ち込み
↓
実行
↓
wavファイル生成
↓
ファイル形式を変換しスマホに転送
↓
着信音に登録
↓
完成!
打ち込む音の定義
高さ/強さ/長さの構造体とします。
音楽についての基本知識的なものは楽典をご参考ください。
高さ
ピアノと同じ88鍵の周波数を以下サイトを参考にしました。
国際式表記ではド
の音をC
と表現しますが、今回は私が打ち込みやすくすることを目的として、独自の表記法を作りました。
高さの表記法(独自)
国際式表記の音階/オクターブ/シャープ(フラット)の表現を転置し、自分が入力しやすいフォーマットに変えました。
ピアノの2オクターブ目のド
のシャープ
(C2#)を例にして、独自表現にしてみましょう。
- 独自表現での音階番号
音階番号(独自) 国際式 音 1 C ド 2 D レ 3 E ミ 4 F ファ 5 G ソ 6 A ラ 7 B シ - シャープ(フラット)
- シャープが付くとき末尾に
s
を付与 - フラット(♭=半音下げる)は使用しません。
- シャープが付くとき末尾に
これを組み合わせ↓のような表現になります。
k | オクターブ(0〜8) | 音階番号(1〜7) | シャープ(s)または無記 |
---|---|---|---|
k | 2 | 1 | s |
つまりC2#
の定数は k21s
となります。
強さ
定数 | 強さ | 意味 | 楽譜記号 |
---|---|---|---|
F | フォルテ | 強く | |
MF | メゾフォルテ | やや強く | |
N | (普通) | - | - |
MP | メゾピアノ | やや弱く | |
P | ピアノ | 弱く | |
M | (無音) | - | - |
- ピアノの場合、和音のうち特定の音だけを強く弾いたり、リズムのアクセントをつけたりすることで、表現が豊かになります。
- ビープ音では、すべてが強すぎると音が割れるので、メロディラインとアクセントは強めに。ベースは弱めでクライマックスはやや強めに、といった表現をしたいと思います。
- 休符を定義しない代わりに、
M
(無音)を定義しました。
長さ
定数 | 長さ | 音符 |
---|---|---|
L1 | 全音符 | |
L2 | 2分音符 | |
L2H | 符点2分音符 | |
L4 | 4分音符 | |
L4H | 符点4分音符 | |
L8 | 8分音符 | |
L8H | 符点8分音符 | |
L16 | 16分音符 | |
L16H | 符点16分音符 | |
L32 | 32分音符 |
サンプルコード
- 諸々の定義をしおえたところで、サンプルコードを実行します。
- wavファイルを生成することができるようになりました。
- 「プー」と音がなるだけのものです。
package main
import (
"math"
"os"
gowav "github.com/youpy/go-wav"
)
type (
Score struct {
K float64 // 鍵
V float64 // 強さ
L float64 // 音符
}
)
// ピアノ88鍵盤
const (
k06 float64 = 27.500
k06s float64 = 29.135
k07 float64 = 30.868
k11 float64 = 32.703
k11s float64 = 34.648
k12 float64 = 36.708
k12s float64 = 38.891
k13 float64 = 41.203
k14 float64 = 43.654
k14s float64 = 46.249
k15 float64 = 48.999
k15s float64 = 51.913
k16 float64 = 55.000
k16s float64 = 58.270
k17 float64 = 61.735
k21 float64 = 65.406
k21s float64 = 69.296
k22 float64 = 73.416
k22s float64 = 77.782
k23 float64 = 82.407
k24 float64 = 87.307
k24s float64 = 92.499
k25 float64 = 97.999
k25s float64 = 103.826
k26 float64 = 110.000
k26s float64 = 116.541
k27 float64 = 123.471
k31 float64 = 130.813
k31s float64 = 138.591
k32 float64 = 146.832
k32s float64 = 155.563
k33 float64 = 164.814
k34 float64 = 174.614
k34s float64 = 184.997
k35 float64 = 195.998
k35s float64 = 207.652
k36 float64 = 220.000
k36s float64 = 233.082
k37 float64 = 246.942
k41 float64 = 261.626
k41s float64 = 277.183
k42 float64 = 293.665
k42s float64 = 311.127
k43 float64 = 329.628
k44 float64 = 349.228
k44s float64 = 369.994
k45 float64 = 391.995
k45s float64 = 415.305
k46 float64 = 440.000
k46s float64 = 466.164
k47 float64 = 493.883
k51 float64 = 523.251
k51s float64 = 554.365
k52 float64 = 587.330
k52s float64 = 622.254
k53 float64 = 659.255
k54 float64 = 698.456
k54s float64 = 739.989
k55 float64 = 783.991
k55s float64 = 830.609
k56 float64 = 880.000
k56s float64 = 932.328
k57 float64 = 987.767
k61 float64 = 1046.502
k61s float64 = 1108.731
k62 float64 = 1174.659
k62s float64 = 1244.508
k63 float64 = 1318.510
k64 float64 = 1396.913
k64s float64 = 1479.978
k65 float64 = 1567.982
k65s float64 = 1661.219
k66 float64 = 1760.000
k66s float64 = 1864.655
k67 float64 = 1975.533
k71 float64 = 2093.005
k71s float64 = 2217.461
k72 float64 = 2349.318
k72s float64 = 2489.016
k73 float64 = 2637.020
k74 float64 = 2793.826
k74s float64 = 2959.955
k75 float64 = 3135.963
k75s float64 = 3322.438
k76 float64 = 3520.000
k76s float64 = 3729.310
k77 float64 = 3951.066
k81 float64 = 4186.009
)
// 音の強さ
const (
F = 0.3 // フォルテ
MF = F / 2 // メゾフォルテ
N = F / 5 // 普通
MP = N / 2 // メゾピアノ
P = N / 5 // ピアノ
M = 0.000 // 無音
)
// サンプリングレート
const Rate = 48000
// テンポ
const Tempo = 180
// 音符の種類
const (
L4 = float64(Rate * 60 / Tempo) // 4分音符
L1 = L2 * 2 // 全音符
L2 = L4 * 2 // 2分音符
L2H = L2 + L4 // 符点2分音符
L8 = L4 / 2 // 8分音符
L4H = L4 + L8 // 符点4分音符
L16 = L8 / 2 // 16分音符
L8H = L8 + L16 // 符点8分音符
L32 = L16 / 2 // 32分音符
L16H = L16 + L32 // 符点16分音符
)
const (
MAX = 60000
MIN = -10000
)
func main() {
sin1 := Sin([]Score{{k45s, F, L1}})
sin2 := Sin([]Score{{k33, F, L1}})
Mix(&sin1, &sin2)
Write(sin1)
}
func Max(a, b int) int {
if a+b > MAX {
return MAX
} else {
return a + b
}
}
func Min(a, b int) int {
if a+b < MIN {
return MIN
} else {
return a + b
}
}
func Sin(score []Score) []gowav.Sample {
var samples []gowav.Sample
for _, s := range score {
for i := 0; i < int(s.L); i++ {
si := math.Sin((float64(i) / Rate) * s.K * 2.0 * math.Pi)
gw := gowav.Sample{}
gw.Values[0] = int(si * 0x7fff * s.V)
samples = append(samples, gw)
}
}
return samples
}
func Mix(wav1, wav2 *[]gowav.Sample) {
for i, _ := range *wav1 {
if len(*wav2) < i {
break
}
if (*wav1)[i].Values[0] < 0 {
(*wav1)[i].Values[0] = Min((*wav1)[i].Values[0], (*wav2)[i].Values[0])
} else {
(*wav1)[i].Values[0] = Max((*wav1)[i].Values[0], (*wav2)[i].Values[0])
}
}
}
func Write(samples []gowav.Sample) error {
fp, err := os.OpenFile("a.wav", os.O_TRUNC|os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
return err
}
defer fp.Close()
writer := gowav.NewWriter(fp, uint32(len(samples)), 2, Rate, 16)
writer.WriteSamples(samples)
return nil
}
できたwavファイル
wavファイルの波形
- ソフトの都合なのかギザってますが、正弦波になっております。
和音
2つの波形値を加算することで実現します。
また、合成する際に音がつぶれないようにするため数値の上限、下限を制限します。
3和音(A,B,C)を合成する場合は、A=A+B
→A=A+C
のステップを踏むことになります。
// 正弦波サンプリングデータ2つを渡して加算処理
func Mix(wav1, wav2 *[]gowav.Sample) {
for i, _ := range *wav1 {
if len(*wav2) < i {
break
}
if (*wav1)[i].Values[0] < 0 {
(*wav1)[i].Values[0] = Min((*wav1)[i].Values[0], (*wav2)[i].Values[0])
} else {
(*wav1)[i].Values[0] = Max((*wav1)[i].Values[0], (*wav2)[i].Values[0])
}
}
}
ハノンを打ち込んでみよう
ハノンとは、ピアニストにとって、指の筋トレのようなものだと思ってください。
内容は機械的ですが、持久力がついたり、粒の揃った音を奏でられるようになるので、世界中で愛用されています。
ハノンの最初、第1番の楽譜を、ホ長調(基準の音がミ
)で、タンタタタン タンタタタン
とリズムをつけたカスタム練習です。
普段、私が練習してるので、なんとなく!
1拍目と3拍目を「強/弱/中/弱」のようなアクセントも付けたいと思います。
それを1オクターブ(12音差)で両手で弾くイメージでL/Rの音をそれぞれ定義。(2和音ですね)
いきなりコードに落とし込むのは大変なので、打ち込み元データはGoogleスプレッドシートで作成しました。(だって人間ですもの)
打ち込み元データ(イメージ)
打ち込み後コード
// ハノン(高音)
func Hanon() []Score {
return []Score{
{k43, F, L8}, {k45s, N, L16}, {k46, N, L16}, {k47, N, L8},
{k51s, MF, L8}, {k47, N, L16}, {k46, N, L16}, {k45s, N, L8},
{k44s, F, L8}, {k46, N, L16}, {k47, N, L16}, {k51s, N, L8},
{k52s, MF, L8}, {k51s, N, L16}, {k47, N, L16}, {k46, N, L8},
{k45s, F, L8}, {k47, N, L16}, {k51s, N, L16}, {k52s, N, L8},
{k53, MF, L8}, {k52s, N, L16}, {k51s, N, L16}, {k47, N, L8},
{k46, F, L8}, {k51s, N, L16}, {k52s, N, L16}, {k53, N, L8},
{k54s, MF, L8}, {k53, N, L16}, {k52s, N, L16}, {k51s, N, L8},
{k47, F, L8}, {k52s, N, L16}, {k53, N, L16}, {k54s, N, L8},
{k55s, MF, L8}, {k54s, N, L16}, {k53, N, L16}, {k52s, N, L8},
{k51s, F, L8}, {k53, N, L16}, {k54s, N, L16}, {k55s, N, L8},
{k56, MF, L8}, {k55s, N, L16}, {k54s, N, L16}, {k53, N, L8},
{k52s, F, L8}, {k54s, N, L16}, {k55s, N, L16}, {k56, N, L8},
{k57, MF, L8}, {k56, N, L16}, {k55s, N, L16}, {k54s, N, L8},
{k53, F, L2},
}
}
// ハノン(低音)
func HanonL() []Score {
return []Score{
{k33, F, L8}, {k35s, N, L16}, {k36, N, L16}, {k37, N, L8},
{k41s, MF, L8}, {k37, N, L16}, {k36, N, L16}, {k35s, N, L8},
{k34s, F, L8}, {k36, N, L16}, {k37, N, L16}, {k41s, N, L8},
{k42s, MF, L8}, {k41s, N, L16}, {k37, N, L16}, {k36, N, L8},
{k35s, F, L8}, {k37, N, L16}, {k41s, N, L16}, {k42s, N, L8},
{k43, MF, L8}, {k42s, N, L16}, {k41s, N, L16}, {k37, N, L8},
{k36, F, L8}, {k41s, N, L16}, {k42s, N, L16}, {k43, N, L8},
{k44s, MF, L8}, {k43, N, L16}, {k42s, N, L16}, {k41s, N, L8},
{k37, F, L8}, {k42s, N, L16}, {k43, N, L16}, {k44s, N, L8},
{k45s, MF, L8}, {k44s, N, L16}, {k43, N, L16}, {k42s, N, L8},
{k41s, F, L8}, {k43, N, L16}, {k44s, N, L16}, {k45s, N, L8},
{k46, MF, L8}, {k45s, N, L16}, {k44s, N, L16}, {k43, N, L8},
{k42s, F, L8}, {k44s, N, L16}, {k45s, N, L16}, {k46, N, L8},
{k47, MF, L8}, {k46, N, L16}, {k45s, N, L16}, {k44s, N, L8},
{k43, F, L2},
}
}
できたwavファイル
いよいよ3和音「ジングル・ベル」を打ち込んでみよう
「ジングル・ベル」を3和音でアレンジしました。
打ち込み後コード
高/中/低に分割されており、いずれも全体の長さは同じであることがポイントです。
たった4小節と短いものですが、外声(多声部楽曲の一番上及び下のパートのこと)のアクセントに気を使いながら、より立体的な仕上げを目指しました。
func Xmas() []Score {
return []Score{
{k51, F, L8}, {k51, F, L8}, {k51, F, L4}, {k51, MF, L8},
{k51, MF, L8}, {k51, MF, L4}, {k51, MF, L8}, {k52s, MF, L8},
{k45s, MF, L8H}, {k46s, N, L16}, {k51, MF, L2}, {k51s, MF, L8},
{k51s, MF, L8}, {k51s, MF, L8}, {k51s, MF, L8}, {k51s, MF, L8},
{k51, MF, L8}, {k51, MF, L8}, {k51, MF, L8}, {k52s, F, L8},
{k52s, F, L8}, {k51s, F, L8}, {k46s, F, L8}, {k45s, F, L2},
}
}
func XmasM() []Score {
return []Score{
{k45s, MF, L8}, {k45s, MF, L8}, {k45, N, L4}, {k44, MF, L8},
{k44, MF, L8}, {k43, N, L4}, {k42s, MF, L8}, {k45s, N, L8},
{k41, MF, L8H}, {k42s, N, L16}, {k45s, MF, L8}, {k45s, MF, L8},
{k45, MF, L8}, {k44s, MF, L8}, {k44s, MF, L8}, {k44s, MF, L8},
{k44, MF, L8}, {k44, MF, L8}, {k43, N, L8}, {k42s, N, L8},
{k42s, N, L8}, {k42s, MF, L8}, {k42s, F, L8}, {k42s, MF, L8},
{k42s, F, L8}, {k41s, MF, L8}, {k41, MF, L2},
}
}
func XmasL() []Score {
return []Score{
{k35s, MF, L4}, {k35, MF, L4}, {k34, N, L4}, {k33, N, L4},
{k35s, N, L4}, {k32s, N, L4}, {k35s, MF, L8}, {k35s, MF, L8},
{k36s, MF, L8}, {k41, MF, L8}, {k36s, MF, L4}, {k32s, N, L4},
{k35s, MF, L4}, {k31s, N, L4}, {k32s, N, L8}, {k32s, N, L8},
{k34, N, L8}, {k35, N, L8}, {k35s, MF, L2},
}
}
できたwavファイル
着メロを作るぞ!
曲は創聖のアクエリオンにしました。なんとなく!
爽快なリズム感がありますね。何年経ってもいいアニソンだと思います。
それにしても待てよ...打ち込むにしても、ちとこれは難しい。
サビのコード進行はこういう感じでしょうか。
なんとなくイメージが固まったところで、まずは、鬼のようにスプシに書きました。
そして、できた3和音の楽譜がこちら。
タッタッタッ
と休符を活用しながら、リズム感を表現。
聴いてみよう
それではお聴きください、こちら!
着メロに登録
wavファイルをaac形式に変換し、.m4r拡張子に変更してiPhoneに転送。
登録されたのがこちら。
スマホに電話をかけてみよう
実際に電話を発信して鳴らしてみました。
わい、感動や...
最後に
アドベントカレンダー担当の日が迫る中、ようやく重い腰をあげたのが3日前w
記事を書くにあたり、曲をアレンジしたり、後付で楽譜をこしらえたりするのに苦労しましたが、楽めました。
ぜひ皆さんもチャレンジしてみてください。
ソース