Python: Changing the speed of sound during playback
Asked Answered
M

1

1

This is my first post. Is it possible to change the speed of a playback during playback? I want to simulate a car engine sound and for this the first step is to change the speed of a looped sample according to the RPM of the engine. I know how to increase the speed of a complete sample using pyaudio by changing the rate of the wave file, but I want to have a contineous change of the rate. Is this possible without using the scikits.samplerate package, which allows resampling (and is quite old) or pysonic, which is superold?

This is what I have at the moment:

import pygame, sys
import numpy as np
import pyaudio
import wave
from pygame.locals import *
import random as rd
import os
import time

pygame.init()

class AudioFile:
    chunk = 1024

    def __init__(self, file, speed):
        """ Init audio stream """
        self.wf = wave.open(file, 'rb')
        self.speed = speed
        self.p = pyaudio.PyAudio()
        self.stream = self.p.open(
            format = self.p.get_format_from_width(self.wf.getsampwidth()),
            channels = 1,
            rate = speed,
            output = True)

    def play(self):
        """ Play entire file """
        data = self.wf.readframes(self.chunk)
        while data != '':
            self.stream.write(data)

    def close(self):
        """ Graceful shutdown """
        self.stream.close()
        self.p.terminate()

a = AudioFile("wave.wav")
a.play()
Merger answered 2/7, 2016 at 22:49 Comment(1)
Yeah is possible, you no matter if when change the speed your pitch change together?Convincing
F
2

You should be able to do something with numpy. I'm not really familiar with wave etc. and I would expect your play() method to include a readframes() inside the loop in some way (as I attemp to do here) but you can probably get the idea from this

def play(self):
    """ Play entire file """
    x0 = np.linspace(0.0, self.chunk - 1.0, self.chunk)
    x1 = np.linspace(0.0, self.chunk - 1.0, self.chunk * self.factor) # i.e. 0.5 will play twice as fast
    data = ''
    while data != '':
        f_data = np.fromstring(self.wf.readframes(self.chunk),
                               dtype=np.int).astype(np.float) # need to use floats for interpolation
        if len(f_data) < self.chunk:
            x1 = x1[:int(len(f_data) * self.factor)]
        data = np.interp(x1, x0, f_data).astype(np.int)
        self.stream.write(data)

Obviously this uses the same speed up or slow down factor for the whole play. If you wanted to change it mid play you would have to modify x1 inside the while loop.

Flutist answered 3/7, 2016 at 15:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.