Reputation: 111
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
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
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
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
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')
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()
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
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