Go言語で音を鳴らす3

背景

PythonとGoはCを介して連携ができるみたいなので,これを使えばPythonの優れた楽器的なライブラリを使えて,Goでシンセサイザーとかを簡単に鳴らせるんじゃないかと考えた.

使うサイトはこれら



環境

  • macOS Big Sur ver. 11.5.1(20G80)  -> 12.6 (21G115)

  • MacBook Air M1, 2020

  • メモリ 8GB

  • Homebreq ver.3.6.3(git revision 86ac48865b7; last commit 2022-10-02)

  • Python 3.10.6

  • pip 22.2.2 from /opt/homebrew/lib/python3.10/site-packages/pip (python 3.10)

  • pyaudio-0.2.12


Step1 Python3, pipのインストール


MacBook-Air ~ % brew install python3

これでpython3のインストールは完了する.

MacBook-Air ~ % curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 2500k  100 2500k    0     0  5982k      0 --:--:-- --:--:-- --:--:-- 5968k

MacBook-Air ~ % python3 get-pip.py
Defaulting to user installation because normal site-packages is not writeable
Collecting pip
  Downloading pip-22.2.2-py3-none-any.whl (2.0 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.0/2.0 MB 1.7 MB/s eta 0:00:00
Installing collected packages: pip
  WARNING: The scripts pip, pip3 and pip3.8 are installed in '/Users/usrname/Library/Python/3.8/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed pip-22.2.2

[notice] A new release of pip available: 20.2.3 -> 22.2.2
[notice] To update, run: /Library/Developer/CommandLineTools/usr/bin/python3 -m pip install --upgrade pip

WARNING: The scripts pip, pip3 and pip3.8 are installed in '/Users/usrname/Library/Python/3.8/bin' which is not on PATH.
って書いてあるからそのディレクトリを見てみよう.

MacBook-Air Library % cd Python
MacBook-Air Python % ls
3.8
MacBook-Air Python % cd 3.8
MacBook-Air 3.8 % ls
bin	lib
MacBook-Air 3.8 % cd bin
MacBook-Air bin % ls
pip	pip3	pip3.8

ちゃんとあった.

一番下のやつの[notice]の
/Library/Developer/CommandLineTools/usr/bin 
のところにもなんかpipいるけどひとまず無視.


pipもインストールできたっぽい.
そしたらPATHを通そう.PATHについての詳しい説明はこのサイトに投げる.


MacBook-Air ~ % ls .zshrc
.zshrc
MacBook-Air ~ % open .zshrc

export PATH=$PATH:/Users/usrname/Library/Python/3.8/binを.zshrcに追加.

MacBook-Air ~ % source .zshrc  


MacBook-Air ~ % pip3 -V                 
pip 22.2.2 from /opt/homebrew/lib/python3.10/site-packages/pip (python 3.10)

pip3にPATHが繋がっているっぽいことも確認できた.



Step2 Synthesizerの簡単な使い方

インストール

で,実際にpip3 install synthesizerしてみたらなんかエラー発生して怒られた
結構長くめんどくなりそうなので別の記事にまとめようと思う.


まぁ簡単に言うとmacOSのバージョンの問題だったから,最新のものにしとけば大丈夫.環境のところに矢印つけて変化させてたのはこれが理由.


MacBook-Air ~ % pip3 install synthesizer
Collecting synthesizer
  Using cached synthesizer-0.2.0.tar.gz (5.4 kB)
  Preparing metadata (setup.py) ... done
Collecting enum34>=1.1.6
  Using cached enum34-1.1.10-py3-none-any.whl (11 kB)
Collecting numpy>=1.13.3
  Using cached numpy-1.23.3-cp310-cp310-macosx_11_0_arm64.whl (13.3 MB)
Collecting scipy>=0.19.1
  Downloading scipy-1.9.1-cp310-cp310-macosx_12_0_arm64.whl (29.9 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 29.9/29.9 MB 1.8 MB/s eta 0:00:00
Building wheels for collected packages: synthesizer
  Building wheel for synthesizer (setup.py) ... done
  Created wheel for synthesizer: filename=synthesizer-0.2.0-py2.py3-none-any.whl size=5879 sha256=15890d8e09df9a0f8dffa533f7b3c65801add56646aca44b5c13811e677b3147
  Stored in directory: /Users/usrname/Library/Caches/pip/wheels/00/07/f7/f6919b025939c72812e61e71bf3316e38bb4f740f661d70a18
Successfully built synthesizer
Installing collected packages: enum34, numpy, scipy, synthesizer
Successfully installed enum34-1.1.10 numpy-1.23.3 scipy-1.9.1 synthesizer-0.2.0


sin波の出力

from synthesizer import Player, Synthesizer, Waveform

# オーディオデバイス出力用クラス
player = Player()
# 引数を指定しなければデフォルトの出力デバイスが選択されます
player.open_stream()

# player.enumerate_device() でデバイスの一覧を表示できます
# player.open_stream(device_name="UA-25EX") のように出力デバイスを指定可能です

synth = Synthesizer(osc1_waveform=Waveform.sine, osc1_volume=1.0, use_osc2=False)
player.play_wave(synth.generate_constant_wave(440.0, 1.0))

でスピーカーを鳴らそうとしたら

MacBook-Air synthe % python3 synthe.py
Traceback (most recent call last):
  File "/Users/usrname/synthe/synthe.py", line 6, in <module>
    player.open_stream()
  File "/opt/homebrew/lib/python3.10/site-packages/synthesizer/player.py", line 38, in open_stream
    raise ImportError("Failed to import pyaudio, please install pyaudio")
ImportError: Failed to import pyaudio, please install pyaudio

って怒られた.どうやらpyaudioもインストールしなくてはいけないらしい.


pyaudioのインストール

brew updateで躓いたので,それは別の記事にまとめる.

躓いてなければ.

brew update
brew install portaudio
pip3 install pyaudio

でできると思う.

でbrew updateを解決したから進んでみると

MacBook-Air ~ % brew install portaudio
Warning: portaudio 19.7.0 is already installed and up-to-date.
To reinstall 19.7.0, run:
  brew reinstall portaudio


portaudioのinstallについては問題なし,というか既に入ってたみたいに言われた.

でもpyaudioのinstallでも怒られてしまった.
今まで同様,別の記事にまとめる.

結果的にはpyaudio-0.2.12がインストールできた.

ちゃんと

MacBook-Air synthe % python3 synthe.py

したら,ポーって音がでた.
これでpyaudioを使ってシンセを鳴らすことまでは可能になった.


Step3 GoからPythonを呼ぶ

ひとまず,サイト内で完成コードとして載せられてるやつをいじっていく.

package main

// #cgo CFLAGS: -I/Library/Frameworks/Python.framework/Versions/3.9/include/python3.9
// #cgo LDFLAGS: -L/Library/Frameworks/Python.framework/Versions/3.9/lib -lpython3.9
// #include <Python.h>
import "C"

func main() {
    //  最後にPythonインタプリタ終了
    defer C.Py_Finalize()
    // Pythonインタプリタの初期化
    C.Py_Initialize()
    // GoのstringをCのcharに型変換(変換しないとPyRun_SimpleStringに型合ってないよって怒られる)
    // cannot use "print(\"Hello, World!\")" (type string) as type *_Ctype_char in argument to _Cfunc_PyRun_SimpleString
    pyCodeStr := `print("Hello, World!")`
    pyCodeChar := C.CString(pyCodeStr)
    // Pythonコードを文字列として受け取ってインタプリタ上で実行
    C.PyRun_SimpleString(pyCodeChar)
}


そのままvscodeにコピペすると
fatal error: 'Python.h' file not found go
って<Pythin.h>のところに赤波線くらう.

まず,cgoが指してるパスのところを見ると,


Python.frameworkなんてないんだが.

cgoでは、Cコンパイラに、使用するCライブラリ(及びそのヘッダファイル)の位置を教えてあげる必要があります。
ヘッダファイルPython.hやその実体であるCライブラリはPythonを落としたディレクトリに存在しますので、その位置を伝える必要があるというわけです。

誤解を恐れずばっくり言うと、

#cgo CFLAGS:でヘッダファイルが存在する位置等をコンパイラに伝えることができます
#cgo LDFLAGS:でCライブラリが存在する位置等をコンパイラに伝えることができます。-LでCライブラリのディレクトリを指定し、-lで使用するライブラリを指定します。Cライブラリはlibfoo.dylibみたいなファイル名になるのですが4、先頭のlibと拡張子を除いたライブラリ名を指定してあげる必要があります

https://qiita.com/kawamou/items/4235e37441e22205de34

らしい.じゃあ自分のPythonがある位置に書き換えないと.
/Library/Developer/CommandLineTools/usr/binに置いてあった気がする.


あった.
LDFLAGSのlibの方は/Users/usrname/Library/Python/3.9にあった.

MacBook-Air ~ % cd Library/Python
MacBook-Air Python % ls
3.8	3.9
MacBook-Air Python % cd 3.9
MacBook-Air 3.9 % ls
bin	lib
MacBook-Air 3.9 % cd lib
MacBook-Air lib % ls -A
python

-lpython3.9ってなんのことなんだろう.
とりあえずもうちょい深くまでディレクトリ掘ってみる.

MacBook-Air lib % cd python
MacBook-Air python % ls
site-packages
MacBook-Air python % cd site-packages
MacBook-Air site-packages % ls
pip			pip-22.2.2.dist-info
MacBook-Air site-packages % cd pip
MacBook-Air pip % ls
__init__.py		__pip-runner__.py	_vendor
__main__.py		_internal		py.typed

特になさそう.
-lpython3.9で検索しても全然ヒットしないし,とりあえず pythonで進めてみよう.

package main

// #cgo CFLAGS: -I/Library/Developer/CommandLineTools/usr/bin/python3.9
// #cgo LDFLAGS: -L/Users/usrname/Library/Python/3.9/lib python
// #include <Python.h>
import "C"

func main() {
    //  最後にPythonインタプリタ終了
    defer C.Py_Finalize()
    // Pythonインタプリタの初期化
    C.Py_Initialize()
    // GoのstringをCのcharに型変換(変換しないとPyRun_SimpleStringに型合ってないよって怒られる)
    // cannot use "print(\"Hello, World!\")" (type string) as type *_Ctype_char in argument to _Cfunc_PyRun_SimpleString
    pyCodeStr := `print("Hello, World!")`
    pyCodeChar := C.CString(pyCodeStr)
    // Pythonコードを文字列として受け取ってインタプリタ上で実行
    C.PyRun_SimpleString(pyCodeChar)
}

修正したら<Python.h>の波線は消えた.

ビルドしてみよう.

MacBook-Air sound_wav % go build -o main
go build sound_wav: invalid flag in #cgo LDFLAGS: python

LDFLAGS,ちょうどわからなかったところが怒られた.

怒られ内容で検索かけたらこれが見つかった.
なんか-lをつけましょうって言われてる.
とりあえずつけてやってみよう.

// #cgo CFLAGS: -I/Library/Developer/CommandLineTools/usr/bin/python3.9
// #cgo LDFLAGS: -L/Users/usrname/Library/Python/3.9/lib -lpython
// #include <Python.h>
MacBook-Air sound_wav % go build -o main
# sound_wav
./main.go:5:11: fatal error: 'Python.h' file not found
 #include <Python.h>
          ^~~~~~~~~~
1 error generated.

なんか違う怒られ方したんだが…
さっきのでは出てこなくてここで出てきてる.

降参して質問してきた.


まとめると,cgoのPATHがやっぱり違ってて,次のコマンドで「Pythonがライブラリをインポートしたときにそのライブラリがあるかどうか探す順番を表示」させる.

MacBook-Air ~ % python3 -c "import sys, pprint; pprint.pprint(sys.path)"
['',
'/opt/homebrew/Cellar/python@3.10/3.10.6_2/Frameworks/Python.framework/Versions/3.10/lib/python310.zip',
'/opt/homebrew/Cellar/python@3.10/3.10.6_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10',
'/opt/homebrew/Cellar/python@3.10/3.10.6_2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/lib-dynload',
'/opt/homebrew/lib/python3.10/site-packages']

その探しに行くPATHの順番はsys.pathの値になっているのでこのようなコマンドになる.


で,この結果と完成コードとして載ってたものを見比べて次のようにした.

// #cgo CFLAGS: -I/opt/homebrew/Cellar/python@3.10/3.10.6_2/Frameworks/Python.framework/Versions/3.10/include/python3.10
// #cgo LDFLAGS: -L/opt/homebrew/Cellar/python@3.10/3.10.6_2/Frameworks/Python.framework/Versions/3.10/lib -lpython3.10
// #include <Python.h>


これでビルドしてみると,今までのPython.hが見つからないというエラーは消えた,

ただし別のエラーが生えた.

MacBook-Air sound_wav % go build -o main
# sound_wav
Undefined symbols for architecture arm64:
  "_Pa_CloseStream", referenced from:
      _main in _x003.o
  "_Pa_GetDefaultOutputDevice", referenced from:
      _main in _x003.o
  "_Pa_GetDeviceInfo", referenced from:
      _main in _x003.o
  "_Pa_Initialize", referenced from:
      _main in _x003.o
  "_Pa_OpenStream", referenced from:
      _main in _x003.o
  "_Pa_StartStream", referenced from:
      _main in _x003.o
  "_Pa_StopStream", referenced from:
      _main in _x003.o
  "_Pa_Terminate", referenced from:
      _main in _x003.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)


この記事が気に入ったらサポートをしてみませんか?