Skip to content

Instantly share code, notes, and snippets.

@crazyguitar
Forked from P4UL-M/README.md
Created August 17, 2023 23:35
Show Gist options
  • Save crazyguitar/010674c4a6bbfda35eb615b755151471 to your computer and use it in GitHub Desktop.
Save crazyguitar/010674c4a6bbfda35eb615b755151471 to your computer and use it in GitHub Desktop.
Changing playback sound speed in realtime with python and multiprocessing

Changing playback sound speed in realtime with python and multiprocessing

This is a script to change the sound speed in real time like for example background music in pygame.

The script generates the raw data of a sound file each time there is space in the queue. The sample will scale to the desired speed which you can modify with sound_Factor.

To play the music, you just need to get buffers from the queue and pass them to your music stream. Exemple for pygame :

# musique update
if channel.get_queue() == None:
    _buffer = generate_music.Sounds_buffer.get()
    channel.queue(pygame.mixer.Sound(_buffer))
elif not channel.get_busy() and not channel.get_queue():
    _buffer = generate_music.Sounds_buffer.get()
    channel.play(pygame.mixer.Sound(_buffer))

The script executes the main function in a Process so as to not overload our main loop.

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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment