././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1713705580.6243143 renardo-0.9.12/0000755000175100001770000000000014611211155012637 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1713705580.6243143 renardo-0.9.12/PKG-INFO0000644000175100001770000003067114611211155013743 0ustar00runnerdockerMetadata-Version: 2.1 Name: renardo Version: 0.9.12 Summary: Launcher/config editor for Renardo livecoding environment Home-page: http://renardo.org/ Author: Elie Gavoty Author-email: eliegavoty@free.fr License: cc-by-sa-4.0 Description-Content-Type: text/markdown Requires-Dist: renardo-lib==0.9.12 Requires-Dist: FoxDotEditor==0.9.12 Requires-Dist: renardo_gatherer==0.1.3 Requires-Dist: psutil Requires-Dist: textual Renardo v0.9 - FoxDot fork =========================== FoxDot is a Python programming environment that provides a fast and user-friendly abstraction to SuperCollider. It also comes with its own IDE, which means it can be used straight out of the box; all you need is Python and SuperCollider and you're ready to go! ## Important If you are having trouble installing using `pip install FoxDot`, try updating Python's `setuptools` and `wheel` libraries using the following code and trying again. ``` pip install -U setuptools pip install -U wheel ``` ### v0.8 Updates - Added `stretch` synth for timestretching samples, similar to `loop` but better and only plays the whole file. Stretches the audio's duration to the `sus` attribute without affecting pitch and does not require the tempo to be known. ```python # Stretches the audio to 4 beats without affecting pitch p1 >> stretch("Basic_Rock_135", dur=4) ``` --- ## Installation and startup #### Prerequisites - [Python 2 or 3](https://www.python.org/) - add Python to your path and install "pip" when prompted during the install. - [SuperCollider 3.8 and above](http://supercollider.github.io/download) - [Tkinter](https://tkdocs.com/tutorial/install.html) - Usually packaged with Python but Linux and MacOS users may need to install using: ```bash $ sudo apt-get install python3-tk (Linux) $ sudo port install py-tkinter (MacOS) ``` #### Recommended - [sc3 plugins](http://sc3-plugins.sourceforge.net/) #### Installing FoxDot - Open up a command prompt and type `pip install --user FoxDot`. This will download and install the latest stable version of FoxDot from the Python Package Index if you have properly configured Python. - You can update FoxDot to the latest version if it's already installed by adding `-U` or `--upgrade` flag to this command. - Alternatively, you can build from source from directly from this repository: ``` bash $ git clone https://github.com/Qirky/FoxDot.git $ cd FoxDot $ python setup.py install ``` - Open SuperCollder and install the FoxDot Quark and its dependencies (this allows FoxDot to communicate with SuperCollider) by entering the following and pressing `Ctrl+Return` (Note: this requires [Git to be installed](http://git-scm.com/) on your machine if it is not already): ```supercollider Quarks.install("FoxDot") ``` - Recompile the SuperCollider class library by going to `Language -> Recompile Class Library` or pressing `Ctrl+Shift+L` #### Startup 1. Open SuperCollider and type in `FoxDot.start` and evaluate this line. SuperCollider is now listening for messages from FoxDot. 2. Start FoxDot by entering `FoxDot` at the command line. If that doesn't work, try `python -m FoxDot`. 3. If you have installed the SC3 Plugins, use the "Code" drop-down menu to select "Use SC3 Plugins". Restart FoxDot and you'll have access to classes found in the SC3 Plugins. 4. Keep up to date with the latest verion of FoxDot by running `pip install FoxDot --upgrade` every few weeks. 5. Check out the [YouTube tutorials](https://www.youtube.com/channel/UCRyrNX07lFcfRSymZEWwl6w) for some in-depth tutorial videos on getting to grips with FoxDot #### Installing with SuperCollider 3.7 or earlier If you are having trouble installing the FoxDot Quark in SuperCollider, it is usually because the version of SuperCollider you are installing doesn’t have the functionality for installing Quarks or it doesn’t work properly. If this is the case, you can download the contents of the following SuperCollider script: [foxdot.scd](http://foxdot.org/wp-content/uploads/foxdot.scd). Once downloaded, open the file in SuperCollider and press Ctrl+Return to run it. This will make SuperCollider start listening for messages from FoxDot. #### Frequently Asked Questions You can find answers to many frequently asked questions on the [FAQ post on the FoxDot discussion forum](http://foxdot.org/forum/?view=thread&id=1). ## Basics ### Executing Code A 'block' of code in FoxDot is made up of consecutive lines of code with no empty lines. Pressing `Ctrl+Return` (or `Cmd+Return` on a Mac) will execute the block of code that the cursor is currently in. Try `print(1 + 1)` to see what happens! ### Player Objects Python supports many different programming paradigms, including procedural and functional, but FoxDot implements a traditional object orientated approach with a little bit of cheating to make it easier to live code. A player object is what FoxDot uses to make music by assigning it a synth (the 'instrument' it will play) and some instructions, such as note pitches. All one and two character variable names are reserved for player objects at startup so, by default, the variables `a`, `bd`, and `p1` are 'empty' player objects. If you use one of these variables to store something else but want to use it as a player object again, or you want to use a variable with more than two characters, you just have to reserve it by creating a `Player` and assigning it like so: ``` python p1 = Player("p1") # The string name is optional ``` To stop a Player, use the `stop` method e.g. `p1.stop()`. If you want to stop all players, you can use the command `Clock.clear()` or the keyboard short-cut `Ctrl+.`, which executes this command. Assigning synths and instructions to a player object is done using the double-arrow operator `>>`. So if you wanted to assign a synth to `p1` called 'pads' (execute `print(SynthDefs)` to see all available synths) you would use the following code: ``` python p1 >> pads([0,1,2,3]) ``` The empty player object, `p1` is now assigned a the 'pads' synth and some playback instructions. `p1` will play the first four notes of the default scale using a SuperCollider `SynthDef` with the name `\pads`. By default, each note lasts for 1 beat at 120 bpm. These defaults can be changed by specifying keyword arguments: ```python p1 >> pads([0,1,2,3], dur=[1/4,3/4], sus=1, vib=4, scale=Scale.minor) ``` The keyword arguments `dur`, `oct`, and `scale` apply to all player objects - any others, such as `vib` in the above example, refer to keyword arguments in the corresponding `SynthDef`. The first argument, `degree`, does not have to be stated explicitly. Notes can be grouped together so that they are played simultaneously using round brackets, `()`. The sequence `[(0,2,4),1,2,3]` will play the the the first harmonic triad of the default scale followed by the next three notes. ### 'Sample Player' Objects In FoxDot, sound files can be played through using a specific SynthDef called `play`. A player object that uses this SynthDef is referred to as a Sample Player object. Instead of specifying a list of numbers to generate notes, the Sample Player takes a string of characters (known as a "PlayString") as its first argument. To see a list of what samples are associated to what characters, use `print(Samples)`. To create a basic drum beat, you can execute the following line of code: ``` python d1 >> play("x-o-") ``` To have samples play simultaneously, you can create a new 'Sample Player' object for some more complex patterns. ``` python bd >> play("x( x) ") hh >> play("---[--]") sn >> play(" o ") ``` Alternatively, you can do this in one line using `<>` arrows to separate patterns you want to play together like so: ```python d1 >> play("<---[--]>< o >") ``` Or you can use `PZip`, the `zip` method, or the `&` sign to create one pattern that does this. This can be useful if you want to perform some function on individual layers later on: ``` python d1 >> play(P["x( x) "].palindrome().zip("---[--]").zip(P[" o "].amen())) # The first item must be a P[] pattern, not a string. d1 >> play(P["x( x) "].palindrome() & "---[--]" & P[" o "].amen()) ``` Grouping characters in round brackets laces the pattern so that on each play through of the sequence of samples, the next character in the group's sample is played. The sequence `(xo)---` would be played back as if it were entered `x---o---`. Using square brackets will force the enclosed samples to played in the same time span as a single character e.g. `--[--]` will play two hi-hat hits at a half beat then two at a quarter beat. You can play a random sample from a selection by using curly braces in your Play String like so: ``` python d1 >> play("x-o{-[--]o[-o]}") ``` There is now the functionality to specify the sample number for an individual sample when using the `play` SynthDef. This can be done from the play string itself by using the bar character in the form `||`. These can also be patterns created using brackets: ```python # Plays the kick drum with sample 2 but the rest with sample 0 p1 >> play("|x2|-o-") # You can use square brackets to play multiple samples p1 >> play("|x[12]| o ") # Round brackets alternate which sample is used on each loop through the sequence p1 >> play("|x(12)| o ") # Curly braces will pick a sample at random p1 >> play("|x{0123}| o ") ``` ## Scheduling Player methods You can perform actions like shuffle, mirror, and rotate on Player Objects just by calling the appropriate method. ```python bd >> play("x o xo ") # Shuffle the contents of bd bd.shuffle() ``` You can schedule these methods by calling the `every` method, which takes a list of durations (in beats), the name of the method as a string, and any other arguments. The following syntax mirrors the string of sample characters after 6 beats, then again 2 beats later and also shuffles it every 8 beats. ```python bd >> play("x-o-[xx]-o(-[oo])").every([6,2], 'mirror').every(8, 'shuffle') ``` ## Documentation [Link to documentation website](https://foxdot.org/docs/) (still in progress) ## Using alternative editors FoxDot comes pre-packaged with its own basic editor so that you don't have to tinker with config files or download any other tools but if you want to use an existing editor you can. [Koltes](https://github.com/KoltesDigital) has written a plugin for the popular Atom editor. You can install it by going to Settings -> Install -> Searching "foxdot" and pressing install on the plug in. Press Ctrl+Alt+f or go to menu -> Packages -> FoxDot -> Toggle to start FoxDot running. ## Running Python files with FoxDot code You can import `FoxDot` into your own Python programs as you would any other module. If you are not writing an interactive program, i.e. only containing FoxDot code, then you need to call a function `Go()` at the end of your program to get playback otherwise the program will terminate immediately. For example your program, `my_file.py`, should look something like this: ```python from FoxDot import * p1 >> pads([0, 1, 2, 3]) d1 >> play("x-o-") Go() ``` Then just run like any other Python program: `python my_file.py` ## Thanks - The SuperCollider development community and, of course, James McCartney, its original developer - PyOSC, Artem Baguinski et al - Members of the Live Coding community who have contributed to the project in one way or another including, but not limited to, Alex McLean, Sean Cotterill, and Dan Hett. - Big thanks to those who have used, tested, and submitted bugs, which have all helped improve FoxDot - Thank you to those who have found solutions for SuperCollider related issues, such as DavidS48 ### Samples FoxDot's audio files have been obtained from a number of sources but I've lost record of which files are attributed to which original author. Here's a list of thanks for the unknowing creators of FoxDot's sample archive. - [Legowelt Sample Kits](https://awolfe.home.xs4all.nl/samples.html) - [Game Boy Drum Kit](http://bedroomproducersblog.com/2015/04/08/game-boy-drum-kit/) - A number of sounds courtesy of Mike Hodnick's live coded album, [Expedition](https://github.com/kindohm/expedition) - Many samples have been obtained from http://freesound.org and have been placed in the public domain via the Creative Commons 0 License: http://creativecommons.org/publicdomain/zero/1.0/ - thank you to the original creators - Other samples have come from the [Dirt Sample Engine](https://github.com/tidalcycles/Dirt-Samples/tree/c2db9a0dc4ffb911febc613cdb9726cae5175223) which is part of the TidalCycles live coding language created by Yaxu - another huge amount of thanks. If you feel I've used a sample where I shouldn't have, please get in touch! ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705576.0 renardo-0.9.12/README.md0000644000175100001770000003002414611211150014110 0ustar00runnerdockerRenardo v0.9 - FoxDot fork =========================== FoxDot is a Python programming environment that provides a fast and user-friendly abstraction to SuperCollider. It also comes with its own IDE, which means it can be used straight out of the box; all you need is Python and SuperCollider and you're ready to go! ## Important If you are having trouble installing using `pip install FoxDot`, try updating Python's `setuptools` and `wheel` libraries using the following code and trying again. ``` pip install -U setuptools pip install -U wheel ``` ### v0.8 Updates - Added `stretch` synth for timestretching samples, similar to `loop` but better and only plays the whole file. Stretches the audio's duration to the `sus` attribute without affecting pitch and does not require the tempo to be known. ```python # Stretches the audio to 4 beats without affecting pitch p1 >> stretch("Basic_Rock_135", dur=4) ``` --- ## Installation and startup #### Prerequisites - [Python 2 or 3](https://www.python.org/) - add Python to your path and install "pip" when prompted during the install. - [SuperCollider 3.8 and above](http://supercollider.github.io/download) - [Tkinter](https://tkdocs.com/tutorial/install.html) - Usually packaged with Python but Linux and MacOS users may need to install using: ```bash $ sudo apt-get install python3-tk (Linux) $ sudo port install py-tkinter (MacOS) ``` #### Recommended - [sc3 plugins](http://sc3-plugins.sourceforge.net/) #### Installing FoxDot - Open up a command prompt and type `pip install --user FoxDot`. This will download and install the latest stable version of FoxDot from the Python Package Index if you have properly configured Python. - You can update FoxDot to the latest version if it's already installed by adding `-U` or `--upgrade` flag to this command. - Alternatively, you can build from source from directly from this repository: ``` bash $ git clone https://github.com/Qirky/FoxDot.git $ cd FoxDot $ python setup.py install ``` - Open SuperCollder and install the FoxDot Quark and its dependencies (this allows FoxDot to communicate with SuperCollider) by entering the following and pressing `Ctrl+Return` (Note: this requires [Git to be installed](http://git-scm.com/) on your machine if it is not already): ```supercollider Quarks.install("FoxDot") ``` - Recompile the SuperCollider class library by going to `Language -> Recompile Class Library` or pressing `Ctrl+Shift+L` #### Startup 1. Open SuperCollider and type in `FoxDot.start` and evaluate this line. SuperCollider is now listening for messages from FoxDot. 2. Start FoxDot by entering `FoxDot` at the command line. If that doesn't work, try `python -m FoxDot`. 3. If you have installed the SC3 Plugins, use the "Code" drop-down menu to select "Use SC3 Plugins". Restart FoxDot and you'll have access to classes found in the SC3 Plugins. 4. Keep up to date with the latest verion of FoxDot by running `pip install FoxDot --upgrade` every few weeks. 5. Check out the [YouTube tutorials](https://www.youtube.com/channel/UCRyrNX07lFcfRSymZEWwl6w) for some in-depth tutorial videos on getting to grips with FoxDot #### Installing with SuperCollider 3.7 or earlier If you are having trouble installing the FoxDot Quark in SuperCollider, it is usually because the version of SuperCollider you are installing doesn’t have the functionality for installing Quarks or it doesn’t work properly. If this is the case, you can download the contents of the following SuperCollider script: [foxdot.scd](http://foxdot.org/wp-content/uploads/foxdot.scd). Once downloaded, open the file in SuperCollider and press Ctrl+Return to run it. This will make SuperCollider start listening for messages from FoxDot. #### Frequently Asked Questions You can find answers to many frequently asked questions on the [FAQ post on the FoxDot discussion forum](http://foxdot.org/forum/?view=thread&id=1). ## Basics ### Executing Code A 'block' of code in FoxDot is made up of consecutive lines of code with no empty lines. Pressing `Ctrl+Return` (or `Cmd+Return` on a Mac) will execute the block of code that the cursor is currently in. Try `print(1 + 1)` to see what happens! ### Player Objects Python supports many different programming paradigms, including procedural and functional, but FoxDot implements a traditional object orientated approach with a little bit of cheating to make it easier to live code. A player object is what FoxDot uses to make music by assigning it a synth (the 'instrument' it will play) and some instructions, such as note pitches. All one and two character variable names are reserved for player objects at startup so, by default, the variables `a`, `bd`, and `p1` are 'empty' player objects. If you use one of these variables to store something else but want to use it as a player object again, or you want to use a variable with more than two characters, you just have to reserve it by creating a `Player` and assigning it like so: ``` python p1 = Player("p1") # The string name is optional ``` To stop a Player, use the `stop` method e.g. `p1.stop()`. If you want to stop all players, you can use the command `Clock.clear()` or the keyboard short-cut `Ctrl+.`, which executes this command. Assigning synths and instructions to a player object is done using the double-arrow operator `>>`. So if you wanted to assign a synth to `p1` called 'pads' (execute `print(SynthDefs)` to see all available synths) you would use the following code: ``` python p1 >> pads([0,1,2,3]) ``` The empty player object, `p1` is now assigned a the 'pads' synth and some playback instructions. `p1` will play the first four notes of the default scale using a SuperCollider `SynthDef` with the name `\pads`. By default, each note lasts for 1 beat at 120 bpm. These defaults can be changed by specifying keyword arguments: ```python p1 >> pads([0,1,2,3], dur=[1/4,3/4], sus=1, vib=4, scale=Scale.minor) ``` The keyword arguments `dur`, `oct`, and `scale` apply to all player objects - any others, such as `vib` in the above example, refer to keyword arguments in the corresponding `SynthDef`. The first argument, `degree`, does not have to be stated explicitly. Notes can be grouped together so that they are played simultaneously using round brackets, `()`. The sequence `[(0,2,4),1,2,3]` will play the the the first harmonic triad of the default scale followed by the next three notes. ### 'Sample Player' Objects In FoxDot, sound files can be played through using a specific SynthDef called `play`. A player object that uses this SynthDef is referred to as a Sample Player object. Instead of specifying a list of numbers to generate notes, the Sample Player takes a string of characters (known as a "PlayString") as its first argument. To see a list of what samples are associated to what characters, use `print(Samples)`. To create a basic drum beat, you can execute the following line of code: ``` python d1 >> play("x-o-") ``` To have samples play simultaneously, you can create a new 'Sample Player' object for some more complex patterns. ``` python bd >> play("x( x) ") hh >> play("---[--]") sn >> play(" o ") ``` Alternatively, you can do this in one line using `<>` arrows to separate patterns you want to play together like so: ```python d1 >> play("<---[--]>< o >") ``` Or you can use `PZip`, the `zip` method, or the `&` sign to create one pattern that does this. This can be useful if you want to perform some function on individual layers later on: ``` python d1 >> play(P["x( x) "].palindrome().zip("---[--]").zip(P[" o "].amen())) # The first item must be a P[] pattern, not a string. d1 >> play(P["x( x) "].palindrome() & "---[--]" & P[" o "].amen()) ``` Grouping characters in round brackets laces the pattern so that on each play through of the sequence of samples, the next character in the group's sample is played. The sequence `(xo)---` would be played back as if it were entered `x---o---`. Using square brackets will force the enclosed samples to played in the same time span as a single character e.g. `--[--]` will play two hi-hat hits at a half beat then two at a quarter beat. You can play a random sample from a selection by using curly braces in your Play String like so: ``` python d1 >> play("x-o{-[--]o[-o]}") ``` There is now the functionality to specify the sample number for an individual sample when using the `play` SynthDef. This can be done from the play string itself by using the bar character in the form `||`. These can also be patterns created using brackets: ```python # Plays the kick drum with sample 2 but the rest with sample 0 p1 >> play("|x2|-o-") # You can use square brackets to play multiple samples p1 >> play("|x[12]| o ") # Round brackets alternate which sample is used on each loop through the sequence p1 >> play("|x(12)| o ") # Curly braces will pick a sample at random p1 >> play("|x{0123}| o ") ``` ## Scheduling Player methods You can perform actions like shuffle, mirror, and rotate on Player Objects just by calling the appropriate method. ```python bd >> play("x o xo ") # Shuffle the contents of bd bd.shuffle() ``` You can schedule these methods by calling the `every` method, which takes a list of durations (in beats), the name of the method as a string, and any other arguments. The following syntax mirrors the string of sample characters after 6 beats, then again 2 beats later and also shuffles it every 8 beats. ```python bd >> play("x-o-[xx]-o(-[oo])").every([6,2], 'mirror').every(8, 'shuffle') ``` ## Documentation [Link to documentation website](https://foxdot.org/docs/) (still in progress) ## Using alternative editors FoxDot comes pre-packaged with its own basic editor so that you don't have to tinker with config files or download any other tools but if you want to use an existing editor you can. [Koltes](https://github.com/KoltesDigital) has written a plugin for the popular Atom editor. You can install it by going to Settings -> Install -> Searching "foxdot" and pressing install on the plug in. Press Ctrl+Alt+f or go to menu -> Packages -> FoxDot -> Toggle to start FoxDot running. ## Running Python files with FoxDot code You can import `FoxDot` into your own Python programs as you would any other module. If you are not writing an interactive program, i.e. only containing FoxDot code, then you need to call a function `Go()` at the end of your program to get playback otherwise the program will terminate immediately. For example your program, `my_file.py`, should look something like this: ```python from FoxDot import * p1 >> pads([0, 1, 2, 3]) d1 >> play("x-o-") Go() ``` Then just run like any other Python program: `python my_file.py` ## Thanks - The SuperCollider development community and, of course, James McCartney, its original developer - PyOSC, Artem Baguinski et al - Members of the Live Coding community who have contributed to the project in one way or another including, but not limited to, Alex McLean, Sean Cotterill, and Dan Hett. - Big thanks to those who have used, tested, and submitted bugs, which have all helped improve FoxDot - Thank you to those who have found solutions for SuperCollider related issues, such as DavidS48 ### Samples FoxDot's audio files have been obtained from a number of sources but I've lost record of which files are attributed to which original author. Here's a list of thanks for the unknowing creators of FoxDot's sample archive. - [Legowelt Sample Kits](https://awolfe.home.xs4all.nl/samples.html) - [Game Boy Drum Kit](http://bedroomproducersblog.com/2015/04/08/game-boy-drum-kit/) - A number of sounds courtesy of Mike Hodnick's live coded album, [Expedition](https://github.com/kindohm/expedition) - Many samples have been obtained from http://freesound.org and have been placed in the public domain via the Creative Commons 0 License: http://creativecommons.org/publicdomain/zero/1.0/ - thank you to the original creators - Other samples have come from the [Dirt Sample Engine](https://github.com/tidalcycles/Dirt-Samples/tree/c2db9a0dc4ffb911febc613cdb9726cae5175223) which is part of the TidalCycles live coding language created by Yaxu - another huge amount of thanks. If you feel I've used a sample where I shouldn't have, please get in touch! ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1713705580.6203144 renardo-0.9.12/renardo/0000755000175100001770000000000014611211155014271 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705576.0 renardo-0.9.12/renardo/PulsarInstance.py0000644000175100001770000000660614611211150017601 0ustar00runnerdockerimport os import subprocess from sys import platform import time import pathlib import psutil from renardo.SCFilesHandling import SC_USER_CONFIG_DIR class PulsarNotFoundError(Exception): pass class PulsarAlreadyRunning(Exception): pass class PulsarInstance: def __init__(self): self.pulsar_process = None self.pulsar_ready = None if platform == "win32": # if chocolatey pulsar_appdata_path = pathlib.Path(os.getenv('LOCALAPPDATA')) / 'Programs' / 'Pulsar' / 'Pulsar.exe' pulsar_c_path = pathlib.WindowsPath(os.getenv('ProgramFiles')) / 'Pulsar' / 'Pulsar.exe' if pulsar_appdata_path.exists(): self.pulsar_exec = [str(pulsar_appdata_path)] self.check_exec = [str(pulsar_appdata_path),'--version'] elif pulsar_c_path.exists(): self.pulsar_exec = [str(pulsar_c_path)] self.check_exec = [str(pulsar_c_path),'--version'] else: self.pulsar_ready = False elif platform == "Darwin": pulsar_path = pathlib.Path("/Applications" / "Pulsar.app") self.pulsar_exec = ['open', str(pulsar_path)] self.check_exec = ['open', str(pulsar_path), "--version"] else: self.pulsar_exec = ["pulsar"] self.check_exec = ["pulsar", "--version"] self.is_pulsar_ready() def is_pulsar_ready(self): if self.pulsar_ready is None: try: completed_process = subprocess.run(self.check_exec, capture_output=True) self.pulsar_ready = completed_process.returncode==0 except: self.pulsar_ready = False return self.pulsar_ready def start_pulsar_subprocess(self): try: if self.is_pulsar_running(): raise PulsarAlreadyRunning() self.pulsar_process = subprocess.Popen( args=self.pulsar_exec, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, ) return "Pulsar started" except Exception as e: return f"Pulsar not started: {e}" def read_stdout_line(self): if self.pulsar_process.returncode is None: return self.pulsar_process.stdout.readline().decode("utf-8") def read_stderr_line(self): if self.pulsar_process.returncode is None: return self.pulsar_process.stderr.readline().decode("utf-8") def evaluate_sclang_code(self, code_string): raw = code_string.encode("utf-8") + b"\x1b" self.pulsar_process.stdin.write(raw) self.pulsar_process.stdin.flush() # TODO : find a way to consistently stop sclang and scsynth when renardo stops/dies # TODO : find a way to name/tag the sclang/synth processes with name renardo to find it better # TODO : Use name renardo for scsynth audio server (for example with JACK Driver) def __del__(self): pass # self.popen.kill() # TODO: fix that the destructor is not called # need to clarify the launch and close process of foxdot/renardo ! # self.popen.wait() def is_pulsar_running(self): running = False for process in psutil.process_iter(): if 'pulsar' in process.name().lower(): running = True return running ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705576.0 renardo-0.9.12/renardo/RenardoApp.py0000644000175100001770000000732314611211150016676 0ustar00runnerdockerfrom .SCFilesHandling import write_sc_renardo_files_in_user_config from .SuperColliderInstance import SupercolliderInstance from .PulsarInstance import PulsarInstance from .RenardoTUI import RenardoTUI from renardo_gatherer.samples_download import SPackManager import argparse import time class RenardoApp: def __init__(self): self.sc_instance = None self.spack_manager = SPackManager() self.args = RenardoApp.parse_args() self.sc_instance = SupercolliderInstance() self.pulsar_instance = PulsarInstance() self.launch() def launch(self): # if args.cli: # if args.dir: # try: # # Use given directory # FoxDotCode.use_sample_directory(args.dir) # except OSError as e: # # Exit with last error # import sys, traceback # sys.exit(traceback.print_exc(limit=1)) # if args.startup: # try: # FoxDotCode.use_startup_file(args.startup) # except OSError as e: # import sys, traceback # sys.exit(traceback.print_exc(limit=1)) # if args.no_startup: # FoxDotCode.no_startup() if self.args.create_scfiles: write_sc_renardo_files_in_user_config() if not (self.args.no_tui or self.args.pipe or self.args.foxdot_editor): RenardoTUI(self).run() if self.args.boot: print("Launching Renardo SC module with SCLang...") self.sc_instance.start_sclang_subprocess() output_line = self.sc_instance.read_stdout_line() while "Welcome to" not in output_line: print(output_line[:-1]) # remove \n at the end to avoid double newline output_line = self.sc_instance.read_stdout_line() self.sc_instance.evaluate_sclang_code("Renardo.start;") time.sleep(3) if self.args.pipe: from renardo_lib import handle_stdin, FoxDotCode # Just take commands from the CLI handle_stdin() elif self.args.foxdot_editor: from renardo_lib import FoxDotCode # Open the GUI from FoxDotEditor.Editor import workspace FoxDot = workspace(FoxDotCode).run() elif self.args.no_tui: print("You need to choose a launching mode : TUI, --pipe or --foxdot-editor...") print("Quitting...") @staticmethod def parse_args(): parser = argparse.ArgumentParser( prog="renardo", description="Live coding with Python and SuperCollider", epilog="More information: https://renardo.org/" ) parser.add_argument('-N', '--no-tui', action='store_true', help="does start renardo TUI") parser.add_argument('-p', '--pipe', action='store_true', help="run Renardo from the command line interface") parser.add_argument('-f', '--foxdot-editor', action='store_true', help="run Renardo with the classic FoxDot code editor") # parser.add_argument('-d', '--dir', action='store', help="use an alternate directory for looking up samples") # parser.add_argument('-s', '--startup', action='store', help="use an alternate startup file") # parser.add_argument('-n', '--no-startup', action='store_true', help="does not load startup.py on boot") # store_false => boot default value = True WTF parser.add_argument('-b', '--boot', action='store_true', help="Boot SuperCollider Renardo instance automatically") parser.add_argument('-c', '--create-scfiles', action='store_true', help="Create Renardo class file and startup file in SuperCollider user conf dir.") return parser.parse_args() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705576.0 renardo-0.9.12/renardo/RenardoTUI.py0000644000175100001770000001454614611211150016624 0ustar00runnerdockerfrom textual.app import App, ComposeResult from textual.reactive import reactive from textual.binding import Binding from textual.css.query import NoMatches from renardo.SCFilesHandling import is_renardo_scfiles_installed, write_sc_renardo_files_in_user_config from renardo.widgets.Widgets import LeftPane from renardo.widgets.TutoTabPane import TutoTabPane from textual import work from textual.containers import Horizontal, Vertical from textual.widgets import ( Header, Footer, Button, Label, TabbedContent, TabPane, Log, ) class RenardoTUI(App[None]): CSS_PATH = "RenardoTUI.tcss" left_pane_mode = reactive("start-renardo") BINDINGS = [ Binding("ctrl+q", "quit", "Quit", show=True, priority=True), ] def __init__(self, renardo_app, *args, **kwargs): super().__init__(*args, **kwargs) self.renardo_app = renardo_app self.left_pane_mode = self.calculate_left_pane_mode() def calculate_left_pane_mode(self): if not self.renardo_app.sc_instance.supercollider_ready: return "sc-not-ready" if not is_renardo_scfiles_installed(): return "init-renardo-scfiles" if not self.renardo_app.spack_manager.is_default_spack_initialized(): return "dl-renardo-samples" return "start-renardo" def watch_left_pane_mode(self): # This is a special method from textual that is not easy to move from the app """watch function textual reactive param""" try: self.query_one(LeftPane).current = self.left_pane_mode except NoMatches: pass def compose(self) -> ComposeResult: yield Header() with TabbedContent(): with TabPane("Welcome", id="welcome-tab"): yield Label("Welcome to renardo terminal user interface (TUI) !!") yield Label("Here you can configure, learn renardo and start it's different modules") with TabPane("Autostart", id="start-tab"): with Horizontal(): with Vertical(): yield LeftPane(initial=self.calculate_left_pane_mode()) with Vertical(): yield Log(id="log-output") yield TutoTabPane(title="Tutorials", id="tuto-tab") yield Footer() @work(exclusive=True, thread=True) def dl_samples_background(self) -> None: log_output_widget = self.query_one("#log-output", Log) self.renardo_app.spack_manager.set_logger(log_output_widget) self.renardo_app.spack_manager.init_default_spack() self.left_pane_mode = self.calculate_left_pane_mode() @work(exclusive=True, thread=True) def init_scfile_background(self) -> None: write_sc_renardo_files_in_user_config() self.query_one("#log-output", Log).write_line("Renardo SC files created in user config") self.left_pane_mode = self.calculate_left_pane_mode() @work(exclusive=True, thread=True) def start_sc_background(self) -> None: if self.renardo_app.sc_instance.start_sclang_subprocess(): self.query_one("#log-output", Log).write_line("Launching Renardo SC module with SCLang...") output_line = self.renardo_app.sc_instance.read_stdout_line() while "Welcome to" not in output_line: self.query_one("#log-output", Log).write_line(output_line) output_line = self.renardo_app.sc_instance.read_stdout_line() self.renardo_app.sc_instance.evaluate_sclang_code("Renardo.start;") self.renardo_app.sc_instance.evaluate_sclang_code("Renardo.midi;") self.query_one("#start-renardo-foxdot-editor-btn", Button).disabled = False if self.renardo_app.pulsar_instance.pulsar_ready: self.query_one("#start-pulsar-btn", Button).disabled = False else: self.query_one("#start-pulsar-btn", Button).label = "Pulsar not ready" while True: self.query_one("#log-output", Log).write_line(self.renardo_app.sc_instance.read_stdout_line()) else: self.query_one("#log-output", Log).write_line("SuperCollider backend already started (sclang backend externally managed)\nIf you want to handle the backend manually you should ensure... \n...you executed Renardo.start; correctly in SuperCollider IDE") self.query_one("#start-renardo-foxdot-editor-btn", Button).disabled = False if self.renardo_app.pulsar_instance.pulsar_ready: self.query_one("#start-pulsar-btn", Button).disabled = False @work(exclusive=True, thread=True) def start_pulsar_background(self) -> None: self.query_one("#log-output", Log).write_line("Launching Renardo SC module with SCLang...") self.renardo_app.pulsar_instance.start_pulsar_subprocess() while True: self.query_one("#log-output", Log).write_line(self.renardo_app.pulsar_instance.read_stdout_line()) @work(exclusive=True, thread=True) def start_foxdoteditor_background(self) -> None: from renardo_lib import FoxDotCode # Open the GUI from FoxDotEditor.Editor import workspace FoxDot = workspace(FoxDotCode).run() self.app.exit(0) # Exit renardo when editor is closed because there is a bug when relaunching editor # def on_radio_set_changed(self, event: RadioSet.Changed) -> None: # self.renardo_app.args.boot = True if event.radio_set.pressed_index == 1 else False def on_button_pressed(self, event: Button.Pressed) -> None: """Event handler called when a button is pressed.""" button_id = event.button.id if button_id == "dl-renardo-samples-btn": self.dl_samples_background() if button_id == "init-renardo-scfiles-btn": self.init_scfile_background() #if button_id == "start-renardo-pipe-btn": # self.renardo_app.args.pipe = True # self.exit() if button_id == "start-pulsar-btn": self.start_pulsar_background() if button_id == "start-sc-btn": self.start_sc_background() if button_id == "start-renardo-foxdot-editor-btn": self.start_foxdoteditor_background() def on_mount(self) -> None: self.title = "Renardo" #self.query_one(RadioSet).focus() def quit(self): # self.renardo_app.sc_instance.sclang_process.kill() self.exit()././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705576.0 renardo-0.9.12/renardo/RenardoTUI.tcss0000644000175100001770000000045414611211150017141 0ustar00runnerdocker/*Screen { align: center; }*/ TabbedContent { align: center middle; } Label { margin: 1 1; } Button { margin: 1 1; padding: 0 2; /* border: thick green 20%; */ } RenardoStartBlock { overflow-y: auto; } Vertical { overflow-y: auto; } RadioSet { width: 50%; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705576.0 renardo-0.9.12/renardo/SCFilesHandling.py0000644000175100001770000001265314611211150017602 0ustar00runnerdockerimport os from pathlib import Path from sys import platform SC_USER_CONFIG_DIR = None # default config path # on windows AppData/Roaming/renardo # on Linux ~/.config/renardo # on MacOS /Users//Library/Application Support/renardo if platform == "linux" or platform == "linux2" : home_path = Path.home() SC_USER_CONFIG_DIR = home_path / '.local' / 'share' / 'SuperCollider' elif platform == "darwin": home_path = Path.home() SC_USER_CONFIG_DIR = home_path / 'Library' / 'Application Support' / 'SuperCollider' elif platform == "win32": appdata_local_path = Path(os.getenv('LOCALAPPDATA')) SC_USER_CONFIG_DIR = appdata_local_path / 'SuperCollider' SC_USER_EXTENSIONS_DIR = SC_USER_CONFIG_DIR / 'Extensions' SCLANG_PROCESS = None def is_renardo_scfiles_installed(): return ( (SC_USER_EXTENSIONS_DIR / 'Renardo.sc').exists() and (SC_USER_EXTENSIONS_DIR / 'StageLimiter.sc').exists() and (SC_USER_CONFIG_DIR / 'start_renardo.scd').exists() ) def write_sc_renardo_files_in_user_config(): renardo_sc_class = ''' Renardo { classvar server; classvar midiout; *start { | remote = false | server = Server.default; server.options.memSize = 8192 * 16; // increase this if you get "alloc failed" messages server.options.maxNodes = 1024 * 32; // increase this if you are getting drop outs and the message "too many nodes" server.options.numOutputBusChannels = 2; // set this to your hardware output channel size, if necessary server.options.numInputBusChannels = 2; // set this to your hardware output channel size, if necessary if (remote, { server.options.bindAddress = "0.0.0.0"; // allow connections from any address }); server.boot(); OSCFunc( { arg msg, time, addr, port; var fn; // Get local filename fn = msg[1].asString; // Print a message to the user ("Loading SynthDef from" + fn).postln; // Add SynthDef to file fn = File(fn, "r"); fn.readAllString.interpret; fn.close; }, 'foxdot' ); StageLimiterBis.activate(2); "Listening for messages from Renardo".postln; } *startRemote { this.start(true); } *midi { arg port=0; MIDIClient.init; midiout = MIDIOut(port); OSCFunc( { arg msg, time, addr, port; var note, vel, sus, channel, nudge; // listen for specific MIDI trigger messages from FoxDot note = msg[2]; vel = msg[3]; sus = msg[4]; channel = msg[5]; nudge = msg[6]; SystemClock.schedAbs(time + nudge, {midiout.noteOn(channel, note, vel)}); SystemClock.schedAbs(time + nudge + sus, {midiout.noteOff(channel, note, vel)}); }, 'foxdot_midi' ); ("Sending Renardo MIDI messages to" + MIDIClient.destinations[port].name).postln; } } ''' stagelimiter_sc_class = ''' // Batuhan Bozkurt 2009 StageLimiterBis { classvar lmSynth, lmFunc, activeSynth; *activate { |numChannels = 2| fork { lmFunc = { { activeSynth = Synth(\\stageLimiter, target: RootNode(Server.default), addAction: \\addToTail ); }.defer(0.01) }; lmSynth = SynthDef(\\stageLimiter, { var input = In.ar(0, numChannels); input = Select.ar(CheckBadValues.ar(input, 0, 0), [input, DC.ar(0), DC.ar(0), input]); ReplaceOut.ar(0, Limiter.ar(input)) ; }).add; Server.default.sync; lmFunc.value; CmdPeriod.add(lmFunc); "StageLimiter active".postln; } } *deactivate { activeSynth.free; CmdPeriod.remove(lmFunc); "StageLimiter inactive...".postln; } } ''' renardo_start_code = ''' Renardo.start; Renardo.midi; ''' with open(SC_USER_EXTENSIONS_DIR / 'StageLimiter.sc', mode="w") as file: file.write(stagelimiter_sc_class) with open(SC_USER_EXTENSIONS_DIR / 'Renardo.sc', mode="w") as file: file.write(renardo_sc_class) with open(SC_USER_CONFIG_DIR / 'start_renardo.scd', mode="w") as file: file.write(renardo_start_code) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705576.0 renardo-0.9.12/renardo/SuperColliderInstance.py0000644000175100001770000000626314611211150021106 0ustar00runnerdockerimport os import subprocess from sys import platform import pathlib import psutil from renardo.SCFilesHandling import SC_USER_CONFIG_DIR class SupercolliderInstance: def __init__(self): self.sclang_process = None self.supercollider_ready = None if platform == "win32": path_glob = list(pathlib.Path("C:\\Program Files").glob("SuperCollider*")) if len(path_glob) == 0: # return if no supercollider folder self.supercollider_ready = False else: sc_dir = path_glob[0] # to match SuperCollider-3.version like folders os.environ["PATH"] += f"{sc_dir};" sclang_path = sc_dir / "sclang.exe" #self.sclang_exec = [str(sclang_path), str(SC_USER_CONFIG_DIR / 'start_renardo.scd')] self.sclang_exec = [str(sclang_path), '-i', 'scqt'] self.check_exec = [str(sclang_path), '-version'] else: #self.sclang_exec = ["sclang", str(SC_USER_CONFIG_DIR / 'start_renardo.scd')] self.sclang_exec = ["sclang", '-i', 'scqt'] self.check_exec = ["sclang", '-version'] self.is_supercollider_ready() def is_supercollider_ready(self): if self.supercollider_ready is None: try: completed_process = subprocess.run(self.check_exec, capture_output=True) self.supercollider_ready = completed_process.returncode==0 except: self.supercollider_ready = False return self.supercollider_ready def start_sclang_subprocess(self): if not self.is_sclang_running(): #print("Auto Launching Renardo SC module with SCLang...") self.sclang_process = subprocess.Popen( args=self.sclang_exec, #shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, ) return True else: return False def read_stdout_line(self): if self.sclang_process.returncode is None: return self.sclang_process.stdout.readline().decode("utf-8") def read_stderr_line(self): if self.sclang_process.returncode is None: return self.sclang_process.stderr.readline().decode("utf-8") def evaluate_sclang_code(self, code_string): raw = code_string.encode("utf-8") + b"\x1b" self.sclang_process.stdin.write(raw) self.sclang_process.stdin.flush() # TODO : find a way to consistently stop sclang and scsynth when renardo stops/dies # TODO : find a way to name/tag the sclang/synth processes with name renardo to find it better # TODO : Use name renardo for scsynth audio server (for example with JACK Driver) def __del__(self): pass # self.popen.kill() # TODO: fix that the destructor is not called # need to clarify the launch and close process of foxdot/renardo ! # self.popen.wait() def is_sclang_running(self): running = False for process in psutil.process_iter(): if 'sclang' in process.name(): running = True return running ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705576.0 renardo-0.9.12/renardo/__init__.py0000644000175100001770000000015314611211150016374 0ustar00runnerdockerfrom .RenardoApp import RenardoApp def entrypoint(): RenardoApp() #def is_Irenardo_initialized(): ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705576.0 renardo-0.9.12/renardo/__main__.py0000644000175100001770000000035314611211150016357 0ustar00runnerdockerfrom .RenardoApp import RenardoApp # main is to call the module with python -m but we want to make a pypi package application with entry_point # More here : https://setuptools.pypa.io/en/latest/userguide/entry_point.html RenardoApp()././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1713705580.6243143 renardo-0.9.12/renardo/widgets/0000755000175100001770000000000014611211155015737 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705576.0 renardo-0.9.12/renardo/widgets/ConfigPane.py0000644000175100001770000000106314611211150020315 0ustar00runnerdockerfrom textual.app import ComposeResult from textual.widgets import TabPane, RadioSet, Label, RadioButton class OpenAutostartDirectly(RadioSet): def compose(self) -> ComposeResult: yield Label("Directly open Autostart tab at startup") yield RadioButton("Yes ") yield RadioButton("No", value=True) class SCOutputNum(RadioSet): def compose(self) -> ComposeResult: yield Label("Number of SuperCollider audio outputs") class ConfigPane(TabPane): def compose(self) -> ComposeResult: yield OpenAutostartDirectly() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705576.0 renardo-0.9.12/renardo/widgets/TutoTabPane.py0000644000175100001770000000230514611211150020472 0ustar00runnerdockerfrom textual.app import ComposeResult from textual.widgets import TabPane, MarkdownViewer from renardo.SCFilesHandling import SC_USER_CONFIG_DIR import requests def download_file(url, filepath): response = requests.get(url) if response.status_code == 200: with open(filepath, 'wb') as f: f.write(response.content) print(f"File downloaded successfully as '{filepath}'") else: print(f"Failed to download file. Status code: {response.status_code}") TUTO_MD_URL = "https://raw.githubusercontent.com/e-lie/renardo-website/master/docs/intro_tuto.md" class TutoTabPane(TabPane): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.markdown_string = "" file_path = SC_USER_CONFIG_DIR / 'in_app_tutorials.md' try: if not (file_path).exists(): download_file(TUTO_MD_URL, file_path) with open(file_path, mode="r") as file: self.markdown_string = file.read() except: self.markdown_string = "Tutorials download failed ! retrying at next renardo launch" def compose(self) -> ComposeResult: yield MarkdownViewer(self.markdown_string)././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705576.0 renardo-0.9.12/renardo/widgets/Widgets.py0000644000175100001770000000323614611211150017716 0ustar00runnerdockerfrom textual.app import ComposeResult from textual.widgets import Static, Button, Label, ContentSwitcher class StartRenardoBlock(Static): def compose(self) -> ComposeResult: #yield Label("Default samples pack downloaded and Renardo SuperCollider files installed") yield Button("Start SuperCollider Backend", id="start-sc-btn") yield Button("Start renardo Pulsar", id="start-pulsar-btn", disabled=True) yield Button("Start renardo FoxDot editor", id="start-renardo-foxdot-editor-btn", disabled=True) #yield Button("Start renardo pipe mode", id="start-renardo-pipe-btn", disabled=True) class SCNotReadyBlock(Static): def compose(self) -> ComposeResult: yield Label("SuperCollider seems not ready. Please install it in default location (see doc)") class DownloadRenardoSamplesBlock(Static): def compose(self) -> ComposeResult: yield Label("Default samples pack needs to be downloaded") yield Button("Download renardo default samples pack", id="dl-renardo-samples-btn") class InitRenardoSCFilesBlock(Static): def compose(self) -> ComposeResult: yield Label("Renardo SuperCollider files need to be installed") yield Button("Create renardo SC Class files and startup code", id="init-renardo-scfiles-btn") class LeftPane(ContentSwitcher): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def compose(self) -> ComposeResult: yield StartRenardoBlock(id="start-renardo") yield SCNotReadyBlock(id="sc-not-ready") yield InitRenardoSCFilesBlock(id="init-renardo-scfiles") yield DownloadRenardoSamplesBlock(id="dl-renardo-samples") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705576.0 renardo-0.9.12/renardo/widgets/__init__.py0000644000175100001770000000000014611211150020031 0ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1713705580.6243143 renardo-0.9.12/renardo.egg-info/0000755000175100001770000000000014611211155015763 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705580.0 renardo-0.9.12/renardo.egg-info/PKG-INFO0000644000175100001770000003067114611211154017066 0ustar00runnerdockerMetadata-Version: 2.1 Name: renardo Version: 0.9.12 Summary: Launcher/config editor for Renardo livecoding environment Home-page: http://renardo.org/ Author: Elie Gavoty Author-email: eliegavoty@free.fr License: cc-by-sa-4.0 Description-Content-Type: text/markdown Requires-Dist: renardo-lib==0.9.12 Requires-Dist: FoxDotEditor==0.9.12 Requires-Dist: renardo_gatherer==0.1.3 Requires-Dist: psutil Requires-Dist: textual Renardo v0.9 - FoxDot fork =========================== FoxDot is a Python programming environment that provides a fast and user-friendly abstraction to SuperCollider. It also comes with its own IDE, which means it can be used straight out of the box; all you need is Python and SuperCollider and you're ready to go! ## Important If you are having trouble installing using `pip install FoxDot`, try updating Python's `setuptools` and `wheel` libraries using the following code and trying again. ``` pip install -U setuptools pip install -U wheel ``` ### v0.8 Updates - Added `stretch` synth for timestretching samples, similar to `loop` but better and only plays the whole file. Stretches the audio's duration to the `sus` attribute without affecting pitch and does not require the tempo to be known. ```python # Stretches the audio to 4 beats without affecting pitch p1 >> stretch("Basic_Rock_135", dur=4) ``` --- ## Installation and startup #### Prerequisites - [Python 2 or 3](https://www.python.org/) - add Python to your path and install "pip" when prompted during the install. - [SuperCollider 3.8 and above](http://supercollider.github.io/download) - [Tkinter](https://tkdocs.com/tutorial/install.html) - Usually packaged with Python but Linux and MacOS users may need to install using: ```bash $ sudo apt-get install python3-tk (Linux) $ sudo port install py-tkinter (MacOS) ``` #### Recommended - [sc3 plugins](http://sc3-plugins.sourceforge.net/) #### Installing FoxDot - Open up a command prompt and type `pip install --user FoxDot`. This will download and install the latest stable version of FoxDot from the Python Package Index if you have properly configured Python. - You can update FoxDot to the latest version if it's already installed by adding `-U` or `--upgrade` flag to this command. - Alternatively, you can build from source from directly from this repository: ``` bash $ git clone https://github.com/Qirky/FoxDot.git $ cd FoxDot $ python setup.py install ``` - Open SuperCollder and install the FoxDot Quark and its dependencies (this allows FoxDot to communicate with SuperCollider) by entering the following and pressing `Ctrl+Return` (Note: this requires [Git to be installed](http://git-scm.com/) on your machine if it is not already): ```supercollider Quarks.install("FoxDot") ``` - Recompile the SuperCollider class library by going to `Language -> Recompile Class Library` or pressing `Ctrl+Shift+L` #### Startup 1. Open SuperCollider and type in `FoxDot.start` and evaluate this line. SuperCollider is now listening for messages from FoxDot. 2. Start FoxDot by entering `FoxDot` at the command line. If that doesn't work, try `python -m FoxDot`. 3. If you have installed the SC3 Plugins, use the "Code" drop-down menu to select "Use SC3 Plugins". Restart FoxDot and you'll have access to classes found in the SC3 Plugins. 4. Keep up to date with the latest verion of FoxDot by running `pip install FoxDot --upgrade` every few weeks. 5. Check out the [YouTube tutorials](https://www.youtube.com/channel/UCRyrNX07lFcfRSymZEWwl6w) for some in-depth tutorial videos on getting to grips with FoxDot #### Installing with SuperCollider 3.7 or earlier If you are having trouble installing the FoxDot Quark in SuperCollider, it is usually because the version of SuperCollider you are installing doesn’t have the functionality for installing Quarks or it doesn’t work properly. If this is the case, you can download the contents of the following SuperCollider script: [foxdot.scd](http://foxdot.org/wp-content/uploads/foxdot.scd). Once downloaded, open the file in SuperCollider and press Ctrl+Return to run it. This will make SuperCollider start listening for messages from FoxDot. #### Frequently Asked Questions You can find answers to many frequently asked questions on the [FAQ post on the FoxDot discussion forum](http://foxdot.org/forum/?view=thread&id=1). ## Basics ### Executing Code A 'block' of code in FoxDot is made up of consecutive lines of code with no empty lines. Pressing `Ctrl+Return` (or `Cmd+Return` on a Mac) will execute the block of code that the cursor is currently in. Try `print(1 + 1)` to see what happens! ### Player Objects Python supports many different programming paradigms, including procedural and functional, but FoxDot implements a traditional object orientated approach with a little bit of cheating to make it easier to live code. A player object is what FoxDot uses to make music by assigning it a synth (the 'instrument' it will play) and some instructions, such as note pitches. All one and two character variable names are reserved for player objects at startup so, by default, the variables `a`, `bd`, and `p1` are 'empty' player objects. If you use one of these variables to store something else but want to use it as a player object again, or you want to use a variable with more than two characters, you just have to reserve it by creating a `Player` and assigning it like so: ``` python p1 = Player("p1") # The string name is optional ``` To stop a Player, use the `stop` method e.g. `p1.stop()`. If you want to stop all players, you can use the command `Clock.clear()` or the keyboard short-cut `Ctrl+.`, which executes this command. Assigning synths and instructions to a player object is done using the double-arrow operator `>>`. So if you wanted to assign a synth to `p1` called 'pads' (execute `print(SynthDefs)` to see all available synths) you would use the following code: ``` python p1 >> pads([0,1,2,3]) ``` The empty player object, `p1` is now assigned a the 'pads' synth and some playback instructions. `p1` will play the first four notes of the default scale using a SuperCollider `SynthDef` with the name `\pads`. By default, each note lasts for 1 beat at 120 bpm. These defaults can be changed by specifying keyword arguments: ```python p1 >> pads([0,1,2,3], dur=[1/4,3/4], sus=1, vib=4, scale=Scale.minor) ``` The keyword arguments `dur`, `oct`, and `scale` apply to all player objects - any others, such as `vib` in the above example, refer to keyword arguments in the corresponding `SynthDef`. The first argument, `degree`, does not have to be stated explicitly. Notes can be grouped together so that they are played simultaneously using round brackets, `()`. The sequence `[(0,2,4),1,2,3]` will play the the the first harmonic triad of the default scale followed by the next three notes. ### 'Sample Player' Objects In FoxDot, sound files can be played through using a specific SynthDef called `play`. A player object that uses this SynthDef is referred to as a Sample Player object. Instead of specifying a list of numbers to generate notes, the Sample Player takes a string of characters (known as a "PlayString") as its first argument. To see a list of what samples are associated to what characters, use `print(Samples)`. To create a basic drum beat, you can execute the following line of code: ``` python d1 >> play("x-o-") ``` To have samples play simultaneously, you can create a new 'Sample Player' object for some more complex patterns. ``` python bd >> play("x( x) ") hh >> play("---[--]") sn >> play(" o ") ``` Alternatively, you can do this in one line using `<>` arrows to separate patterns you want to play together like so: ```python d1 >> play("<---[--]>< o >") ``` Or you can use `PZip`, the `zip` method, or the `&` sign to create one pattern that does this. This can be useful if you want to perform some function on individual layers later on: ``` python d1 >> play(P["x( x) "].palindrome().zip("---[--]").zip(P[" o "].amen())) # The first item must be a P[] pattern, not a string. d1 >> play(P["x( x) "].palindrome() & "---[--]" & P[" o "].amen()) ``` Grouping characters in round brackets laces the pattern so that on each play through of the sequence of samples, the next character in the group's sample is played. The sequence `(xo)---` would be played back as if it were entered `x---o---`. Using square brackets will force the enclosed samples to played in the same time span as a single character e.g. `--[--]` will play two hi-hat hits at a half beat then two at a quarter beat. You can play a random sample from a selection by using curly braces in your Play String like so: ``` python d1 >> play("x-o{-[--]o[-o]}") ``` There is now the functionality to specify the sample number for an individual sample when using the `play` SynthDef. This can be done from the play string itself by using the bar character in the form `||`. These can also be patterns created using brackets: ```python # Plays the kick drum with sample 2 but the rest with sample 0 p1 >> play("|x2|-o-") # You can use square brackets to play multiple samples p1 >> play("|x[12]| o ") # Round brackets alternate which sample is used on each loop through the sequence p1 >> play("|x(12)| o ") # Curly braces will pick a sample at random p1 >> play("|x{0123}| o ") ``` ## Scheduling Player methods You can perform actions like shuffle, mirror, and rotate on Player Objects just by calling the appropriate method. ```python bd >> play("x o xo ") # Shuffle the contents of bd bd.shuffle() ``` You can schedule these methods by calling the `every` method, which takes a list of durations (in beats), the name of the method as a string, and any other arguments. The following syntax mirrors the string of sample characters after 6 beats, then again 2 beats later and also shuffles it every 8 beats. ```python bd >> play("x-o-[xx]-o(-[oo])").every([6,2], 'mirror').every(8, 'shuffle') ``` ## Documentation [Link to documentation website](https://foxdot.org/docs/) (still in progress) ## Using alternative editors FoxDot comes pre-packaged with its own basic editor so that you don't have to tinker with config files or download any other tools but if you want to use an existing editor you can. [Koltes](https://github.com/KoltesDigital) has written a plugin for the popular Atom editor. You can install it by going to Settings -> Install -> Searching "foxdot" and pressing install on the plug in. Press Ctrl+Alt+f or go to menu -> Packages -> FoxDot -> Toggle to start FoxDot running. ## Running Python files with FoxDot code You can import `FoxDot` into your own Python programs as you would any other module. If you are not writing an interactive program, i.e. only containing FoxDot code, then you need to call a function `Go()` at the end of your program to get playback otherwise the program will terminate immediately. For example your program, `my_file.py`, should look something like this: ```python from FoxDot import * p1 >> pads([0, 1, 2, 3]) d1 >> play("x-o-") Go() ``` Then just run like any other Python program: `python my_file.py` ## Thanks - The SuperCollider development community and, of course, James McCartney, its original developer - PyOSC, Artem Baguinski et al - Members of the Live Coding community who have contributed to the project in one way or another including, but not limited to, Alex McLean, Sean Cotterill, and Dan Hett. - Big thanks to those who have used, tested, and submitted bugs, which have all helped improve FoxDot - Thank you to those who have found solutions for SuperCollider related issues, such as DavidS48 ### Samples FoxDot's audio files have been obtained from a number of sources but I've lost record of which files are attributed to which original author. Here's a list of thanks for the unknowing creators of FoxDot's sample archive. - [Legowelt Sample Kits](https://awolfe.home.xs4all.nl/samples.html) - [Game Boy Drum Kit](http://bedroomproducersblog.com/2015/04/08/game-boy-drum-kit/) - A number of sounds courtesy of Mike Hodnick's live coded album, [Expedition](https://github.com/kindohm/expedition) - Many samples have been obtained from http://freesound.org and have been placed in the public domain via the Creative Commons 0 License: http://creativecommons.org/publicdomain/zero/1.0/ - thank you to the original creators - Other samples have come from the [Dirt Sample Engine](https://github.com/tidalcycles/Dirt-Samples/tree/c2db9a0dc4ffb911febc613cdb9726cae5175223) which is part of the TidalCycles live coding language created by Yaxu - another huge amount of thanks. If you feel I've used a sample where I shouldn't have, please get in touch! ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705580.0 renardo-0.9.12/renardo.egg-info/SOURCES.txt0000644000175100001770000000100414611211154017641 0ustar00runnerdockerREADME.md setup.py renardo/PulsarInstance.py renardo/RenardoApp.py renardo/RenardoTUI.py renardo/RenardoTUI.tcss renardo/SCFilesHandling.py renardo/SuperColliderInstance.py renardo/__init__.py renardo/__main__.py renardo.egg-info/PKG-INFO renardo.egg-info/SOURCES.txt renardo.egg-info/dependency_links.txt renardo.egg-info/entry_points.txt renardo.egg-info/requires.txt renardo.egg-info/top_level.txt renardo/widgets/ConfigPane.py renardo/widgets/TutoTabPane.py renardo/widgets/Widgets.py renardo/widgets/__init__.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705580.0 renardo-0.9.12/renardo.egg-info/dependency_links.txt0000644000175100001770000000000114611211154022030 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705580.0 renardo-0.9.12/renardo.egg-info/entry_points.txt0000644000175100001770000000005714611211154021262 0ustar00runnerdocker[console_scripts] renardo = renardo:entrypoint ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705580.0 renardo-0.9.12/renardo.egg-info/requires.txt0000644000175100001770000000012014611211154020353 0ustar00runnerdockerrenardo-lib==0.9.12 FoxDotEditor==0.9.12 renardo_gatherer==0.1.3 psutil textual ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705580.0 renardo-0.9.12/renardo.egg-info/top_level.txt0000644000175100001770000000001014611211154020503 0ustar00runnerdockerrenardo ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1713705580.6243143 renardo-0.9.12/setup.cfg0000644000175100001770000000004614611211155014460 0ustar00runnerdocker[egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713705576.0 renardo-0.9.12/setup.py0000644000175100001770000000171614611211150014351 0ustar00runnerdocker#!/usr/bin/env python from setuptools import setup with open("README.md", "r") as f: long_description=f.read() setup( name='renardo', version="0.9.12", description='Launcher/config editor for Renardo livecoding environment', author='Elie Gavoty', author_email='eliegavoty@free.fr', license='cc-by-sa-4.0', url='http://renardo.org/', packages=[ 'renardo', 'renardo.widgets', ], long_description=long_description, long_description_content_type="text/markdown", # entry_points={'gui_scripts' : ['FoxDotEditor = FoxDotEditor.__init__:main']}, # data_files=[('', 'LICENSE')], package_data = {'renardo': ['RenardoTUI.tcss'],}, install_requires=[ 'renardo-lib==0.9.12', 'FoxDotEditor==0.9.12', 'renardo_gatherer==0.1.3', 'psutil', 'textual', ], entry_points={ 'console_scripts': [ 'renardo = renardo:entrypoint', ] } )