Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
4
Help us understand the problem. What is going on with this article?

More than 1 year has passed since last update.

@ozaki_physics

Pythonでキーボードをピアノの鍵盤と想定して音を鳴らす

ピアノの鍵盤

やっほー! こんにちはー!
物理好きのオザキ(@sena0801masato)です。

今回は、キーボードをピアノの鍵盤だと想定して、Pythonを使ってパソコンから音を鳴らしてみたいと思います。
思いついたのは、アニメ『四月は君の嘘』を見て感動した時でした笑
音楽は全然詳しくないのでむしろ教えてください。

この記事の対象者

  • パソコンから音階を鳴らしてみたい!
  • 標準モジュールだけ使いたい
  • 音源をダウンロードしたくない
  • Windowsを使っている

今後改善したいこと

  • ビープ音ではなく、ダウンロードした音源を使えるようにしたい
  • 和音を出力したい
  • 長押しで単一の音を出し続けたい

音階について

音階というと、ドレミファソラシを思い浮かべると思います。
この7音に加えて黒鍵の5音を加えた計12音で周回し、1オクターブ上のドからまたスタートします。

音階の基準となる周波数はラの440Hzとされており、1オクターブ上がると周波数は2倍になります。
そのため、12半音で2倍より1半音で周波数は$2^{1/12}$倍されます。

今回は基準のラ(440Hz)が含まれるオクターブを4段階目とし、音のことをpitchとして変数名を定義しました。

ドイツ語の音階表記
C4
ド# Cis4
D4
レ# Dis4
E4
ファ F4
ファ# Fis4
G4
ソ# Gis4
ラ(440Hz) A4
ラ# Ais4
H4
高いド C5

苦労した点

キーボードの入力をどのようにプログラムが受け取るかに悩みました。毎回音程を確定させるためにエンターを押すのもピアノっぽくないので、inputは使えず、最終的にgetchを使いました。
getchは打鍵を読み取り、読み出された文字を返しますが、コンソールには何もエコーバックされず、Enterを押す必要もないというまさにピッタリの標準ライブラリがありました。
(これに行きつくまでに時間がかかりました... 普通ならPygameなどを使うみたいです。)

完成したコード

digital_piano_ver01.py
from winsound import Beep
from msvcrt import getch

# 半音のズレが何倍か定義
onestep_pitch = 2 ** (1.0/12.0)
# 音を鳴らす時間をミリ秒で定義
duration = 300

# 音を鳴らす関数を定義
def play_pitch(frequency, duration):
    Beep(frequency, duration)
# 半音上げ下げする関数を定義
def down_pitch(base_pitch):
    return int(round(base_pitch / onestep_pitch))
def up_pitch(base_pitch):
    return int(round(base_pitch * onestep_pitch))

# 各音程の周波数を定義
A4 = 440
Ais4 = up_pitch(A4)
H4 = up_pitch(Ais4)
C5 = up_pitch(H4)
Cis5 = up_pitch(C5)
D5 = up_pitch(Cis5)
Dis5 = up_pitch(D5)
E5 = up_pitch(Dis5)

Gis4 = down_pitch(A4)
G4 = down_pitch(Gis4)
Fis4 = down_pitch(G4)
F4 = down_pitch(Fis4)
E4 = down_pitch(F4)
Dis4 = down_pitch(E4)
D4 = down_pitch(Dis4)
Cis4 = down_pitch(D4)
C4 = down_pitch(Cis4)
H3 = down_pitch(C4)
Ais3 = down_pitch(H3)
A3 = down_pitch(Ais3)

# キーボードと音程を関連づける。キーボードの"d"がC4、つまりドの音など
pitchs = {}
pitchs["a"] = A3
pitchs["w"] = Ais3
pitchs["s"] = H3
pitchs["d"] = C4
pitchs["r"] = Cis4
pitchs["f"] = D4
pitchs["t"] = Dis4
pitchs["g"] = E4
pitchs["h"] = F4
pitchs["u"] = Fis4
pitchs["j"] = G4
pitchs["i"] = Gis4
pitchs["k"] = A4
pitchs["o"] = Ais4
pitchs["l"] = H4
pitchs[";"] = C5
pitchs["@"] = Cis5
pitchs[":"] = D5
pitchs["["] = Dis5
pitchs["]"] = E5

while True:
    # 入力されたキーを認識する
    bytes_keyboard = getch()
    # バイト列から文字列に変換する
    str_keyboard = bytes_keyboard.decode("utf-8")
    # 文字列を小文字に揃える
    pitch = str_keyboard.lower()
    print("音階", pitch)
    # 押したキーが辞書の中に存在するとき、音を鳴らす
    if pitch in pitchs:
        play_pitch(pitchs[pitch], duration)
    # 終了させるときはqを押す
    elif pitch == 'q':
        break

まとめ

とりあえず、ピアノっぽいものはできました。でも改善の余地はまだまだあります。とても気になっているのが和音が出せないこと、長押ししても定義したミリ秒で音が切れてしまうことです。
この2つは非同期処理を使ったらうまくいけないかなぁと思っているので、誰かできたら教えてください笑。
他のアイデアとしては、音を波形から生成したら、和音と長押しして減衰する音が表現できるのではないかと考えています。
実際に売られている電子ピアノってすごいなと思うばかりでした笑

ではでは!

参考文献

winsound --- Windows 用の音声再生インタフェース
msvcrt --- MS VC++実行時システムの有用なルーチン群
Pythonでキーイベントを取得したかったけどできなかった話←できました。
Tkinter でGUI音遊びソフト作り(4) - Python で音を出す

4
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
4
Help us understand the problem. What is going on with this article?