1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonで任意の周波数の音を鳴らし、波形を可視化するスクリプト

Posted at

Pythonを使って

  • 特定の周波数の音を鳴らす
  • 複数周波数を同時に鳴らして和音を作る
  • その波形をリアルタイムに可視化する
    ということをやってみます。

できること

このスクリプトでできることは以下の通りです。

  • 任意の周波数(例: 440Hz)を再生
  • 複数の周波数を同時に再生(和音)
  • 再生している音の 波形をMatplotlibで可視化
    • 全体波形
    • 最初の数ミリ秒を拡大表示

実行例

単音(A4 = 440Hz)

python play_frequency.py 440

和音(Aメジャー: A-C#-E)

python play_frequency.py 440,554,659

再生時間を指定

python play_frequency.py 440 3.0

使用ライブラリ

pip install numpy sounddevice matplotlib
  • numpy: 波形生成
  • sounddevice: 音声再生
  • matplotlib: 波形の可視化

コード例

#!/usr/bin/env python3
"""
Simple script to play specific frequency sounds.
Usage:
  Single frequency: python play_frequency.py [frequency_hz] [duration_seconds]
  Multiple frequencies: python play_frequency.py freq1,freq2,freq3 [duration_seconds]

Examples:
  python play_frequency.py 440          # Play A4 note
  python play_frequency.py 440,554,659  # Play A major chord
"""

import numpy as np
import sounddevice as sd
import sys
import matplotlib.pyplot as plt


def visualize_waveform(wave, sample_rate, duration, title="Waveform"):
    """
    Display a visualization of the waveform.

    Args:
        wave: The audio waveform data
        sample_rate: Sample rate in Hz
        duration: Duration in seconds
        title: Title for the plot
    """
    # Create time array for x-axis
    t = np.linspace(0, duration, len(wave), endpoint=False)

    # Set up the plot
    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8))

    # Plot 1: Full waveform
    ax1.plot(t, wave, color='blue', linewidth=0.5)
    ax1.set_xlabel('Time (s)')
    ax1.set_ylabel('Amplitude')
    ax1.set_title(f'{title} - Full Waveform')
    ax1.grid(True, alpha=0.3)
    ax1.set_xlim(0, duration)

    # Plot 2: Zoomed view (first 0.05 seconds to see individual waves)
    zoom_duration = min(0.05, duration)
    zoom_samples = int(sample_rate * zoom_duration)
    t_zoom = t[:zoom_samples]
    wave_zoom = wave[:zoom_samples]

    ax2.plot(t_zoom, wave_zoom, color='red', linewidth=1.5, marker='o', markersize=3)
    ax2.set_xlabel('Time (s)')
    ax2.set_ylabel('Amplitude')
    ax2.set_title(f'{title} - Zoomed View (First {zoom_duration}s)')
    ax2.grid(True, alpha=0.3)
    ax2.set_xlim(0, zoom_duration)

    plt.tight_layout()
    plt.show()


def play_frequency(frequency=440, duration=2.0, sample_rate=44100, amplitude=0.3):
    """
    Play a pure tone at the specified frequency.

    Args:
        frequency: Frequency in Hz (default: 440 Hz, which is A4 note)
        duration: Duration in seconds (default: 2.0)
        sample_rate: Sample rate in Hz (default: 44100)
        amplitude: Volume level between 0.0 and 1.0 (default: 0.3)
    """
    # Generate time array
    t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)

    # Generate sine wave
    wave = amplitude * np.sin(2 * np.pi * frequency * t)
    wave *= 10

    # Play the sound
    print(f"Playing {frequency} Hz for {duration} seconds...")

    # Start playing the sound (non-blocking)
    sd.play(wave, sample_rate)

    # Show visualization (blocking - keeps window open until closed)
    visualize_waveform(wave, sample_rate, duration, f"Frequency: {frequency} Hz")

    # Wait for sound to finish if still playing
    sd.wait()
    print("Done!")


def play_multiple_frequencies(frequencies, duration=2.0, sample_rate=44100, amplitude=0.3):
    """
    Play multiple frequencies simultaneously by mixing sine waves.

    Args:
        frequencies: List of frequencies in Hz
        duration: Duration in seconds (default: 2.0)
        sample_rate: Sample rate in Hz (default: 44100)
        amplitude: Volume level between 0.0 and 1.0 (default: 0.3)
    """
    if not frequencies:
        print("Error: No frequencies provided")
        return

    # Generate time array
    t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)

    # Mix all frequencies together
    wave = np.zeros_like(t)
    for freq in frequencies:
        wave += np.sin(2 * np.pi * freq * t)

    # Normalize to prevent clipping (divide by number of frequencies)
    wave = wave / len(frequencies)

    # Apply amplitude
    wave = amplitude * wave

    # Play the sound
    freq_str = ", ".join([f"{f} Hz" for f in frequencies])
    print(f"Playing {len(frequencies)} frequencies simultaneously: {freq_str}")
    print(f"Duration: {duration} seconds")

    # Start playing the sound (non-blocking)
    sd.play(wave, sample_rate)

    # Show visualization (blocking - keeps window open until closed)
    visualize_waveform(wave, sample_rate, duration, f"Mixed Frequencies: {freq_str}")

    # Wait for sound to finish if still playing
    sd.wait()
    print("Done!")


if __name__ == "__main__":
    # Default values
    frequencies = [440]  # A4 note
    dur = 2.0   # 2 seconds

    # Parse command line arguments
    if len(sys.argv) > 1:
        freq_input = sys.argv[1]

        # Check if input contains comma (multiple frequencies)
        if ',' in freq_input:
            try:
                frequencies = [float(f.strip()) for f in freq_input.split(',')]
            except ValueError:
                print(f"Invalid frequency list: {freq_input}")
                print("Format: freq1,freq2,freq3 (e.g., 440,554,659)")
                sys.exit(1)
        else:
            # Single frequency
            try:
                frequencies = [float(freq_input)]
            except ValueError:
                print(f"Invalid frequency: {freq_input}")
                sys.exit(1)

    if len(sys.argv) > 2:
        try:
            dur = float(sys.argv[2])
        except ValueError:
            print(f"Invalid duration: {sys.argv[2]}")
            sys.exit(1)

    # Play the frequency or frequencies
    if len(frequencies) == 1:
        play_frequency(frequency=frequencies[0], duration=dur)
    else:
        play_multiple_frequencies(frequencies=frequencies, duration=dur)

複数周波数の合成(和音)

for freq in frequencies:
    wave += np.sin(2 * np.pi * freq * t)

wave /= len(frequencies)

複数の正弦波を足し合わせることで、和音を作っています。
割り算で正規化しないと音割れ(クリッピング)が起きやすくなります。

まとめ

時々必要になるので、使いやすいものができてよかった。

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?