1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

オークファングループAdvent Calendar 2022

Day 17

【Go】打ち込みでスマホの着メロを作ろう【3和音】

Last updated at Posted at 2022-12-16

昭和エンジニアの四谷です。

自作プログラムと打ち込み譜面で、スマホの着メロを作りたいと思います!
タイトルに「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 フォルテ 強く f
MF メゾフォルテ やや強く mf
N (普通) - -
MP メゾピアノ やや弱く mp
P ピアノ 弱く p
M (無音) - -
  • ピアノの場合、和音のうち特定の音だけを強く弾いたり、リズムのアクセントをつけたりすることで、表現が豊かになります。
  • ビープ音では、すべてが強すぎると音が割れるので、メロディラインとアクセントは強めに。ベースは弱めでクライマックスはやや強めに、といった表現をしたいと思います。
  • 休符を定義しない代わりに、M(無音)を定義しました。

長さ

定数 長さ 音符 
L1 全音符 l1
L2 2分音符 l2
L2H 符点2分音符 l2h
L4 4分音符 l4
L4H 符点4分音符 l4h
L8 8分音符 l8
L8H 符点8分音符 l8h
L16 16分音符 l16
L16H 符点16分音符 l16h
L32 32分音符 l32

サンプルコード

  • 諸々の定義をしおえたところで、サンプルコードを実行します。
  • 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ファイル

Downloadして聴く

wavファイルの波形

  • ソフトの都合なのかギザってますが、正弦波になっております。

正弦波


和音

2つの波形値を加算することで実現します。
また、合成する際に音がつぶれないようにするため数値の上限、下限を制限します。
3和音(A,B,C)を合成する場合は、A=A+BA=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番の楽譜を、ホ長調(基準の音が)で、タンタタタン タンタタタンとリズムをつけたカスタム練習です。
普段、私が練習してるので、なんとなく!

hanon1

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ファイル

Downloadして聴く


いよいよ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ファイル

Downloadして聴く


着メロを作るぞ!

曲は創聖のアクエリオンにしました。なんとなく!
爽快なリズム感がありますね。何年経ってもいいアニソンだと思います。
それにしても待てよ...打ち込むにしても、ちとこれは難しい。

サビのコード進行はこういう感じでしょうか。

なんとなくイメージが固まったところで、まずは、鬼のようにスプシに書きました。

アクエリスプシ
(つらい!! つらすぎる!!)

そして、できた3和音の楽譜がこちら。

アクエリオン

タッタッタッ と休符を活用しながら、リズム感を表現。

聴いてみよう

それではお聴きください、こちら!

Downloadして聴く


着メロに登録

wavファイルをaac形式に変換し、.m4r拡張子に変更してiPhoneに転送。
登録されたのがこちら。

chakumero.jpg


スマホに電話をかけてみよう

実際に電話を発信して鳴らしてみました。

Downloadして聴く

 
 
 
 
 
 
 
 
 
 
わい、感動や...


最後に

アドベントカレンダー担当の日が迫る中、ようやく重い腰をあげたのが3日前w
記事を書くにあたり、曲をアレンジしたり、後付で楽譜をこしらえたりするのに苦労しましたが、楽めました。
ぜひ皆さんもチャレンジしてみてください。


ソース

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?