A sound is read from a file (WAV, MP3, AAC, FLAC and the other usual kinds), or made by a
function of time. manimgx places each sound at its moment in the video, and mixes them into
one sound track.
Give a sound to play with animations, and it starts with them. To start one at any moment,
add it: the scene goes on while it plays. A sound plays to its end, or until you stop the
clip that adding it gives back.
A sound's methods make a changed sound: quieter or louder, faded in or out, trimmed, looped,
faster, panned, or ducked under a voice.
Make one from a file, the bytes of one, or samples; edit it (each edit makes a new
sound); place it in a scene: add_sound starts it now and
takes no time, while play plays it as a part of the play, which
then lasts at least as long as the sound. In a composition it starts when its window
opens: LaggedStart(*(AnimationGroup(FadeIn(d), click) for d in dots)) clicks as each
dot appears. It changes nothing on screen, and a play's run_time does not change it:
a sound lasts its duration.
m.Sound(source,*,rate=None)
source
A file's path, a file's bytes, or samples: floats from -1 to 1, one
column per channel (a 1-D array is mono), copied as they are given.
rate
The samples' rate, in samples a second; only for samples.
def__init__(self,source:Source,*,rate:int|None=None)->None:ifisinstance(source,np.ndarray)!=(rateisnotNone):raiseValueError("give a rate with samples, and only with samples")ifisinstance(source,str|os.PathLike)andnotPath(source).is_file():raiseFileNotFoundError(f"no sound file at {os.fspath(source)!r}")ifisinstance(source,np.ndarray):# its own: the samples as they were givensource=np.array(source,copy=True,subok=True)self.source=sourceself.rate=rateself._edit=_Edit()super().__init__(None)
defsamples(self)->np.ndarray:"""Its samples at `RATE`: floats, one column per channel (1 or 2)."""made=self.__dict__.get("_made")ifmadeisNone:made=self.__dict__["_made"]=_make(self)returnmade
defduration(self)->float:"""How long it lasts, in seconds (inf for a sound looped until stopped)."""ifself._edit.loopisnotNone:returnself._edit.loopreturnlen(self.samples)/RATE
defrun_time(self)->float:"""Its duration: a sound plays for as long as it lasts. An endless one (`loop()`) cannot be played: add it ([`add_sound`][manimgx.Scene.add_sound]), and stop it. """ifself._edit.loop==math.inf:raiseValueError("an endless sound (loop()) never ends: add it with add_sound, and stop"" its clip, instead of playing it")returnself.duration
deftrim(self,start:float=0.0,end:float|None=None)->Self:"""The part of this sound from `start` to `end` seconds (None: its end)."""returnself._but(start=start,end=end)
defloop(self,duration:float|None=None)->Self:"""This sound repeated to last `duration` seconds; None: until the film ends, or until its clip is [stopped][manimgx.audio.sound.Clip.stop]."""returnself._but(loop=math.infifdurationisNoneelseduration)
defspeed(self,factor:float)->Self:"""This sound played `factor` times as fast, as a tape is: higher and shorter."""returnself._but(speed=self._edit.speed*factor)
This sound dipping by decibels while anything speaks (while a
Speech plays): music under a narration. It eases down
DUCK_ATTACK seconds before the speech and back up over DUCK_RELEASE after.
defduck(self,decibels:float=12)->Self:"""This sound dipping by `decibels` while anything speaks (while a [`Speech`][manimgx.audio.Speech] plays): music under a narration. It eases down `DUCK_ATTACK` seconds before the speech and back up over `DUCK_RELEASE` after. """returnself._but(duck=decibels)
@classmethoddefof(cls,function:Callable[[np.ndarray],np.ndarray],duration:float)->"Sound":"""A sound made by a function of time: `function(t)`, given the times of its samples (seconds, an array), returns their values (-1 to 1). Examples: `Sound.of(lambda t: 0.3 * np.sin(2 * np.pi * 440 * t), 1)`: a second of A. """t=np.arange(round(duration*RATE))/RATEreturncls(np.asarray(function(t),np.float32),rate=RATE)
Start a sound: now, or time_offset seconds from now; it takes no time.
The scene goes on at once, and the sound plays over whatever follows, until it
ends (or the film does, or its clip is stopped). Called by an updater, it starts
at the instant the updater runs for: a bounce sounds exactly at the bounce. To
wait for a sound, play it instead.
self.add_sound(sound,time_offset=0,gain=None)
sound
The sound, or its file.
time_offset
How long after now it starts, in seconds.
gain
How much louder it plays, in decibels (negative: quieter).
defadd_sound(self,sound:str|os.PathLike[str]|Sound,time_offset:float=0,gain:float|None=None,)->Clip:"""Start a sound: now, or `time_offset` seconds from now; it takes no time. The scene goes on at once, and the sound plays over whatever follows, until it ends (or the film does, or its clip is stopped). Called by an updater, it starts at the instant the updater runs for: a bounce sounds exactly at the bounce. To wait for a sound, [`play`][manimgx.Scene.play] it instead. Args: sound: The sound, or its file. time_offset: How long after now it starts, in seconds. gain: How much louder it plays, in decibels (negative: quieter). Returns: Its clip: [`stop`][manimgx.audio.sound.Clip.stop] it to end it early. """frommanimgx.audio.soundimportClip,Soundifnotisinstance(sound,Sound):sound=Sound(sound)ifgain:sound=sound.gain(gain)# decoded now: a file it cannot read fails here, not at the end of the render_=sound.samplesclip=Clip(sound,clock.now+_exact(time_offset))self.film.clips.append(clip)returnclip
defstop(self,fade:float=0.05)->None:"""Stop the sound: it fades out from now (the instant the scene is computing) over `fade` seconds, and is silent after."""self.end,self.fade=clock.now+Fraction(fade).limit_denominator(RATE),fade