diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ec57342 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +__pycache__ +.venv +p3dss.egg-info +dist/ +build/ +tests/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f2f1e41 --- /dev/null +++ b/LICENSE @@ -0,0 +1,18 @@ +Copyright (c) 2021 moonburnt + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d310d91 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +*Audio effects support for panda3d* + +# p3dae + +## Description: + +**p3dae** is a library dedicated to implementation of various audio effects for +panda3d's AudioManager. Its started as attempt to fix the "clicky noise" issue +appearing on switch of some tracks, caused by lack of crossfading - and, as for +now, only implements fade in/fade out/crossfade functions. Its goals are to be as +simple and flexible as possible (meaning it should work independant of particular +AudioManager instance) and dont rely on third party libs unless necessary + +## Dependencies: + +For the time being, this project depends solely on panda3d + +## Limitations: + +If you want to crossfade tracks - instance of AudioManager to which they belong, +should have concurrent sound limit set to 2 or more. For how to set things up, +[check official documentation](https://docs.panda3d.org/1.10/python/reference/panda3d.core.AudioManager#panda3d.core.AudioManager.setConcurrentSoundLimit). + +## Usage: + +- Install library with setup.py +- Check [usage examples](https://github.com/moonburnt/p3dae/tree/master/example) + +## License: + +This software has been licensed under [MIT](LICENSE). For license of media used +in example snippets, see [media_info.txt]( +https://github.com/moonburnt/p3dae/tree/master/example/media_info.txt) + diff --git a/example/Ove Melaa Supa Powa Loop B.ogg b/example/Ove Melaa Supa Powa Loop B.ogg new file mode 100644 index 0000000..7e244e0 Binary files /dev/null and b/example/Ove Melaa Supa Powa Loop B.ogg differ diff --git a/example/OveMelaa - Trance Bit Bit Loop.ogg b/example/OveMelaa - Trance Bit Bit Loop.ogg new file mode 100644 index 0000000..6957547 Binary files /dev/null and b/example/OveMelaa - Trance Bit Bit Loop.ogg differ diff --git a/example/example.py b/example/example.py new file mode 100644 index 0000000..9c5e8e1 --- /dev/null +++ b/example/example.py @@ -0,0 +1,64 @@ +import p3dae +from direct.showbase.ShowBase import ShowBase +import logging + +SONG_0 = "./OveMelaa - Trance Bit Bit Loop.ogg" +SONG_1 = "./Ove Melaa Supa Powa Loop B.ogg" + +#this library supports logging via python's logger module. But if you wont use +#it, nothing bad will happen - you just wont get status updates in terminal +log = logging.getLogger() +log.setLevel(logging.DEBUG) +handler = logging.StreamHandler() +log.addHandler(handler) + +class Game(ShowBase): + def __init__(self): + # Setting up base stuff + super().__init__() + self.disable_mouse() + + #because in this example we are using default musicManager for simplicity, + #increasing its concurrent sound limit from default (1) to ensure things + #will crossfade properly + base.musicManager.set_concurrent_sound_limit(2) + + self.music_effects = p3dae.AudioEffects() + + #loading up sounds and ensuring they wont stop preemptively + self.song_0 = loader.loadMusic(SONG_0) + self.song_1 = loader.loadMusic(SONG_1) + self.song_0.setLoop(True) + self.song_1.setLoop(True) + + #fade in example, where we start the track and slowly increase its volume. + #Keep in mind that track's volume and AudioManager's volume are different + #things that affect eachother, but dont override + self.music_effects.fade_in(self.song_0, volume = 1.0, speed = 3) + + #after 5 secs of playback, crossfading it with second track + base.taskMgr.doMethodLater(5, self.crossfade_tracks, + f"crossfade {self.song_0} and {self.song_1}") + + def crossfade_tracks(self, event): + #crossfading example, where we make one track slowly switch to other and + #then stop. For best experience, use small timeframes (in between 0.5 and + #3 seconds) and make fade in and fade out speeds match eachother + self.music_effects.crossfade(song = self.song_1, + active_songs = [self.song_0], + fade_in_speed = 2.5, + fade_out_speed = 2.5) + base.taskMgr.doMethodLater(10, self.fadeout_track, f"fade out {self.song_1}") + + return + + def fadeout_track(self, event): + #fading out example, where we fade out song with default settings + self.music_effects.fade_out(self.song_1) + + print("Thats it!") + return + +if __name__ == "__main__": + play = Game() + play.run() diff --git a/example/media_info.txt b/example/media_info.txt new file mode 100644 index 0000000..f7f3d43 --- /dev/null +++ b/example/media_info.txt @@ -0,0 +1,3 @@ +Music assets used in these examples are from https://opengameart.org/content/oves-essential-game-audio-pack-collection-160-files-updated + +They are made by OveMelaa and licensed under CC-BY 3.0 diff --git a/p3dae/__init__.py b/p3dae/__init__.py new file mode 100644 index 0000000..0fb048e --- /dev/null +++ b/p3dae/__init__.py @@ -0,0 +1,5 @@ +from .audio_effects import * + +import logging + +logging.getLogger(__name__).addHandler(logging.NullHandler()) diff --git a/p3dae/audio_effects.py b/p3dae/audio_effects.py new file mode 100644 index 0000000..b864c53 --- /dev/null +++ b/p3dae/audio_effects.py @@ -0,0 +1,83 @@ +from direct.interval.LerpInterval import LerpFunctionInterval +from direct.interval.IntervalGlobal import * +#from panda3d.core import AudioManager +import logging + +log = logging.getLogger(__name__) + +class AudioEffects: + '''Class that implements ability to use various sound effects, such as fading + and crossfading. For crossfade function to work, set_concurrent_sound_limit() + of AudioManager attached to sounds should be set to 2 or more''' + def __init__(self): + log.debug("Initializing AudioEffects instance") + + def fade_out(self, song, speed = 0.5, stop:bool = True, reset_volume:bool = True): + '''Slowly decrease song's volume within 'speed' amount of seconds. + Then stop it (unless this option is disabled) and reset its volume to + initial value (unless disabled aswell)''' + + #idk if this needs option to dont completely disable sound, but stop it + #at some certain volume #TODO + sequence = Sequence() + + current_volume = song.get_volume() + fade_out_interval = LerpFunctionInterval(song.set_volume, + duration = speed, + fromData = current_volume, + toData = 0) + + sequence.append(fade_out_interval) + if stop: + sequence.append(Func(song.stop)) + + if reset_volume: + sequence.append(Func(song.set_volume, current_volume)) + + log.debug(f"Fading out {song.getName()}") + sequence.start() + + def fade_in(self, song, volume:float = 1.0, speed = 0.5): + '''Slowly increase song's volume within 'speed' amount of seconds, + until its volume reach specified amount''' + #idk yet if there is a point in implementing safety checks for volume + sequence = Sequence() + + #its a bit confusing, but status isnt bool and return 2 on playing + if song.status() != 2: + sequence.append(Func(song.play)) + + fade_in_interval = LerpFunctionInterval(song.set_volume, + duration = speed, + fromData = 0, + toData = volume) + + sequence.append(fade_in_interval) + + log.debug(f"Fading in {song.getName()}") + sequence.start() + + def crossfade(self, song, active_songs:list = None, stop_active:bool = True, + fade_in_volume:float = 1.0, reset_volume:bool = True, + fade_out_speed = 0.5, fade_in_speed = 0.5): + '''Slowly increase one song's volume, while decreasing others into silence. + For the best effect, fade_out_speed and fade_in_speed should be set same''' + #no safety checks for negative fading values yet #TODO + parallel = Parallel() + + if active_songs: + for track in active_songs: + print(track) + parallel.append(Func(self.fade_out, + song = track, + speed = fade_out_speed, + stop = stop_active, + reset_volume = reset_volume)) + + parallel.append(Func(self.fade_in, + song = song, + volume = fade_in_volume, + speed = fade_in_speed)) + + log.debug(f"Crossfading {song.getName()}") + parallel.start() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..68b819c --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +panda3d>=1.10.8 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..9c65113 --- /dev/null +++ b/setup.py @@ -0,0 +1,21 @@ +from setuptools import find_packages, setup + +VERSION = "0.1.0" + +with open("README.md") as f: + long_description = f.read() + +setup( + name="p3dae", + version=VERSION, + description="p3dae - audio effects library for Panda3D", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/moonburnt/p3dae", + author="moonburnt", + author_email="moonburnt@disroot.org", + license="MIT", + classifiers=["Programming Language :: Python :: 3"], + packages=find_packages(), + install_requires=["panda3d>=1.10"], + )