Andy_hs
Andy_hs

Reputation: 111

Python Simple Audio Tone Generator

Looking for a (simple) Python tone generator to use in following script running on the RaspberryPi with USB sound card. On-fly tone on/off and frequency change are required.

import serial, time

ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=0.1)

def monitor(inp=0):
    if inp != inpold:
        if inp != 0:
            ser.setDTR(1)   # LED on (GPIO?)
                # start tone here, generate tone forever or change tone freq
        else:
            ser.setDTR(0)   # LED off
                # stop tone without clicks
        inpold = inp

while True:
    time.sleep(0.01)        # min length tone pulse 10 milliseconds
    input = ser.getCTS()    # or GPIO input
    monitor(input)

Upvotes: 9

Views: 29928

Answers (4)

Ginger
Ginger

Reputation: 293

Try pysinewave. It allows you to start, stop, and smoothly change pitch and volume of a tone.
Example:

from pysinewave import SineWave
import time

sinewave = SineWave(pitch = 12)
sinewave.play()
time.sleep(1)
sinewave.stop()

Upvotes: 3

juunitaki
juunitaki

Reputation: 1

There is an example of tone generation in realtime using PySDL2. UP and DOWN keys are used to change the frequency.

import sys
import sdl2
import sdl2.ext
import math
import struct
import ctypes

basefreq = 110
nframes = 0

@ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(sdl2.Uint8), ctypes.c_int)
def playNext(notused, stream, len):
    global nframes
    for i in range(0, len, 4):
        t = (nframes + i) / 44100
        left  = int(math.sin(2 * math.pi * t * (basefreq - 1)) * 32000)
        right = int(math.sin(2 * math.pi * t * (basefreq + 1)) * 32000)
        stream[i]   = left & 0xff
        stream[i+1] = (left >> 8) & 0xff
        stream[i+2] = right & 0xff
        stream[i+3] = (right >> 8) & 0xff
    nframes += len

def initAudio():
    spec = sdl2.SDL_AudioSpec(0, 0, 0, 0)
    spec.callback = playNext
    spec.freq = 44100
    spec.format = sdl2.AUDIO_S16SYS
    spec.channels = 2
    spec.samples = 1024

    devid = sdl2.SDL_OpenAudioDevice(None, 0, spec, None, 0)
    sdl2.SDL_PauseAudioDevice(devid, 0)

def run():
    global basefreq
    sdl2.SDL_Init(sdl2.SDL_INIT_AUDIO | sdl2.SDL_INIT_TIMER | sdl2.SDL_INIT_VIDEO)
    window = sdl2.ext.Window("Tone Generator", size=(800, 600))
    window.show()
    running = True
    initAudio()
    while running:
        events = sdl2.ext.get_events()
        for event in events:
            if event.type == sdl2.SDL_QUIT:
                running = False
                break
            elif event.type == sdl2.SDL_KEYDOWN:
                if event.key.keysym.sym == sdl2.SDLK_UP:
                    basefreq *= 2
                elif event.key.keysym.sym == sdl2.SDLK_DOWN:
                    basefreq /= 2
               break
        sdl2.SDL_Delay(20)
    return 0

if __name__ == "__main__":
    sys.exit(run())

Upvotes: 0

Medoalmasry
Medoalmasry

Reputation: 546

So I've found several ways to do this and I am going to lay them in order of feasibility (easiest to apply first):-


Assumptions about the tone:-

  • Wave type = Sinusodial

  • Frequency = 440Hz


Way 1 (Offline track, no sound device/backend hassle)

1- Use the Audacity software (or any similar software) to create a particular tone and export it to a file.

2- From Audacity, pick "Generate" from the tabs above then choose "Tone" and put 440 next to the frequency.

3- From Audacity, pick "File" from the tabs above then choose "Export" and select export as any extension you like, preferably mp3. 'out.mp3'

4- pip install playsound

5- In python

import playsound
playsound.playsound('out.mp3')

Way 2 (flexible, but got to make sure backend works fine)

1- pip install pygame

2- If you're working under a Linux environment then please make sure you install the following libraries

libsdl1.2-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libsdl-ttf2.0-dev

3- In python

import numpy
import pygame

sampleRate = 44100
freq = 440

pygame.mixer.init(44100,-16,2,512)
# sampling frequency, size, channels, buffer

# Sampling frequency
# Analog audio is recorded by sampling it 44,100 times per second, 
# and then these samples are used to reconstruct the audio signal 
# when playing it back.

# size
# The size argument represents how many bits are used for each 
# audio sample. If the value is negative then signed sample 
# values will be used.

# channels
# 1 = mono, 2 = stereo

# buffer
# The buffer argument controls the number of internal samples 
# used in the sound mixer. It can be lowered to reduce latency, 
# but sound dropout may occur. It can be raised to larger values
# to ensure playback never skips, but it will impose latency on sound playback. 

arr = numpy.array([4096 * numpy.sin(2.0 * numpy.pi * freq * x / sampleRate) for x in range(0, sampleRate)]).astype(numpy.int16)
arr2 = numpy.c_[arr,arr]
sound = pygame.sndarray.make_sound(arr2)
sound.play(-1)
pygame.time.delay(1000)
sound.stop()

Way 3 (Sinusoidal Wave)

use this if all you need is a sinusoidal wave

1- pip install pysine

2- if you're working under a Linux environment then please make sure you install the following library

portaudio19-dev

however, if you're working under a Windows environment then please make sure you install this using pipwin

pipwin install pysine

3- In python

import pysine
pysine.sine(frequency=440.0, duration=1.0) 

Upvotes: 10

Andy_hs
Andy_hs

Reputation: 111

Spend a lot of time with pyaudio but with pygame is very simple. Thanks http://shallowsky.com/blog/programming/python-play-chords.html

import pygame, pygame.sndarray
import numpy
import scipy.signal
from time import sleep

sample_rate = 48000
pygame.mixer.pre_init(sample_rate, -16, 1, 1024)
pygame.init()

def square_wave(hz, peak, duty_cycle=.5, n_samples=sample_rate):
    t = numpy.linspace(0, 1, 500 * 440/hz, endpoint=False)
    wave = scipy.signal.square(2 * numpy.pi * 5 * t, duty=duty_cycle)
    wave = numpy.resize(wave, (n_samples,))
    return (peak / 2 * wave.astype(numpy.int16))

def audio_freq(freq = 800):
    global sound
    sample_wave = square_wave(freq, 4096)
    sound = pygame.sndarray.make_sound(sample_wave)
# TEST
audio_freq()
sound.play(-1)
sleep(0.5)
sound.stop()
audio_freq(1000)
#sleep(1)
sound.play(-1)
sleep(2)
sound.stop()
sleep(1)
sound.play(-1)
sleep(0.5)

Upvotes: 2

Related Questions