注釈

こんにちは、SunFounder Raspberry Pi & Arduino & ESP32 Enthusiasts Community on Facebookへようこそ!ここで仲間と一緒にRaspberry Pi、Arduino、ESP32について深く学びましょう。

参加する理由

  • 専門サポート: アフターサービスの問題や技術的な課題をコミュニティとチームの助けを借りて解決します。

  • 学びと共有: スキルを向上させるためのヒントやチュートリアルを交換します。

  • 独占プレビュー: 新製品の発表や予告を早期に入手できます。

  • 特別割引: 最新製品に対する特別割引を享受できます。

  • フェスティバルプロモーションとギブアウェイ: ギブアウェイやホリデープロモーションに参加できます。

👉 一緒に探究し、創造する準備ができましたか?今すぐ[ここ]をクリックして参加してください!

22. 「きらきら星」を演奏する

このレッスンでは、音楽と技術の交差点を探ります。異なる音楽の音高が周波数の変化によってどのように生成されるかを学び、この原理を使ってArduinoでブザーを制御します。レッスン終了時には、音楽の周波数の基本を理解し、Arduinoをプログラムしてシンプルなメロディを演奏できるようになります。

このレッスンで学ぶこと:

  • 音楽の音高が特定の周波数に対応する方法

  • 配列を使用して音楽のノートを保存し操作する方法

  • Arduinoをプログラムして「きらきら星」を演奏する方法

音楽の周波数と音の生成

_images/7_sound.png

様々な楽器は、周波数を変えることで異なる音高を生成します。 例えば、ピアノでは鍵盤を叩くと対応する弦が振動し、特定の音高を生成します。 科学者と音楽家はこれらの振動周波数を正確に測定することで、様々な音楽調律方法と音高基準を開発しました。

Arduinoや他のマイクロコントローラを制御してブザーに電気信号を送ると、ブザーのダイアフラムが信号の周波数に応じて高速に振動し、音を生成します。例えば、440 Hzに設定された信号は、標準の音楽音高「A4」を生成し、音楽調律の基準点となります。 周波数が上昇または下降すると、生成される音高も上昇または下降し、音楽作曲において低音から高音までの範囲を実現します。

西洋音楽では、オクターブはCからBまでの12音(半音)を含み、次に高いCに戻ります。

例えば、中央のC(通常C4と呼ばれる)の周波数は約261.63 Hzです。ノートの周波数は次の式を使用して計算できます:

_images/7_music_format.png

ここで、f_0は基準音高(通常はA4、周波数440Hz)で、nは基準音高から目標音高までの半音ステップ数(正の数は上昇、負の数は下降)です。 この式を使用して、任意のノートの周波数を計算できます。

ここに周波数表のセットがあります:

  • C (C4): 262 Hz (実際には261.63 Hzに近く、262に丸められています)

  • D (D4): 294 Hz

  • E (E4): 330 Hz

  • F (F4): 349 Hz

  • G (G4): 392 Hz

  • A (A4): 440 Hz

  • B (B4): 494 Hz

次に、Arduinoとブザーを使って「きらきら星」の最初の2行を演奏してみましょう:

注釈

「きらきら星」のメロディはシンプルなノートの組み合わせに基づいており、 フランスの作曲家ウォルフガング・アマデウス・モーツァルトが作曲した「Ah vous dirai-je, Maman」のバリエーションに基づいています。 初心者が学ぶのに非常に適しています。

以下に「きらきら星」の基本的な楽譜を示します。

C C G G A A G
F F E E D D C
G G F F E E D
G G F F E E D
C C G G A A G
F F E E D D C

回路の構築

必要な部品

1 * Arduino Uno R3

1 * ブレッドボード

1 * パッシブブザー

ジャンパワイヤー

list_uno_r3

list_breadboard

list_passive_buzzer

list_wire

1 * USBケーブル

list_usb_cable

ステップバイステップの構築

このレッスンでは、レッスン21と同じ回路を使用します。

_images/16_morse_code.png

Code Creation - Array

  1. Arduino IDEを開き、「ファイル」メニューから「新しいスケッチ」を選択して新しいプロジェクトを開始します。

  2. Ctrl + S を押すか「保存」をクリックして、スケッチを Lesson22_Array として保存します。

  3. 次に、コードの最初に配列を作成し、「きらきら星」の音符をその配列に格納します。

// Cメジャースケールの音符の周波数を定義する(ミドルCから始まるオクターブ)
int c = 262;
int d = 294;
int e = 330;
int f = 349;
int g = 392;
int a = 440;
int b = 494;
int C = 523;  // 高いC

// メロディの音符の並びを含む配列を定義
int melody[] = { c, c, g, g, a, a, g, f, f, e, e, d, d, c, g, g, f, f, e, e, d, g, g, f, f, e, e, d, c, c, g, g, a, a, g, f, f, e, e, d, d, c };

配列は、Arduinoプログラミングにおいて同じ型の複数の要素を格納するためのデータ構造です。 非常に基本的で強力なツールであり、適切に使用するとプログラムの効率とパフォーマンスを大幅 に向上させることができます。配列は、整数、浮動小数点数、文字などの型の要素を格納できます。

変数や関数を作成するのと同様に、配列を作成する際も配列の型と配列名を指定します - int melody[]

{} の中の要素は配列要素と呼ばれ、インデックス0から始まります。そのため、 melody[0] は最初の c(262) に、 melody[13]c(262) に等しいです。

  1. 次に、 melody[] 配列のインデックス0と13の要素をシリアルモニターに表示します。

// Cメジャースケールの音符の周波数を定義する(ミドルCから始まるオクターブ)
int c = 262;
int d = 294;
int e = 330;
int f = 349;
int g = 392;
int a = 440;
int b = 494;
int C = 523;  // 高いC

// メロディの音符の並びを含む配列を定義
int melody[] = { c, c, g, g, a, a, g, f, f, e, e, d, d, c, g, g, f, f, e, e, d, g, g, f, f, e, e, d, c, c, g, g, a, a, g, f, f, e, e, d, d, c };

void setup() {
  // 一度だけ実行するセットアップコードをここに記述
  Serial.begin(9600);  // 9600ボーレートでシリアル通信を初期化
  Serial.println(melody[0]);
  Serial.println(melody[13]);
}

void loop() {
  // 繰り返し実行するメインコードをここに記述
}
  1. コードをArduino Uno R3にアップロードした後、シリアルモニターを開くと、2つの262が表示されます。

262
262
  1. 配列 melody[] の各要素を1つずつ表示したい場合は、まず配列の長さを知る必要があります。 sizeof() 関数を使用して配列の要素数を計算できます。

void setup() {
  // 一度だけ実行するセットアップコードをここに記述
  Serial.begin(9600);  // 9600ボーレートでシリアル通信を初期化
  int notes = sizeof(melody) / sizeof(melody[0]); // 要素数を計算
}
  • sizeof(melody) は配列内の全要素が使用する総バイト数を返します。

  • sizeof(melody[0]) は配列の1要素が使用するバイト数を返します。

  • 総バイト数を要素あたりのバイト数で割ることで、配列の全要素数が得られます。

  1. 次に、 for 文を使用して melody[] 配列の要素を繰り返し処理し、 Serial.println() 関数を使用してそれらを表示します。

// Cメジャースケールの音符の周波数を定義する(ミドルCから始まるオクターブ)
int c = 262;
int d = 294;
int e = 330;
int f = 349;
int g = 392;
int a = 440;
int b = 494;
int C = 523;  // 高いC

// メロディの音符の並びを含む配列を定義
int melody[] = { c, c, g, g, a, a, g, f, f, e, e, d, d, c, g, g, f, f, e, e, d, g, g, f, f, e, e, d, c, c, g, g, a, a, g, f, f, e, e, d, d, c };


void setup() {
  // 一度だけ実行するセットアップコードをここに記述
  Serial.begin(9600);                              // 9600ボーレートでシリアル通信を初期化
  int notes = sizeof(melody) / sizeof(melody[0]);  // 要素数を計算
  // メロディ配列内の各音符をループ処理
  for (int i = 0; i < notes; i = i + 1) {
    // 各音符の周波数をシリアルモニターに表示
    Serial.println(melody[i]);
  }
}

void loop() {
  // 繰り返し実行するメインコードをここに記述
}
  1. コードをArduino Uno R3にアップロードした後、シリアルモニターを開き、配列 melody[] の要素が1つずつ印刷されるのを確認します。

262
262
392
392
440
440
392
349
349
330
...

質問

配列の要素に対して操作を行うこともできます。例えば、 Serial.println(melody[i] * 1.3); に変更すると、どのようなデータが得られるでしょうか?その理由も考えてみましょう。

コード作成 - きらきら星を演奏

配列を作成し、配列要素にアクセスし、それらの長さと操作を計算する方法を理解したところで、これらの知識を活用して、パッシブブザーを使って「きらきら星」を演奏するプログラムを作成しましょう。

  1. 以前に保存したスケッチ Lesson22_Array を開きます。

  2. 「ファイル」メニューから「名前を付けて保存」を選択し、 Lesson22_Little_Star と名前を変更して「保存」をクリックします。

  3. 最初にブザーピンを定義します。

const int buzzerPin = 9;  // ブザーのピンを定数として9に割り当てます
  1. 次に、音符の持続時間を格納する別の配列を作成します。

// 音符の並びとその持続時間をミリ秒単位で設定
int melody[] = { c, c, g, g, a, a, g, f, f, e, e, d, d, c, g, g, f, f, e, e, d, g, g, f, f, e, e, d, c, c, g, g, a, a, g, f, f, e, e, d, d, c };
int noteDurations[] = { 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000 };
  1. 次に、コードの一部を void setup() から void loop() に移動します。

void setup() {
  // 一度だけ実行するセットアップコードをここに記述
  Serial.begin(9600);                              // 9600ボーレートでシリアル通信を初期化
}

void loop() {
  // 繰り返し実行するメインコードをここに記述
  int notes = sizeof(melody) / sizeof(melody[0]);  // 要素数を計算
  // メロディ配列内の各音符をループ処理
  for (int i = 0; i < notes; i = i + 1) {
    // 各音符の周波数をシリアルモニターに表示
    Serial.println(melody[i]);
  }
}
  1. for 文の中で、印刷コードをコメントアウトし、 tone() 関数を使用して音符を演奏します。

void loop() {
  // 繰り返し実行するメインコードをここに記述
  int notes = sizeof(melody) / sizeof(melody[0]);  // 要素数を計算
  // メロディ配列内の各音符をループ処理
  for (int i = 0; i < notes; i = i + 1) {
    // 各音符の周波数をシリアルモニターに表示
    // Serial.println(melody[i]);

    tone(buzzerPin, melody[i], noteDurations[i]);  // 音符を演奏
  }
}
  1. 各音符を再生した後、メロディーをより自然にするために、2つの音符の間に短い間隔を設けます。ここでは、音符の持続時間に1.30を掛けて間隔を計算し、メロディーが急ぎすぎないようにします。

void loop() {
  // 繰り返し実行するメインコードをここに記述
  int notes = sizeof(melody) / sizeof(melody[0]);  // 要素数を計算
  // メロディ配列内の各音符をループ処理
  for (int i = 0; i < notes; i = i + 1) {
    // 各音符の周波数をシリアルモニターに表示
    // Serial.println(melody[i]);

    tone(buzzerPin, melody[i], noteDurations[i]);  // 音符を再生
    delay(noteDurations[i] * 1.30);                // 音符の変更前に待機
  }
}
  1. 現在のピンからの音出力を停止するために noTone() 関数を使用します。これは、各音符が次の音符と混ざらずに明確に再生されるようにするために必要なステップです。

void loop() {
  // 繰り返し実行するメインコードをここに記述
  int notes = sizeof(melody) / sizeof(melody[0]);  // 要素数を計算
  // メロディ配列内の各音符をループ処理
  for (int i = 0; i < notes; i = i + 1) {
    // 各音符の周波数をシリアルモニターに表示
    // Serial.println(melody[i]);

    tone(buzzerPin, melody[i], noteDurations[i]);  // 音符を再生
    delay(noteDurations[i] * 1.30);                // 音符の変更前に待機
    noTone(buzzerPin);                             // 音符の再生を停止
  }
}
  1. 完全なコードは以下の通りで、このコードをArduino Uno R3にアップロードすると、「きらきら星」をブザーで聞くことができます。

int buzzerPin = 9;  // ブザーのピンを定数として9に割り当てます

// ドの音階の周波数を定義します(中央のドから始まるオクターブ)
int c = 262;
int d = 294;
int e = 330;
int f = 349;
int g = 392;
int a = 440;
int b = 494;
int C = 523;  // 高いド

// 音符の並びとその持続時間をミリ秒単位で設定
int melody[] = { c, c, g, g, a, a, g, f, f, e, e, d, d, c, g, g, f, f, e, e, d, g, g, f, f, e, e, d, c, c, g, g, a, a, g, f, f, e, e, d, d, c };
int noteDurations[] = { 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000, 500, 500, 500, 500, 500, 500, 1000 };

void setup() {
  // 一度だけ実行するセットアップコードをここに記述
  Serial.begin(9600);                              // 9600ボーレートでシリアル通信を初期化
}

void loop() {
  // 繰り返し実行するメインコードをここに記述
  int notes = sizeof(melody) / sizeof(melody[0]);  // 要素数を計算
  // メロディ配列内の各音符をループ処理
  for (int i = 0; i < notes; i = i + 1) {
    // 各音符の周波数をシリアルモニターに表示
    // Serial.println(melody[i]);

    tone(buzzerPin, melody[i], noteDurations[i]);  // 音符を再生
    delay(noteDurations[i] * 1.30);                // 音符の変更前に待機
    noTone(buzzerPin);                             // 音符の再生を停止
  }
}
  1. 最後に、コードを保存して作業スペースを整頓することを忘れないでください。

質問

回路のパッシブブザーをアクティブブザーに置き換えると、「きらきら星」を再生できるでしょうか?その理由を考えてみましょう。

まとめ

今回のレッスンでは、配列を使用してデータを保存し、配列の長さを計算し、配列内の要素にインデックスを付け、各要素に対して操作を行う方法を学びました。音符の周波数とタイミングの間隔を配列に格納し、それらをforループで反復処理することで、「きらきら星」をパッシブブザーで演奏するプログラムを成功させました。

さらに、 noTone() 関数を使用して音符の再生を一時停止する方法も学びました。

このレッスンでは、配列操作とプログラミングにおける制御構造の理解を深めただけでなく、これらの概念を電子部品を使用して音楽を作成するためにどのように適用できるかを示し、理論的知識を実践的な応用に結びつける楽しく魅力的な方法を体験しました。