LoginSignup
0
1

More than 1 year has passed since last update.

PythonからWindows APIを使ってサウンド再生

Last updated at Posted at 2021-11-01

はじめに

ctypesパッケージを使ってWindows APIを呼び出すサンプルとしてサウンドを再生してみた。

ソース

短縮版

winsound版

beep3.py
import struct
import winsound

data = b"\x90\x90\x70\x70" * 500 + b"\x90\x90\x90\x90\x70\x70\x70\x70" * 250

buf = struct.pack("<4sL4s" "4sLHHLLHH" "4sL",
    b"RIFF", 36 + len(data), b"WAVE",
    b"fmt ", 16, 1, 1, 8000, 8000, 1, 8,
    b"data", len(data)) + data
winsound.PlaySound(buf, winsound.SND_MEMORY)

winmm.sndPlaySound版

beep4.py
import struct
import ctypes
winmm = ctypes.windll.winmm

SND_SYNC = 0x0000
SND_MEMORY = 0x0004

data = b"\x90\x90\x70\x70" * 500 + b"\x90\x90\x90\x90\x70\x70\x70\x70" * 250

buf = struct.pack("<4sL4s" "4sLHHLLHH" "4sL",
    b"RIFF", 36 + len(data), b"WAVE",
    b"fmt ", 16, 1, 1, 8000, 8000, 1, 8,
    b"data", len(data)) + data
winmm.sndPlaySoundA(buf, SND_MEMORY | SND_SYNC)

winmm方式

beep.py
from ctypes import *
from ctypes.wintypes import *
import time

winmm = windll.winmm

WAVE_FORMAT_PCM = 1
WAVE_MAPPER = UINT(-1)
CALLBACK_NULL = 0x0000_0000

class WAVEFORMATEX(Structure):
    _fields_ = [
        ("wFormatTag", WORD),
        ("nChannels", WORD),
        ("nSamplesPerSec", DWORD),
        ("nAvgBytesPerSec", DWORD),
        ("nBlockAlign", WORD),
        ("wBitsPerSample", WORD),
        ("cbSize", WORD),
    ]

class WAVEHDR(Structure):
    _fields_ = [
        ("lpData", LPSTR),
        ("dwBufferLength", DWORD),
        ("dwBytesRecorded", DWORD),
        ("dwUser", PDWORD),
        ("dwFlags", DWORD),
        ("dwLoops", DWORD),
        ("lpNext", LPVOID),
        ("reserved", PDWORD),
    ]

def main():
    data = b"\x90\x90\x70\x70" * 500 + b"\x90\x90\x90\x90\x70\x70\x70\x70" * 250

    hwo = HANDLE()
    wfx = WAVEFORMATEX()
    wfx.wFormatTag = WAVE_FORMAT_PCM
    wfx.nChannels = 1
    wfx.nSamplesPerSec = 8000
    wfx.nAvgBytesPerSec = 8000
    wfx.nBlockAlign = 1
    wfx.wBitsPerSample = 8
    wfx.cbSize = 0
    winmm.waveOutOpen(byref(hwo), WAVE_MAPPER, wfx, 0, 0, CALLBACK_NULL)

    wh = WAVEHDR()
    wh.lpData = data
    wh.dwBufferLength = len(data)
    winmm.waveOutPrepareHeader(hwo, byref(wh), sizeof(wh))
    winmm.waveOutWrite(hwo, byref(wh), sizeof(wh))
    time.sleep(0.6)

    winmm.waveOutReset(hwo)
    winmm.waveOutUnprepareHeader(hwo, byref(wh), sizeof(wh))
    winmm.waveOutClose(hwo)

main()

winsound方式

beep2.py
import io
import winsound
from ctypes import LittleEndianStructure
from ctypes.wintypes import BYTE, WORD, DWORD
FOURCC = BYTE * 4

WAVE_FORMAT_PCM = 0x0001

class WAVEFORMATEX(LittleEndianStructure):
    _fields_ = [
        ("wFormatTag", WORD),
        ("nChannels", WORD),
        ("nSamplesPerSec", DWORD),
        ("nAvgBytesPerSec", DWORD),
        ("nBlockAlign", WORD),
        ("wBitsPerSample", WORD),
    ]

class WAVEFILEHEADER(LittleEndianStructure):
    _fields_ = [
        ("RIFF_ckid", FOURCC),
        ("RIFF_cksize", DWORD),
        ("fccType", FOURCC),
        ("fmt_ckid", FOURCC),
        ("fmt_cksize", DWORD),
        ("wfx", WAVEFORMATEX),
        ("data_ckid", FOURCC),
        ("data_cksize", DWORD),
    ]

def main():
    data = b"\x90\x90\x70\x70" * 500 + b"\x90\x90\x90\x90\x70\x70\x70\x70" * 250
    data_len = len(data)

    wfx = WAVEFORMATEX()
    wfx.wFormatTag = WAVE_FORMAT_PCM
    wfx.nChannels = 1
    wfx.nSamplesPerSec = 8000
    wfx.nAvgBytesPerSec = 8000
    wfx.nBlockAlign = 1
    wfx.wBitsPerSample = 8

    wfh = WAVEFILEHEADER()
    wfh.RIFF_ckid = FOURCC(*b"RIFF")
    wfh.RIFF_cksize = 36 + data_len
    wfh.fccType = FOURCC(*b"WAVE")
    wfh.fmt_ckid = FOURCC(*b"fmt ")
    wfh.fmt_cksize = 16
    wfh.wfx = wfx
    wfh.data_ckid = FOURCC(*b"data")
    wfh.data_cksize = data_len

    buf = io.BytesIO()
    buf.write(wfh)
    buf.write(data)

    winsound.PlaySound(buf.getvalue(), winsound.SND_MEMORY)

main()

おまけ

coin.py
import struct
import winsound

SAMPLE_RATE = 48000
theta = 0

def main():
    data = bytearray()
    tone(data, 83, .1)
    tone(data, 88, 1)

    wav = struct.pack("<4sL4s" "4sLHHLLHH" "4sL",
        b"RIFF", 36 + len(data), b"WAVE",
        b"fmt ", 16, 1, 1, SAMPLE_RATE, SAMPLE_RATE, 1, 8,
        b"data", len(data)) + data
    winsound.PlaySound(wav, winsound.SND_MEMORY)

def tone(data, note, sec):
    global theta
    freq = 440 * 2**((note - 69) / 12)
    len = int(SAMPLE_RATE * sec)
    for i in range(len):
        h = int(16 * (1 - i / SAMPLE_RATE))
        t = theta / SAMPLE_RATE
        d = 0x80 + (h if t < .5 else -h)
        data.append(d)
        theta = (theta + freq) % SAMPLE_RATE

main()
0
1
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
0
1