0

I'm trying to play multiple sine waves at once in Python using pysinewave, but I need to make them sound quieter as they go up in pitch. The problem I'm running into is making them at different volumes when playing them at the same time.

Here's what I've tried:

import time
from pysinewave import SineWave

base_freq = 440
num_harmonics = 10
base_volume = 20

sinewaves = []
for n in range(1, num_harmonics + 1):
    s = SineWave(pitch_per_second=0)
    s.set_frequency(base_freq * n)
    print(base_freq * n)
    s.set_volume(base_volume/n)
    sinewaves.append(s)

for s in sinewaves:
    s.play()

time.sleep(3)

for s in sinewaves:
    s.stop()
4
  • pysinewave does not seem to be a widely known package, so I estimate chances of you finding an expert in that package here are slim. With that being the case, perhaps explain how the observed behavior of the code presented differs from what you expect? It's not clear to me, at least, that the behavior would fail to satisfy the criteria you give. Commented Nov 9 at 14:34
  • Thanks for the reply. I'm trying to generate sine waves that all play at the same time, but as they get higher in pitch, they should get quieter. Thus, creating a harmonic series. I couldn't find a very reliable library for this, so I just went with this one, but I'm not really sure how, or if, the volume parameter works. Thank you for your help. Commented Nov 9 at 15:25
  • The other way to do it is create a buffer long enough to contain the highest harmonic with at least 12 samples and sum all the sine wave components into that. Then play if back through a DAC. You appear to be generating f(t) = sin(wt)+sin(2wt)/2+sin(3wt)/3...+sin(Nwt)/N which will be a rough approximation of a saw tooth wave |\|\|\|\. What does it look like on a scope? Commented Nov 9 at 15:46
  • Yes, @Techy, I understand what you say you want. What is unclear is how what you get differs from that. Or how you're measuring / judging. Commented Nov 9 at 16:43

3 Answers 3

0
    s.set_volume(base_volume / n)

The documentation includes a Note on volume which recommends that you call set_volume(decibels).

Human acoustic perception has logarithmic sensitivity, which is not yet reflected in the OP code.

Sign up to request clarification or add additional context in comments.

1 Comment

I think this is likely a key insight, though that's not entirely clear because the OP has so far been coy about what they perceive to be wrong with their original solution. This answer would be improved, however, by being more explicit about the implications of the volume units being logarithmic, and by suggesting an approach to handling that more appropriately.
0

A few ideas ...

  • there is a parameter SineWave.decibels_per_second, that controls the rate at which the volume is changed to achieve your targeted rate. I believe that your sleep time of 3 seconds is not sufficient for you to reach the targeted volume for each sound wave.

  • did you try to listen to each wave individually to verify that each wave has the pitch/volume that you want?

  • I modified your code a bit to define the db_per_second parameter, define the volume as 1/2 of the prior volume, extend the sleep periods to 10 seconds and (a) generate each wave and listen to it individually, and then (b) add all together for a final sound. I verified by this method that the volume does indeed change in pitch, and the volume decreases from 65dB to 45dB (phone app sound meter)

You can listen to this, and I believe find the solution that you want.

For consideration, since the multiple waves are going to initiate at different start times (even if microseconds) there may be a phase difference in the waves. I could not detect that audibly for this experiment, but did not spend any time to think about it beyond typing this note. ;) But, in some scenarios, this may generate modulation in volume that is audible.


import time
from pysinewave import SineWave

base_freq = 440
num_harmonics = 6
this_volume = 128

ls_wave = []
for n in range(1, num_harmonics + 1):
    s = SineWave(pitch_per_second=0,
                 decibels_per_second=10)

    this_pitch = base_freq * n
    this_volume = this_volume / 2

    s.set_frequency(this_pitch)
    s.set_volume(this_volume)
    ls_wave.append(s)

    print(f'freq: {this_pitch}')
    print(f'volume: {this_volume}')

    s.play()
    time.sleep(10)
    s.stop()

    print(20*'-=')

print('all together ...')
for this_wave in ls_wave:
    this_wave.play()

time.sleep(20)

for this_wave in ls_wave:
    this_wave.stop()

Comments

0

set_volume() doesn’t take a “0-100” style volume, it takes decibels.
So base_volume/n isn’t doing what you think.

Give each harmonic a lower dB value instead, e.g.:

base_freq = 440
num_harmonics = 10
base_db = 0      # loudest
step_db = 6      # each harmonic ~half as loud

sinewaves = []
for n in range(1, num_harmonics + 1):
    s = SineWave(pitch_per_second=0, decibels_per_second=0)
    s.set_frequency(base_freq * n)
    s.set_volume(base_db - step_db * (n-1))  # 0, -6, -12, ...
    sinewaves.append(s)

Higher-pitched waves now play simultaneously but progressively quieter.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.