def addFx(sound, effects, pad=3000, fade_in=100, fade_out=100): # Add padding if pad > 0: sound += AudioSegment.silent(duration=pad, frame_rate=sound.frame_rate) # convert pydub sound to np array samples = np.array(sound.get_array_of_samples()) samples = samples.astype(np.int16) chain = AudioEffectsChain() for effect, value in effects: if effect == "reverb" and value > 0: chain.reverb(reverberance=value) elif effect == "distortion" and value > 0: chain.overdrive(gain=value) elif effect == "highpass" and value > 0: chain.highpass(value) elif effect == "lowpass" and value > 0: chain.lowpass(value) elif effect == "bass": frequency = 100 gain = value if isinstance(value, tuple): gain, frequency = value print("%s, %s" % (gain, frequency)) chain.highshelf(gain=gain, frequency=frequency) elif effect == "echo": echoStr = "echo 0.8 0.9" amount = value count = 1 # check if we have echo count indicated if isinstance(value, tuple): amount, count = value for i in range(count): # amount between 10 (robot) and 1000 (mountains) echoStr += " %s 0.3" % amount chain.custom(echoStr) elif effect == "tempo" and value != 1.0 and value != 1: chain.tempo(factor=value) # apply reverb effect fx = (chain) y = fx(samples) # convert it back to an array and create a new sound clip newData = array.array(sound.array_type, y) newSound = sound._spawn(newData) dur = len(newSound) newSound = newSound.fade_in(min(fade_in, dur)).fade_out(min(fade_out, dur)) return newSound
def __call__(self, wav_file): if not Path(wav_file).exists(): print(wav_file) raise IOError sr, wav = scipy.io.wavfile.read(wav_file) if wav.ndim > 1 and wav.shape[1] > 1: logger.error("wav file has two or more channels") sys.exit(1) if type(wav[0]) is np.int32: wav = wav.astype('float32', copy=False) / 2147483648.0 elif type(wav[0]) is np.int16: wav = wav.astype('float32', copy=False) / 32768.0 elif type(wav[0]) is np.uint8: wav = wav.astype('float32', copy=False) / 256.0 - 128.0 fx = AudioEffectsChain() if self.resample: if self.sample_rate > sr: ratio = int(self.sample_rate / sr) fx.upsample(ratio) elif self.sample_rate < sr: ratio = int(sr / self.sample_rate) fx.custom(f"downsample {ratio}") if self.tempo: tempo_change = np.random.uniform(*self.tempo_range) fx.tempo(tempo_change, opt_flag="s") if self.pitch: pitch_change = np.random.uniform(*self.pitch_range) fx.pitch(pitch_change) # dithering fx.custom(f"dither -s") wav = fx(wav, sample_in=sr, sample_out=self.sample_rate) #wav = wav / max(abs(wav)) # normalize audio power gain = 0.1 wav_energy = np.sqrt(np.sum(np.power(wav, 2)) / wav.size) wav = gain * wav / wav_energy # sample-domain padding if self.padding: wav = np.pad(wav, self.num_padding, mode='constant') # sample-domain offset if self.offset: offset = np.random.randint(*self.offset_range) wav = np.roll(wav, offset, axis=0) if self.noise: snr = 10.0**(np.random.uniform(*self.noise_range) / 10.0) noise = np.random.normal(0, 1, wav.shape) noise_energy = np.sqrt(np.sum(np.power(noise, 2)) / noise.size) wav = wav + snr * gain * noise / noise_energy #filename = wav_file.replace(".wav", "_augmented.wav") #scipy.io.wavfile.write(filename, self.sample_rate, wav) return torch.FloatTensor(wav)
def main(): # Parsing for command line arguments parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter, description= ("Creates a vaporwave (slowed, with reverb) remix of a given MP3 file, with" " multiple audio effects available, and the option of playing over a looped" " GIF as a video."), ) parser.add_argument( "-o", "--output", dest="output_name", help= ("Name of output file(s), instead of audio file name with the addition of" " '_vaporised'."), type=str, ) required_arguments = parser.add_argument_group("required arguments") required_arguments.add_argument( "-a", "--audio", dest="audio_input", help="Input audio file to vaporise (.mp3)", type=str, required=True, ) audio_arguments = parser.add_argument_group( "audio arguments", "these arguments control audio effects that will be applied by default", ) audio_arguments.add_argument( "-s", "--speed", dest="speed_ratio", help="Ratio of new playback speed to old speed.", type=float, default=0.75, ) audio_arguments.add_argument( "-p", "--pitch", dest="pitch_shift", help="Pitch shift (100ths of a semitone).", type=float, default=-75, ) audio_arguments.add_argument( "-l", "--lowpass", dest="lowpass_cutoff", help="Cutoff for lowpass filter (Hz).", type=int, default=3500, ) audio_arguments_optional = parser.add_argument_group( "extra audio arguments", "these arguments control extra, optional audio effects") audio_arguments_optional.add_argument( "-b", "--bass", dest="bass_boost", help="Add a bass boost effect (e.g. --bass 3).", type=int, default=None, ) audio_arguments_optional.add_argument( "-ga", "--gain", dest="gain_db", help="Applies gain (dB).", type=int, default=None, ) audio_arguments_optional.add_argument( "-op", "--oops", dest="oops", help= ("Applies Out Of Phase Stereo effect. This is sometimes known as the" " ‘karaoke’ effect as it often has the effect of removing most or all of" " the vocals from a recording."), action="store_true", ) audio_arguments_optional.add_argument( "-ph", "--phaser", dest="phaser", help="Enable phaser effect.", action="store_true", ) audio_arguments_optional.add_argument( "-tr", "--tremolo", dest="tremolo", help="Enable tremolo effect.", action="store_true", ) audio_arguments_optional.add_argument( "-co", "--compand", dest="compand", help="Enable compand, which compresses the dynamic range of the audio.", action="store_true", ) video_arguments = parser.add_argument_group( "video arguments", "optional arguments, result in an MP4 video output in addition to the MP3" " audio", ) video_arguments.add_argument( "-g", "--gif", dest="gif_file", help= ("Input GIF file to loop. Without a GIF, only an MP3 is created. With a GIF," " an MP4 video is also created."), type=str, ) video_arguments.add_argument( "-sb", "--sobel", dest="sobel_filter", help="Applies a Sobel filter to video output.", action="store_true", ) args = parser.parse_args() # Setting name of output file if args.output_name is None: # If no output name is given, add "_vaporised" to input audio file name audio_input_string = re.sub(".mp3", "", str(args.audio_input)) audio_output = audio_input_string + "_vaporised.mp3" video_output = audio_input_string + "_vaporised.mp4" else: # Otherwise, use the output file name given via the command line output_string = re.sub(".mp3", "", str(args.output_name)) output_string = re.sub(".mp4", "", str(output_string)) audio_output = output_string + ".mp3" video_output = output_string + ".mp4" if args.audio_input == args.output_name: print("ERROR: Input and output name are identical") sys.exit() # Creating an audio effects chain, beginning with... if args.bass_boost: # ...bass boost effect bass_boost = f'{"bass "}{args.bass_boost}' fx = AudioEffectsChain().custom(bass_boost) fx = fx.pitch(args.pitch_shift) else: # ...pitch shift fx = AudioEffectsChain().pitch(args.pitch_shift) # Adding OOPS to audio effects chain if args.oops: fx = fx.custom("oops") # Adding tremolo effect to the audio effects chain if args.tremolo: fx = fx.tremolo(freq=500, depth=50) # Adding phaser to the audio effects chain if args.phaser: # fx.phaser(gain_in, gain_out, delay, decay, speed) fx = fx.phaser(0.9, 0.8, 2, 0.2, 0.5) # Adding gain to the audio effects chain if args.gain_db is not None: fx = fx.gain(db=args.gain_db) # Adding compand to the audio effects chain if args.compand: fx = fx.compand() # Adding reverb, lowpass filter, speed alteration to audio effects chain fx = fx.speed(args.speed_ratio).lowpass(args.lowpass_cutoff).reverb() # Applying audio effects fx(args.audio_input, audio_output) def apply_sobel(image): # returns image with Sobel filter applied return sobel(image.astype(float)) # Create video if a GIF file is provided if args.gif_file is None: # If no GIF is provided, exit here print("Script finished at", datetime.datetime.now().strftime("%H:%M:%S")) print("Vaporised MP3 file (audio):", audio_output) sys.exit() else: # If a GIF is provided, loop it for the length of the vaporised audio file mp3_movedit = movedit.AudioFileClip(audio_output) gif_movedit = movedit.VideoFileClip(args.gif_file) number_of_loops = float(mp3_movedit.duration / gif_movedit.duration) gif_looped = gif_movedit.loop(number_of_loops) # Applies Sobel filter to looped GIF, if --sobel is used if args.sobel_filter: gif_looped = gif_looped.fl_image(apply_sobel) gif_looped_with_audio = gif_looped.set_audio(mp3_movedit) gif_looped_with_audio.write_videofile(video_output) print("Script finished at", datetime.datetime.now().strftime("%H:%M:%S")) print("Vaporised MP3 file (audio):", audio_output) print("Vaporised MP4 file (video):", video_output)