├── .gitignore ├── LICENSE ├── README.md ├── docs ├── README.html └── README.md ├── setup.py ├── soundstretch └── __init__.py └── tests ├── Makefile ├── soundstretch-test └── soundstretch-test.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # sound stretch 1.2 3 | 4 | This uses the Paulstretch algorithm that is released under Public Domain 5 | 6 | ## install: 7 | 8 | ```bash 9 | pip3 install soundstretch 10 | ``` 11 | 12 | ## usage: 13 | 14 | ```python 15 | # import soundstretch 16 | from soundstretch import SoundStretch 17 | # use default settings 18 | success = SoundStretch("input.wav","result.wav") 19 | # use specific stretch factor and window size settings 20 | success = SoundStretch("input.wav", "result.wav", 8.0, 0.25) 21 | 22 | ``` 23 | 24 | ## requirements 25 | - Numpy 26 | - Scipy 27 | -------------------------------------------------------------------------------- /docs/README.html: -------------------------------------------------------------------------------- 1 | README

sound stretch 1.2

1059 |

This uses the Paulstretch algorithm that is released under Public Domain

1060 |

install:

1061 |
pip3 install soundstretch
1062 | 
1063 | 1064 |

usage:

1065 |
# import soundstretch
1066 | from soundstretch import SoundStretch
1067 | # use default settings
1068 | success = SoundStretch("input.wav","result.wav")
1069 | # use specific stretch factor and window size settings
1070 | success = SoundStretch("input.wav", "result.wav", 8.0, 0.25)
1071 | 
1072 | 1073 |

requirements

1074 |
-------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | 2 | # sound stretch 1.2 3 | 4 | This uses the Paulstretch algorithm that is released under Public Domain 5 | 6 | ## install: 7 | 8 | ```bash 9 | pip3 install soundstretch 10 | ``` 11 | 12 | ## usage: 13 | 14 | ```python 15 | # import soundstretch 16 | from soundstretch import SoundStretch 17 | # use default settings 18 | success = SoundStretch("input.wav","result.wav") 19 | # use specific stretch factor and window size settings 20 | success = SoundStretch("input.wav", "result.wav", 8.0, 0.25) 21 | 22 | ``` 23 | 24 | ## requirements 25 | - Numpy 26 | - Scipy 27 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup,find_packages 2 | 3 | long_description = ''' 4 | 5 | # sound stretch 1.2 6 | 7 | This uses the Paulstretch algorithm that is released under Public Domain 8 | 9 | ## usage: 10 | 11 | from soundstretch import SoundStretch 12 | 13 | #### use default settings 14 | 15 | SoundStretch("input.wav","result.wav") 16 | 17 | #### use specific stretch factor and window size settings 18 | 19 | SoundStretch("input.wav", "result.wav", 8.0, 0.25) 20 | 21 | ## Requirements 22 | 23 | - Numpy 24 | 25 | - Scipy 26 | 27 | ''' 28 | 29 | setup(\ 30 | name='soundstretch', 31 | version='1.2', 32 | description='SoundStretch based on Paulstretch algorithm', 33 | long_description=long_description, 34 | long_description_content_type="text/markdown", 35 | url='https://github.com/jbvolmer/soundstretch', 36 | author='J. Volmer', 37 | author_email='josephbvolmer@gmail.com', 38 | license='Public Domain', 39 | packages=find_packages(), 40 | install_requires=[ 41 | "numpy", 42 | "scipy", 43 | ], 44 | zip_safe=False) 45 | 46 | -------------------------------------------------------------------------------- /soundstretch/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | ''' 4 | SoundStretch 5 | based on Paul's Extreme Sound Stretch algorithm (Paulstretch) 6 | 7 | ''' 8 | 9 | import sys 10 | import os 11 | from numpy import * 12 | import scipy.io.wavfile 13 | import wave 14 | import argparse 15 | 16 | ################################################################################ 17 | 18 | class SoundStretch(object): 19 | def __init__(self, sample_file, outfilename, stretch=8.0, windowsize_seconds=0.25): 20 | (samplerate,smp) = self.load_wav(sample_file) 21 | self.samplerate=samplerate 22 | self.smp=smp 23 | self.stretch=stretch 24 | self.windowsize_seconds=windowsize_seconds 25 | self.outfilename=outfilename 26 | self.pstretch() 27 | def load_wav(self, filename): 28 | try: 29 | wavedata=scipy.io.wavfile.read(filename) 30 | samplerate=int(wavedata[0]) 31 | smp=wavedata[1]*(1.0/32768.0) 32 | smp=smp.transpose() 33 | if len(smp.shape)==1: smp=tile(smp,(2,1)) 34 | return (samplerate,smp) 35 | except: 36 | print("Error loading wav: {}".format(filename)) 37 | return None 38 | def optimize_windowsize(self,n): 39 | orig_n=n 40 | while True: 41 | n=orig_n 42 | while (n%2)==0: n/=2 43 | while (n%3)==0: n/=3 44 | while (n%5)==0: n/=5 45 | if n<2: break 46 | orig_n+=1 47 | return orig_n 48 | def pstretch(self): 49 | nchannels=self.smp.shape[0] 50 | outfile=wave.open(self.outfilename,"wb") 51 | outfile.setsampwidth(2) 52 | outfile.setframerate(self.samplerate) 53 | outfile.setnchannels(nchannels) 54 | windowsize=int(self.windowsize_seconds*self.samplerate) 55 | windowsize=16 if windowsize<16 else windowsize 56 | windowsize=self.optimize_windowsize(windowsize) 57 | windowsize=int(windowsize/2)*2 58 | half_windowsize=int(windowsize/2) 59 | nsamples=self.smp.shape[1] 60 | end_size=int(self.samplerate*0.05) 61 | end_size=16 if end_size<16 else end_size 62 | self.smp[:,nsamples-end_size:nsamples]*=linspace(1,0,end_size) 63 | start_pos=0.0 64 | displace_pos=(windowsize*0.5)/self.stretch 65 | window=pow(1.0-pow(linspace(-1.0,1.0,windowsize),2.0),1.25) 66 | old_windowed_buf=zeros((2,windowsize)) 67 | done=False 68 | while not done: 69 | istart_pos=int(floor(start_pos)) 70 | buf=self.smp[:,istart_pos:istart_pos+windowsize] 71 | buf=buf if buf.shape[1]>=windowsize else append(buf,zeros((2,windowsize-buf.shape[1])),1) 72 | buf=buf*window 73 | freqs=abs(fft.rfft(buf)) 74 | ph=random.uniform(0,2*pi,(nchannels,freqs.shape[1]))*1j 75 | freqs=freqs*exp(ph) 76 | buf=fft.irfft(freqs) 77 | buf*=window 78 | output=buf[:,0:half_windowsize] + old_windowed_buf[:,half_windowsize:windowsize] 79 | old_windowed_buf=buf 80 | output[output>1.0]=1.0 81 | output[output<-1.0]=-1.0 82 | flattened=output.ravel(order='F') 83 | frames=int16( flattened * 32767.0 ).tostring() 84 | outfile.writeframes(frames) 85 | start_pos+=displace_pos 86 | sys.stdout.write ("%d %% \r" % int(100.0*start_pos/nsamples)) 87 | sys.stdout.flush() 88 | if start_pos>=nsamples: done=True 89 | if done: print("100 %") 90 | outfile.close() 91 | return os.path.exists(self.outfilename) and os.path.isfile(self.outfilename) 92 | 93 | 94 | ################################################################################ 95 | 96 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | all: clean soundstretch-test test 2 | 3 | soundstretch-test: 4 | cp soundstretch-test.py soundstretch-test 5 | chmod u+x soundstretch-test 6 | 7 | test: 8 | ./soundstretch-test -i test.wav -s 8.0 -w .25 -o temp.wav 9 | 10 | clean: 11 | rm -rf soundstretch-test temp.wav 12 | -------------------------------------------------------------------------------- /tests/soundstretch-test: -------------------------------------------------------------------------------- 1 | #! /usr/local/bin/python3 2 | 3 | # infinite chill / 2017 4 | 5 | import sys 6 | import argparse 7 | from soundstretch import SoundStretch 8 | 9 | def main(): 10 | print ("Sound Stretch") 11 | parser = argparse.ArgumentParser(description='ssstretch a wav file.') 12 | parser.add_argument("-i", "--input", dest="input", help="input wav file", required=True) 13 | parser.add_argument("-s", "--stretch", dest="stretch",help="stretch value; 1.0 = no stretch", type=float, default=8.0) 14 | parser.add_argument("-w", "--window_size", dest="window_size",help="window size in seconds", type=float, default=0.25) 15 | parser.add_argument("-o", "--output", dest="output", help="output wav path", required=True) 16 | args = parser.parse_args() 17 | 18 | success = SoundStretch(args.input,args.output,args.stretch,args.window_size) 19 | result="finished: {}".format(args.output) if success else "error: {}".format(args.output) 20 | print(result) 21 | 22 | 23 | if __name__ == '__main__': 24 | main() 25 | -------------------------------------------------------------------------------- /tests/soundstretch-test.py: -------------------------------------------------------------------------------- 1 | #! /usr/local/bin/python3 2 | 3 | import sys 4 | import argparse 5 | from soundstretch import SoundStretch 6 | 7 | def main(): 8 | print ("Sound Stretch") 9 | parser = argparse.ArgumentParser(description='ssstretch a wav file.') 10 | parser.add_argument("-i", "--input", dest="input", help="input wav file", required=True) 11 | parser.add_argument("-s", "--stretch", dest="stretch",help="stretch value; 1.0 = no stretch", type=float, default=8.0) 12 | parser.add_argument("-w", "--window_size", dest="window_size",help="window size in seconds", type=float, default=0.25) 13 | parser.add_argument("-o", "--output", dest="output", help="output wav path", required=True) 14 | args = parser.parse_args() 15 | 16 | success = SoundStretch(args.input,args.output,args.stretch,args.window_size) 17 | result="finished: {}".format(args.output) if success else "error: {}".format(args.output) 18 | print(result) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | 24 | --------------------------------------------------------------------------------