ピコWによるサウンド出力のすべて

ピコWのサウンド タイトルイメージ

Raspberry Pi Pico Wは、Raspberry Pi独自のマイコンチップRP2040を搭載したパワフルなマイコンボードです。RP2040ベースのマイコンボードのセレクションをチェックできます。 これ または、このまま読み進めて、Pico Wを使ったサウンド出力について知っておくべきことをすべてご覧ください。

音楽プレーヤーを作ったり、オーディオ・スペクトラム・アナライザーを作ったり、ビートボックスを作ったりしようと思ったことはありませんか?そんなあなたのための記事です!

ここでは、パルス幅変調(PWM)の世界、Pico Wの通信プロトコル、Pico Wのワイヤレス接続を活用する方法を紹介します。

ピコWイメージ

パルス幅変調

Pico W(あるいは他のマイクロコントローラー)でサウンドを再生する1つの方法は、パルス幅変調(Pulse-Width Modulation)、略してPWMです。

PWMは、デジタル信号を使ってアナログ機器を制御するために使われる手法です。マイクロコントローラーのデジタル出力はオンかオフ(0か1)にしかなりませんが、PWMを使えばアナログ信号を模倣することができます(PWMは真のアナログ信号ではないことを述べておきます)。(PWMは、電源の状態をオンとオフの間で高速に切り替え、それによって電流を制御することで実現します。

PWMについて詳しくはこちらをご覧ください!

ビデオもご覧ください:

PWMは超効率的で、電力損失がほとんどなく、非常に正確であるため、さまざまなオーディオ効果を生み出すために多くのアプリケーションで使用されている技術である。

Pico WのすべてのピンはPWMが可能です。コーディングの問題です!

PWMの動作をご覧いただくために、例を挙げて説明しよう。

プロジェクト

Pico WからPWMでオーディオを生成するデモをするために、私はオス端のワニ口ケーブルを2本、ブレッドボード、ヘッドフォンを使った。

また、ハンダ付けを避けたい場合は、ワニ口ケーブルのもう一方の端をヘッドフォンやステレオプラグに直接クリップしてみることもできる(鼓膜を守るためにも、ヘッドホンは使わないことを強くお勧めする!).

ヘッドフォンに接続されたピコW

赤いワニ口ケーブルを23番目のピンに差し込んだが、これはグラウンド・ピンだ(どのグラウンド・ピンでも構わない)。PWMはGPピンのどれからでも生成できる。上の写真でわかるように、私はGP0を使っている。

最近のヘッドフォン・セットやステレオ・ジャックには3つか4つのセクションがあり、外側の2つは左オーディオと右オーディオになっている。従って、私は赤いワニ口ケーブルを左に、黒いワニ口ケーブルを右にクリップした。

このセットアップに従うなら、ワニ口ケーブルが接触しないように注意すること。

ピコWをヘッドフォンにつないだ別の画像

物理的なセットアップが終わったら、次はオーディオファイルのダウンロードです。

.wavファイルを使用する場合は、以下の仕様に合っていることを確認してください:

1. チャンネル:モノラル(ステレオではない)

2. ビットレート:22kHz以下

3. サンプリング16ビット

CircuitPythonを使って.wavでオーディオを生成するコード例です:

# PiCockpit.com
# audio output via digital PWM

import board
# loads a wav file for audio playback
from audiocore import WaveFile
# the audiopwmio module contains classes to provide access to audio ID
from audiopwmio import PWMAudioOut

# outputs an analog audio signal by varying the PWM duty cycle under the hood
audio_out = PWMAudioOut(board.GP0)

def play_wave(filename):
    with open(filename, "rb") as wave_file:
        sample = WaveFile(wave_file)
        audio_out.play(sample, loop=True)
# outputs an analog audio signal by varying the PWM duty cycle under the hood
audio_out = PWMAudioOut(board.GP0)

def play_wave(filename):
    with open(filename, "rb") as wave_file:
        sample = WaveFile(wave_file)
        audio_out.play(sample, loop=True)

        while audio_out.playing:
            pass

# audio will loop until program interruption
play_wave("meditation-music.wav")

.wavファイルと同じ仕様を満たす必要がある.mp3ファイルを使用している場合は、このコード例を参考にしてください:

# PiCockpit.com
# audio output via digital PWM

import board
# loads an mp3 file for audio playback
from audiomp3 import MP3Decoder
# the audiopwmio module contains classes to provide access to audio IO
from audiopwmio import PWMAudioOut

# outputs an analog audio signal by varying the PWM duty cycle under the hood
audio_out = PWMAudioOut(board.GP0)

mp3 = MP3Decoder("meditation-music.mp3")

audio_out.play(mp3)
while audio_out.playing:
    pass

.mp3ファイルでさらに注意しなければならないのは、一定のビットレートです。オーディオが歪んで聞こえる場合は、サンプルレートとビットレートをさらに下げてみてください。私が得られた最高ビットレートは192kbpsでした。

CircuitPythonではなくMicroPythonを使用している場合は、デューティ・サイクルを設定し、周波数を変化させることで、PWMで3つの異なるトーンを生成する以下のコード例を参照してください。

# PiCockpit.com

from machine import Pin, PWM
from utime import sleep

# lower right corner with USB connector on top
SPEAKER_PIN = 16

# create a PWM object on this pin
speaker = PWM(Pin(SPEAKER_PIN))

# the time each tone will be on
ON_TIME = .25
# the time between tones
OFF_TIME = .1

# low tone
speaker.duty_u16(1000)
speaker.freq(300)
sleep(ON_TIME)
speaker.duty_u16(0)
sleep(OFF_TIME)

# high tone
speaker.duty_u16(1000)
speaker.freq(800)
sleep(ON_TIME)
speaker.duty_u16(0)
sleep(OFF_TIME)

# medium tone
speaker.duty_u16(1000)
speaker.freq(400)
sleep(ON_TIME)

# turn off the PWM
speaker.duty_u16(0)

通信プロトコル

Pico Wと外部パーツが互いに通信するために、様々なプロトコルが用意されています。MicroPython の machine ライブラリを使ったことがあれば、Pico W の GPIO ピンの標準通信プロトコルが GPIOZero ライブラリであることはご存知でしょう。さらに、Pico Wは他の通信プロトコルもサポートしています。

ここでは、UART、SPI、I2C、I2Sについて少しお話ししたいと思います。これらはすべて、Pico Wでオーディオを扱う上で一定の利点と欠点を提供するからです。

まず、UART(Universal Asynchronous Receiver/Transmitter)プロトコルは、理論的には実装が簡単で、必要な配線は2本だけ、信頼性も高い。しかし、UARTは通信範囲が狭く、帯域幅も限られており、他の1つのデバイスとの通信しかサポートできません。

さらに、私の調査を通して、UARTはPico Wでサウンドを再生するのに適していないことがわかりました。Pico WでUARTをオーディオ出力に使ったことがある方は、ぜひコメントで教えてください!

とくべつもくてきじぎょうたい

UARTがうまく機能しないのに対し、シリアル・ペリフェラル・インターフェース(SPI)はデータ転送速度が速く、信頼性も高い。SPIの最大の欠点は、コントローラーが1つしかないことと、4本のワイヤーを使用することです。以下のコードで動作させることができた:

# PiCockpit.com
# registers addresses for the PCM5102A module
REG_POWER_CONTROL = 0x02
REG_MODE_CONTROL = 0x03
REG_DAC_CONTROL = 0x0A
REG_DAC_VOLUME = 0x0B

# powers on the PCM5102A module
cs_pin.value(0)
spi.write(bytes([REG_POWER_CONTROL, 0x08]))
cs_pin.value(1)

# sets the mode to I2S
cs_pin.value(0)
spi.write(bytes([REG_MODE_CONTROL, 0x04]))
cs_pin.value(1)

# sets the DAC control bits
cs_pin.value(0)
spi.write(bytes([REG_DAC_CONTROL, 0x00]))
cs_spin.value(1)

# sets the DAC volume
cs_pin.value(0)
spi.write(bytes([REG_DAC_VOLUME, 0x88]))
cs_pin.value(1)

# generates a sine save for testing
sample_rate = 44100
frequency = 440
duration = 5
samples = int(sample_rate * duration)
amplitude = 0x7FFF
sine_wave = bytearray(samplex * 2)
for i in range(samples):
    value = int(amplitude * math.sin(2 * math.pi * frequency * i / sample_rate))
    sine_wave[i*2] = value & 0xFF
    sine_wave[i*2+1] = (value >> 8) & 0xFF

# writes the sine wave to the PCM5102A module
cs_pin.value(0)
spi.write(sine_wave)
cs_pin.value(1)

# Waits for the sound to finish playing
utime.sleep(duration)

# Powers off the PCM5102A module
cs_pin.value(0)
spi.write(bytes([REG_POWER_CONTROL, 0x00]))
cs_pin.value(1)

I2C

I2Cは2本のワイヤーを必要とするだけで、実装が簡単で、低消費電力である。UARTと同様、I2Cは通信範囲と帯域幅に制限がありますが、UARTと異なり、I2Cは複数のデバイスと通信できます。しかし最も重要なことは、以下のコードでI2Cを動作させることができるということだ:

# PiCockpit.com

import machine
import utime

# I2C bus
i2c = machine.I2C(0, sda=machine.Pin(0), scl=machine.Pin(1), freq=400000)

# I2C address of the PCM5102A module
i2c_address = 0x4C

# register addresses for the PCM5102A module
REG_POWER_CONTROL = 0x02
REG_MODE_CONTROL = 0x03
REG_DAC_CONTROL = 0x0A
REG_DAC_VOLUME = 0x0B

# powers on the PCM5102A module
i2c.writeto_mem(i2c_address, REG_POWER_CONTROL, b'\x08')

# sets the mode to I2S
i2c.writeto_mem(i2c_address, REG_MODE_CONTROL, b'\x04')

# sets the DAC controls
i2c.writeto_mem(i2c_address, REG_DAC_CONTROL, b'\x00')

# sets the DAC volume
i2c.writeto_mem(i2c_address, REG_DAC_VOLUME, b'\x88')

# generates a sine wave to test
sample_rate = 44100
frequency = 440
duration = 5
samples = int(sample_rate * duration)
amplitude = 0x7FFF
sine_wave = bytearray(samples * 2)
for i in range(samples):
    value = int(amplitude * math.sin(2 * math.pi * frequency * i / sample_rate))
    sine_wave[i*2] = value & 0xFF
    sine_wave[i*2+1] = (value >> 8) & 0xFF

# writes the sine wave to the PCM5102A module
i2c.writeto(i2c_address, sine_wave)

# waits for the sound to finish playing
utime.sleep(duration)

# power of the PCM5102A module
i2c.writeto_mem(i2c_address, REG_POWER_CONTROL, b'\x00')

I2Cの方が複雑でなく、1つのバスで複数のデバイスをサポートしていることから、データ転送量が少なくて済むシンプルなオーディオ・アプリケーションには、I2Cの方が適していると思われます。しかし、より高速なデータ転送レートをサポートするSPIは、より高品質なオーディオ再生に適しているかもしれません。

I2S

最後の4番目のプロトコルはInter-Integrated Sound(I2S)で、オーディオデータの転送に最適化されたプロトコルです。I2Sは高忠実度のオーディオを提供し、データを素早く転送し、信頼性が高く、使いやすいので、Pico Wからオーディオを生成するのに理想的なプロトコルです。さらに、I2Sをサポートするハードウェアコンポーネントには、以下のような幅広い選択肢があります。 ピモロニ・ピコ・オーディオ・パック.

ピモロニ・ピコ・オーディオ・パック

I2Sの音を聞くには、MicroPythonからCircuitPythonに切り替える必要がある。しかし、Pico Audio PackをPico Wのヘッダーにそっと押し込んで、Audio Packの'USB'側がPico WのマイクロUSBの方向に向いていることを確認してください。

ヘッドフォンや他のデバイスを接続する!

もうひとつ、この曲は音量を少し下げた方がいいかもしれない(後で私に感謝することになる!)。

そしてこれがサンプルコードだ:

# PiCockpit.com

import audiobusio
import audiocore
import board
import array
import time
import math

# sets up I2S, pointing at the correct pins
i2s = audiobusio.I2SOut(board.GP10, board.GP11, board.GP9)

# generates one period of sine wave
# calculates sine wave's length with floor division based on the sample rate and frequency
length = 8000 // 400

# creates an array of signed 16-bit integers based on the length
sine_wave =array.array("H", [0] * length)

for i in range(length):
# calculates sample value with the sin function
    sine_wave[i] = int(math.sin(math.pi * 2 * i / length) * (2 ** 14) + 2 ** 14)

# creates a raw audio sample buffer in memory using the audiocore library
sine_wave = audiocore.RawSample(sine_wave, sample_rate=8000)

i2s.play(sine_wave, loop=True)
time.sleep(1)
i2s.stop()

Wi-Fi

Pico Wがワイヤレス機能をアピールしているので、ワイヤレス接続を利用したものをお見せしよう。

このコードはURLから.mp3を取得し、ダウンロードが完了したらファイルから再生する:

# PiCockpit.com

import adafruit_requests
import wifi
import socketpool
import ssl
import board
import audiomp3
import audiobusio
import os

# sets up I2S for sound output
audio = audiobusio.I2SOut(board.GP10, board.GP11, board.GP9)

# url for the mp3 file to download, 1 minute mp3
mp3_url = 'https://codeskulptor-demos.commondatastorage.googleapis.com/descent/background%20music.mp3'

# chunk size for downloading the mp3 file in parts
chunk_size = 1024

# connects to SSID
wifi.radio.connect(os.getenv('WIFI_SSID'), os.getenv('WIFI_PASSWORD'))

pool = socketpool.SocketPool(wifi.radio)
requests = adafruit_requests.Session(pool, ssl.create_default_context())

# requests the url and writes the mp3 to a file
try:
    doc = requests.get(mp3_url)
    # opens the file in binary write mode
    with open('sound.mp3', 'wb') as f:
    # writes the file in chunks
    for chunk in doc.iter_content(chunk_size=chunk_size):
        f.write(chunk)
except Exception as e:
    print("Connection error:\n", str(e))

# plays the mp3 file from flash memory
try:
    # creates an mp3 object
    mp3 = audiomp3.MP3Decoder(open("sound.mp3", "rb"))
    # optimizes the sample rate for your setup before playing
    mp3.sample_rate=24000
    # mp3.bits_per_sample=50
    audio.play(mp3)

    while audio.playing:
        pass

    print("Done playing!")
except Exception as e:
    print("Error in creating mp3 or playing audio:\n", str(e))

settings.toml:

# PiCockpit.com
# SSID is your network name
# replace myssid with your wi-fi name
WIFI_SSID = "myssid"

# replace mypassword with your wifi password
WIFI_PASSWORD - "mypassword"

.mp3のダウンロードには実際のオーディオの長さよりも時間がかかるかもしれないことに注意してください。これを動作させるためには、boot.pyで書き込み権限を有効にし、ガベージコレクションによってダウンロード用のメモリを増やす必要がありました:

# PiCockpit.com

import gc
import storage

gc.collect(150000)
storage.remount("/", False)

外部ストレージを追加し、ボタンをいくつか取り出し、オンラインラジオ局から音楽をストリーミングしてみよう。これで、Raspberry Pi Pico Wでお気に入りの音楽を楽しむのを止めることはできない。

結論

Pico Wには音を出すための様々なオプションがあるが、Pico Wが提供する限られたスペースに注意してほしい。

この記事は、ジータ・Bが執筆した記事に基づくものです。

3コメント

  1. Rob Frohne on 12月 19, 2023 at 9:23 pm となります。

    UARTを使ってUSB経由でサウンドバイトを送ることができた。 Pico USBを使えば、ほぼ6Mbpsが出ます。 https://github.com/earlephilhower/arduino-pico/discussions/1765

  2. Prof Patrick Palmer on 2月 23, 2024 at 8:07 pm となります。

    、、、ーこれはーこれはー、ーこのーこれはー超ー役に立つー!お気に入りの方法」だけでなく、すべての方法を網羅しています。 PICOをほとんどの実験に使うアナログ・エレクトロニクス上級のクラスを教えるのに必要なものです。 PWMの基本的な音色から高品質のI2S機能まで!ー他のー

  3. HarloTek on 3月 9, 2024 at 11:52 am となります。

    ブルートゥースについてはどうですか?

コメントを残す