zarox commited on
Commit
8c9b8a9
·
verified ·
1 Parent(s): 1d63493
Files changed (1) hide show
  1. fusion.py +161 -0
fusion.py ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ import soundfile
4
+ from pydub import AudioSegment
5
+ from pedalboard import (
6
+ Pedalboard,
7
+ Reverb,
8
+ )
9
+
10
+
11
+ class Fusion(AudioSegment):
12
+ class InvalidMusicFileError(Exception):
13
+ def __init__(self, file_name):
14
+ self.file_name = file_name
15
+ super().__init__(f"Invalid music file: {file_name}. File not found or not recognized as a valid music file.")
16
+
17
+ @classmethod
18
+ async def loadSound(cls, inputFile: str):
19
+ """
20
+ Loads and returns the MP3 or WAV (whichever is found) source sound file.
21
+ Stops program execution if file not found.
22
+ """
23
+
24
+ if os.path.isfile(inputFile) and inputFile.lower().endswith(('.mp3', '.wav', '.aac', '.ogg', '.flac', '.m4a')):
25
+ return cls.from_file(inputFile, format=inputFile.split(".")[-1])
26
+ else:
27
+ raise cls.InvalidMusicFileError(inputFile)
28
+
29
+ @classmethod
30
+ async def effect8D(
31
+ cls,
32
+ sound,
33
+ panBoundary: int = 100, # Perctange of dist from center that audio source can go
34
+ jumpPercentage: int = 5, # Percentage of dist b/w L-R to jump at a time
35
+ timeLtoR: int = 10000, # Time taken for audio source to move from left to right in ms
36
+ volumeMultiplier: int = 6 # Max volume DB increase at edges
37
+ ):
38
+ """
39
+ Generates the 8d sound effect by splitting the audio into multiple smaller pieces,
40
+ pans each piece to make the sound source seem like it is moving from L to R and R to L in loop,
41
+ decreases volume towards center position to make the movement sound like it is a circle
42
+ instead of straight line.
43
+ """
44
+ piecesCtoR = panBoundary / jumpPercentage
45
+
46
+ # Total pieces when audio source moves from extreme left to extreme right
47
+ piecesLtoR = piecesCtoR * 2
48
+
49
+ # Time length of each piece
50
+ pieceTime = int(timeLtoR / piecesLtoR)
51
+
52
+ pan = []
53
+ left = -panBoundary # Audio source to start from extreme left
54
+
55
+ while left <= panBoundary: # Until audio source position reaches extreme right
56
+ pan.append(left) # Append the position to pan array
57
+ left += jumpPercentage # Increment to next position
58
+
59
+ # Above loop generates number in range -100 to 100, this converts it to -1.0 to 1.0 scale
60
+ pan = [x / 100 for x in pan]
61
+
62
+ sound8d = sound[0] # Stores the 8d sound
63
+ panIndex = 0 # Index of current pan position of pan array
64
+
65
+ # We loop through the pan array forward once, and then in reverse (L to R, then R to L)
66
+ iteratePanArrayForward = True
67
+
68
+ # Loop through starting time of each piece
69
+ for time in range(0, len(sound) - pieceTime, pieceTime):
70
+
71
+ # time + pieceTime = ending time of piece
72
+ piece = sound[time : time + pieceTime]
73
+
74
+ # If at first element of pan array (Left) then iterate forward
75
+ if panIndex == 0:
76
+ iteratePanArrayForward = True
77
+
78
+ # If at last element of pan array (Right) then iterate backward
79
+ if panIndex == len(pan) - 1:
80
+ iteratePanArrayForward = False
81
+
82
+ # (panBoundary / 100) brings panBoundary to the same scale as elements of pan array i.e. -1.0 to 1.0
83
+ # abs(pan[panIndex]) / (panBoundary / 100) = 1 for extreme left/right and 0 for center
84
+ # abs(pan[panIndex]) / (panBoundary / 100) * volumeMultiplier = volumeMultiplier for extreme left/right and 0 for center
85
+ # Hence, volAdjust = 0 for extreme left/right and volumeMultiplier for center
86
+ volAdjust = volumeMultiplier - (
87
+ abs(pan[panIndex]) / (panBoundary / 100) * volumeMultiplier
88
+ )
89
+
90
+ # Decrease piece volume by volAdjust i.e. max volume at extreme left/right and decreases towards center
91
+ piece -= volAdjust
92
+
93
+ # Pan the piece of sound according to the pan array element
94
+ pannedPiece = piece.pan(pan[panIndex])
95
+
96
+ # Iterates the pan array from left to right, then right to left, then left to right and so on..
97
+ if iteratePanArrayForward:
98
+ panIndex += 1
99
+ else:
100
+ panIndex -= 1
101
+
102
+ # Add this panned piece of sound with adjusted volume to the 8d sound
103
+ sound8d = sound8d + pannedPiece
104
+
105
+ return sound8d
106
+
107
+ @classmethod
108
+ async def effectSlowed(cls, sound, speedMultiplier: float = 0.92 ): # Slowdown audio, 1.0 means original speed, 0.5 half speed etc
109
+ """
110
+ Increases sound frame rate to slow it down.
111
+ Returns slowed down version of the sound.
112
+ """
113
+
114
+ soundSlowedDown = sound._spawn(
115
+ sound.raw_data,
116
+ overrides={"frame_rate": int(sound.frame_rate * speedMultiplier)},
117
+ )
118
+ soundSlowedDown.set_frame_rate(sound.frame_rate)
119
+ return soundSlowedDown
120
+
121
+
122
+ @classmethod
123
+ async def effectReverb(
124
+ cls,
125
+ sound,
126
+ roomSize: float = 0.8,
127
+ damping: float = 1,
128
+ width : float = 0.5,
129
+ wetLevel: float = 0.3,
130
+ dryLevel: float= 0.8,
131
+ tempFile: str = "tempWavFileForReverb"
132
+ ):
133
+ """
134
+ Adds reverb effect to the sound.
135
+ """
136
+ outputFile = tempFile+".wav"
137
+ # Convert the sound to a format usable by the pedalboard library
138
+ with open(outputFile, "wb") as out_f:
139
+ sound.export(out_f, format="wav")
140
+ sound, sampleRate = soundfile.read(outputFile)
141
+
142
+ # Define the reverb settings
143
+ addReverb = Pedalboard(
144
+ [Reverb(room_size=roomSize, damping=damping, width=width, wet_level=wetLevel, dry_level=dryLevel)]
145
+ )
146
+
147
+ # Add the reverb effect to the sound and return
148
+ reverbedSound = addReverb(sound, sample_rate=sampleRate)
149
+ with soundfile.SoundFile(outputFile, "w", samplerate=sampleRate, channels=sound.shape[1]) as f:
150
+ f.write(sound)
151
+ sound = cls.from_wav(outputFile)
152
+ os.remove(outputFile)
153
+ return sound
154
+
155
+ @classmethod
156
+ async def saveSound(cls, sound, outputFile: str = "output"):
157
+ """
158
+ Save the sound in MP3 format.
159
+ """
160
+ sound.export(outputFile + ".mp3", format="mp3")
161
+ return f"{outputFile}.mp3"