File size: 6,243 Bytes
8c9b8a9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import os

import soundfile
from pydub import AudioSegment
from pedalboard import (
    Pedalboard,
    Reverb,
)


class Fusion(AudioSegment):
    class InvalidMusicFileError(Exception):
        def __init__(self, file_name):
            self.file_name = file_name
            super().__init__(f"Invalid music file: {file_name}. File not found or not recognized as a valid music file.")

    @classmethod
    async def loadSound(cls, inputFile: str):
        """
        Loads and returns the MP3 or WAV (whichever is found) source sound file.
        Stops program execution if file not found.
        """

        if os.path.isfile(inputFile) and inputFile.lower().endswith(('.mp3', '.wav', '.aac', '.ogg', '.flac', '.m4a')):
            return cls.from_file(inputFile, format=inputFile.split(".")[-1])
        else:
            raise cls.InvalidMusicFileError(inputFile)

    @classmethod
    async def effect8D(
        cls,
        sound,
        panBoundary: int = 100,  # Perctange of dist from center that audio source can go
        jumpPercentage: int = 5,  # Percentage of dist b/w L-R to jump at a time
        timeLtoR: int = 10000,  # Time taken for audio source to move from left to right in ms
        volumeMultiplier: int = 6  # Max volume DB increase at edges
    ):
        """
        Generates the 8d sound effect by splitting the audio into multiple smaller pieces,
        pans each piece to make the sound source seem like it is moving from L to R and R to L in loop,
        decreases volume towards center position to make the movement sound like it is a circle
        instead of straight line.
        """
        piecesCtoR = panBoundary / jumpPercentage

        # Total pieces when audio source moves from extreme left to extreme right
        piecesLtoR = piecesCtoR * 2

        # Time length of each piece
        pieceTime = int(timeLtoR / piecesLtoR)

        pan = []
        left = -panBoundary  # Audio source to start from extreme left

        while left <= panBoundary:  # Until audio source position reaches extreme right
            pan.append(left)  # Append the position to pan array
            left += jumpPercentage  # Increment to next position

        # Above loop generates number in range -100 to 100, this converts it to -1.0 to 1.0 scale
        pan = [x / 100 for x in pan]

        sound8d = sound[0]  # Stores the 8d sound
        panIndex = 0  # Index of current pan position of pan array

        # We loop through the pan array forward once, and then in reverse (L to R, then R to L)
        iteratePanArrayForward = True

        # Loop through starting time of each piece
        for time in range(0, len(sound) - pieceTime, pieceTime):

            # time + pieceTime = ending time of piece
            piece = sound[time : time + pieceTime]

            # If at first element of pan array (Left) then iterate forward
            if panIndex == 0:
                iteratePanArrayForward = True

            # If at last element of pan array (Right) then iterate backward
            if panIndex == len(pan) - 1:
                iteratePanArrayForward = False

            # (panBoundary / 100) brings panBoundary to the same scale as elements of pan array i.e. -1.0 to 1.0
            # abs(pan[panIndex]) / (panBoundary / 100) = 1 for extreme left/right and 0 for center
            # abs(pan[panIndex]) / (panBoundary / 100) * volumeMultiplier = volumeMultiplier for extreme left/right and 0 for center
            # Hence, volAdjust = 0 for extreme left/right and volumeMultiplier for center
            volAdjust = volumeMultiplier - (
                abs(pan[panIndex]) / (panBoundary / 100) * volumeMultiplier
            )

            # Decrease piece volume by volAdjust i.e. max volume at extreme left/right and decreases towards center
            piece -= volAdjust

            # Pan the piece of sound according to the pan array element
            pannedPiece = piece.pan(pan[panIndex])

            # Iterates the pan array from left to right, then right to left, then left to right and so on..
            if iteratePanArrayForward:
                panIndex += 1
            else:
                panIndex -= 1

            # Add this panned piece of sound with adjusted volume to the 8d sound
            sound8d = sound8d + pannedPiece

        return sound8d

    @classmethod
    async def effectSlowed(cls, sound, speedMultiplier: float = 0.92 ): # Slowdown audio, 1.0 means original speed, 0.5 half speed etc
        """
        Increases sound frame rate to slow it down.
        Returns slowed down version of the sound.
        """

        soundSlowedDown = sound._spawn(
            sound.raw_data,
            overrides={"frame_rate": int(sound.frame_rate * speedMultiplier)},
        )
        soundSlowedDown.set_frame_rate(sound.frame_rate)
        return soundSlowedDown


    @classmethod
    async def effectReverb(
        cls,
        sound,
        roomSize: float = 0.8, 
        damping: float = 1,
        width : float = 0.5,
        wetLevel: float = 0.3,
        dryLevel: float= 0.8,
        tempFile: str = "tempWavFileForReverb"
    ):
        """
        Adds reverb effect to the sound.
        """
        outputFile = tempFile+".wav"
        # Convert the sound to a format usable by the pedalboard library
        with open(outputFile, "wb") as out_f:
            sound.export(out_f, format="wav")
        sound, sampleRate = soundfile.read(outputFile)

        # Define the reverb settings
        addReverb = Pedalboard(
            [Reverb(room_size=roomSize, damping=damping, width=width, wet_level=wetLevel, dry_level=dryLevel)]
        )

        # Add the reverb effect to the sound and return
        reverbedSound = addReverb(sound, sample_rate=sampleRate)
        with soundfile.SoundFile(outputFile, "w", samplerate=sampleRate, channels=sound.shape[1]) as f:
            f.write(sound)
        sound = cls.from_wav(outputFile)
        os.remove(outputFile)
        return sound

    @classmethod
    async def saveSound(cls, sound, outputFile: str = "output"):
        """
        Save the sound in MP3 format.
        """
        sound.export(outputFile + ".mp3", format="mp3")
        return f"{outputFile}.mp3"