|
from multiprocessing import Process,Value,Queue |
|
from pydub import AudioSegment |
|
import logging |
|
|
|
""" |
|
A scrit that allow real time musique speed modification with calcul on another Proccess to avoid overloading the main boucle |
|
""" |
|
|
|
# method from https://stackoverflow.com/questions/51434897/how-to-change-audio-playback-speed-using-pydub |
|
def speed_change(sound:AudioSegment, speed=1.0): |
|
# Manually override the frame_rate. This tells the computer how many |
|
# samples to play per second |
|
sound_with_altered_frame_rate = sound._spawn(sound.raw_data, overrides={ |
|
"frame_rate": int(sound.frame_rate * speed) |
|
}) |
|
|
|
# convert the sound with altered frame rate to a standard frame rate |
|
# so that regular playback programs will work right. They often only |
|
# know how to play audio at standard frame rate (like 44.1k) |
|
return sound_with_altered_frame_rate.set_frame_rate(sound.frame_rate) |
|
|
|
musique_original = AudioSegment.from_file("Halloween LOOP.wav") |
|
|
|
sound_factor = Value('d',1) |
|
sound_offset = 500 |
|
|
|
Sounds_buffer = Queue(maxsize=2) # maxsize will be interfering with the update (less = more reactive but less resilient to lag) |
|
|
|
def generator(sound_buffer:Queue,factor:Value,offset:int=500): |
|
# var declaration |
|
musique_alternate = musique_original # current study of the musique |
|
player_offset = offset # offset of each sample |
|
max_occilation = 0.01 # max modification of speed between two sample (lesser => more faded) |
|
local_factor = factor.value # local factor of speed |
|
|
|
while True: |
|
if not Sounds_buffer.full(): # if the Queue is not full |
|
# if local factor need update |
|
if local_factor<factor.value: |
|
local_factor += max_occilation |
|
elif local_factor>factor.value: |
|
local_factor -= max_occilation |
|
|
|
if len(musique_alternate) > player_offset*local_factor: # check if we can take a normal sample in the study |
|
sample, musique_alternate = musique_alternate[:player_offset*local_factor], musique_alternate[player_offset*local_factor:] |
|
else: # else we just play what rest and loop the study back |
|
sample,musique_alternate = musique_alternate,musique_original |
|
|
|
_sample = speed_change(sample,local_factor) |
|
buffer = bytearray(_sample.raw_data) |
|
sound_buffer.put(buffer) # buffer send to the partaged Queue |
|
|
|
p = Process(target=generator,args=(Sounds_buffer,sound_factor,sound_offset),daemon=True) |
Sorry I'm not sure to understand what you means. If it's that you want to access the actual factor of the player. It's totally possible you just have to access the attribute
.valueof the factor value you passed when creating your Process.Here is an example of the class I personally used to manage my worker :
I just init my class in a variable accessible in my main loop and when I want to access or change the value of the factor I just do
mygenerator.speed_factor.valueandmygenerator.speed_factor.value = x. If you want to access the frame_rate of theAudioSegmentdirectly without interacting with the factor I guess you will need to adapt the function a little bit. Since you are working in another process you will need to use aValue()to communicate between both process.