I am 'new' to using the PyAudio module in Python 3.7, and have already done a few 'neat' things with the PyAudio interface. I have figured out how to generate and play some 'custom pitches' bunched together in sequences where the data first gets converted to a -32768 to +32768 range (using int(n).to_bytes() and then n = data.from_bytes()to convert back and forth from bytes to integer, alter the values, and then convert back to bytes again for the stream).
While my values are integers, I can divide by 2 to 'halve' my volume for the 'custom pitches', but, when I divide by 2 if I make 'n' (the integer variable used for the integer values) equal to the converted data of the 'sounds\hello.wav' file, it doesn't 'halve' the volume, but it creates unwanted white noise instead. If I don’t divide by 2, my 'sounds\hello.wav' file plays fine.
Where my comments are in 'all caps', that is where the 'problem' is. The 'capitalized comment section' shows four different 'options' that can be used for the value of 'n', before n gets converted back to bytes, and gets written to the stream. Three of those four 'options' work, but I have been trying to figure out why 'the fourth option' is giving me a 'problem'. The 'four options' that 'reproduces the problem' is why my code generates two warnings, and is not a 'programmatical problem'. What I am working on, might one day help create a whole new technology for sound and music. Here is my code...
import math
import time
import wave
import pyaudio
pitches = 0
position = []
start = time.time()
started = True
oldTime = 0
delta = 0
run_time = 0
val = []
lastVal = []
lastVal2 = []
count = 0
def get_pitches():
global val
global run_time
global lastVal
global lastVal2
global position
global pitches
n = 0
val = []
pitches = 0
# Store the offset and the increment (through time) into result.
run_time += delta
# PITCHES GO HERE.
n += add_pitch_with_time_stamp(offset = 0.0, increment = 0.0, volume = 0.5, pitch_stamp=[0.01, 0.015, 0.02, 0.015], time_stamp=[.5, .5, .5, .5], transition_time_stamp = [10, 10, 10, 10], voice = "sounds\\ah.wav")
n += add_pitch_with_time_stamp(offset = 0.0, increment = 0.0, volume = 0.5, pitch_stamp=[0.01, 0.02, 0.03, 0.02], time_stamp=[1, 1, 1, 1], transition_time_stamp = [10, 10, 10, 10], voice = "sounds\\ah.wav")
#n += add_pitch(offset = .01, increment = .1, volume = 1)
#n += add_pitch(offset = 0.015, increment = -.001, volume = 1)
#n += add_pitch(offset = 0.04, increment = 0, volume = 1)
# Average out the pitches before returning n.
if pitches != 0:
n /= pitches
return n
def add_pitch(offset, increment, volume):
global pitches
global delta
global run_time
global val
global lastVal
global lastVal2
global position
# Match the size of arrays for positions and last recorded values.
if pitches >= len(position):
position.append(0)
if pitches >= len(lastVal):
lastVal.append(0)
if pitches >= len(lastVal2):
lastVal2.append(0)
# Get the calculated pitch for the wave.
pitch = ((run_time - start) * increment) + offset
# If the pitch is out of range set the result to 0.
if 0.3 > pitch >= 0:
if pitches < len(lastVal):
lastVal2[pitches] = lastVal[pitches]
val.append((1 + math.sin(((position[len(val) - 1]) * pitch) * math.pi * 2) * 0.5 * volume) - 0.5)
if pitches < len(lastVal):
lastVal[pitches] = val[len(val) - 1]
result = ((val[len(val) - 1] * 0x7f) + 0x80)
else:
result = 0
# Increase pitches per function call to determine the average value for n.
pitches += 1
else:
result = 0
return result
def add_pitch_with_time_stamp(offset, increment, volume, pitch_stamp, time_stamp=None, transition_time_stamp=None, voice=None):
global pitches
global delta
global run_time
global val
global lastVal
global lastVal2
global position
# Match size for time stamp.
for i in range(0, len(time_stamp)):
if (i + 1) > len(time_stamp):
time_stamp.append(1)
# Match size for transition time stamp.
for i in range(0, len(pitch_stamp)):
if (i + 1) > len(transition_time_stamp):
transition_time_stamp.append(1)
# Get a total time modulation from the time stamp.
time_modulation = 0
for i in range(0, len(time_stamp)):
time_modulation += time_stamp[i]
# Get the time index.
time_flow = (time.time() - start) % time_modulation
# Store the time transitions into f.
f = get_transition_value(pitch_stamp, transition_time_stamp, time_stamp, time_flow)
# Match the size of arrays for positions and last recorded values.
if pitches >= len(position):
position.append(0)
if pitches >= len(lastVal):
lastVal.append(0)
if pitches >= len(lastVal2):
lastVal2.append(0)
# Get the calculated pitch for the wave.
pitch = (((run_time - start) * increment) + (f + offset))
# If the pitch is out of range set the result to 0.
if 0.3 > pitch >= 0:
if pitches < len(lastVal):
lastVal2[pitches] = lastVal[pitches]
#print (data2)
if voice is None:
val.append((1 + math.sin(((position[len(val) - 1]) * pitch) * math.pi * 2) * 0.5 * volume) - 0.5)
else:
val.append((1 + math.sin(((position[len(val) - 1]) * pitch) * math.pi * 2) * 0.5 * volume) - 0.5)
if pitches < len(lastVal):
lastVal[pitches] = val[len(val) - 1]
result = ((val[len(val) - 1] * 0x7f) + 0x80)
else:
result = 0
# Increase pitches per function call to determine the average value for n.
pitches += 1
else:
result = 0
return result
def get_transition_value(value_list, transition_list, t_stamp, t_flow):
t_total = 0
t_position = t_flow
t_index = 0
for i in range(0, len(t_stamp)):
t_total += t_stamp[i]
if t_flow >= t_total:
t_position -= t_stamp[i]
t_index = i + 1
#t_process is the fraction of time between each transition.
t_process = t_position / t_stamp[t_index]
# Get the current value from the time stamp.
v_floor = value_list[t_index % len(value_list)]
# Get the next value from the time stamp.
v_ceil = value_list[(t_index + 1) % len(value_list)]
# Determine the 'power' between each transition
transform_power = transition_list[int(t_flow) % len(value_list)]
return transition(v_floor, v_ceil, math.pow(t_process % 1, transform_power))
def transition (down, up, mid):
# Another function for finding in between values.
return (down * (1 - mid)) + (up * mid)
def get_delta_time():
# Store the delta time into a delta variable.
global delta
global oldTime
delta = time.time()-oldTime
oldTime = time.time()
def do_pitches():
global pitches
global position
global started
global lastVal
global lastVal2
global count
global delta
# Create an interface to PortAudio
p = pyaudio.PyAudio()
wf = wave.open("sounds\\hello.wav", 'rb')
# Open a .Stream object to write the WAV file to
# 'output = True' indicates that the sound will be played rather than recorded
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()), # 8bit
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
try:
while True:
# Make a variable called 'n', and set it to 'silent' (0).
pitches = 0
# Store pitches in n.
n: int = 0
if started:
position = []
lastVal = []
lastVal2 = []
#n = get_pitches()
for i in range(0, pitches):
position.append(0)
lastVal.append(0)
lastVal2.append(0)
started = False
# Read the voice data 1 frame at a time.
data2 = wf.readframes(1)
# Convert the data from byte format, into an integer value ranging from -32768 to 32768.
v = int.from_bytes(data2, 'big')
# NOTE:
#
# If I use this line only, without dividing the value of n by 2,
# it works fine.
#
# This line takes the values of all the pitches (averaged) placed
# in the get_pitches() function before later converting 'n' to a
# byte value (called 'data') and then writing 'data' to the stream.
#
n = (transition(n, get_pitches(), 1))
# NOTE:
#
# If I use this line only, without dividing the value of n by 2,
# it works fine.
#
# This line will play a .wav file called 'sound\hello.wav' before
# later converting n to a byte value (called data) and writing
# data to the stream.
#
n = v
# NOTE:
#
# If I use this line only, dividing the value of n by 2, it
# works fine.
#
# This line takes the values of all the pitches (averaged) in
# the get_pitches() function ... and will 'halve' the volume
# (as it is supposed to do since i 'half-ed' the value).
#
# The value of n later gets converted to a byte value (called
# 'data') and gets written to the stream.
#
n = (transition(n, get_pitches(), 1)) / 2
# NOTE:
#
# ***problem***: if I use this line only, and dividing the value
# of n by 2, this produces unwanted white noise instead of
# 'halving' the volume, even though i divided the value
# of n by 2.
#
# This line should play a .wav file called 'sound\hello.wav'
# before later converting n to a byte value (called data)
# and writing data to the stream.
#
n = int(v / 2)
# Convert the value of 'n' into bytes.
data = int(n).to_bytes(2, 'big')
#wf.setpos(int((time.time() - start) % wf.getsampwidth()))
#print (int(time.time() % wf.getsampwidth()))
# Writing data to stream makes the sound.
stream.write(data)
# Write voice to voice stream.
#stream2.write(data2)
# Increment position so that the 'n' result (from getPitches)
# produces a sine-wave.
for i in range (0, len(position)):
position[i] += 1
# Limit each position to 1000 chunks to prevent popping.
if count % 1000 == 0:
position[i] = 0
get_delta_time()
count += 1
except KeyboardInterrupt:
pass
# In the case the while loop breaks.
stream.close()
p.terminate()
do_pitches()