From 833b34e85b65e5f97dd32f7338eabdb79da259df Mon Sep 17 00:00:00 2001 From: gkvoelkl Date: Sun, 4 Dec 2016 17:41:29 +0100 Subject: [PATCH 01/49] Version 0.2.0 --- psonic.py | 81 ++++++++++++++++++- python-sonic.ipynb | 188 +++++++++++++++++++++++++++++++++++++++++++-- setup.py | 2 +- 3 files changed, 262 insertions(+), 9 deletions(-) diff --git a/psonic.py b/psonic.py index 5930b36..da89476 100644 --- a/psonic.py +++ b/psonic.py @@ -21,6 +21,7 @@ #SOFTWARE. import random import time +import threading from pythonosc import osc_message_builder # osc support from pythonosc import udp_client @@ -54,6 +55,29 @@ def __init__(self, name, inter): self.name = name self.inter = inter +class Message: + """ + For sending messages between threads + """ + def __init__(self): + self._condition = threading.Condition() + + def cue(self): + with self._condition: + self._condition.notifyAll() # Message to threads + + def sync(self): + with self._condition: + self._condition.wait() # Wait for message + +## Decorator ## + +def in_thread(func): + def wrapper(): + _thread = threading.Thread(target=func) + _thread.start() + return wrapper + ## Notes ## C2 = 36 @@ -164,6 +188,15 @@ def __init__(self, name, inter): SAW = Synth('saw') BEEP = Synth('beep') TRI = Synth('tri') +DTRI = Synth('dtri') #Sonic Pi 2.10 +PLUCK = Synth('pluck') +CHIPLEAD = Synth('chiplead') +CHIPBASS = Synth('chipbass') +CHIPNOISE = Synth('chipnoise') +TECHSAWS = Synth('tech_saws') #Sonic Pi 2.11 +SOUND_IN = Synth('sound_in') +SOUND_IN_STEREO = Synth('sound_in_stereo') + ## Scale Mode (from sonic pi)## DIATONIC = 'diatonic' @@ -415,6 +448,8 @@ def __init__(self, name, inter): DRUM_CYMBAL_PEDAL = Sample('drum_cymbal_pedal') DRUM_BASS_SOFT = Sample('drum_bass_soft') DRUM_BASS_HARD = Sample('drum_bass_hard') +DRUM_COWBELL = Sample('drum_cowbell') +DRUM_ROLL = Sample('drum_roll') ## Electric Sounds ELEC_TRIANGLE = Sample('elec_triangle') @@ -450,9 +485,13 @@ def __init__(self, name, inter): ## Miscellaneous Sounds MISC_BURP = Sample('misc_burp') +MISC_CROW = Sample('misc_crow') +MISC_CINEBOOM = Sample('misc_cineboom') ## Percurssive Sounds PERC_BELL = Sample('perc_bell') +PERC_SWASH = Sample('perc_swash') +PERC_TILL = Sample('perc_till') ## Ambient Sounds AMBI_SOFT_BUZZ = Sample('ambi_soft_buzz') @@ -494,6 +533,44 @@ def __init__(self, name, inter): LOOP_COMPUS = Sample('loop_compus') LOOP_AMEN = Sample('loop_amen') LOOP_AMEN_FULL = Sample('loop_amen_full') +LOOP_SAFARI = Sample('loop_safari') +LOOP_TABLA = Sample('loop_tabla') + +## Tabla +TABLA_TAS1 = Sample('tabla_tas1') +TABLA_TAS2 = Sample('tabla_tas2') +TABLA_TAS3 = Sample('tabla_tas3') +TABLA_KE1 = Sample('tabla_ke1') +TABLA_KE2 = Sample('tabla_ke2') +TABLA_KE3 = Sample('tabla_ke3') +TABLA_NA = Sample('tabla_na') +TABLA_NA_O = Sample('tabla_na_o') +TABLA_TUN1 = Sample('tabla_tun1') +TABLA_TUN2 = Sample('tabla_tun2') +TABLA_TUN3 = Sample('tabla_tun3') +TABLA_TE1 = Sample('tabla_te1') +TABLA_TE2 = Sample('tabla_te2') +TABLA_TE_NE = Sample('tabla_te_ne') +TABLA_TE_M = Sample('tabla_te_m') +TABLA_GHE1 = Sample('tabla_ghe1') +TABLA_GHE2 = Sample('tabla_ghe2') +TABLA_GHE3 = Sample('tabla_ghe3') +TABLA_GHE4 = Sample('tabla_ghe4') +TABLA_GHE5 = Sample('tabla_ghe5') +TABLA_GHE6 = Sample('tabla_ghe6') +TABLA_GHE7 = Sample('tabla_ghe7') +TABLA_GHE8 = Sample('tabla_ghe8') +TABLA_DHEC = Sample('tabla_dhec') +TABLA_NA_S = Sample('tabla_na_s') +TABLA_RE = Sample('tabla_re') + + +# Vinyl +VINYL_BACKSPIN = Sample('vinyl_backspin') +VINYL_REWIND = Sample('vinyl_rewind') +VINYL_SCRATCH = Sample('vinyl_scratch') +VINYL_HISS = Sample('vinyl_hiss') + ## Module attributes ## _current_synth = BEEP @@ -555,7 +632,8 @@ def play_pattern(notes): play_pattern_timed(notes, 1) -def sample(sample, rate=None, attack=None, sustain=None, release=None, start=None, finish=None, amp=None, pan=None): +def sample(sample, rate=None, attack=None, sustain=None, release=None,beat_stretch=None, + start=None, finish=None, amp=None, pan=None): parameters = [] parameter = '' command = '' @@ -564,6 +642,7 @@ def sample(sample, rate=None, attack=None, sustain=None, release=None, start=Non if attack is not None: parameters.append('attack: {0}'.format(attack)) if sustain is not None: parameters.append('sustain: {0}'.format(sustain)) if release is not None: parameters.append('release: {0}'.format(release)) + if beat_stretch is not None:parameters.append('beat_stretch: {0}'.format(beat_stretch)) if start is not None: parameters.append('start: {0}'.format(start)) if finish is not None: parameters.append('finish: {0}'.format(finish)) if amp is not None: parameters.append('amp: {0}'.format(amp)) diff --git a/python-sonic.ipynb b/python-sonic.ipynb index 63f1c33..6f6c50d 100644 --- a/python-sonic.ipynb +++ b/python-sonic.ipynb @@ -29,7 +29,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "* First you need Python 3 (https://www.python.org, ) - Python 3.4 should work, because it's the development environment\n", + "* First you need Python 3 (https://www.python.org, ) - Python 3.5 should work, because it's the development environment\n", "* Then Sonic Pi (https://sonic-pi.net) - That makes the sound\n", "* Modul python-osc (https://pypi.python.org/pypi/python-osc) - Connection between Python and Sonic Pi Server\n", "* And this modul python-sonic - simply copy the source\n", @@ -70,6 +70,22 @@ "* Only the notes from C2 to C6" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Changelog" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "|Version | |\n", + "|--------------|------------------------------------------------------------------------------------------|\n", + "| 0.2.0 | Some changes for Sonic Pi 2.11. Simpler multi-threading with decorator *@in_thread*. Messaging with *cue* and *sync*. |" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -86,7 +102,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": { "collapsed": false }, @@ -122,7 +138,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": { "collapsed": false }, @@ -261,9 +277,9 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 4, "metadata": { - "collapsed": true + "collapsed": false }, "outputs": [], "source": [ @@ -362,6 +378,32 @@ " sleep(1)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Sample slicing" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from psonic import *\n", + "\n", + "number_of_pieces = 8\n", + "\n", + "for i in range(16):\n", + " s = random.randrange(0,number_of_pieces)/number_of_pieces #sample starts at 0.0 and finishes at 1.0\n", + " f = s + (1.0/number_of_pieces)\n", + " sample(LOOP_AMEN,beat_stretch=2,start=s,finish=f)\n", + " sleep(2.0/number_of_pieces)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -517,6 +559,104 @@ "To synchronize the thread, so that they play a note at the same time, you can use *Condition*. One function sends a message with *condition.notifyAll* the other waits until the message comes *condition.wait*." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "More simple with decorator __@in_thread__" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Press Enter to continue...\n" + ] + }, + { + "data": { + "text/plain": [ + "''" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from psonic import *\n", + "from random import choice\n", + "\n", + "tick = Message()\n", + "\n", + "@in_thread\n", + "def random_riff():\n", + " use_synth(PROPHET)\n", + " sc = scale(E3, MINOR)\n", + " while True:\n", + " s = random.choice([0.125,0.25,0.5])\n", + " tick.sync()\n", + " for i in range(8):\n", + " r = random.choice([0.125, 0.25, 1, 2])\n", + " n = random.choice(sc)\n", + " co = random.randint(30,100)\n", + " play(n, release = r, cutoff = co)\n", + " sleep(s)\n", + " \n", + "@in_thread\n", + "def drums():\n", + " while True:\n", + " tick.cue()\n", + " for i in range(16):\n", + " r = random.randrange(1,10)\n", + " sample(DRUM_BASS_HARD, rate=r)\n", + " sleep(0.125)\n", + "\n", + "random_riff()\n", + "drums()\n", + "\n", + "input(\"Press Enter to continue...\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from psonic import *\n", + "\n", + "tick = Message()\n", + "\n", + "@in_thread\n", + "def metronom():\n", + " while True:\n", + " tick.cue()\n", + " sleep(1)\n", + " \n", + "@in_thread\n", + "def instrument():\n", + " while True:\n", + " tick.sync()\n", + " sample(DRUM_HEAVY_KICK)\n", + "\n", + "metronom()\n", + "instrument()\n", + "\n", + "while True:\n", + " pass" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -605,6 +745,39 @@ "play_pattern_timed(scale(C3, MAJOR_PENTATONIC, num_octaves = 2), 0.125, release = 0.1)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The function *scale* returns a list with all notes of a scale. So you can use list methodes or functions. For example to play arpeggios descending or shuffeld." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import random\n", + "\n", + "s = scale(C3, MAJOR)\n", + "s" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "play_pattern_timed(s.reverse(), 0.125, release = 0.1)\n", + "play_pattern_timed(random.shuffle(s), 0.125, release = 0.1)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1218,8 +1391,9 @@ } ], "metadata": { + "anaconda-cloud": {}, "kernelspec": { - "display_name": "Python 3", + "display_name": "Python [default]", "language": "python", "name": "python3" }, @@ -1233,7 +1407,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.5.1" + "version": "3.5.2" } }, "nbformat": 4, diff --git a/setup.py b/setup.py index cc3d74a..8664551 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ setup( name='python-sonic', - version='0.1.4', + version='0.2.0', description='Programming Music with Sonic Pi or Supercollider', long_description=long_description, url='https://github.com/gkvoelkl/python-sonic', From b8c4c4f44b8d96399baf6f233c3f5b9de1f4b1e2 Mon Sep 17 00:00:00 2001 From: gkvoelkl Date: Sun, 4 Dec 2016 17:42:55 +0100 Subject: [PATCH 02/49] Version 0.2.0 --- README.rst | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index b7a2536..8a75e24 100644 --- a/README.rst +++ b/README.rst @@ -14,7 +14,7 @@ If you like it, use it. If you have some suggestions, tell me Installation ------------ -- First you need Python 3 (https://www.python.org, ) - Python 3.4 +- First you need Python 3 (https://www.python.org, ) - Python 3.5 should work, because it's the development environment - Then Sonic Pi (https://sonic-pi.net) - That makes the sound - Modul python-osc (https://pypi.python.org/pypi/python-osc) - @@ -36,6 +36,16 @@ Limitations python-sonic - Only the notes from C2 to C6 +Changelog +--------- + ++------------+---------------------------------------------------------------+ +| Version | | ++============+===============================================================+ +| 0.2.0 | Some changes for Sonic Pi 2.11. Simpler multi-threading with | +| | decorator *@in\_thread*. Messaging with *cue* and *sync*. | ++------------+---------------------------------------------------------------+ + Examples -------- @@ -156,6 +166,20 @@ Play some random notes play(random.choice([C5,E5,G5])) sleep(1) +Sample slicing + +.. code:: python + + from psonic import * + + number_of_pieces = 8 + + for i in range(16): + s = random.randrange(0,number_of_pieces)/number_of_pieces #sample starts at 0.0 and finishes at 1.0 + f = s + (1.0/number_of_pieces) + sample(LOOP_AMEN,beat_stretch=2,start=s,finish=f) + sleep(2.0/number_of_pieces) + An infinite loop and if .. code:: python @@ -282,6 +306,81 @@ you can use *Condition*. One function sends a message with *condition.notifyAll* the other waits until the message comes *condition.wait*. +More simple with decorator \_\_@in\_thread\_\_ + +.. code:: python + + from psonic import * + from random import choice + + tick = Message() + + @in_thread + def random_riff(): + use_synth(PROPHET) + sc = scale(E3, MINOR) + while True: + s = random.choice([0.125,0.25,0.5]) + tick.sync() + for i in range(8): + r = random.choice([0.125, 0.25, 1, 2]) + n = random.choice(sc) + co = random.randint(30,100) + play(n, release = r, cutoff = co) + sleep(s) + + @in_thread + def drums(): + while True: + tick.cue() + for i in range(16): + r = random.randrange(1,10) + sample(DRUM_BASS_HARD, rate=r) + sleep(0.125) + + random_riff() + drums() + + input("Press Enter to continue...") + + +.. parsed-literal:: + + Press Enter to continue... + + + + +.. parsed-literal:: + + '' + + + +.. code:: python + + from psonic import * + + tick = Message() + + @in_thread + def metronom(): + while True: + tick.cue() + sleep(1) + + @in_thread + def instrument(): + while True: + tick.sync() + sample(DRUM_HEAVY_KICK) + + metronom() + instrument() + + while True: + pass + Play a list of notes .. code:: python @@ -322,6 +421,22 @@ Play scales play_pattern_timed(scale(C3, MAJOR, num_octaves = 2), 0.125, release = 0.1) play_pattern_timed(scale(C3, MAJOR_PENTATONIC, num_octaves = 2), 0.125, release = 0.1) +The function *scale* returns a list with all notes of a scale. So you +can use list methodes or functions. For example to play arpeggios +descending or shuffeld. + +.. code:: python + + import random + + s = scale(C3, MAJOR) + s + +.. code:: python + + play_pattern_timed(s.reverse(), 0.125, release = 0.1) + play_pattern_timed(random.shuffle(s), 0.125, release = 0.1) + Live Loop ~~~~~~~~~ From 76e3a521a8b33a281698fd17db3ed2d19e8547b6 Mon Sep 17 00:00:00 2001 From: Oliver Steele Date: Thu, 16 Mar 2017 07:18:28 +0800 Subject: [PATCH 03/49] Fix pythonosc.osc_message.ParseError exception in synthServer.stop() Fixes #7 --- psonic.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/psonic.py b/psonic.py index da89476..430d3b9 100644 --- a/psonic.py +++ b/psonic.py @@ -773,10 +773,11 @@ def test_connection(self): # abort("ERROR: Sonic Pi is not listening on #{PORT} - is it running?") pass - def send_command(self, address, argument=''): + def send_command(self, address, *args): msg = osc_message_builder.OscMessageBuilder(address=address) msg.add_arg('SONIC_PI_PYTHON') - msg.add_arg(argument) + for arg in args: + msg.add_arg(arg) msg = msg.build() self.client.send(msg) From 32147fcf2c51abb7f8afeea34017b982348e0845 Mon Sep 17 00:00:00 2001 From: gkvoelkl Date: Mon, 7 Aug 2017 18:49:06 +0200 Subject: [PATCH 04/49] Version 0.3.0 --- LICENSE | 0 README.rst | 230 ++++++++++++++++++++++----------- psonic.py | 253 +++++++++++++++++++++++++++--------- python-sonic.ipynb | 310 +++++++++++++++++++++++++++++++++------------ setup.py | 3 +- 5 files changed, 577 insertions(+), 219 deletions(-) mode change 100644 => 100755 LICENSE mode change 100644 => 100755 README.rst mode change 100644 => 100755 psonic.py mode change 100644 => 100755 python-sonic.ipynb mode change 100644 => 100755 setup.py diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.rst b/README.rst old mode 100644 new mode 100755 index 8a75e24..a8a3a8a --- a/README.rst +++ b/README.rst @@ -23,9 +23,7 @@ Installation Or try -.. code:: python - - $ pip install python-sonic +$ pip install python-sonic That should work. @@ -45,25 +43,27 @@ Changelog | 0.2.0 | Some changes for Sonic Pi 2.11. Simpler multi-threading with | | | decorator *@in\_thread*. Messaging with *cue* and *sync*. | +------------+---------------------------------------------------------------+ +| 0.3.0 | OSC Communication | ++------------+---------------------------------------------------------------+ Examples -------- Many of the examples are inspired from the help menu in *Sonic Pi*. -.. code:: python +.. code:: ipython3 from psonic import * The first sound -.. code:: python +.. code:: ipython3 play(70) #play MIDI note 70 Some more notes -.. code:: python +.. code:: ipython3 play(72) sleep(1) @@ -73,7 +73,7 @@ Some more notes In more tratitional music notation -.. code:: python +.. code:: ipython3 play(C5) sleep(0.5) @@ -83,7 +83,7 @@ In more tratitional music notation Play sharp notes like *F#* or dimished ones like *Eb* -.. code:: python +.. code:: ipython3 play(Fs5) sleep(0.5) @@ -92,7 +92,7 @@ Play sharp notes like *F#* or dimished ones like *Eb* Play louder (parameter amp) or from a different direction (parameter pan) -.. code:: python +.. code:: ipython3 play(72,amp=2) sleep(0.5) @@ -100,7 +100,7 @@ pan) Different synthesizer sounds -.. code:: python +.. code:: ipython3 use_synth(SAW) play(38) @@ -113,46 +113,46 @@ Different synthesizer sounds ADSR *(Attack, Decay, Sustain and Release)* Envelope -.. code:: python +.. code:: ipython3 play (60, attack=0.5, decay=1, sustain_level=0.4, sustain=2, release=0.5) sleep(4) Play some samples -.. code:: python +.. code:: ipython3 sample(AMBI_LUNAR_LAND, amp=0.5) -.. code:: python +.. code:: ipython3 sample(LOOP_AMEN,pan=-1) sleep(0.877) sample(LOOP_AMEN,pan=1) -.. code:: python +.. code:: ipython3 sample(LOOP_AMEN,rate=0.5) -.. code:: python +.. code:: ipython3 sample(LOOP_AMEN,rate=1.5) -.. code:: python +.. code:: ipython3 sample(LOOP_AMEN,rate=-1)#back -.. code:: python +.. code:: ipython3 sample(DRUM_CYMBAL_OPEN,attack=0.01,sustain=0.3,release=0.1) -.. code:: python +.. code:: ipython3 sample(LOOP_AMEN,start=0.5,finish=0.8,rate=-0.2,attack=0.3,release=1) Play some random notes -.. code:: python +.. code:: ipython3 import random @@ -160,7 +160,7 @@ Play some random notes play(random.randrange(50, 100)) sleep(0.5) -.. code:: python +.. code:: ipython3 for i in range(3): play(random.choice([C5,E5,G5])) @@ -168,7 +168,7 @@ Play some random notes Sample slicing -.. code:: python +.. code:: ipython3 from psonic import * @@ -182,7 +182,7 @@ Sample slicing An infinite loop and if -.. code:: python +.. code:: ipython3 while True: if one_in(2): @@ -212,14 +212,14 @@ An infinite loop and if --> 589 time.sleep(duration) 590 _debug('sleep', duration) 591 - + KeyboardInterrupt: If you want to hear more than one sound at a time, use Threads. -.. code:: python +.. code:: ipython3 import random from psonic import * @@ -249,7 +249,7 @@ If you want to hear more than one sound at a time, use Threads. Every function *bass\_sound* and *snare\_sound* have its own thread. Your can hear them running. -.. code:: python +.. code:: ipython3 from psonic import * from threading import Thread, Condition @@ -291,7 +291,7 @@ Your can hear them running. .. parsed-literal:: Press Enter to continue... - + @@ -308,7 +308,7 @@ you can use *Condition*. One function sends a message with More simple with decorator \_\_@in\_thread\_\_ -.. code:: python +.. code:: ipython3 from psonic import * from random import choice @@ -347,7 +347,7 @@ More simple with decorator \_\_@in\_thread\_\_ .. parsed-literal:: Press Enter to continue... - + @@ -357,7 +357,7 @@ More simple with decorator \_\_@in\_thread\_\_ -.. code:: python +.. code:: ipython3 from psonic import * @@ -383,7 +383,7 @@ More simple with decorator \_\_@in\_thread\_\_ Play a list of notes -.. code:: python +.. code:: ipython3 from psonic import * @@ -394,7 +394,7 @@ Play a list of notes Play chords -.. code:: python +.. code:: ipython3 play(chord(E4, MINOR)) sleep(1) @@ -407,7 +407,7 @@ Play chords Play arpeggios -.. code:: python +.. code:: ipython3 play_pattern( chord(E4, 'm7')) play_pattern_timed( chord(E4, 'm7'), 0.25) @@ -415,7 +415,7 @@ Play arpeggios Play scales -.. code:: python +.. code:: ipython3 play_pattern_timed(scale(C3, MAJOR), 0.125, release = 0.1) play_pattern_timed(scale(C3, MAJOR, num_octaves = 2), 0.125, release = 0.1) @@ -425,17 +425,33 @@ The function *scale* returns a list with all notes of a scale. So you can use list methodes or functions. For example to play arpeggios descending or shuffeld. -.. code:: python +.. code:: ipython3 import random + from psonic import * s = scale(C3, MAJOR) s -.. code:: python - play_pattern_timed(s.reverse(), 0.125, release = 0.1) - play_pattern_timed(random.shuffle(s), 0.125, release = 0.1) + + +.. parsed-literal:: + + [48, 50, 52, 53, 55, 57, 59, 60] + + + +.. code:: ipython3 + + s.reverse() + +.. code:: ipython3 + + + play_pattern_timed(s, 0.125, release = 0.1) + random.shuffle(s) + play_pattern_timed(s, 0.125, release = 0.1) Live Loop ~~~~~~~~~ @@ -444,7 +460,7 @@ One of the best in SONIC PI is the *Live Loop*. While a loop is playing music you can change it and hear the change. Let's try it in Python, too. -.. code:: python +.. code:: ipython3 from psonic import * from threading import Thread @@ -467,7 +483,7 @@ too. .. parsed-literal:: Press Enter to continue...Y - + @@ -479,21 +495,21 @@ too. Now change the function *my\_loop* und you can hear it. -.. code:: python +.. code:: ipython3 def my_loop(): use_synth(TB303) play (60, release= 0.3) sleep (0.25) -.. code:: python +.. code:: ipython3 def my_loop(): use_synth(TB303) play (chord(E3, MINOR), release= 0.3) sleep(0.5) -.. code:: python +.. code:: ipython3 def my_loop(): use_synth(TB303) @@ -506,7 +522,7 @@ Restart Now with two live loops which are synch. -.. code:: python +.. code:: ipython3 from psonic import * from threading import Thread, Condition @@ -547,7 +563,7 @@ Now with two live loops which are synch. .. parsed-literal:: Press Enter to continue...y - + @@ -557,13 +573,13 @@ Now with two live loops which are synch. -.. code:: python +.. code:: ipython3 def loop_foo(): play (A4, release = 0.5) sleep (0.5) -.. code:: python +.. code:: ipython3 def loop_bar(): sample (DRUM_HEAVY_KICK) @@ -572,7 +588,7 @@ Now with two live loops which are synch. If would be nice if we can stop the loop with a simple command. With stop event it works. -.. code:: python +.. code:: ipython3 from psonic import * from threading import Thread, Condition, Event @@ -616,7 +632,7 @@ stop event it works. .. parsed-literal:: Press Enter to continue...y - + @@ -626,13 +642,13 @@ stop event it works. -.. code:: python +.. code:: ipython3 stop_event.set() More complex live loops -.. code:: python +.. code:: ipython3 sc = Ring(scale(E3, MINOR_PENTATONIC)) @@ -649,7 +665,7 @@ More complex live loops Now a simple structure with four live loops -.. code:: python +.. code:: ipython3 import random from psonic import * @@ -709,7 +725,7 @@ Now a simple structure with four live loops .. parsed-literal:: Press Enter to continue...y - + @@ -721,28 +737,28 @@ Now a simple structure with four live loops After starting the loops you can change them -.. code:: python +.. code:: ipython3 def live_1(): sample(BD_HAUS,amp=2) sleep(0.5) pass -.. code:: python +.. code:: ipython3 def live_2(): #sample(AMBI_CHOIR, rate=0.4) #sleep(1) pass -.. code:: python +.. code:: ipython3 def live_3(): use_synth(TB303) play(E2, release=4,cutoff=120,cutoff_attack=1) sleep(4) -.. code:: python +.. code:: ipython3 def live_4(): notes = scale(E3, MINOR_PENTATONIC, num_octaves=2) @@ -752,18 +768,94 @@ After starting the loops you can change them And stop. -.. code:: python +.. code:: ipython3 stop_event.set() +Creating Sound +~~~~~~~~~~~~~~ + +.. code:: ipython3 + + from psonic import * + + synth(SINE, note=D4) + synth(SQUARE, note=D4) + synth(TRI, note=D4, amp=0.4) + +.. code:: ipython3 + + detune = 0.7 + synth(SQUARE, note = E4) + synth(SQUARE, note = E4+detune) + +.. code:: ipython3 + + detune=0.1 # Amplitude shaping + synth(SQUARE, note = E2, release = 2) + synth(SQUARE, note = E2+detune, amp = 2, release = 2) + synth(GNOISE, release = 2, amp = 1, cutoff = 60) + synth(GNOISE, release = 0.5, amp = 1, cutoff = 100) + synth(NOISE, release = 0.2, amp = 1, cutoff = 90) + +Next Step +~~~~~~~~~ + +Using FX *Not implemented yet* + +.. code:: ipython3 + + from psonic import * + + with Fx(SLICER): + synth(PROPHET,note=E2,release=8,cutoff=80) + synth(PROPHET,note=E2+4,release=8,cutoff=80) + +.. code:: ipython3 + + with Fx(SLICER, phase=0.125, probability=0.6,prob_pos=1): + synth(TB303, note=E2, cutoff_attack=8, release=8) + synth(TB303, note=E3, cutoff_attack=4, release=8) + synth(TB303, note=E4, cutoff_attack=2, release=8) + +OSC Communication (Sonic Pi Ver. 3.x or better) +----------------------------------------------- + +In Sonic Pi version 3 or better you can work with messages. + +.. code:: ipython3 + + from psonic import * + +First you need a programm in the Sonic Pi server that receives messages. +You can write it in th GUI or send one with Python. + +.. code:: ipython3 + + run("""live_loop :foo do + use_real_time + a, b, c = sync "/osc/trigger/prophet" + synth :prophet, note: a, cutoff: b, sustain: c + end """) + +Now send a message to Sonic Pi. + +.. code:: ipython3 + + send_message('/trigger/prophet', 70, 100, 8) + +.. code:: ipython3 + + stop() + More Examples ------------- -.. code:: python +.. code:: ipython3 from psonic import * -.. code:: python +.. code:: ipython3 #Inspired by Steve Reich Clapping Music @@ -776,23 +868,11 @@ More Examples if clapping[(i+k)%12] == 1: sample(DRUM_HEAVY_KICK,pan=0.5) sleep (0.25) -More Informations ------------------ +Projects that use Python-Sonic +------------------------------ -Sonic Pi -~~~~~~~~ - -.. - -OSC -~~~ - -.. - -MIDI -~~~~ - -.. +Raspberry Pi sonic-track.py a Sonic-pi Motion Track Demo +https://github.com/pageauc/sonic-track Sources ------- diff --git a/psonic.py b/psonic.py old mode 100644 new mode 100755 index da89476..c41dc17 --- a/psonic.py +++ b/psonic.py @@ -1,24 +1,24 @@ -#The MIT License (MIT) +# The MIT License (MIT) # -#Copyright (c) 2016 G. Völkl +# Copyright (c) 2016 G. Völkl # -#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: +# 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 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. +# 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. import random import time import threading @@ -35,6 +35,7 @@ class Synth: """ Synthesizer """ + def __init__(self, name): self.name = name @@ -43,6 +44,7 @@ class Sample: """ Sample """ + def __init__(self, name): self.name = name @@ -51,14 +53,26 @@ class ChordQuality: """ Chord Quality """ + def __init__(self, name, inter): self.name = name self.inter = inter + +class FxName: + """ + FX name + """ + + def __init__(self, name): + self.name = name + + class Message: """ For sending messages between threads """ + def __init__(self): self._condition = threading.Condition() @@ -70,12 +84,32 @@ def sync(self): with self._condition: self._condition.wait() # Wait for message + +class Fx: + """ + FX Effects + """ + + def __init__(self, mode, phase=0.24, probability=0, prob_pos=0): + self.mode = mode + self.phase = phase + self.probability = probability + self.prob_pos = prob_pos + + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + ## Decorator ## def in_thread(func): def wrapper(): _thread = threading.Thread(target=func) _thread.start() + return wrapper @@ -96,7 +130,7 @@ def wrapper(): A2 = 45 As2 = 46 Bb2 = As2 -B3 = 47 +B2 = 47 C3 = 48 Cs3 = 49 Db3 = Cs3 @@ -155,6 +189,7 @@ def wrapper(): ## Synthezier ## DULL_BELL = Synth('dull_bell') PRETTY_BELL = Synth('pretty_bell') +SINE = Synth('sine') SQUARE = Synth('square') PULSE = Synth('pulse') SUBPULSE = Synth('subpulse') @@ -188,16 +223,13 @@ def wrapper(): SAW = Synth('saw') BEEP = Synth('beep') TRI = Synth('tri') -DTRI = Synth('dtri') #Sonic Pi 2.10 -PLUCK = Synth('pluck') -CHIPLEAD = Synth('chiplead') +CHIPLEAD = Synth('chiplead') # Sonic Pi 2.10 CHIPBASS = Synth('chipbass') CHIPNOISE = Synth('chipnoise') -TECHSAWS = Synth('tech_saws') #Sonic Pi 2.11 +TECHSAWS = Synth('tech_saws') # Sonic Pi 2.11 SOUND_IN = Synth('sound_in') SOUND_IN_STEREO = Synth('sound_in_stereo') - ## Scale Mode (from sonic pi)## DIATONIC = 'diatonic' IONIAN = 'ionian' @@ -286,28 +318,28 @@ def wrapper(): 'diatonic': _ionian_sequence, 'ionian': _ionian_sequence, 'major': _ionian_sequence, - 'dorian': _ionian_sequence[1:]+_ionian_sequence[:1], #rotate 1 - 'phrygian': _ionian_sequence[2:]+_ionian_sequence[:2], #rotate(2) - 'lydian': _ionian_sequence[3:]+_ionian_sequence[:3], #rotate(3) - 'mixolydian': _ionian_sequence[4:]+_ionian_sequence[:4], #rotate(4) - 'aeolian': _ionian_sequence[5:]+_ionian_sequence[:5], #rotate(5) - 'minor': _ionian_sequence[5:]+_ionian_sequence[:5], #rotate(5) - 'locrian': _ionian_sequence[6:]+_ionian_sequence[:6], #rotate(6) + 'dorian': _ionian_sequence[1:] + _ionian_sequence[:1], # rotate 1 + 'phrygian': _ionian_sequence[2:] + _ionian_sequence[:2], # rotate(2) + 'lydian': _ionian_sequence[3:] + _ionian_sequence[:3], # rotate(3) + 'mixolydian': _ionian_sequence[4:] + _ionian_sequence[:4], # rotate(4) + 'aeolian': _ionian_sequence[5:] + _ionian_sequence[:5], # rotate(5) + 'minor': _ionian_sequence[5:] + _ionian_sequence[:5], # rotate(5) + 'locrian': _ionian_sequence[6:] + _ionian_sequence[:6], # rotate(6) 'hex_major6': _hex_sequence, - 'hex_dorian': _hex_sequence[1:]+_hex_sequence[:1], #rotate(1) - 'hex_phrygian': _hex_sequence[2:]+_hex_sequence[:2], #rotate(2) - 'hex_major7': _hex_sequence[3:]+_hex_sequence[:3], #rotate(3) - 'hex_sus': _hex_sequence[4:]+_hex_sequence[:4], #rotate(4) - 'hex_aeolian': _hex_sequence[5:]+_hex_sequence[:5], #rotate(5) + 'hex_dorian': _hex_sequence[1:] + _hex_sequence[:1], # rotate(1) + 'hex_phrygian': _hex_sequence[2:] + _hex_sequence[:2], # rotate(2) + 'hex_major7': _hex_sequence[3:] + _hex_sequence[:3], # rotate(3) + 'hex_sus': _hex_sequence[4:] + _hex_sequence[:4], # rotate(4) + 'hex_aeolian': _hex_sequence[5:] + _hex_sequence[:5], # rotate(5) 'minor_pentatonic': _pentatonic_sequence, 'yu': _pentatonic_sequence, - 'major_pentatonic': _pentatonic_sequence[1:]+_pentatonic_sequence[:1], #rotate(1) - 'gong': _pentatonic_sequence[1:]+_pentatonic_sequence[:1], #rotate(1) - 'egyptian': _pentatonic_sequence[2:]+_pentatonic_sequence[:2], #rotate(2) - 'shang': _pentatonic_sequence[2:]+_pentatonic_sequence[:2], #rotate(2) - 'jiao': _pentatonic_sequence[3:]+_pentatonic_sequence[:3], #rotate(3) - 'zhi': _pentatonic_sequence[4:]+_pentatonic_sequence[:4], #rotate(4) - 'ritusen': _pentatonic_sequence[4:]+_pentatonic_sequence[:4], #rotate(4) + 'major_pentatonic': _pentatonic_sequence[1:] + _pentatonic_sequence[:1], # rotate(1) + 'gong': _pentatonic_sequence[1:] + _pentatonic_sequence[:1], # rotate(1) + 'egyptian': _pentatonic_sequence[2:] + _pentatonic_sequence[:2], # rotate(2) + 'shang': _pentatonic_sequence[2:] + _pentatonic_sequence[:2], # rotate(2) + 'jiao': _pentatonic_sequence[3:] + _pentatonic_sequence[:3], # rotate(3) + 'zhi': _pentatonic_sequence[4:] + _pentatonic_sequence[:4], # rotate(4) + 'ritusen': _pentatonic_sequence[4:] + _pentatonic_sequence[:4], # rotate(4) 'whole_tone': [2, 2, 2, 2, 2, 2], 'whole': [2, 2, 2, 2, 2, 2], 'chromatic': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], @@ -355,13 +387,13 @@ def wrapper(): 'chinese': [4, 2, 1, 4, 1], 'lydian_minor': [2, 2, 2, 1, 1, 2, 2], 'i': _ionian_sequence, - 'ii': _ionian_sequence[1:]+_ionian_sequence[:1], #rotate(1) - 'iii': _ionian_sequence[2:]+_ionian_sequence[:2], #rotate(2) - 'iv': _ionian_sequence[3:]+_ionian_sequence[:3], #rotate(3) - 'v': _ionian_sequence[4:]+_ionian_sequence[:4], #rotate(4) - 'vi': _ionian_sequence[5:]+_ionian_sequence[:5], #rotate(5) - 'vii': _ionian_sequence[6:]+_ionian_sequence[:6], #rotate(6), - 'viii': _ionian_sequence[7:]+_ionian_sequence[:7]} #rotate(7) + 'ii': _ionian_sequence[1:] + _ionian_sequence[:1], # rotate(1) + 'iii': _ionian_sequence[2:] + _ionian_sequence[:2], # rotate(2) + 'iv': _ionian_sequence[3:] + _ionian_sequence[:3], # rotate(3) + 'v': _ionian_sequence[4:] + _ionian_sequence[:4], # rotate(4) + 'vi': _ionian_sequence[5:] + _ionian_sequence[:5], # rotate(5) + 'vii': _ionian_sequence[6:] + _ionian_sequence[:6], # rotate(6), + 'viii': _ionian_sequence[7:] + _ionian_sequence[:7]} # rotate(7) ## Chord Quality (from sonic pi) ## MAJOR7 = "major7" @@ -564,13 +596,24 @@ def wrapper(): TABLA_NA_S = Sample('tabla_na_s') TABLA_RE = Sample('tabla_re') - # Vinyl VINYL_BACKSPIN = Sample('vinyl_backspin') VINYL_REWIND = Sample('vinyl_rewind') VINYL_SCRATCH = Sample('vinyl_scratch') VINYL_HISS = Sample('vinyl_hiss') +## FX +BITCRUSHER = FxName('bitcrusher') +COMPRESSOR = FxName('compressor') +ECHO = FxName('echo') +FLANGER = FxName('flanger') +KRUSH = FxName('krush') +LPF = FxName('lpf') +PAN = FxName('pan') +PANSLICER = FxName('panslicer') +REVERB = FxName('reverb') +SLICER = FxName('slicer') +WOBBLE = FxName('wobble') ## Module attributes ## _current_synth = BEEP @@ -582,6 +625,31 @@ def use_synth(synth): _current_synth = synth +def synth(name, note=None, attack=None, decay=None, sustain_level=None, sustain=None, release=None, cutoff=None, + cutoff_attack=None, amp=None, pan=None): + parameters = [] + parameter = '' + + if note is not None: parameters.append('note: {0}'.format(note)) + if attack is not None: parameters.append('attack: {0}'.format(attack)) + if cutoff_attack is not None: parameters.append('cutoff_attack: {0}'.format(cutoff_attack)) + if decay is not None: parameters.append('decay: {0}'.format(decay)) + if sustain_level is not None: parameters.append('sustain_level: {0}'.format(sustain_level)) + if sustain is not None: parameters.append('sustain: {0}'.format(sustain)) + if release is not None: parameters.append('release: {0}'.format(release)) + if cutoff is not None: parameters.append('cutoff: {0}'.format(cutoff)) + + if amp is not None: parameters.append('amp: {0}'.format(amp)) + if pan is not None: parameters.append('pan: {0}'.format(pan)) + + if len(parameters) > 0: parameter = ',' + ','.join(parameters) + + command = 'synth :{0}{1}'.format(name.name, parameter) + + _debug('synth command={}'.format(command)) + synth_server.synth(command) + + def play(note, attack=None, decay=None, sustain_level=None, sustain=None, release=None, cutoff=None, cutoff_attack=None, amp=None, pan=None): parameters = [] @@ -602,9 +670,8 @@ def play(note, attack=None, decay=None, sustain_level=None, sustain=None, releas command = 'play {0}{1}'.format(note, parameter) - command = 'use_synth :{0}\n'.format(_current_synth.name) + command _debug('play command={}'.format(command)) - synthServer.run(command) + synth_server.play(command) def play_pattern_timed(notes, times, release=None): @@ -619,7 +686,7 @@ def play_pattern_timed(notes, times, release=None): for t in times: for i in notes: - play(i,release=release) + play(i, release=release) sleep(t) @@ -632,7 +699,7 @@ def play_pattern(notes): play_pattern_timed(notes, 1) -def sample(sample, rate=None, attack=None, sustain=None, release=None,beat_stretch=None, +def sample(sample, rate=None, attack=None, sustain=None, release=None, beat_stretch=None, start=None, finish=None, amp=None, pan=None): parameters = [] parameter = '' @@ -642,7 +709,7 @@ def sample(sample, rate=None, attack=None, sustain=None, release=None,beat_stret if attack is not None: parameters.append('attack: {0}'.format(attack)) if sustain is not None: parameters.append('sustain: {0}'.format(sustain)) if release is not None: parameters.append('release: {0}'.format(release)) - if beat_stretch is not None:parameters.append('beat_stretch: {0}'.format(beat_stretch)) + if beat_stretch is not None: parameters.append('beat_stretch: {0}'.format(beat_stretch)) if start is not None: parameters.append('start: {0}'.format(start)) if finish is not None: parameters.append('finish: {0}'.format(finish)) if amp is not None: parameters.append('amp: {0}'.format(amp)) @@ -656,7 +723,7 @@ def sample(sample, rate=None, attack=None, sustain=None, release=None,beat_stret command = 'sample "{0}"{1}'.format(sample, parameter) _debug('sample command={}'.format(command)) - synthServer.run(command) + synth_server.sample(command) def sleep(duration): @@ -665,7 +732,7 @@ def sleep(duration): :param duration: :return: """ - time.sleep(duration) + synth_server.sleep(duration) _debug('sleep', duration) @@ -721,6 +788,15 @@ def scale(root_note, scale_mode, num_octaves=1): return result +def run(command): + synth_server.run(command) + +def stop(): + synth_server.stop() + +def send_message(message, *parameter): + synth_server.send_message(message, *parameter) + ## Compound classes ## class Ring: @@ -747,13 +823,14 @@ def choose(self): # random choose ## Connection classes ## -class SonicPi(): +class SonicPi: """ Communiction to Sonic Pi """ UDP_IP = "127.0.0.1" UDP_PORT = 4557 + UDP_PORT_OSC_MESSAGE = 4559 GUI_ID = 'SONIC_PI_PYTHON' RUN_COMMAND = "/run-code" @@ -761,6 +838,20 @@ class SonicPi(): def __init__(self): self.client = udp_client.UDPClient(SonicPi.UDP_IP, SonicPi.UDP_PORT) + self.client_for_messages = udp_client.UDPClient(SonicPi.UDP_IP, SonicPi.UDP_PORT_OSC_MESSAGE) + + def sample(self, command): + self.run(command) + + def play(self, command): + command = 'use_synth :{0}\n'.format(_current_synth.name) + command + self.run(command) + + def synth(self, command): + self.run(command) + + def sleep(self, duration): + time.sleep(duration) def run(self, command): self.send_command(SonicPi.RUN_COMMAND, command) @@ -776,13 +867,54 @@ def test_connection(self): def send_command(self, address, argument=''): msg = osc_message_builder.OscMessageBuilder(address=address) msg.add_arg('SONIC_PI_PYTHON') - msg.add_arg(argument) + if argument != "": + msg.add_arg(argument) msg = msg.build() self.client.send(msg) + def send_message(self,message, *parameters): + msg = osc_message_builder.OscMessageBuilder(message) + for p in parameters: + msg.add_arg(p) + msg = msg.build() + self.client_for_messages.send(msg) -synthServer = SonicPi() +class SonicPiNew: + """ + Communiction to Sonic Pi + """ + + UDP_IP = "127.0.0.1" + UDP_PORT = 4559 + + def __init__(self): + self.client = udp_client.UDPClient(SonicPiNew.UDP_IP, SonicPiNew.UDP_PORT) + self.commandServer = SonicPi() + # x= 'live_loop :py do\n nv=sync "/SENDOSC"\n puts nv\n eval(nv[0])\nend' + # self.commandServer.run(x) + + def set_OSC_receiver(self, source): + self.commandServer.run(source) + + def send(self, address, *message): + msg = osc_message_builder.OscMessageBuilder(address) + for m in message: + msg.add_arg(m) + msg = msg.build() + self.client.send(msg) + + def sample(self, command): + self.send(command) + + def play(self, command): + self.send(command) + + def sleep(self, duration): + time.sleep(duration) + + +synth_server = SonicPi() ## system functions ## @@ -790,6 +922,7 @@ def send_command(self, address, argument=''): def _debug(*allargs): # simple debug function for working in different environments if __debug: print(allargs) + if __name__ == '__main__': use_synth(SAW) play(C5, amp=2, pan=-1) diff --git a/python-sonic.ipynb b/python-sonic.ipynb old mode 100644 new mode 100755 index 6f6c50d..4aef40c --- a/python-sonic.ipynb +++ b/python-sonic.ipynb @@ -38,12 +38,10 @@ ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "raw", "metadata": { "collapsed": true }, - "outputs": [], "source": [ "$ pip install python-sonic" ] @@ -83,7 +81,8 @@ "source": [ "|Version | |\n", "|--------------|------------------------------------------------------------------------------------------|\n", - "| 0.2.0 | Some changes for Sonic Pi 2.11. Simpler multi-threading with decorator *@in_thread*. Messaging with *cue* and *sync*. |" + "| 0.2.0 | Some changes for Sonic Pi 2.11. Simpler multi-threading with decorator *@in_thread*. Messaging with *cue* and *sync*. |\n", + "| 0.3.0 | OSC Communication | " ] }, { @@ -102,9 +101,9 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -120,9 +119,9 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 5, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -138,9 +137,9 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 10, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -160,9 +159,9 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 11, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -204,7 +203,7 @@ "cell_type": "code", "execution_count": 6, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -222,9 +221,9 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 12, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -247,9 +246,9 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 13, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -279,7 +278,7 @@ "cell_type": "code", "execution_count": 4, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -389,7 +388,7 @@ "cell_type": "code", "execution_count": 5, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -414,9 +413,7 @@ { "cell_type": "code", "execution_count": 18, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "ename": "KeyboardInterrupt", @@ -452,7 +449,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -492,9 +489,7 @@ { "cell_type": "code", "execution_count": 1, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -569,9 +564,7 @@ { "cell_type": "code", "execution_count": 6, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -630,7 +623,7 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -668,7 +661,7 @@ "cell_type": "code", "execution_count": 1, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -691,7 +684,7 @@ "cell_type": "code", "execution_count": 2, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -716,7 +709,7 @@ "cell_type": "code", "execution_count": 3, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -734,9 +727,9 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -754,13 +747,23 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[48, 50, 52, 53, 55, 57, 59, 60]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import random\n", + "from psonic import *\n", "\n", "s = scale(C3, MAJOR)\n", "s" @@ -768,14 +771,27 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "s.reverse()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, "metadata": { "collapsed": true }, "outputs": [], "source": [ - "play_pattern_timed(s.reverse(), 0.125, release = 0.1)\n", - "play_pattern_timed(random.shuffle(s), 0.125, release = 0.1)" + "\n", + "play_pattern_timed(s, 0.125, release = 0.1)\n", + "random.shuffle(s)\n", + "play_pattern_timed(s, 0.125, release = 0.1)" ] }, { @@ -795,9 +811,7 @@ { "cell_type": "code", "execution_count": 5, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -875,7 +889,7 @@ "cell_type": "code", "execution_count": 8, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -903,9 +917,7 @@ { "cell_type": "code", "execution_count": 10, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -998,9 +1010,7 @@ { "cell_type": "code", "execution_count": 1, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -1084,7 +1094,7 @@ "cell_type": "code", "execution_count": 4, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -1112,9 +1122,7 @@ { "cell_type": "code", "execution_count": 11, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -1229,7 +1237,7 @@ "cell_type": "code", "execution_count": 14, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -1243,7 +1251,7 @@ "cell_type": "code", "execution_count": 13, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ @@ -1276,87 +1284,223 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## More Examples" + "### Creating Sound" ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 4, + "metadata": { + "collapsed": true, + "scrolled": true + }, + "outputs": [], + "source": [ + "from psonic import *\n", + "\n", + "synth(SINE, note=D4)\n", + "synth(SQUARE, note=D4)\n", + "synth(TRI, note=D4, amp=0.4)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, "metadata": { "collapsed": true }, "outputs": [], "source": [ - "from psonic import *" + "detune = 0.7\n", + "synth(SQUARE, note = E4)\n", + "synth(SQUARE, note = E4+detune)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": { - "collapsed": false + "collapsed": true }, "outputs": [], "source": [ - "#Inspired by Steve Reich Clapping Music\n", - "\n", - "clapping = [1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0]\n", + "detune=0.1 # Amplitude shaping\n", + "synth(SQUARE, note = E2, release = 2)\n", + "synth(SQUARE, note = E2+detune, amp = 2, release = 2)\n", + "synth(GNOISE, release = 2, amp = 1, cutoff = 60)\n", + "synth(GNOISE, release = 0.5, amp = 1, cutoff = 100)\n", + "synth(NOISE, release = 0.2, amp = 1, cutoff = 90)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Next Step" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Using FX *Not implemented yet*" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from psonic import *\n", "\n", - "for i in range(13):\n", - " for j in range(4):\n", - " for k in range(12): \n", - " if clapping[k] ==1 : sample(DRUM_SNARE_SOFT,pan=-0.5)\n", - " if clapping[(i+k)%12] == 1: sample(DRUM_HEAVY_KICK,pan=0.5)\n", - " sleep (0.25)" + "with Fx(SLICER):\n", + " synth(PROPHET,note=E2,release=8,cutoff=80)\n", + " synth(PROPHET,note=E2+4,release=8,cutoff=80)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "with Fx(SLICER, phase=0.125, probability=0.6,prob_pos=1):\n", + " synth(TB303, note=E2, cutoff_attack=8, release=8)\n", + " synth(TB303, note=E3, cutoff_attack=4, release=8)\n", + " synth(TB303, note=E4, cutoff_attack=2, release=8)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## More Informations" + "## OSC Communication (Sonic Pi Ver. 3.x or better)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Sonic Pi" + "In Sonic Pi version 3 or better you can work with messages." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from psonic import *" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - ".." + "First you need a programm in the Sonic Pi server that receives messages. You can write it in th GUI or send one with Python." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "run(\"\"\"live_loop :foo do\n", + " use_real_time\n", + " a, b, c = sync \"/osc/trigger/prophet\"\n", + " synth :prophet, note: a, cutoff: b, sustain: c\n", + "end \"\"\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### OSC" + "Now send a message to Sonic Pi." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "send_message('/trigger/prophet', 70, 100, 8)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "stop()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - ".." + "## More Examples" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from psonic import *" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "#Inspired by Steve Reich Clapping Music\n", + "\n", + "clapping = [1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0]\n", + "\n", + "for i in range(13):\n", + " for j in range(4):\n", + " for k in range(12): \n", + " if clapping[k] ==1 : sample(DRUM_SNARE_SOFT,pan=-0.5)\n", + " if clapping[(i+k)%12] == 1: sample(DRUM_HEAVY_KICK,pan=0.5)\n", + " sleep (0.25)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### MIDI" + "## Projects that use Python-Sonic" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - ".." + "Raspberry Pi sonic-track.py a Sonic-pi Motion Track Demo https://github.com/pageauc/sonic-track" ] }, { @@ -1393,7 +1537,7 @@ "metadata": { "anaconda-cloud": {}, "kernelspec": { - "display_name": "Python [default]", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -1407,9 +1551,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.5.2" + "version": "3.6.2" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 1 } diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 index 8664551..37e4089 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ setup( name='python-sonic', - version='0.2.0', + version='0.3.0', description='Programming Music with Sonic Pi or Supercollider', long_description=long_description, url='https://github.com/gkvoelkl/python-sonic', @@ -29,6 +29,7 @@ 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', ], keywords= [ From 4caf749aa53029c18d22e5db7eb52a7e37910445 Mon Sep 17 00:00:00 2001 From: gkvoelkl Date: Mon, 7 Aug 2017 19:05:34 +0200 Subject: [PATCH 05/49] Version 0.3.0 --- psonic.py | 1 + 1 file changed, 1 insertion(+) diff --git a/psonic.py b/psonic.py index c41dc17..3f9e033 100755 --- a/psonic.py +++ b/psonic.py @@ -19,6 +19,7 @@ # 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. + import random import time import threading From e76365d43021b0065b77686e0669909825a74df2 Mon Sep 17 00:00:00 2001 From: gkvoelkl Date: Mon, 7 Aug 2017 19:08:48 +0200 Subject: [PATCH 06/49] Version 0.3.0 --- psonic.py | 1 - 1 file changed, 1 deletion(-) diff --git a/psonic.py b/psonic.py index 3f9e033..c41dc17 100755 --- a/psonic.py +++ b/psonic.py @@ -19,7 +19,6 @@ # 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. - import random import time import threading From 6076e3b7887ee39cb56dc47261f9fb47e5ebd0bb Mon Sep 17 00:00:00 2001 From: Jan Siqueira Date: Mon, 9 Oct 2017 21:25:27 -0300 Subject: [PATCH 07/49] Update, sort and add duration of samples Listing all samples from Sonic Pi 3.0.1 --- psonic.py | 266 +++++++++++++++++++++++++++++------------------------- 1 file changed, 144 insertions(+), 122 deletions(-) diff --git a/psonic.py b/psonic.py index c41dc17..e36a68d 100755 --- a/psonic.py +++ b/psonic.py @@ -45,8 +45,9 @@ class Sample: Sample """ - def __init__(self, name): + def __init__(self, name, duration): self.name = name + self.duration = duration class ChordQuality: @@ -462,145 +463,157 @@ def wrapper(): ## Sample ## ## Drum Sounds -DRUM_HEAVY_KICK = Sample('drum_heavy_kick') -DRUM_TOM_MID_SOFT = Sample('drum_tom_mid_soft') -DRUM_TOM_MID_HARD = Sample('drum_tom_mid_hard') -DRUM_TOM_LO_SOFT = Sample('drum_tom_lo_soft') -DRUM_TOM_LO_HARD = Sample('drum_tom_lo_hard') -DRUM_TOM_HI_SOFT = Sample('drum_tom_hi_soft') -DRUM_TOM_HI_HARD = Sample('drum_tom_hi_hard') -DRUM_SPLASH_SOFT = Sample('drum_splash_soft') -DRUM_SPLASH_HARD = Sample('drum_splash_hard') -DRUM_SNARE_SOFT = Sample('drum_snare_soft') -DRUM_SNARE_HARD = Sample('drum_snare_hard') -DRUM_CYMBAL_SOFT = Sample('drum_cymbal_soft') -DRUM_CYMBAL_HARD = Sample('drum_cymbal_hard') -DRUM_CYMBAL_OPEN = Sample('drum_cymbal_open') -DRUM_CYMBAL_CLOSED = Sample('drum_cymbal_closed') -DRUM_CYMBAL_PEDAL = Sample('drum_cymbal_pedal') -DRUM_BASS_SOFT = Sample('drum_bass_soft') -DRUM_BASS_HARD = Sample('drum_bass_hard') -DRUM_COWBELL = Sample('drum_cowbell') -DRUM_ROLL = Sample('drum_roll') +DRUM_BASS_HARD = Sample('drum_bass_hard', 0.6951927437641723) +DRUM_BASS_SOFT = Sample('drum_bass_soft', 0.5624489795918367) +DRUM_COWBELL = Sample('drum_cowbell', 0.34988662131519277) +DRUM_CYMBAL_CLOSED = Sample('drum_cymbal_closed', 0.2069387755102041) +DRUM_CYMBAL_HARD = Sample('drum_cymbal_hard', 1.6388208616780044) +DRUM_CYMBAL_OPEN = Sample('drum_cymbal_open', 1.8043764172335601) +DRUM_CYMBAL_PEDAL = Sample('drum_cymbal_pedal', 0.2721995464852608) +DRUM_CYMBAL_SOFT = Sample('drum_cymbal_soft', 0.9497278911564626) +DRUM_HEAVY_KICK = Sample('drum_heavy_kick', 0.2701360544217687) +DRUM_ROLL = Sample('drum_roll', 6.241678004535148) +DRUM_SNARE_HARD = Sample('drum_snare_hard', 0.44492063492063494) +DRUM_SNARE_SOFT = Sample('drum_snare_soft', 0.3147845804988662) +DRUM_SPLASH_HARD = Sample('drum_splash_hard', 2.5961904761904764) +DRUM_SPLASH_SOFT = Sample('drum_splash_soft', 1.857981859410431) +DRUM_TOM_HI_HARD = Sample('drum_tom_hi_hard', 0.7376643990929705) +DRUM_TOM_HI_SOFT = Sample('drum_tom_hi_soft', 0.6290249433106576) +DRUM_TOM_LO_HARD = Sample('drum_tom_lo_hard', 1.0002267573696144) +DRUM_TOM_LO_SOFT = Sample('drum_tom_lo_soft', 0.8634013605442177) +DRUM_TOM_MID_HARD = Sample('drum_tom_mid_hard', 0.7342176870748299) +DRUM_TOM_MID_SOFT = Sample('drum_tom_mid_soft', 0.6721315192743764) ## Electric Sounds -ELEC_TRIANGLE = Sample('elec_triangle') -ELEC_SNARE = Sample('elec_snare') -ELEC_LO_SNARE = Sample('elec_lo_snare') -ELEC_HI_SNARE = Sample('elec_hi_snare') -ELEC_MID_SNARE = Sample('elec_mid_snare') -ELEC_CYMBAL = Sample('elec_cymbal') -ELEC_SOFT_KICK = Sample('elec_soft_kick') -ELEC_FILT_SNARE = Sample('elec_filt_snare') -ELEC_FUZZ_TOM = Sample('elec_fuzz_tom') -ELEC_CHIME = Sample('elec_chime') -ELEC_BONG = Sample('elec_bong') -ELEC_TWANG = Sample('elec_twang') -ELEC_WOOD = Sample('elec_wood') -ELEC_POP = Sample('elec_pop') -ELEC_BEEP = Sample('elec_beep') -ELEC_BLIP = Sample('elec_blip') -ELEC_BLIP2 = Sample('elec_blip2') -ELEC_PING = Sample('elec_ping') -ELEC_BELL = Sample('elec_bell') -ELEC_FLIP = Sample('elec_flip') -ELEC_TICK = Sample('elec_tick') -ELEC_HOLLOW_KICK = Sample('elec_hollow_kick') -ELEC_TWIP = Sample('elec_twip') -ELEC_PLIP = Sample('elec_plip') -ELEC_BLUP = Sample('elec_blup') +ELEC_BEEP = Sample('elec_beep', 0.10578231292517007) +ELEC_BELL = Sample('elec_bell', 0.27825396825396825) +ELEC_BLIP = Sample('elec_blip', 0.15816326530612246) +ELEC_BLIP2 = Sample('elec_blip2', 0.1840816326530612) +ELEC_BLUP = Sample('elec_blup', 0.6632199546485261) +ELEC_BONG = Sample('elec_bong', 0.3464852607709751) +ELEC_CHIME = Sample('elec_chime', 2.2775510204081635) +ELEC_CYMBAL = Sample('elec_cymbal', 0.563219954648526) +ELEC_FILT_SNARE = Sample('elec_filt_snare', 1.5158503401360544) +ELEC_FLIP = Sample('elec_flip', 0.07476190476190477) +ELEC_FUZZ_TOM = Sample('elec_fuzz_tom', 0.37179138321995464) +ELEC_HI_SNARE = Sample('elec_hi_snare', 0.19736961451247165) +ELEC_HOLLOW_KICK = Sample('elec_hollow_kick', 0.15564625850340136) +ELEC_LO_SNARE = Sample('elec_lo_snare', 0.6607936507936508) +ELEC_MID_SNARE = Sample('elec_mid_snare', 0.7256235827664399) +ELEC_PING = Sample('elec_ping', 0.21226757369614513) +ELEC_PLIP = Sample('elec_plip', 0.19882086167800453) +ELEC_POP = Sample('elec_pop', 0.08680272108843537) +ELEC_SNARE = Sample('elec_snare', 0.3893197278911565) +ELEC_SOFT_KICK = Sample('elec_soft_kick', 0.1364172335600907) +ELEC_TICK = Sample('elec_tick', 0.01943310657596372) +ELEC_TRIANGLE = Sample('elec_triangle', 0.22294784580498866) +ELEC_TWANG = Sample('elec_twang', 0.6243083900226757) +ELEC_TWIP = Sample('elec_twip', 0.10140589569160997) +ELEC_WOOD = Sample('elec_wood', 0.47811791383219954) ## Sounds featuring guitars -GUIT_HARMONICS = Sample('guit_harmonics') -GUIT_E_FIFTHS = Sample('guit_e_fifths') -GUIT_E_SLIDE = Sample('guit_e_slide') +GUIT_E_FIFTHS = Sample('guit_e_fifths', 5.971791383219955) +GUIT_E_SLIDE = Sample('guit_e_slide', 4.325192743764172) +GUIT_EM9 = Sample('guit_em9', 9.972063492063493) +GUIT_HARMONICS = Sample('guit_harmonics', 3.5322675736961453) ## Miscellaneous Sounds -MISC_BURP = Sample('misc_burp') -MISC_CROW = Sample('misc_crow') -MISC_CINEBOOM = Sample('misc_cineboom') +MISC_BURP = Sample('misc_burp', 0.7932879818594104) +MISC_CINEBOOM = Sample('misc_cineboom', 7.922675736961451) +MISC_CROW = Sample('misc_crow', 0.48063492063492064) ## Percurssive Sounds -PERC_BELL = Sample('perc_bell') -PERC_SWASH = Sample('perc_swash') -PERC_TILL = Sample('perc_till') +PERC_BELL = Sample('perc_bell', 6.719206349206349) +PERC_SNAP = Sample('perc_snap', 0.30795918367346936) +PERC_SNAP2 = Sample('perc_snap2', 0.17414965986394557) +PERC_SWASH = Sample('perc_swash', 0.3195011337868481) +PERC_TILL = Sample('perc_till', 2.665736961451247) ## Ambient Sounds -AMBI_SOFT_BUZZ = Sample('ambi_soft_buzz') -AMBI_SWOOSH = Sample('ambi_swoosh') -AMBI_DRONE = Sample('ambi_drone') -AMBI_GLASS_HUM = Sample('ambi_glass_hum') -AMBI_GLASS_RUB = Sample('ambi_glass_rub') -AMBI_HAUNTED_HUM = Sample('ambi_haunted_hum') -AMBI_PIANO = Sample('ambi_piano') -AMBI_LUNAR_LAND = Sample('ambi_lunar_land') -AMBI_DARK_WOOSH = Sample('ambi_dark_woosh') -AMBI_CHOIR = Sample('ambi_choir') +AMBI_CHOIR = Sample('ambi_choir', 1.5715419501133787) +AMBI_DARK_WOOSH = Sample('ambi_dark_woosh', 3.7021315192743764) +AMBI_DRONE = Sample('ambi_drone', 4.40843537414966) +AMBI_GLASS_HUM = Sample('ambi_glass_hum', 10.0) +AMBI_GLASS_RUB = Sample('ambi_glass_rub', 3.1493650793650794) +AMBI_HAUNTED_HUM = Sample('ambi_haunted_hum', 9.78156462585034) +AMBI_LUNAR_LAND = Sample('ambi_lunar_land', 7.394240362811791) +AMBI_PIANO = Sample('ambi_piano', 2.811746031746032) +AMBI_SOFT_BUZZ = Sample('ambi_soft_buzz', 0.7821541950113379) +AMBI_SWOOSH = Sample('ambi_swoosh', 1.8484580498866212) ## Bass Sounds -BASS_HIT_C = Sample('bass_hit_c') -BASS_HARD_C = Sample('bass_hard_c') -BASS_THICK_C = Sample('bass_thick_c') -BASS_DROP_C = Sample('bass_drop_c') -BASS_WOODSY_C = Sample('bass_woodsy_c') -BASS_VOXY_C = Sample('bass_voxy_c') -BASS_VOXY_HIT_C = Sample('bass_voxy_hit_c') -BASS_DNB_F = Sample('bass_dnb_f') - -BD_808 = Sample('bd_808') -BD_ADA = Sample('bd_ada') -BD_BOOM = Sample('bd_boom') -BD_FAT = Sample('bd_fat') -BD_GAS = Sample('bd_gas') -BD_HAUS = Sample('bd_haus') -BD_KLUB = Sample('bd_klub') -BD_PURE = Sample('bd_pure') -BD_SONE = Sample('bd_sone') -BD_TEK = Sample('bd_tek') -BD_ZOME = Sample('bd_zome') -BD_ZUM = Sample('bd_zum') +BASS_DNB_F = Sample('bass_dnb_f', 0.8705442176870748) +BASS_DROP_C = Sample('bass_drop_c', 2.3589115646258505) +BASS_HARD_C = Sample('bass_hard_c', 1.5) +BASS_HIT_C = Sample('bass_hit_c', 0.6092517006802721) +BASS_THICK_C = Sample('bass_thick_c', 3.9680725623582767) +BASS_VOXY_C = Sample('bass_voxy_c', 6.23469387755102) +BASS_VOXY_HIT_C = Sample('bass_voxy_hit_c', 0.457437641723356) +BASS_WOODSY_C = Sample('bass_woodsy_c', 3.252267573696145) + +## Snare Drums Sounds +SN_DOLF = Sample('sn_dolf', 0.37759637188208617) +SN_DUB = Sample('sn_dub', 0.2781179138321995) +SN_ZOME = Sample('sn_zome', 0.4787528344671202) + +## Bass Drums Sounds +BD_808 = Sample('bd_808', 0.5597505668934241) +BD_ADA = Sample('bd_ada', 0.10179138321995465) +BD_BOOM = Sample('bd_boom', 1.7142857142857142) +BD_FAT = Sample('bd_fat', 0.23219954648526078) +BD_GAS = Sample('bd_gas', 0.4471428571428571) +BD_HAUS = Sample('bd_haus', 0.21993197278911564) +BD_KLUB = Sample('bd_klub', 0.368843537414966) +BD_PURE = Sample('bd_pure', 0.43324263038548755) +BD_SONE = Sample('bd_sone', 0.4089115646258503) +BD_TEK = Sample('bd_tek', 0.24024943310657595) +BD_ZOME = Sample('bd_zome', 0.45972789115646256) +BD_ZUM = Sample('bd_zum', 0.13158730158730159) ## Sounds for Looping -LOOP_INDUSTRIAL = Sample('loop_industrial') -LOOP_COMPUS = Sample('loop_compus') -LOOP_AMEN = Sample('loop_amen') -LOOP_AMEN_FULL = Sample('loop_amen_full') -LOOP_SAFARI = Sample('loop_safari') -LOOP_TABLA = Sample('loop_tabla') +LOOP_AMEN = Sample('loop_amen', 1.753310657596372) +LOOP_AMEN_FULL = Sample('loop_amen_full', 6.857142857142857) +LOOP_BREAKBEAT = Sample('loop_breakbeat', 1.9047619047619047) +LOOP_COMPUS = Sample('loop_compus', 6.486485260770975) +LOOP_GARZUL = Sample('loop_garzul', 8.0) +LOOP_INDUSTRIAL = Sample('loop_industrial', 0.8837414965986394) +LOOP_MIKA = Sample('loop_mika', 8.0) +LOOP_SAFARI = Sample('loop_safari', 8.005079365079364) +LOOP_TABLA = Sample('loop_tabla', 10.673990929705216) ## Tabla -TABLA_TAS1 = Sample('tabla_tas1') -TABLA_TAS2 = Sample('tabla_tas2') -TABLA_TAS3 = Sample('tabla_tas3') -TABLA_KE1 = Sample('tabla_ke1') -TABLA_KE2 = Sample('tabla_ke2') -TABLA_KE3 = Sample('tabla_ke3') -TABLA_NA = Sample('tabla_na') -TABLA_NA_O = Sample('tabla_na_o') -TABLA_TUN1 = Sample('tabla_tun1') -TABLA_TUN2 = Sample('tabla_tun2') -TABLA_TUN3 = Sample('tabla_tun3') -TABLA_TE1 = Sample('tabla_te1') -TABLA_TE2 = Sample('tabla_te2') -TABLA_TE_NE = Sample('tabla_te_ne') -TABLA_TE_M = Sample('tabla_te_m') -TABLA_GHE1 = Sample('tabla_ghe1') -TABLA_GHE2 = Sample('tabla_ghe2') -TABLA_GHE3 = Sample('tabla_ghe3') -TABLA_GHE4 = Sample('tabla_ghe4') -TABLA_GHE5 = Sample('tabla_ghe5') -TABLA_GHE6 = Sample('tabla_ghe6') -TABLA_GHE7 = Sample('tabla_ghe7') -TABLA_GHE8 = Sample('tabla_ghe8') -TABLA_DHEC = Sample('tabla_dhec') -TABLA_NA_S = Sample('tabla_na_s') -TABLA_RE = Sample('tabla_re') +TABLA_DHEC = Sample('tabla_dhec', 0.3250793650793651) +TABLA_GHE1 = Sample('tabla_ghe1', 0.5912244897959184) +TABLA_GHE2 = Sample('tabla_ghe2', 2.6607256235827665) +TABLA_GHE3 = Sample('tabla_ghe3', 2.3908163265306124) +TABLA_GHE4 = Sample('tabla_ghe4', 0.7960997732426304) +TABLA_GHE5 = Sample('tabla_ghe5', 3.560045351473923) +TABLA_GHE6 = Sample('tabla_ghe6', 3.6011337868480724) +TABLA_GHE7 = Sample('tabla_ghe7', 2.1512698412698414) +TABLA_GHE8 = Sample('tabla_ghe8', 0.5817913832199546) +TABLA_KE1 = Sample('tabla_ke1', 0.04342403628117914) +TABLA_KE2 = Sample('tabla_ke2', 0.04403628117913832) +TABLA_KE3 = Sample('tabla_ke3', 0.07571428571428572) +TABLA_NA = Sample('tabla_na', 0.7198185941043084) +TABLA_NA_O = Sample('tabla_na_o', 1.4889795918367348) +TABLA_NA_S = Sample('tabla_na_s', 0.23582766439909297) +TABLA_RE = Sample('tabla_re', 0.2815419501133787) +TABLA_TAS1 = Sample('tabla_tas1', 1.1116553287981858) +TABLA_TAS2 = Sample('tabla_tas2', 1.4338321995464853) +TABLA_TAS3 = Sample('tabla_tas3', 1.2364625850340136) +TABLA_TE1 = Sample('tabla_te1', 0.17777777777777778) +TABLA_TE2 = Sample('tabla_te2', 0.33233560090702946) +TABLA_TE_M = Sample('tabla_te_m', 0.28879818594104306) +TABLA_TE_NE = Sample('tabla_te_ne', 0.15310657596371882) +TABLA_TUN1 = Sample('tabla_tun1', 2.3394104308390022) +TABLA_TUN2 = Sample('tabla_tun2', 2.693514739229025) +TABLA_TUN3 = Sample('tabla_tun3', 2.0956009070294783) # Vinyl -VINYL_BACKSPIN = Sample('vinyl_backspin') -VINYL_REWIND = Sample('vinyl_rewind') -VINYL_SCRATCH = Sample('vinyl_scratch') -VINYL_HISS = Sample('vinyl_hiss') +VINYL_BACKSPIN = Sample('vinyl_backspin', 1.06124716553288) +VINYL_HISS = Sample('vinyl_hiss', 8.0) +VINYL_REWIND = Sample('vinyl_rewind', 2.6804761904761905) +VINYL_SCRATCH = Sample('vinyl_scratch', 0.27383219954648524) ## FX BITCRUSHER = FxName('bitcrusher') @@ -735,6 +748,15 @@ def sleep(duration): synth_server.sleep(duration) _debug('sleep', duration) +def sample_duration(sample): + """ + Returns the duration of the sample (in seconds) + + :param sample: + :return: number + """ + return sample.duration + def one_in(max): """ From 62567386065cd9322896f473e2e7cd8fe91bdbb8 Mon Sep 17 00:00:00 2001 From: Toomas Ormisson Date: Sun, 18 Feb 2018 22:06:14 +0200 Subject: [PATCH 08/49] Fixed file permissions and added some structure. No implementation changes. --- .gitignore | 62 +++++++++++++++++++++++++++++++++++ LICENSE | 0 README.rst | 0 psonic/__init__.py | 2 ++ psonic/api.py | 1 + psonic.py => psonic/psonic.py | 0 python-sonic.ipynb | 0 setup.py | 40 +++++++++++++--------- tests/__init__.py | 0 tests/conftest.py | 0 tests/test_psonic.py | 0 11 files changed, 89 insertions(+), 16 deletions(-) create mode 100644 .gitignore mode change 100755 => 100644 LICENSE mode change 100755 => 100644 README.rst create mode 100644 psonic/__init__.py create mode 100644 psonic/api.py rename psonic.py => psonic/psonic.py (100%) mode change 100755 => 100644 mode change 100755 => 100644 python-sonic.ipynb mode change 100755 => 100644 setup.py create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/test_psonic.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..71e6852 --- /dev/null +++ b/.gitignore @@ -0,0 +1,62 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# pyenv python configuration file +.python-version diff --git a/LICENSE b/LICENSE old mode 100755 new mode 100644 diff --git a/README.rst b/README.rst old mode 100755 new mode 100644 diff --git a/psonic/__init__.py b/psonic/__init__.py new file mode 100644 index 0000000..973a698 --- /dev/null +++ b/psonic/__init__.py @@ -0,0 +1,2 @@ +from .api import * + diff --git a/psonic/api.py b/psonic/api.py new file mode 100644 index 0000000..37bb5dc --- /dev/null +++ b/psonic/api.py @@ -0,0 +1 @@ +from .psonic import * diff --git a/psonic.py b/psonic/psonic.py old mode 100755 new mode 100644 similarity index 100% rename from psonic.py rename to psonic/psonic.py diff --git a/python-sonic.ipynb b/python-sonic.ipynb old mode 100755 new mode 100644 diff --git a/setup.py b/setup.py old mode 100755 new mode 100644 index 37e4089..94b1e31 --- a/setup.py +++ b/setup.py @@ -9,6 +9,21 @@ with open(path.join(here, 'README.rst'), encoding='utf-8') as f: long_description = f.read() +requirements = [ + 'python-osc', + +] + +setup_requirements = [ + 'pytest-runner', + +] + +test_requirements = [ + 'pytest', +] + + setup( name='python-sonic', version='0.3.0', @@ -17,9 +32,13 @@ url='https://github.com/gkvoelkl/python-sonic', author='gkvoelkl', author_email='gkvoelkl@nelson-games.de', - + packages=[ + 'psonic', + ], license='MIT', - + zip_safe=False, + include_package_data=True, + install_requires=requirements, classifiers=[ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', @@ -31,7 +50,6 @@ 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', ], - keywords= [ 'music', 'sonic pi', @@ -42,18 +60,8 @@ 'supercollider', 'synthesis' ], - - #packages=find_packages(), - py_modules=['psonic'], - install_requires=['python-osc'], - - # To provide executable scripts, use entry points in preference to the - # "scripts" keyword. Entry points provide cross-platform support and allow - # pip to create the appropriate form of executable for the target platform. - #entry_points={ - # 'console_scripts': [ - # 'sample=sample:main', - # ], - #}, + test_suite='tests', + tests_require=test_requirements, + setup_requires=setup_requirements, ) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_psonic.py b/tests/test_psonic.py new file mode 100644 index 0000000..e69de29 From 2a72b5173ee2d07bf5fbe87130a9c9da61668762 Mon Sep 17 00:00:00 2001 From: Toomas Ormisson Date: Sun, 18 Feb 2018 22:33:24 +0200 Subject: [PATCH 09/49] Aded test for original functionality --- .gitignore | 1 + tests/test_psonic.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/.gitignore b/.gitignore index 71e6852..03fd1c5 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ parts/ sdist/ var/ *.egg-info/ +.pytest_cache/ .installed.cfg *.egg diff --git a/tests/test_psonic.py b/tests/test_psonic.py index e69de29..a6e3231 100644 --- a/tests/test_psonic.py +++ b/tests/test_psonic.py @@ -0,0 +1,37 @@ +from psonic import * + +def test_originl_behaviour(): + play(72) + play(G5) + sleep(0.5) + play(72,amp=2) + play(74,pan=-1) + use_synth(SAW) + use_synth(PROPHET) + play( + 60, attack=0.5, decay=1, sustain_level=0.4, + sustain=2, release=0.5 + ) + sample( + LOOP_AMEN, start=0.5, finish=0.8, + rate=-0.2,attack=0.3,release=1 + ) + sample( + DRUM_CYMBAL_OPEN, attack=0.01, + sustain=0.3, release=0.1 + ) + play([64, 67, 71], amp = 0.3) + play(chord(E4, MINOR7)) + play_pattern_timed( + scale( + C3, MAJOR_PENTATONIC, num_octaves = 2 + ), + 0.125, release = 0.1, + ) + synth(TRI, note=D4, amp=0.4) + detune = 0.7 + synth(SQUARE, note = E4+detune) + synth(GNOISE, release = 0.5, amp = 1, cutoff = 100) + + + From 9fffa582fba3f4f0e8f197611b2add4c2bda6470 Mon Sep 17 00:00:00 2001 From: Toomas Ormisson Date: Sun, 18 Feb 2018 22:53:03 +0200 Subject: [PATCH 10/49] Separated samples and loops from psonic.py --- psonic/api.py | 20 ++++ psonic/psonic.py | 173 +------------------------------ psonic/samples/__init__.py | 9 ++ psonic/samples/ambient.py | 29 ++++++ psonic/samples/bass.py | 25 +++++ psonic/samples/drums.py | 84 +++++++++++++++ psonic/samples/electric.py | 58 +++++++++++ psonic/samples/guitars.py | 18 ++++ psonic/samples/loops/__init__.py | 12 +++ psonic/samples/misc.py | 15 +++ psonic/samples/percussions.py | 19 ++++ psonic/samples/tabla.py | 62 +++++++++++ psonic/samples/vinyl.py | 16 +++ setup.py | 2 + 14 files changed, 374 insertions(+), 168 deletions(-) create mode 100644 psonic/samples/__init__.py create mode 100644 psonic/samples/ambient.py create mode 100644 psonic/samples/bass.py create mode 100644 psonic/samples/drums.py create mode 100644 psonic/samples/electric.py create mode 100644 psonic/samples/guitars.py create mode 100644 psonic/samples/loops/__init__.py create mode 100644 psonic/samples/misc.py create mode 100644 psonic/samples/percussions.py create mode 100644 psonic/samples/tabla.py create mode 100644 psonic/samples/vinyl.py diff --git a/psonic/api.py b/psonic/api.py index 37bb5dc..a81fe0a 100644 --- a/psonic/api.py +++ b/psonic/api.py @@ -1 +1,21 @@ from .psonic import * +from .samples import * +from .samples.loops import * +from .samples.ambient import * +from .samples.bass import * +from .samples.drums import * +from .samples.electric import * +from .samples.guitars import * +from .samples.misc import * +from .samples.percussions import * +from .samples.tabla import * +from .samples.vinyl import * + + + + + + + + + diff --git a/psonic/psonic.py b/psonic/psonic.py index e36a68d..bec7edc 100644 --- a/psonic/psonic.py +++ b/psonic/psonic.py @@ -22,7 +22,7 @@ import random import time import threading - +from psonic.samples import Sample from pythonosc import osc_message_builder # osc support from pythonosc import udp_client @@ -40,16 +40,6 @@ def __init__(self, name): self.name = name -class Sample: - """ - Sample - """ - - def __init__(self, name, duration): - self.name = name - self.duration = duration - - class ChordQuality: """ Chord Quality @@ -460,161 +450,6 @@ def wrapper(): "diminished7": [0, 3, 6, 9], "i7": [0, 3, 6, 9]} -## Sample ## - -## Drum Sounds -DRUM_BASS_HARD = Sample('drum_bass_hard', 0.6951927437641723) -DRUM_BASS_SOFT = Sample('drum_bass_soft', 0.5624489795918367) -DRUM_COWBELL = Sample('drum_cowbell', 0.34988662131519277) -DRUM_CYMBAL_CLOSED = Sample('drum_cymbal_closed', 0.2069387755102041) -DRUM_CYMBAL_HARD = Sample('drum_cymbal_hard', 1.6388208616780044) -DRUM_CYMBAL_OPEN = Sample('drum_cymbal_open', 1.8043764172335601) -DRUM_CYMBAL_PEDAL = Sample('drum_cymbal_pedal', 0.2721995464852608) -DRUM_CYMBAL_SOFT = Sample('drum_cymbal_soft', 0.9497278911564626) -DRUM_HEAVY_KICK = Sample('drum_heavy_kick', 0.2701360544217687) -DRUM_ROLL = Sample('drum_roll', 6.241678004535148) -DRUM_SNARE_HARD = Sample('drum_snare_hard', 0.44492063492063494) -DRUM_SNARE_SOFT = Sample('drum_snare_soft', 0.3147845804988662) -DRUM_SPLASH_HARD = Sample('drum_splash_hard', 2.5961904761904764) -DRUM_SPLASH_SOFT = Sample('drum_splash_soft', 1.857981859410431) -DRUM_TOM_HI_HARD = Sample('drum_tom_hi_hard', 0.7376643990929705) -DRUM_TOM_HI_SOFT = Sample('drum_tom_hi_soft', 0.6290249433106576) -DRUM_TOM_LO_HARD = Sample('drum_tom_lo_hard', 1.0002267573696144) -DRUM_TOM_LO_SOFT = Sample('drum_tom_lo_soft', 0.8634013605442177) -DRUM_TOM_MID_HARD = Sample('drum_tom_mid_hard', 0.7342176870748299) -DRUM_TOM_MID_SOFT = Sample('drum_tom_mid_soft', 0.6721315192743764) - -## Electric Sounds -ELEC_BEEP = Sample('elec_beep', 0.10578231292517007) -ELEC_BELL = Sample('elec_bell', 0.27825396825396825) -ELEC_BLIP = Sample('elec_blip', 0.15816326530612246) -ELEC_BLIP2 = Sample('elec_blip2', 0.1840816326530612) -ELEC_BLUP = Sample('elec_blup', 0.6632199546485261) -ELEC_BONG = Sample('elec_bong', 0.3464852607709751) -ELEC_CHIME = Sample('elec_chime', 2.2775510204081635) -ELEC_CYMBAL = Sample('elec_cymbal', 0.563219954648526) -ELEC_FILT_SNARE = Sample('elec_filt_snare', 1.5158503401360544) -ELEC_FLIP = Sample('elec_flip', 0.07476190476190477) -ELEC_FUZZ_TOM = Sample('elec_fuzz_tom', 0.37179138321995464) -ELEC_HI_SNARE = Sample('elec_hi_snare', 0.19736961451247165) -ELEC_HOLLOW_KICK = Sample('elec_hollow_kick', 0.15564625850340136) -ELEC_LO_SNARE = Sample('elec_lo_snare', 0.6607936507936508) -ELEC_MID_SNARE = Sample('elec_mid_snare', 0.7256235827664399) -ELEC_PING = Sample('elec_ping', 0.21226757369614513) -ELEC_PLIP = Sample('elec_plip', 0.19882086167800453) -ELEC_POP = Sample('elec_pop', 0.08680272108843537) -ELEC_SNARE = Sample('elec_snare', 0.3893197278911565) -ELEC_SOFT_KICK = Sample('elec_soft_kick', 0.1364172335600907) -ELEC_TICK = Sample('elec_tick', 0.01943310657596372) -ELEC_TRIANGLE = Sample('elec_triangle', 0.22294784580498866) -ELEC_TWANG = Sample('elec_twang', 0.6243083900226757) -ELEC_TWIP = Sample('elec_twip', 0.10140589569160997) -ELEC_WOOD = Sample('elec_wood', 0.47811791383219954) - -## Sounds featuring guitars -GUIT_E_FIFTHS = Sample('guit_e_fifths', 5.971791383219955) -GUIT_E_SLIDE = Sample('guit_e_slide', 4.325192743764172) -GUIT_EM9 = Sample('guit_em9', 9.972063492063493) -GUIT_HARMONICS = Sample('guit_harmonics', 3.5322675736961453) - -## Miscellaneous Sounds -MISC_BURP = Sample('misc_burp', 0.7932879818594104) -MISC_CINEBOOM = Sample('misc_cineboom', 7.922675736961451) -MISC_CROW = Sample('misc_crow', 0.48063492063492064) - -## Percurssive Sounds -PERC_BELL = Sample('perc_bell', 6.719206349206349) -PERC_SNAP = Sample('perc_snap', 0.30795918367346936) -PERC_SNAP2 = Sample('perc_snap2', 0.17414965986394557) -PERC_SWASH = Sample('perc_swash', 0.3195011337868481) -PERC_TILL = Sample('perc_till', 2.665736961451247) - -## Ambient Sounds -AMBI_CHOIR = Sample('ambi_choir', 1.5715419501133787) -AMBI_DARK_WOOSH = Sample('ambi_dark_woosh', 3.7021315192743764) -AMBI_DRONE = Sample('ambi_drone', 4.40843537414966) -AMBI_GLASS_HUM = Sample('ambi_glass_hum', 10.0) -AMBI_GLASS_RUB = Sample('ambi_glass_rub', 3.1493650793650794) -AMBI_HAUNTED_HUM = Sample('ambi_haunted_hum', 9.78156462585034) -AMBI_LUNAR_LAND = Sample('ambi_lunar_land', 7.394240362811791) -AMBI_PIANO = Sample('ambi_piano', 2.811746031746032) -AMBI_SOFT_BUZZ = Sample('ambi_soft_buzz', 0.7821541950113379) -AMBI_SWOOSH = Sample('ambi_swoosh', 1.8484580498866212) - -## Bass Sounds -BASS_DNB_F = Sample('bass_dnb_f', 0.8705442176870748) -BASS_DROP_C = Sample('bass_drop_c', 2.3589115646258505) -BASS_HARD_C = Sample('bass_hard_c', 1.5) -BASS_HIT_C = Sample('bass_hit_c', 0.6092517006802721) -BASS_THICK_C = Sample('bass_thick_c', 3.9680725623582767) -BASS_VOXY_C = Sample('bass_voxy_c', 6.23469387755102) -BASS_VOXY_HIT_C = Sample('bass_voxy_hit_c', 0.457437641723356) -BASS_WOODSY_C = Sample('bass_woodsy_c', 3.252267573696145) - -## Snare Drums Sounds -SN_DOLF = Sample('sn_dolf', 0.37759637188208617) -SN_DUB = Sample('sn_dub', 0.2781179138321995) -SN_ZOME = Sample('sn_zome', 0.4787528344671202) - -## Bass Drums Sounds -BD_808 = Sample('bd_808', 0.5597505668934241) -BD_ADA = Sample('bd_ada', 0.10179138321995465) -BD_BOOM = Sample('bd_boom', 1.7142857142857142) -BD_FAT = Sample('bd_fat', 0.23219954648526078) -BD_GAS = Sample('bd_gas', 0.4471428571428571) -BD_HAUS = Sample('bd_haus', 0.21993197278911564) -BD_KLUB = Sample('bd_klub', 0.368843537414966) -BD_PURE = Sample('bd_pure', 0.43324263038548755) -BD_SONE = Sample('bd_sone', 0.4089115646258503) -BD_TEK = Sample('bd_tek', 0.24024943310657595) -BD_ZOME = Sample('bd_zome', 0.45972789115646256) -BD_ZUM = Sample('bd_zum', 0.13158730158730159) - -## Sounds for Looping -LOOP_AMEN = Sample('loop_amen', 1.753310657596372) -LOOP_AMEN_FULL = Sample('loop_amen_full', 6.857142857142857) -LOOP_BREAKBEAT = Sample('loop_breakbeat', 1.9047619047619047) -LOOP_COMPUS = Sample('loop_compus', 6.486485260770975) -LOOP_GARZUL = Sample('loop_garzul', 8.0) -LOOP_INDUSTRIAL = Sample('loop_industrial', 0.8837414965986394) -LOOP_MIKA = Sample('loop_mika', 8.0) -LOOP_SAFARI = Sample('loop_safari', 8.005079365079364) -LOOP_TABLA = Sample('loop_tabla', 10.673990929705216) - -## Tabla -TABLA_DHEC = Sample('tabla_dhec', 0.3250793650793651) -TABLA_GHE1 = Sample('tabla_ghe1', 0.5912244897959184) -TABLA_GHE2 = Sample('tabla_ghe2', 2.6607256235827665) -TABLA_GHE3 = Sample('tabla_ghe3', 2.3908163265306124) -TABLA_GHE4 = Sample('tabla_ghe4', 0.7960997732426304) -TABLA_GHE5 = Sample('tabla_ghe5', 3.560045351473923) -TABLA_GHE6 = Sample('tabla_ghe6', 3.6011337868480724) -TABLA_GHE7 = Sample('tabla_ghe7', 2.1512698412698414) -TABLA_GHE8 = Sample('tabla_ghe8', 0.5817913832199546) -TABLA_KE1 = Sample('tabla_ke1', 0.04342403628117914) -TABLA_KE2 = Sample('tabla_ke2', 0.04403628117913832) -TABLA_KE3 = Sample('tabla_ke3', 0.07571428571428572) -TABLA_NA = Sample('tabla_na', 0.7198185941043084) -TABLA_NA_O = Sample('tabla_na_o', 1.4889795918367348) -TABLA_NA_S = Sample('tabla_na_s', 0.23582766439909297) -TABLA_RE = Sample('tabla_re', 0.2815419501133787) -TABLA_TAS1 = Sample('tabla_tas1', 1.1116553287981858) -TABLA_TAS2 = Sample('tabla_tas2', 1.4338321995464853) -TABLA_TAS3 = Sample('tabla_tas3', 1.2364625850340136) -TABLA_TE1 = Sample('tabla_te1', 0.17777777777777778) -TABLA_TE2 = Sample('tabla_te2', 0.33233560090702946) -TABLA_TE_M = Sample('tabla_te_m', 0.28879818594104306) -TABLA_TE_NE = Sample('tabla_te_ne', 0.15310657596371882) -TABLA_TUN1 = Sample('tabla_tun1', 2.3394104308390022) -TABLA_TUN2 = Sample('tabla_tun2', 2.693514739229025) -TABLA_TUN3 = Sample('tabla_tun3', 2.0956009070294783) - -# Vinyl -VINYL_BACKSPIN = Sample('vinyl_backspin', 1.06124716553288) -VINYL_HISS = Sample('vinyl_hiss', 8.0) -VINYL_REWIND = Sample('vinyl_rewind', 2.6804761904761905) -VINYL_SCRATCH = Sample('vinyl_scratch', 0.27383219954648524) - ## FX BITCRUSHER = FxName('bitcrusher') COMPRESSOR = FxName('compressor') @@ -638,8 +473,10 @@ def use_synth(synth): _current_synth = synth -def synth(name, note=None, attack=None, decay=None, sustain_level=None, sustain=None, release=None, cutoff=None, - cutoff_attack=None, amp=None, pan=None): +def synth( + name, note=None, attack=None, decay=None, + sustain_level=None, sustain=None, release=None, + cutoff=None, cutoff_attack=None, amp=None, pan=None): parameters = [] parameter = '' diff --git a/psonic/samples/__init__.py b/psonic/samples/__init__.py new file mode 100644 index 0000000..0c48619 --- /dev/null +++ b/psonic/samples/__init__.py @@ -0,0 +1,9 @@ + +class Sample(object): + """ + Sample + """ + + def __init__(self, name, duration): + self.name = name + self.duration = duration diff --git a/psonic/samples/ambient.py b/psonic/samples/ambient.py new file mode 100644 index 0000000..914495a --- /dev/null +++ b/psonic/samples/ambient.py @@ -0,0 +1,29 @@ +from . import Sample + +## Ambient Sounds +AMBI_CHOIR = Sample('ambi_choir', 1.5715419501133787) +AMBI_DARK_WOOSH = Sample('ambi_dark_woosh', 3.7021315192743764) +AMBI_DRONE = Sample('ambi_drone', 4.40843537414966) +AMBI_GLASS_HUM = Sample('ambi_glass_hum', 10.0) +AMBI_GLASS_RUB = Sample('ambi_glass_rub', 3.1493650793650794) +AMBI_HAUNTED_HUM = Sample('ambi_haunted_hum', 9.78156462585034) +AMBI_LUNAR_LAND = Sample('ambi_lunar_land', 7.394240362811791) +AMBI_PIANO = Sample('ambi_piano', 2.811746031746032) +AMBI_SOFT_BUZZ = Sample('ambi_soft_buzz', 0.7821541950113379) +AMBI_SWOOSH = Sample('ambi_swoosh', 1.8484580498866212) + +def get_sounds(): + + sounds = dict( + AMBI_CHOIR = AMBI_CHOIR, + AMBI_DARK_WOOSH = AMBI_DARK_WOOSH, + AMBI_DRONE = AMBI_DRONE, + AMBI_GLASS_HUM = AMBI_GLASS_HUM, + AMBI_GLASS_RUB = AMBI_GLASS_RUB, + AMBI_HAUNTED_HUM = AMBI_HAUNTED_HUM, + AMBI_LUNAR_LAND = AMBI_LUNAR_LAND, + AMBI_PIANO = AMBI_PIANO, + AMBI_SOFT_BUZZ = AMBI_SOFT_BUZZ, + AMBI_SWOOSH = AMBI_SWOOSH, + ) + return sounds diff --git a/psonic/samples/bass.py b/psonic/samples/bass.py new file mode 100644 index 0000000..868f07a --- /dev/null +++ b/psonic/samples/bass.py @@ -0,0 +1,25 @@ +from . import Sample + +## Bass Sounds +BASS_DNB_F = Sample('bass_dnb_f', 0.8705442176870748) +BASS_DROP_C = Sample('bass_drop_c', 2.3589115646258505) +BASS_HARD_C = Sample('bass_hard_c', 1.5) +BASS_HIT_C = Sample('bass_hit_c', 0.6092517006802721) +BASS_THICK_C = Sample('bass_thick_c', 3.9680725623582767) +BASS_VOXY_C = Sample('bass_voxy_c', 6.23469387755102) +BASS_VOXY_HIT_C = Sample('bass_voxy_hit_c', 0.457437641723356) +BASS_WOODSY_C = Sample('bass_woodsy_c', 3.252267573696145) + + +def get_sounds(): + sounds = dict( + BASS_DNB_F = BASS_DNB_F, + BASS_DROP_C = BASS_DROP_C, + BASS_HARD_C = BASS_HARD_C, + BASS_HIT_C = BASS_HIT_C, + BASS_THICK_C = BASS_THICK_C, + BASS_VOXY_C = BASS_VOXY_C, + BASS_VOXY_HIT_C = BASS_VOXY_HIT_C, + BASS_WOODSY_C = BASS_WOODSY_C, + ) + return sounds diff --git a/psonic/samples/drums.py b/psonic/samples/drums.py new file mode 100644 index 0000000..6bb4b57 --- /dev/null +++ b/psonic/samples/drums.py @@ -0,0 +1,84 @@ +from . import Sample + +## Drum Sounds +DRUM_BASS_HARD = Sample('drum_bass_hard', 0.6951927437641723) +DRUM_BASS_SOFT = Sample('drum_bass_soft', 0.5624489795918367) +DRUM_COWBELL = Sample('drum_cowbell', 0.34988662131519277) +DRUM_CYMBAL_CLOSED = Sample('drum_cymbal_closed', 0.2069387755102041) +DRUM_CYMBAL_HARD = Sample('drum_cymbal_hard', 1.6388208616780044) +DRUM_CYMBAL_OPEN = Sample('drum_cymbal_open', 1.8043764172335601) +DRUM_CYMBAL_PEDAL = Sample('drum_cymbal_pedal', 0.2721995464852608) +DRUM_CYMBAL_SOFT = Sample('drum_cymbal_soft', 0.9497278911564626) +DRUM_HEAVY_KICK = Sample('drum_heavy_kick', 0.2701360544217687) +DRUM_ROLL = Sample('drum_roll', 6.241678004535148) +DRUM_SNARE_HARD = Sample('drum_snare_hard', 0.44492063492063494) +DRUM_SNARE_SOFT = Sample('drum_snare_soft', 0.3147845804988662) +DRUM_SPLASH_HARD = Sample('drum_splash_hard', 2.5961904761904764) +DRUM_SPLASH_SOFT = Sample('drum_splash_soft', 1.857981859410431) +DRUM_TOM_HI_HARD = Sample('drum_tom_hi_hard', 0.7376643990929705) +DRUM_TOM_HI_SOFT = Sample('drum_tom_hi_soft', 0.6290249433106576) +DRUM_TOM_LO_HARD = Sample('drum_tom_lo_hard', 1.0002267573696144) +DRUM_TOM_LO_SOFT = Sample('drum_tom_lo_soft', 0.8634013605442177) +DRUM_TOM_MID_HARD = Sample('drum_tom_mid_hard', 0.7342176870748299) +DRUM_TOM_MID_SOFT = Sample('drum_tom_mid_soft', 0.6721315192743764) + +## Snare Drums Sounds +SN_DOLF = Sample('sn_dolf', 0.37759637188208617) +SN_DUB = Sample('sn_dub', 0.2781179138321995) +SN_ZOME = Sample('sn_zome', 0.4787528344671202) + +## Bass Drums Sounds +BD_808 = Sample('bd_808', 0.5597505668934241) +BD_ADA = Sample('bd_ada', 0.10179138321995465) +BD_BOOM = Sample('bd_boom', 1.7142857142857142) +BD_FAT = Sample('bd_fat', 0.23219954648526078) +BD_GAS = Sample('bd_gas', 0.4471428571428571) +BD_HAUS = Sample('bd_haus', 0.21993197278911564) +BD_KLUB = Sample('bd_klub', 0.368843537414966) +BD_PURE = Sample('bd_pure', 0.43324263038548755) +BD_SONE = Sample('bd_sone', 0.4089115646258503) +BD_TEK = Sample('bd_tek', 0.24024943310657595) +BD_ZOME = Sample('bd_zome', 0.45972789115646256) +BD_ZUM = Sample('bd_zum', 0.13158730158730159) + +def get_sounds(): + sounds = dict( + DRUM_BASS_HARD = DRUM_BASS_HARD, + DRUM_BASS_SOFT = DRUM_BASS_SOFT, + DRUM_COWBELL = DRUM_COWBELL, + DRUM_CYMBAL_CLOSED = DRUM_CYMBAL_CLOSED, + DRUM_CYMBAL_HARD = DRUM_CYMBAL_HARD, + DRUM_CYMBAL_OPEN = DRUM_CYMBAL_OPEN, + DRUM_CYMBAL_PEDAL = DRUM_CYMBAL_PEDAL, + DRUM_CYMBAL_SOFT = DRUM_CYMBAL_SOFT, + DRUM_HEAVY_KICK = DRUM_HEAVY_KICK, + DRUM_ROLL = DRUM_ROLL, + DRUM_SNARE_HARD = DRUM_SNARE_HARD, + DRUM_SNARE_SOFT = DRUM_SNARE_SOFT, + DRUM_SPLASH_HARD = DRUM_SPLASH_HARD, + DRUM_SPLASH_SOFT = DRUM_SPLASH_SOFT, + DRUM_TOM_HI_HARD = DRUM_TOM_HI_HARD, + DRUM_TOM_HI_SOFT = DRUM_TOM_HI_SOFT, + DRUM_TOM_LO_HARD = DRUM_TOM_LO_HARD, + DRUM_TOM_LO_SOFT = DRUM_TOM_LO_SOFT, + DRUM_TOM_MID_HARD = DRUM_TOM_MID_HARD, + DRUM_TOM_MID_SOFT = DRUM_TOM_MID_SOFT, + + SN_DOLF = SN_DOLF, + SN_DUB = SN_DUB, + SN_ZOME = SN_ZOME, + + BD_808 = BD_808, + BD_ADA = BD_ADA, + BD_BOOM = BD_BOOM, + BD_FAT = BD_FAT, + BD_GAS = BD_GAS, + BD_HAUS = BD_HAUS, + BD_KLUB = BD_KLUB, + BD_PURE = BD_PURE, + BD_SONE = BD_SONE, + BD_TEK = BD_TEK, + BD_ZOME = BD_ZOME, + BD_ZUM = BD_ZUM, + ) + return sounds diff --git a/psonic/samples/electric.py b/psonic/samples/electric.py new file mode 100644 index 0000000..d305198 --- /dev/null +++ b/psonic/samples/electric.py @@ -0,0 +1,58 @@ +from . import Sample + +## Electric Sounds +ELEC_BEEP = Sample('elec_beep', 0.10578231292517007) +ELEC_BELL = Sample('elec_bell', 0.27825396825396825) +ELEC_BLIP = Sample('elec_blip', 0.15816326530612246) +ELEC_BLIP2 = Sample('elec_blip2', 0.1840816326530612) +ELEC_BLUP = Sample('elec_blup', 0.6632199546485261) +ELEC_BONG = Sample('elec_bong', 0.3464852607709751) +ELEC_CHIME = Sample('elec_chime', 2.2775510204081635) +ELEC_CYMBAL = Sample('elec_cymbal', 0.563219954648526) +ELEC_FILT_SNARE = Sample('elec_filt_snare', 1.5158503401360544) +ELEC_FLIP = Sample('elec_flip', 0.07476190476190477) +ELEC_FUZZ_TOM = Sample('elec_fuzz_tom', 0.37179138321995464) +ELEC_HI_SNARE = Sample('elec_hi_snare', 0.19736961451247165) +ELEC_HOLLOW_KICK = Sample('elec_hollow_kick', 0.15564625850340136) +ELEC_LO_SNARE = Sample('elec_lo_snare', 0.6607936507936508) +ELEC_MID_SNARE = Sample('elec_mid_snare', 0.7256235827664399) +ELEC_PING = Sample('elec_ping', 0.21226757369614513) +ELEC_PLIP = Sample('elec_plip', 0.19882086167800453) +ELEC_POP = Sample('elec_pop', 0.08680272108843537) +ELEC_SNARE = Sample('elec_snare', 0.3893197278911565) +ELEC_SOFT_KICK = Sample('elec_soft_kick', 0.1364172335600907) +ELEC_TICK = Sample('elec_tick', 0.01943310657596372) +ELEC_TRIANGLE = Sample('elec_triangle', 0.22294784580498866) +ELEC_TWANG = Sample('elec_twang', 0.6243083900226757) +ELEC_TWIP = Sample('elec_twip', 0.10140589569160997) +ELEC_WOOD = Sample('elec_wood', 0.47811791383219954) + +def get_sounds(): + sounds = dict( + ELEC_BEEP = ELEC_BEEP, + ELEC_BELL = ELEC_BELL, + ELEC_BLIP = ELEC_BLIP, + ELEC_BLIP2 = ELEC_BLIP2, + ELEC_BLUP = ELEC_BLUP, + ELEC_BONG = ELEC_BONG, + ELEC_CHIME = ELEC_CHIME, + ELEC_CYMBAL = ELEC_CYMBAL, + ELEC_FILT_SNARE = ELEC_FILT_SNARE, + ELEC_FLIP = ELEC_FLIP, + ELEC_FUZZ_TOM = ELEC_FUZZ_TOM, + ELEC_HI_SNARE = ELEC_HI_SNARE, + ELEC_HOLLOW_KICK = ELEC_HOLLOW_KICK, + ELEC_LO_SNARE = ELEC_LO_SNARE, + ELEC_MID_SNARE = ELEC_MID_SNARE, + ELEC_PING = ELEC_PING, + ELEC_PLIP = ELEC_PLIP, + ELEC_POP = ELEC_POP, + ELEC_SNARE = ELEC_SNARE, + ELEC_SOFT_KICK = ELEC_SOFT_KICK, + ELEC_TICK = ELEC_TICK, + ELEC_TRIANGLE = ELEC_TRIANGLE, + ELEC_TWANG = ELEC_TWANG, + ELEC_TWIP = ELEC_TWIP, + ELEC_WOOD = ELEC_WOOD, + ) + return sounds diff --git a/psonic/samples/guitars.py b/psonic/samples/guitars.py new file mode 100644 index 0000000..9d54245 --- /dev/null +++ b/psonic/samples/guitars.py @@ -0,0 +1,18 @@ +from . import Sample + +## Sounds featuring guitars +GUIT_E_FIFTHS = Sample('guit_e_fifths', 5.971791383219955) +GUIT_E_SLIDE = Sample('guit_e_slide', 4.325192743764172) +GUIT_EM9 = Sample('guit_em9', 9.972063492063493) +GUIT_HARMONICS = Sample('guit_harmonics', 3.5322675736961453) + +def get_sounds(): + + sounds = dict( + GUIT_E_FIFTHS = GUIT_E_FIFTHS, + GUIT_E_SLIDE = GUIT_E_SLIDE, + GUIT_EM9 = GUIT_EM9, + GUIT_HARMONICS = GUIT_HARMONICS, + ) + return sounds + diff --git a/psonic/samples/loops/__init__.py b/psonic/samples/loops/__init__.py new file mode 100644 index 0000000..6ed41a3 --- /dev/null +++ b/psonic/samples/loops/__init__.py @@ -0,0 +1,12 @@ +from psonic.samples import Sample + +## Sounds for Looping +LOOP_AMEN = Sample('loop_amen', 1.753310657596372) +LOOP_AMEN_FULL = Sample('loop_amen_full', 6.857142857142857) +LOOP_BREAKBEAT = Sample('loop_breakbeat', 1.9047619047619047) +LOOP_COMPUS = Sample('loop_compus', 6.486485260770975) +LOOP_GARZUL = Sample('loop_garzul', 8.0) +LOOP_INDUSTRIAL = Sample('loop_industrial', 0.8837414965986394) +LOOP_MIKA = Sample('loop_mika', 8.0) +LOOP_SAFARI = Sample('loop_safari', 8.005079365079364) +LOOP_TABLA = Sample('loop_tabla', 10.673990929705216) diff --git a/psonic/samples/misc.py b/psonic/samples/misc.py new file mode 100644 index 0000000..b6783e3 --- /dev/null +++ b/psonic/samples/misc.py @@ -0,0 +1,15 @@ +from . import Sample +## Miscellaneous Sounds +MISC_BURP = Sample('misc_burp', 0.7932879818594104) +MISC_CINEBOOM = Sample('misc_cineboom', 7.922675736961451) +MISC_CROW = Sample('misc_crow', 0.48063492063492064) + +def get_sounds(): + + sounds = dict( + MISC_BURP = MISC_BURP, + MISC_CINEBOOM = MISC_CINEBOOM, + MISC_CROW = MISC_CROW, + ) + return sounds + diff --git a/psonic/samples/percussions.py b/psonic/samples/percussions.py new file mode 100644 index 0000000..a7f1c70 --- /dev/null +++ b/psonic/samples/percussions.py @@ -0,0 +1,19 @@ +from . import Sample +## Percurssive Sounds +PERC_BELL = Sample('perc_bell', 6.719206349206349) +PERC_SNAP = Sample('perc_snap', 0.30795918367346936) +PERC_SNAP2 = Sample('perc_snap2', 0.17414965986394557) +PERC_SWASH = Sample('perc_swash', 0.3195011337868481) +PERC_TILL = Sample('perc_till', 2.665736961451247) + + +def get_sounds(): + + sounds = dict( + PERC_BELL = PERC_BELL, + PERC_SNAP = PERC_SNAP, + PERC_SNAP2 = PERC_SNAP2, + PERC_SWASH = PERC_SWASH, + PERC_TILL = PERC_TILL, + ) + return sounds diff --git a/psonic/samples/tabla.py b/psonic/samples/tabla.py new file mode 100644 index 0000000..be35d8f --- /dev/null +++ b/psonic/samples/tabla.py @@ -0,0 +1,62 @@ +from . import Sample + +## Tabla +TABLA_DHEC = Sample('tabla_dhec', 0.3250793650793651) +TABLA_GHE1 = Sample('tabla_ghe1', 0.5912244897959184) +TABLA_GHE2 = Sample('tabla_ghe2', 2.6607256235827665) +TABLA_GHE3 = Sample('tabla_ghe3', 2.3908163265306124) +TABLA_GHE4 = Sample('tabla_ghe4', 0.7960997732426304) +TABLA_GHE5 = Sample('tabla_ghe5', 3.560045351473923) +TABLA_GHE6 = Sample('tabla_ghe6', 3.6011337868480724) +TABLA_GHE7 = Sample('tabla_ghe7', 2.1512698412698414) +TABLA_GHE8 = Sample('tabla_ghe8', 0.5817913832199546) +TABLA_KE1 = Sample('tabla_ke1', 0.04342403628117914) +TABLA_KE2 = Sample('tabla_ke2', 0.04403628117913832) +TABLA_KE3 = Sample('tabla_ke3', 0.07571428571428572) +TABLA_NA = Sample('tabla_na', 0.7198185941043084) +TABLA_NA_O = Sample('tabla_na_o', 1.4889795918367348) +TABLA_NA_S = Sample('tabla_na_s', 0.23582766439909297) +TABLA_RE = Sample('tabla_re', 0.2815419501133787) +TABLA_TAS1 = Sample('tabla_tas1', 1.1116553287981858) +TABLA_TAS2 = Sample('tabla_tas2', 1.4338321995464853) +TABLA_TAS3 = Sample('tabla_tas3', 1.2364625850340136) +TABLA_TE1 = Sample('tabla_te1', 0.17777777777777778) +TABLA_TE2 = Sample('tabla_te2', 0.33233560090702946) +TABLA_TE_M = Sample('tabla_te_m', 0.28879818594104306) +TABLA_TE_NE = Sample('tabla_te_ne', 0.15310657596371882) +TABLA_TUN1 = Sample('tabla_tun1', 2.3394104308390022) +TABLA_TUN2 = Sample('tabla_tun2', 2.693514739229025) +TABLA_TUN3 = Sample('tabla_tun3', 2.0956009070294783) + +def get_sounds(): + + sounds = dict( + TABLA_DHEC = TABLA_DHEC, + TABLA_GHE1 = TABLA_GHE1, + TABLA_GHE2 = TABLA_GHE2, + TABLA_GHE3 = TABLA_GHE3, + TABLA_GHE4 = TABLA_GHE4, + TABLA_GHE5 = TABLA_GHE5, + TABLA_GHE6 = TABLA_GHE6, + TABLA_GHE7 = TABLA_GHE7, + TABLA_GHE8 = TABLA_GHE8, + TABLA_KE1 = TABLA_KE1, + TABLA_KE2 = TABLA_KE2, + TABLA_KE3 = TABLA_KE3, + TABLA_NA = TABLA_NA, + TABLA_NA_O = TABLA_NA_O, + TABLA_NA_S = TABLA_NA_S, + TABLA_RE = TABLA_RE, + TABLA_TAS1 = TABLA_TAS1, + TABLA_TAS2 = TABLA_TAS2, + TABLA_TAS3 = TABLA_TAS3, + TABLA_TE1 = TABLA_TE1, + TABLA_TE2 = TABLA_TE2, + TABLA_TE_M = TABLA_TE_M, + TABLA_TE_NE = TABLA_TE_NE, + TABLA_TUN1 = TABLA_TUN1, + TABLA_TUN2 = TABLA_TUN2, + TABLA_TUN3 = TABLA_TUN3, + ) + return sounds + diff --git a/psonic/samples/vinyl.py b/psonic/samples/vinyl.py new file mode 100644 index 0000000..c172a1d --- /dev/null +++ b/psonic/samples/vinyl.py @@ -0,0 +1,16 @@ +from . import Sample +# Vinyl +VINYL_BACKSPIN = Sample('vinyl_backspin', 1.06124716553288) +VINYL_HISS = Sample('vinyl_hiss', 8.0) +VINYL_REWIND = Sample('vinyl_rewind', 2.6804761904761905) +VINYL_SCRATCH = Sample('vinyl_scratch', 0.27383219954648524) + +def get_sounds(): + + sounds = dict( + VINYL_BACKSPIN = VINYL_BACKSPIN, + VINYL_HISS = VINYL_HISS, + VINYL_REWIND = VINYL_REWIND, + VINYL_SCRATCH = VINYL_SCRATCH, + ) + return sounds diff --git a/setup.py b/setup.py index 94b1e31..19376cc 100644 --- a/setup.py +++ b/setup.py @@ -34,6 +34,8 @@ author_email='gkvoelkl@nelson-games.de', packages=[ 'psonic', + 'psonic.samples', + 'psonic.samples.loops', ], license='MIT', zip_safe=False, From 40ca33d4cffc593ead8ab4eb2a60180b06844595 Mon Sep 17 00:00:00 2001 From: Toomas Ormisson Date: Sun, 18 Feb 2018 22:54:42 +0200 Subject: [PATCH 11/49] Since changed original files, increased version number. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 19376cc..cab71a5 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ setup( name='python-sonic', - version='0.3.0', + version='0.3.1', description='Programming Music with Sonic Pi or Supercollider', long_description=long_description, url='https://github.com/gkvoelkl/python-sonic', From 0d358285e4748ff151bf2bcef30883408aae30b5 Mon Sep 17 00:00:00 2001 From: Toomas Ormisson Date: Wed, 21 Feb 2018 22:07:56 +0200 Subject: [PATCH 12/49] Bit by bit, trying to make the codebase comprehensible --- psonic/api.py | 12 +- psonic/effects.py | 24 ++ psonic/internals/__init__.py | 0 psonic/internals/chords.py | 65 ++++++ psonic/internals/scales.py | 87 ++++++++ psonic/notes.py | 72 ++++++ psonic/psonic.py | 412 +---------------------------------- psonic/scales.py | 80 +++++++ psonic/synthesizers.py | 56 +++++ setup.py | 1 + 10 files changed, 399 insertions(+), 410 deletions(-) create mode 100644 psonic/effects.py create mode 100644 psonic/internals/__init__.py create mode 100644 psonic/internals/chords.py create mode 100644 psonic/internals/scales.py create mode 100644 psonic/notes.py create mode 100644 psonic/scales.py create mode 100644 psonic/synthesizers.py diff --git a/psonic/api.py b/psonic/api.py index a81fe0a..68db161 100644 --- a/psonic/api.py +++ b/psonic/api.py @@ -1,4 +1,8 @@ from .psonic import * +from .notes import * +from .scales import * +from .synthesizers import * +from .effects import * from .samples import * from .samples.loops import * from .samples.ambient import * @@ -10,12 +14,8 @@ from .samples.percussions import * from .samples.tabla import * from .samples.vinyl import * - - - - - - +from .internals.chords import * +from .internals.scales import * diff --git a/psonic/effects.py b/psonic/effects.py new file mode 100644 index 0000000..f673b7c --- /dev/null +++ b/psonic/effects.py @@ -0,0 +1,24 @@ +class FxName: + """ + FX name + """ + + def __init__(self, name): + self.name = name + +## FX +BITCRUSHER = FxName('bitcrusher') +COMPRESSOR = FxName('compressor') +ECHO = FxName('echo') +FLANGER = FxName('flanger') +KRUSH = FxName('krush') +LPF = FxName('lpf') +PAN = FxName('pan') +PANSLICER = FxName('panslicer') +REVERB = FxName('reverb') +SLICER = FxName('slicer') +WOBBLE = FxName('wobble') + + + + diff --git a/psonic/internals/__init__.py b/psonic/internals/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/psonic/internals/chords.py b/psonic/internals/chords.py new file mode 100644 index 0000000..e468611 --- /dev/null +++ b/psonic/internals/chords.py @@ -0,0 +1,65 @@ +_CHORD_QUALITY = { + 'major': [0, 4, 7], + 'minor': [0, 3, 7], + 'major7': [0, 4, 7, 11], + 'dom7': [0, 4, 7, 10], + 'minor7': [0, 3, 7, 10], + 'aug': [0, 4, 8], + 'dim': [0, 3, 6], + 'dim7': [0, 3, 6, 9], + '1': [0], + "5": [0, 7], + "+5": [0, 4, 8], + "m+5": [0, 3, 8], + "sus2": [0, 2, 7], + "sus4": [0, 5, 7], + "6": [0, 4, 7, 9], + "m6": [0, 3, 7, 9], + "7sus2": [0, 2, 7, 10], + "7sus4": [0, 5, 7, 10], + "7-5": [0, 4, 6, 10], + "m7-5": [0, 3, 6, 10], + "7+5": [0, 4, 8, 10], + "m7+5": [0, 3, 8, 10], + "9": [0, 4, 7, 10, 14], + "m9": [0, 3, 7, 10, 14], + "m7+9": [0, 3, 7, 10, 14], + "maj9": [0, 4, 7, 11, 14], + "9sus4": [0, 5, 7, 10, 14], + "6*9": [0, 4, 7, 9, 14], + "m6*9": [0, 3, 9, 7, 14], + "7-9": [0, 4, 7, 10, 13], + "m7-9": [0, 3, 7, 10, 13], + "7-10": [0, 4, 7, 10, 15], + "9+5": [0, 10, 13], + "m9+5": [0, 10, 14], + "7+5-9": [0, 4, 8, 10, 13], + "m7+5-9": [0, 3, 8, 10, 13], + "11": [0, 4, 7, 10, 14, 17], + "m11": [0, 3, 7, 10, 14, 17], + "maj11": [0, 4, 7, 11, 14, 17], + "11+": [0, 4, 7, 10, 14, 18], + "m11+": [0, 3, 7, 10, 14, 18], + "13": [0, 4, 7, 10, 14, 17, 21], + "m13": [0, 3, 7, 10, 14, 17, 21], + "M": [0, 4, 7], + "m": [0, 3, 7], + "7": [0, 4, 7, 10], + "M7": [0, 4, 7, 11], + "m7": [0, 3, 7], + "augmented": [0, 4, 8], + "a": [0, 4, 8], + "diminished": [0, 3, 6], + "i": [0, 3, 6], + "diminished7": [0, 3, 6, 9], + "i7": [0, 3, 6, 9] +} +## Chord Quality (from sonic pi) ## +MAJOR7 = "major7" +DOM7 = "dom7" +MINOR7 = "minor7" +AUG = "aug" +DIM = "dim" +DIM7 = "dim7" + + diff --git a/psonic/internals/scales.py b/psonic/internals/scales.py new file mode 100644 index 0000000..f338790 --- /dev/null +++ b/psonic/internals/scales.py @@ -0,0 +1,87 @@ +_ionian_sequence = [2, 2, 1, 2, 2, 2, 1] +_hex_sequence = [2, 2, 1, 2, 2, 3] +_pentatonic_sequence = [3, 2, 2, 3, 2] + +_SCALE_MODE = { + 'diatonic': _ionian_sequence, + 'ionian': _ionian_sequence, + 'major': _ionian_sequence, + 'dorian': _ionian_sequence[1:] + _ionian_sequence[:1], # rotate 1 + 'phrygian': _ionian_sequence[2:] + _ionian_sequence[:2], # rotate(2) + 'lydian': _ionian_sequence[3:] + _ionian_sequence[:3], # rotate(3) + 'mixolydian': _ionian_sequence[4:] + _ionian_sequence[:4], # rotate(4) + 'aeolian': _ionian_sequence[5:] + _ionian_sequence[:5], # rotate(5) + 'minor': _ionian_sequence[5:] + _ionian_sequence[:5], # rotate(5) + 'locrian': _ionian_sequence[6:] + _ionian_sequence[:6], # rotate(6) + 'hex_major6': _hex_sequence, + 'hex_dorian': _hex_sequence[1:] + _hex_sequence[:1], # rotate(1) + 'hex_phrygian': _hex_sequence[2:] + _hex_sequence[:2], # rotate(2) + 'hex_major7': _hex_sequence[3:] + _hex_sequence[:3], # rotate(3) + 'hex_sus': _hex_sequence[4:] + _hex_sequence[:4], # rotate(4) + 'hex_aeolian': _hex_sequence[5:] + _hex_sequence[:5], # rotate(5) + 'minor_pentatonic': _pentatonic_sequence, + 'yu': _pentatonic_sequence, + 'major_pentatonic': _pentatonic_sequence[1:] + _pentatonic_sequence[:1], # rotate(1) + 'gong': _pentatonic_sequence[1:] + _pentatonic_sequence[:1], # rotate(1) + 'egyptian': _pentatonic_sequence[2:] + _pentatonic_sequence[:2], # rotate(2) + 'shang': _pentatonic_sequence[2:] + _pentatonic_sequence[:2], # rotate(2) + 'jiao': _pentatonic_sequence[3:] + _pentatonic_sequence[:3], # rotate(3) + 'zhi': _pentatonic_sequence[4:] + _pentatonic_sequence[:4], # rotate(4) + 'ritusen': _pentatonic_sequence[4:] + _pentatonic_sequence[:4], # rotate(4) + 'whole_tone': [2, 2, 2, 2, 2, 2], + 'whole': [2, 2, 2, 2, 2, 2], + 'chromatic': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + 'harmonic_minor': [2, 1, 2, 2, 1, 3, 1], + 'melodic_minor_asc': [2, 1, 2, 2, 2, 2, 1], + 'hungarian_minor': [2, 1, 3, 1, 1, 3, 1], + 'octatonic': [2, 1, 2, 1, 2, 1, 2, 1], + 'messiaen1': [2, 2, 2, 2, 2, 2], + 'messiaen2': [1, 2, 1, 2, 1, 2, 1, 2], + 'messiaen3': [2, 1, 1, 2, 1, 1, 2, 1, 1], + 'messiaen4': [1, 1, 3, 1, 1, 1, 3, 1], + 'messiaen5': [1, 4, 1, 1, 4, 1], + 'messiaen6': [2, 2, 1, 1, 2, 2, 1, 1], + 'messiaen7': [1, 1, 1, 2, 1, 1, 1, 1, 2, 1], + 'super_locrian': [1, 2, 1, 2, 2, 2, 2], + 'hirajoshi': [2, 1, 4, 1, 4], + 'kumoi': [2, 1, 4, 2, 3], + 'neapolitan_major': [1, 2, 2, 2, 2, 2, 1], + 'bartok': [2, 2, 1, 2, 1, 2, 2], + 'bhairav': [1, 3, 1, 2, 1, 3, 1], + 'locrian_major': [2, 2, 1, 1, 2, 2, 2], + 'ahirbhairav': [1, 3, 1, 2, 2, 1, 2], + 'enigmatic': [1, 3, 2, 2, 2, 1, 1], + 'neapolitan_minor': [1, 2, 2, 2, 1, 3, 1], + 'pelog': [1, 2, 4, 1, 4], + 'augmented2': [1, 3, 1, 3, 1, 3], + 'scriabin': [1, 3, 3, 2, 3], + 'harmonic_major': [2, 2, 1, 2, 1, 3, 1], + 'melodic_minor_desc': [2, 1, 2, 2, 1, 2, 2], + 'romanian_minor': [2, 1, 3, 1, 2, 1, 2], + 'hindu': [2, 2, 1, 2, 1, 2, 2], + 'iwato': [1, 4, 1, 4, 2], + 'melodic_minor': [2, 1, 2, 2, 2, 2, 1], + 'diminished2': [2, 1, 2, 1, 2, 1, 2, 1], + 'marva': [1, 3, 2, 1, 2, 2, 1], + 'melodic_major': [2, 2, 1, 2, 1, 2, 2], + 'indian': [4, 1, 2, 3, 2], + 'spanish': [1, 3, 1, 2, 1, 2, 2], + 'prometheus': [2, 2, 2, 5, 1], + 'diminished': [1, 2, 1, 2, 1, 2, 1, 2], + 'todi': [1, 2, 3, 1, 1, 3, 1], + 'leading_whole': [2, 2, 2, 2, 2, 1, 1], + 'augmented': [3, 1, 3, 1, 3, 1], + 'purvi': [1, 3, 2, 1, 1, 3, 1], + 'chinese': [4, 2, 1, 4, 1], + 'lydian_minor': [2, 2, 2, 1, 1, 2, 2], + 'i': _ionian_sequence, + 'ii': _ionian_sequence[1:] + _ionian_sequence[:1], # rotate(1) + 'iii': _ionian_sequence[2:] + _ionian_sequence[:2], # rotate(2) + 'iv': _ionian_sequence[3:] + _ionian_sequence[:3], # rotate(3) + 'v': _ionian_sequence[4:] + _ionian_sequence[:4], # rotate(4) + 'vi': _ionian_sequence[5:] + _ionian_sequence[:5], # rotate(5) + 'vii': _ionian_sequence[6:] + _ionian_sequence[:6], # rotate(6), + 'viii': _ionian_sequence[7:] + _ionian_sequence[:7], # rotate(7) +} + + diff --git a/psonic/notes.py b/psonic/notes.py new file mode 100644 index 0000000..67f9fc0 --- /dev/null +++ b/psonic/notes.py @@ -0,0 +1,72 @@ +## Notes ## +C2 = 36 +Cs2 = 37 +Db2 = Cs2 +D2 = 38 +Ds2 = 39 +Eb2 = Ds2 +E2 = 40 +F2 = 41 +Fs2 = 42 +Gb2 = Fs2 +G2 = 43 +Gs2 = 44 +Ab2 = Gs2 +A2 = 45 +As2 = 46 +Bb2 = As2 +B2 = 47 +C3 = 48 +Cs3 = 49 +Db3 = Cs3 +D3 = 50 +Ds3 = 51 +Eb3 = Ds3 +E3 = 52 +F3 = 53 +Fs3 = 54 +Gb3 = Fs3 +G3 = 55 +Gs3 = 56 +Ab3 = Gs3 +A3 = 57 +As3 = 58 +Bb3 = As3 +B3 = 59 +C4 = 60 +Cs4 = 61 +Db4 = Cs4 +D4 = 62 +Ds4 = 63 +Eb4 = Ds4 +E4 = 64 +F4 = 65 +Fs4 = 66 +Gb4 = Fs4 +G4 = 67 +Gs4 = 68 +Ab4 = Gs4 +A4 = 69 +As4 = 70 +Bb4 = As4 +B4 = 71 +C5 = 72 +Cs5 = 73 +Db5 = Cs5 +D5 = 74 +Ds5 = 75 +Eb5 = Ds5 +E5 = 76 +F5 = 77 +Fs5 = 78 +Gb5 = Fs5 +G5 = 79 +Gs5 = 80 +Ab5 = Gs5 +A5 = 81 +As5 = 82 +Bb5 = As5 +B5 = 83 +C6 = 84 + +R = 0 diff --git a/psonic/psonic.py b/psonic/psonic.py index bec7edc..2c9abd7 100644 --- a/psonic/psonic.py +++ b/psonic/psonic.py @@ -1,28 +1,13 @@ -# The MIT License (MIT) -# -# Copyright (c) 2016 G. Völkl -# -# 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. import random import time import threading -from psonic.samples import Sample + +#TODO: Optimize imports +from .samples import Sample +from .synthesizers import BEEP +from .internals.chords import _CHORD_QUALITY +from .internals.scales import _SCALE_MODE + from pythonosc import osc_message_builder # osc support from pythonosc import udp_client @@ -30,16 +15,6 @@ ## Base Classes ## - -class Synth: - """ - Synthesizer - """ - - def __init__(self, name): - self.name = name - - class ChordQuality: """ Chord Quality @@ -50,14 +25,6 @@ def __init__(self, name, inter): self.inter = inter -class FxName: - """ - FX name - """ - - def __init__(self, name): - self.name = name - class Message: """ @@ -94,9 +61,8 @@ def __exit__(self, exc_type, exc_val, exc_tb): pass -## Decorator ## - def in_thread(func): + """Thread decorator""" def wrapper(): _thread = threading.Thread(target=func) _thread.start() @@ -104,365 +70,6 @@ def wrapper(): return wrapper -## Notes ## -C2 = 36 -Cs2 = 37 -Db2 = Cs2 -D2 = 38 -Ds2 = 39 -Eb2 = Ds2 -E2 = 40 -F2 = 41 -Fs2 = 42 -Gb2 = Fs2 -G2 = 43 -Gs2 = 44 -Ab2 = Gs2 -A2 = 45 -As2 = 46 -Bb2 = As2 -B2 = 47 -C3 = 48 -Cs3 = 49 -Db3 = Cs3 -D3 = 50 -Ds3 = 51 -Eb3 = Ds3 -E3 = 52 -F3 = 53 -Fs3 = 54 -Gb3 = Fs3 -G3 = 55 -Gs3 = 56 -Ab3 = Gs3 -A3 = 57 -As3 = 58 -Bb3 = As3 -B3 = 59 -C4 = 60 -Cs4 = 61 -Db4 = Cs4 -D4 = 62 -Ds4 = 63 -Eb4 = Ds4 -E4 = 64 -F4 = 65 -Fs4 = 66 -Gb4 = Fs4 -G4 = 67 -Gs4 = 68 -Ab4 = Gs4 -A4 = 69 -As4 = 70 -Bb4 = As4 -B4 = 71 -C5 = 72 -Cs5 = 73 -Db5 = Cs5 -D5 = 74 -Ds5 = 75 -Eb5 = Ds5 -E5 = 76 -F5 = 77 -Fs5 = 78 -Gb5 = Fs5 -G5 = 79 -Gs5 = 80 -Ab5 = Gs5 -A5 = 81 -As5 = 82 -Bb5 = As5 -B5 = 83 -C6 = 84 - -R = 0 - -## Synthezier ## -DULL_BELL = Synth('dull_bell') -PRETTY_BELL = Synth('pretty_bell') -SINE = Synth('sine') -SQUARE = Synth('square') -PULSE = Synth('pulse') -SUBPULSE = Synth('subpulse') -DTRI = Synth('dtri') -DPULSE = Synth('dpulse') -FM = Synth('fm') -MOD_FM = Synth('mod_fm') -MOD_SAW = Synth('mod_saw') -MOD_DSAW = Synth('mod_dsaw') -MOD_SINE = Synth('mod_sine') -MOD_TRI = Synth('mod_tri') -MOD_PULSE = Synth('mod_pulse') -SUPERSAW = Synth('supersaw') -HOOVER = Synth('hoover') -SYNTH_VIOLIN = Synth('synth_violin') -PLUCK = Synth('pluck') -PIANO = Synth('piano') -GROWL = Synth('growl') -DARK_AMBIENCE = Synth('dark_ambience') -DARK_SEA_HORN = Synth('dark_sea_horn') -HOLLOW = Synth('hollow') -ZAWA = Synth('zawa') -NOISE = Synth('noise') -GNOISE = Synth('gnoise') -BNOISE = Synth('bnoise') -CNOISE = Synth('cnoise') -DSAW = Synth('dsaw') -TB303 = Synth('tb303') -BLADE = Synth('blade') -PROPHET = Synth('prophet') -SAW = Synth('saw') -BEEP = Synth('beep') -TRI = Synth('tri') -CHIPLEAD = Synth('chiplead') # Sonic Pi 2.10 -CHIPBASS = Synth('chipbass') -CHIPNOISE = Synth('chipnoise') -TECHSAWS = Synth('tech_saws') # Sonic Pi 2.11 -SOUND_IN = Synth('sound_in') -SOUND_IN_STEREO = Synth('sound_in_stereo') - -## Scale Mode (from sonic pi)## -DIATONIC = 'diatonic' -IONIAN = 'ionian' -MAJOR = 'major' -DORIAN = 'dorian' -PHRYGIAN = 'phrygian' -LYDIAN = 'lydian' -MIXOLYDIAN = 'mixolydian' -AEOLIAN = 'aeolian' -MINOR = 'minor' -LOCRIAN = 'locrian' -HEX_MAJOR6 = 'hex_major6' -HEX_DORIAN = 'hex_dorian' -HEX_PHRYGIAN = 'hex_phrygian' -HEX_MAJOR7 = 'hex_major7' -HEX_SUS = 'hex_sus' -HEX_AEOLIAN = 'hex_aeolian' -MINOR_PENTATONIC = 'minor_pentatonic' -YU = 'yu' -MAJOR_PENTATONIC = 'major_pentatonic' -GONG = 'gong' -EGYPTIAN = 'egyptian' -SHANG = 'shang' -JIAO = 'jiao' -ZHI = 'zhi' -RITUSEN = 'ritusen' -WHOLE_TONE = 'whole_tone' -WHOLE = 'whole' -CHROMATIC = 'chromatic' -HARMONIC_MINOR = 'harmonic_minor' -MELODIC_MINOR_ASC = 'melodic_minor_asc' -HUNGARIAN_MINOR = 'hungarian_minor' -OCTATONIC = 'octatonic' -MESSIAEN1 = 'messiaen1' -MESSIAEN2 = 'messiaen2' -MESSIAEN3 = 'messiaen3' -MESSIAEN4 = 'messiaen4' -MESSIAEN5 = 'messiaen5' -MESSIAEN6 = 'messiaen6' -MESSIAEN7 = 'messiaen7' -SUPER_LOCRIAN = 'super_locrian' -HIRAJOSHI = 'hirajoshi' -KUMOI = 'kumoi' -NEAPLOLITAN_MAJOR = 'neapolitan_major' -BARTOK = 'bartok' -BHAIRAV = 'bhairav' -LOCRIAN_MAJOR = 'locrian_major' -AHIRBHAIRAV = 'ahirbhairav' -ENIGMATIC = 'enigmatic' -NEAPLOLITAN_MINOR = 'neapolitan_minor' -PELOG = 'pelog' -AUGMENTED2 = 'augmented2' -SCRIABIN = 'scriabin' -HARMONIC_MAJOR = 'harmonic_major' -MELODIC_MINOR_DESC = 'melodic_minor_desc' -ROMANIAN_MINOR = 'romanian_minor' -HINDU = 'hindu' -IWATO = 'iwato' -MELODIC_MINOR = 'melodic_minor' -DIMISHED2 = 'diminished2' -MARVA = 'marva' -MELODIC_MAJOR = 'melodic_major' -INDIAN = 'indian' -SPANISH = 'spanish' -PROMETHEUS = 'prometheus' -DIMISHED = 'diminished' -TODI = 'todi' -LEADING_WHOLE = 'leading_whole' -AUGMENTED = 'augmented' -PRUVI = 'purvi' -CHINESE = 'chinese' -LYDIAN_MINOR = 'lydian_minor' -I = 'i' -II = 'ii' -III = 'iii' -IV = 'iv' -V = 'v' -VI = 'vi' -VII = 'vii' -VIII = 'viii' -_ionian_sequence = [2, 2, 1, 2, 2, 2, 1] -_hex_sequence = [2, 2, 1, 2, 2, 3] -_pentatonic_sequence = [3, 2, 2, 3, 2] - -_SCALE_MODE = { - 'diatonic': _ionian_sequence, - 'ionian': _ionian_sequence, - 'major': _ionian_sequence, - 'dorian': _ionian_sequence[1:] + _ionian_sequence[:1], # rotate 1 - 'phrygian': _ionian_sequence[2:] + _ionian_sequence[:2], # rotate(2) - 'lydian': _ionian_sequence[3:] + _ionian_sequence[:3], # rotate(3) - 'mixolydian': _ionian_sequence[4:] + _ionian_sequence[:4], # rotate(4) - 'aeolian': _ionian_sequence[5:] + _ionian_sequence[:5], # rotate(5) - 'minor': _ionian_sequence[5:] + _ionian_sequence[:5], # rotate(5) - 'locrian': _ionian_sequence[6:] + _ionian_sequence[:6], # rotate(6) - 'hex_major6': _hex_sequence, - 'hex_dorian': _hex_sequence[1:] + _hex_sequence[:1], # rotate(1) - 'hex_phrygian': _hex_sequence[2:] + _hex_sequence[:2], # rotate(2) - 'hex_major7': _hex_sequence[3:] + _hex_sequence[:3], # rotate(3) - 'hex_sus': _hex_sequence[4:] + _hex_sequence[:4], # rotate(4) - 'hex_aeolian': _hex_sequence[5:] + _hex_sequence[:5], # rotate(5) - 'minor_pentatonic': _pentatonic_sequence, - 'yu': _pentatonic_sequence, - 'major_pentatonic': _pentatonic_sequence[1:] + _pentatonic_sequence[:1], # rotate(1) - 'gong': _pentatonic_sequence[1:] + _pentatonic_sequence[:1], # rotate(1) - 'egyptian': _pentatonic_sequence[2:] + _pentatonic_sequence[:2], # rotate(2) - 'shang': _pentatonic_sequence[2:] + _pentatonic_sequence[:2], # rotate(2) - 'jiao': _pentatonic_sequence[3:] + _pentatonic_sequence[:3], # rotate(3) - 'zhi': _pentatonic_sequence[4:] + _pentatonic_sequence[:4], # rotate(4) - 'ritusen': _pentatonic_sequence[4:] + _pentatonic_sequence[:4], # rotate(4) - 'whole_tone': [2, 2, 2, 2, 2, 2], - 'whole': [2, 2, 2, 2, 2, 2], - 'chromatic': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - 'harmonic_minor': [2, 1, 2, 2, 1, 3, 1], - 'melodic_minor_asc': [2, 1, 2, 2, 2, 2, 1], - 'hungarian_minor': [2, 1, 3, 1, 1, 3, 1], - 'octatonic': [2, 1, 2, 1, 2, 1, 2, 1], - 'messiaen1': [2, 2, 2, 2, 2, 2], - 'messiaen2': [1, 2, 1, 2, 1, 2, 1, 2], - 'messiaen3': [2, 1, 1, 2, 1, 1, 2, 1, 1], - 'messiaen4': [1, 1, 3, 1, 1, 1, 3, 1], - 'messiaen5': [1, 4, 1, 1, 4, 1], - 'messiaen6': [2, 2, 1, 1, 2, 2, 1, 1], - 'messiaen7': [1, 1, 1, 2, 1, 1, 1, 1, 2, 1], - 'super_locrian': [1, 2, 1, 2, 2, 2, 2], - 'hirajoshi': [2, 1, 4, 1, 4], - 'kumoi': [2, 1, 4, 2, 3], - 'neapolitan_major': [1, 2, 2, 2, 2, 2, 1], - 'bartok': [2, 2, 1, 2, 1, 2, 2], - 'bhairav': [1, 3, 1, 2, 1, 3, 1], - 'locrian_major': [2, 2, 1, 1, 2, 2, 2], - 'ahirbhairav': [1, 3, 1, 2, 2, 1, 2], - 'enigmatic': [1, 3, 2, 2, 2, 1, 1], - 'neapolitan_minor': [1, 2, 2, 2, 1, 3, 1], - 'pelog': [1, 2, 4, 1, 4], - 'augmented2': [1, 3, 1, 3, 1, 3], - 'scriabin': [1, 3, 3, 2, 3], - 'harmonic_major': [2, 2, 1, 2, 1, 3, 1], - 'melodic_minor_desc': [2, 1, 2, 2, 1, 2, 2], - 'romanian_minor': [2, 1, 3, 1, 2, 1, 2], - 'hindu': [2, 2, 1, 2, 1, 2, 2], - 'iwato': [1, 4, 1, 4, 2], - 'melodic_minor': [2, 1, 2, 2, 2, 2, 1], - 'diminished2': [2, 1, 2, 1, 2, 1, 2, 1], - 'marva': [1, 3, 2, 1, 2, 2, 1], - 'melodic_major': [2, 2, 1, 2, 1, 2, 2], - 'indian': [4, 1, 2, 3, 2], - 'spanish': [1, 3, 1, 2, 1, 2, 2], - 'prometheus': [2, 2, 2, 5, 1], - 'diminished': [1, 2, 1, 2, 1, 2, 1, 2], - 'todi': [1, 2, 3, 1, 1, 3, 1], - 'leading_whole': [2, 2, 2, 2, 2, 1, 1], - 'augmented': [3, 1, 3, 1, 3, 1], - 'purvi': [1, 3, 2, 1, 1, 3, 1], - 'chinese': [4, 2, 1, 4, 1], - 'lydian_minor': [2, 2, 2, 1, 1, 2, 2], - 'i': _ionian_sequence, - 'ii': _ionian_sequence[1:] + _ionian_sequence[:1], # rotate(1) - 'iii': _ionian_sequence[2:] + _ionian_sequence[:2], # rotate(2) - 'iv': _ionian_sequence[3:] + _ionian_sequence[:3], # rotate(3) - 'v': _ionian_sequence[4:] + _ionian_sequence[:4], # rotate(4) - 'vi': _ionian_sequence[5:] + _ionian_sequence[:5], # rotate(5) - 'vii': _ionian_sequence[6:] + _ionian_sequence[:6], # rotate(6), - 'viii': _ionian_sequence[7:] + _ionian_sequence[:7]} # rotate(7) - -## Chord Quality (from sonic pi) ## -MAJOR7 = "major7" -DOM7 = "dom7" -MINOR7 = "minor7" -AUG = "aug" -DIM = "dim" -DIM7 = "dim7" - -_CHORD_QUALITY = { - 'major': [0, 4, 7], - 'minor': [0, 3, 7], - 'major7': [0, 4, 7, 11], - 'dom7': [0, 4, 7, 10], - 'minor7': [0, 3, 7, 10], - 'aug': [0, 4, 8], - 'dim': [0, 3, 6], - 'dim7': [0, 3, 6, 9], - '1': [0], - "5": [0, 7], - "+5": [0, 4, 8], - "m+5": [0, 3, 8], - "sus2": [0, 2, 7], - "sus4": [0, 5, 7], - "6": [0, 4, 7, 9], - "m6": [0, 3, 7, 9], - "7sus2": [0, 2, 7, 10], - "7sus4": [0, 5, 7, 10], - "7-5": [0, 4, 6, 10], - "m7-5": [0, 3, 6, 10], - "7+5": [0, 4, 8, 10], - "m7+5": [0, 3, 8, 10], - "9": [0, 4, 7, 10, 14], - "m9": [0, 3, 7, 10, 14], - "m7+9": [0, 3, 7, 10, 14], - "maj9": [0, 4, 7, 11, 14], - "9sus4": [0, 5, 7, 10, 14], - "6*9": [0, 4, 7, 9, 14], - "m6*9": [0, 3, 9, 7, 14], - "7-9": [0, 4, 7, 10, 13], - "m7-9": [0, 3, 7, 10, 13], - "7-10": [0, 4, 7, 10, 15], - "9+5": [0, 10, 13], - "m9+5": [0, 10, 14], - "7+5-9": [0, 4, 8, 10, 13], - "m7+5-9": [0, 3, 8, 10, 13], - "11": [0, 4, 7, 10, 14, 17], - "m11": [0, 3, 7, 10, 14, 17], - "maj11": [0, 4, 7, 11, 14, 17], - "11+": [0, 4, 7, 10, 14, 18], - "m11+": [0, 3, 7, 10, 14, 18], - "13": [0, 4, 7, 10, 14, 17, 21], - "m13": [0, 3, 7, 10, 14, 17, 21], - "M": [0, 4, 7], - "m": [0, 3, 7], - "7": [0, 4, 7, 10], - "M7": [0, 4, 7, 11], - "m7": [0, 3, 7], - "augmented": [0, 4, 8], - "a": [0, 4, 8], - "diminished": [0, 3, 6], - "i": [0, 3, 6], - "diminished7": [0, 3, 6, 9], - "i7": [0, 3, 6, 9]} - -## FX -BITCRUSHER = FxName('bitcrusher') -COMPRESSOR = FxName('compressor') -ECHO = FxName('echo') -FLANGER = FxName('flanger') -KRUSH = FxName('krush') -LPF = FxName('lpf') -PAN = FxName('pan') -PANSLICER = FxName('panslicer') -REVERB = FxName('reverb') -SLICER = FxName('slicer') -WOBBLE = FxName('wobble') - ## Module attributes ## _current_synth = BEEP @@ -681,7 +288,6 @@ def choose(self): # random choose ## Connection classes ## - class SonicPi: """ Communiction to Sonic Pi @@ -775,9 +381,7 @@ def sleep(self, duration): synth_server = SonicPi() - ## system functions ## - def _debug(*allargs): # simple debug function for working in different environments if __debug: print(allargs) diff --git a/psonic/scales.py b/psonic/scales.py new file mode 100644 index 0000000..b4888ce --- /dev/null +++ b/psonic/scales.py @@ -0,0 +1,80 @@ +## Scale Mode (from sonic pi)## +DIATONIC = 'diatonic' +IONIAN = 'ionian' +MAJOR = 'major' +DORIAN = 'dorian' +PHRYGIAN = 'phrygian' +LYDIAN = 'lydian' +MIXOLYDIAN = 'mixolydian' +AEOLIAN = 'aeolian' +MINOR = 'minor' +LOCRIAN = 'locrian' +HEX_MAJOR6 = 'hex_major6' +HEX_DORIAN = 'hex_dorian' +HEX_PHRYGIAN = 'hex_phrygian' +HEX_MAJOR7 = 'hex_major7' +HEX_SUS = 'hex_sus' +HEX_AEOLIAN = 'hex_aeolian' +MINOR_PENTATONIC = 'minor_pentatonic' +YU = 'yu' +MAJOR_PENTATONIC = 'major_pentatonic' +GONG = 'gong' +EGYPTIAN = 'egyptian' +SHANG = 'shang' +JIAO = 'jiao' +ZHI = 'zhi' +RITUSEN = 'ritusen' +WHOLE_TONE = 'whole_tone' +WHOLE = 'whole' +CHROMATIC = 'chromatic' +HARMONIC_MINOR = 'harmonic_minor' +MELODIC_MINOR_ASC = 'melodic_minor_asc' +HUNGARIAN_MINOR = 'hungarian_minor' +OCTATONIC = 'octatonic' +MESSIAEN1 = 'messiaen1' +MESSIAEN2 = 'messiaen2' +MESSIAEN3 = 'messiaen3' +MESSIAEN4 = 'messiaen4' +MESSIAEN5 = 'messiaen5' +MESSIAEN6 = 'messiaen6' +MESSIAEN7 = 'messiaen7' +SUPER_LOCRIAN = 'super_locrian' +HIRAJOSHI = 'hirajoshi' +KUMOI = 'kumoi' +NEAPLOLITAN_MAJOR = 'neapolitan_major' +BARTOK = 'bartok' +BHAIRAV = 'bhairav' +LOCRIAN_MAJOR = 'locrian_major' +AHIRBHAIRAV = 'ahirbhairav' +ENIGMATIC = 'enigmatic' +NEAPLOLITAN_MINOR = 'neapolitan_minor' +PELOG = 'pelog' +AUGMENTED2 = 'augmented2' +SCRIABIN = 'scriabin' +HARMONIC_MAJOR = 'harmonic_major' +MELODIC_MINOR_DESC = 'melodic_minor_desc' +ROMANIAN_MINOR = 'romanian_minor' +HINDU = 'hindu' +IWATO = 'iwato' +MELODIC_MINOR = 'melodic_minor' +DIMISHED2 = 'diminished2' +MARVA = 'marva' +MELODIC_MAJOR = 'melodic_major' +INDIAN = 'indian' +SPANISH = 'spanish' +PROMETHEUS = 'prometheus' +DIMISHED = 'diminished' +TODI = 'todi' +LEADING_WHOLE = 'leading_whole' +AUGMENTED = 'augmented' +PRUVI = 'purvi' +CHINESE = 'chinese' +LYDIAN_MINOR = 'lydian_minor' +I = 'i' +II = 'ii' +III = 'iii' +IV = 'iv' +V = 'v' +VI = 'vi' +VII = 'vii' +VIII = 'viii' diff --git a/psonic/synthesizers.py b/psonic/synthesizers.py new file mode 100644 index 0000000..bb3bc81 --- /dev/null +++ b/psonic/synthesizers.py @@ -0,0 +1,56 @@ + + +class Synth: + """ + Synthesizer + """ + + def __init__(self, name): + self.name = name + + +## Synthezier ## +DULL_BELL = Synth('dull_bell') +PRETTY_BELL = Synth('pretty_bell') +SINE = Synth('sine') +SQUARE = Synth('square') +PULSE = Synth('pulse') +SUBPULSE = Synth('subpulse') +DTRI = Synth('dtri') +DPULSE = Synth('dpulse') +FM = Synth('fm') +MOD_FM = Synth('mod_fm') +MOD_SAW = Synth('mod_saw') +MOD_DSAW = Synth('mod_dsaw') +MOD_SINE = Synth('mod_sine') +MOD_TRI = Synth('mod_tri') +MOD_PULSE = Synth('mod_pulse') +SUPERSAW = Synth('supersaw') +HOOVER = Synth('hoover') +SYNTH_VIOLIN = Synth('synth_violin') +PLUCK = Synth('pluck') +PIANO = Synth('piano') +GROWL = Synth('growl') +DARK_AMBIENCE = Synth('dark_ambience') +DARK_SEA_HORN = Synth('dark_sea_horn') +HOLLOW = Synth('hollow') +ZAWA = Synth('zawa') +NOISE = Synth('noise') +GNOISE = Synth('gnoise') +BNOISE = Synth('bnoise') +CNOISE = Synth('cnoise') +DSAW = Synth('dsaw') +TB303 = Synth('tb303') +BLADE = Synth('blade') +PROPHET = Synth('prophet') +SAW = Synth('saw') +BEEP = Synth('beep') +TRI = Synth('tri') +CHIPLEAD = Synth('chiplead') # Sonic Pi 2.10 +CHIPBASS = Synth('chipbass') +CHIPNOISE = Synth('chipnoise') +TECHSAWS = Synth('tech_saws') # Sonic Pi 2.11 +SOUND_IN = Synth('sound_in') +SOUND_IN_STEREO = Synth('sound_in_stereo') + + diff --git a/setup.py b/setup.py index cab71a5..de94cbb 100644 --- a/setup.py +++ b/setup.py @@ -36,6 +36,7 @@ 'psonic', 'psonic.samples', 'psonic.samples.loops', + 'psonic.internals', ], license='MIT', zip_safe=False, From 2bffc033d2574812dac8eb4c5830598d8c124fb5 Mon Sep 17 00:00:00 2001 From: Toomas Ormisson Date: Wed, 21 Feb 2018 22:20:57 +0200 Subject: [PATCH 13/49] simplified "play" function. --- psonic/psonic.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/psonic/psonic.py b/psonic/psonic.py index 2c9abd7..d306420 100644 --- a/psonic/psonic.py +++ b/psonic/psonic.py @@ -109,20 +109,10 @@ def synth( def play(note, attack=None, decay=None, sustain_level=None, sustain=None, release=None, cutoff=None, cutoff_attack=None, amp=None, pan=None): - parameters = [] - parameter = '' - - if attack is not None: parameters.append('attack: {0}'.format(attack)) - if cutoff_attack is not None: parameters.append('cutoff_attack: {0}'.format(cutoff_attack)) - if decay is not None: parameters.append('decay: {0}'.format(decay)) - if sustain_level is not None: parameters.append('sustain_level: {0}'.format(sustain_level)) - if sustain is not None: parameters.append('sustain: {0}'.format(sustain)) - if release is not None: parameters.append('release: {0}'.format(release)) - if cutoff is not None: parameters.append('cutoff: {0}'.format(cutoff)) - - if amp is not None: parameters.append('amp: {0}'.format(amp)) - if pan is not None: parameters.append('pan: {0}'.format(pan)) + arguments = locals() + parameters = ['{0}: {1}'.format(k, v) for k, v in arguments.items() if v is not None] + parameter = '' if len(parameters) > 0: parameter = ',' + ','.join(parameters) command = 'play {0}{1}'.format(note, parameter) From ed3d8bbc1ac4d03c5525463d85834c36ee940818 Mon Sep 17 00:00:00 2001 From: Toomas Ormisson Date: Wed, 21 Feb 2018 22:29:46 +0200 Subject: [PATCH 14/49] simplified "synth" function. --- psonic/psonic.py | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/psonic/psonic.py b/psonic/psonic.py index d306420..d4b8c6f 100644 --- a/psonic/psonic.py +++ b/psonic/psonic.py @@ -25,7 +25,6 @@ def __init__(self, name, inter): self.inter = inter - class Message: """ For sending messages between threads @@ -80,25 +79,13 @@ def use_synth(synth): _current_synth = synth -def synth( - name, note=None, attack=None, decay=None, +def synth(name, note=None, attack=None, decay=None, sustain_level=None, sustain=None, release=None, cutoff=None, cutoff_attack=None, amp=None, pan=None): - parameters = [] - parameter = '' - - if note is not None: parameters.append('note: {0}'.format(note)) - if attack is not None: parameters.append('attack: {0}'.format(attack)) - if cutoff_attack is not None: parameters.append('cutoff_attack: {0}'.format(cutoff_attack)) - if decay is not None: parameters.append('decay: {0}'.format(decay)) - if sustain_level is not None: parameters.append('sustain_level: {0}'.format(sustain_level)) - if sustain is not None: parameters.append('sustain: {0}'.format(sustain)) - if release is not None: parameters.append('release: {0}'.format(release)) - if cutoff is not None: parameters.append('cutoff: {0}'.format(cutoff)) - - if amp is not None: parameters.append('amp: {0}'.format(amp)) - if pan is not None: parameters.append('pan: {0}'.format(pan)) + arguments = locals() + parameters = ['{0}: {1}'.format(k, v) for k, v in arguments.items() if v is not None] + parameter = '' if len(parameters) > 0: parameter = ',' + ','.join(parameters) command = 'synth :{0}{1}'.format(name.name, parameter) @@ -107,8 +94,9 @@ def synth( synth_server.synth(command) -def play(note, attack=None, decay=None, sustain_level=None, sustain=None, release=None, cutoff=None, - cutoff_attack=None, amp=None, pan=None): +def play(note, attack=None, decay=None, + sustain_level=None, sustain=None, release=None, + cutoff=None, cutoff_attack=None, amp=None, pan=None): arguments = locals() parameters = ['{0}: {1}'.format(k, v) for k, v in arguments.items() if v is not None] @@ -254,7 +242,6 @@ def send_message(message, *parameter): synth_server.send_message(message, *parameter) ## Compound classes ## - class Ring: """ ring buffer From 94796d2f33247244e40ebc4aa561147acb5fc4a8 Mon Sep 17 00:00:00 2001 From: Toomas Ormisson Date: Fri, 23 Feb 2018 17:55:08 +0200 Subject: [PATCH 15/49] separated responsibilities, simplified a little, added tests for imports. --- psonic/api.py | 63 +++++++++++++ psonic/internals/chords.py | 9 ++ psonic/psonic.py | 177 +++++++++---------------------------- tests/test_psonic.py | 23 ++++- 4 files changed, 135 insertions(+), 137 deletions(-) diff --git a/psonic/api.py b/psonic/api.py index 68db161..36bc542 100644 --- a/psonic/api.py +++ b/psonic/api.py @@ -1,3 +1,5 @@ +import random +import threading from .psonic import * from .notes import * from .scales import * @@ -17,5 +19,66 @@ from .internals.chords import * from .internals.scales import * +def in_thread(func): + """Thread decorator""" + + def wrapper(): + _thread = threading.Thread(target=func) + _thread.start() + + return wrapper + + +class Fx: + """FX Effects""" + + def __init__(self, mode, phase=0.24, probability=0, prob_pos=0): + self.mode = mode + self.phase = phase + self.probability = probability + self.prob_pos = prob_pos + + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + +class Ring: + """ring buffer""" + + def __init__(self, data): + self.data = data + self.index = -1 + + def __iter__(self): # return Iterator + return self + + def __next__(self): # return Iterator next element + self.index += 1 + if self.index == len(self.data): + self.index = 0 + return self.data[self.index] + + def choose(self): # random choose + return random.choice(self.data) + + +# Not used anywhere +class Message: + """For sending messages between threads""" + + def __init__(self): + self._condition = threading.Condition() + + def cue(self): + with self._condition: + self._condition.notifyAll() # Message to threads + + def sync(self): + with self._condition: + self._condition.wait() # Wait for message + + diff --git a/psonic/internals/chords.py b/psonic/internals/chords.py index e468611..d505969 100644 --- a/psonic/internals/chords.py +++ b/psonic/internals/chords.py @@ -1,3 +1,12 @@ + +# Not used anywhere +class ChordQuality: + """Chord Quality""" + + def __init__(self, name, inter): + self.name = name + self.inter = inter + _CHORD_QUALITY = { 'major': [0, 4, 7], 'minor': [0, 3, 7], diff --git a/psonic/psonic.py b/psonic/psonic.py index d4b8c6f..7c3885c 100644 --- a/psonic/psonic.py +++ b/psonic/psonic.py @@ -1,10 +1,9 @@ import random import time -import threading - #TODO: Optimize imports from .samples import Sample -from .synthesizers import BEEP +from .synthesizers import BEEP, SAW +from .notes import C5 from .internals.chords import _CHORD_QUALITY from .internals.scales import _SCALE_MODE @@ -14,61 +13,6 @@ __debug = False -## Base Classes ## -class ChordQuality: - """ - Chord Quality - """ - - def __init__(self, name, inter): - self.name = name - self.inter = inter - - -class Message: - """ - For sending messages between threads - """ - - def __init__(self): - self._condition = threading.Condition() - - def cue(self): - with self._condition: - self._condition.notifyAll() # Message to threads - - def sync(self): - with self._condition: - self._condition.wait() # Wait for message - - -class Fx: - """ - FX Effects - """ - - def __init__(self, mode, phase=0.24, probability=0, prob_pos=0): - self.mode = mode - self.phase = phase - self.probability = probability - self.prob_pos = prob_pos - - def __enter__(self): - pass - - def __exit__(self, exc_type, exc_val, exc_tb): - pass - - -def in_thread(func): - """Thread decorator""" - def wrapper(): - _thread = threading.Thread(target=func) - _thread.start() - - return wrapper - - ## Module attributes ## _current_synth = BEEP @@ -110,8 +54,7 @@ def play(note, attack=None, decay=None, def play_pattern_timed(notes, times, release=None): - """ - play notes + """play notes :param notes: :param times: :return: @@ -126,32 +69,22 @@ def play_pattern_timed(notes, times, release=None): def play_pattern(notes): - """ - - :param notes: + """:param notes: :return: """ play_pattern_timed(notes, 1) -def sample(sample, rate=None, attack=None, sustain=None, release=None, beat_stretch=None, - start=None, finish=None, amp=None, pan=None): - parameters = [] +def sample(sample, rate=None, attack=None, sustain=None, + release=None, beat_stretch=None, start=None, + finish=None, amp=None, pan=None): + + arguments = locals() + parameters = ['{0}: {1}'.format(k, v) for k, v in arguments.items() if v is not None] parameter = '' command = '' - if rate is not None: parameters.append('rate: {0}'.format(rate)) - if attack is not None: parameters.append('attack: {0}'.format(attack)) - if sustain is not None: parameters.append('sustain: {0}'.format(sustain)) - if release is not None: parameters.append('release: {0}'.format(release)) - if beat_stretch is not None: parameters.append('beat_stretch: {0}'.format(beat_stretch)) - if start is not None: parameters.append('start: {0}'.format(start)) - if finish is not None: parameters.append('finish: {0}'.format(finish)) - if amp is not None: parameters.append('amp: {0}'.format(amp)) - if pan is not None: parameters.append('pan: {0}'.format(pan)) - if len(parameters) > 0: parameter = ',' + ','.join(parameters) - if type(sample) == Sample: command = 'sample :{0}{1}'.format(sample.name, parameter) else: @@ -162,8 +95,7 @@ def sample(sample, rate=None, attack=None, sustain=None, release=None, beat_stre def sleep(duration): - """ - the same as time.sleep + """the same as time.sleep :param duration: :return: """ @@ -171,9 +103,7 @@ def sleep(duration): _debug('sleep', duration) def sample_duration(sample): - """ - Returns the duration of the sample (in seconds) - + """Returns the duration of the sample (in seconds) :param sample: :return: number """ @@ -181,8 +111,7 @@ def sample_duration(sample): def one_in(max): - """ - random function returns True in one of max cases + """random function returns True in one of max cases :param max: :return: boolean """ @@ -190,9 +119,7 @@ def one_in(max): def chord(root_note, chord_quality): - """ - Generates a list of notes of a chord - + """Generates a list of notes of a chord :param root_note: :param chord_quality: :return: list @@ -210,9 +137,7 @@ def chord(root_note, chord_quality): def scale(root_note, scale_mode, num_octaves=1): - """ - Genarates a liste of notes of scale - + """Genarates a liste of notes of scale :param root_note: :param scale_mode: :param num_octaves: @@ -242,35 +167,24 @@ def send_message(message, *parameter): synth_server.send_message(message, *parameter) ## Compound classes ## -class Ring: - """ - ring buffer - """ +class SonicPiCommon: - def __init__(self, data): - self.data = data - self.index = -1 + UDP_IP = "127.0.0.1" - def __iter__(self): # return Iterator - return self + def send(self, command): + pass - def __next__(self): # return Iterator next element - self.index += 1 - if self.index == len(self.data): - self.index = 0 - return self.data[self.index] + def sleep(self, duration): + time.sleep(duration) - def choose(self): # random choose - return random.choice(self.data) + def sample(self, command): + self.send(command) ## Connection classes ## -class SonicPi: - """ - Communiction to Sonic Pi - """ +class SonicPi(SonicPiCommon): + """Communiction to Sonic Pi""" - UDP_IP = "127.0.0.1" UDP_PORT = 4557 UDP_PORT_OSC_MESSAGE = 4559 GUI_ID = 'SONIC_PI_PYTHON' @@ -279,23 +193,23 @@ class SonicPi: STOP_COMMAND = "/stop-all-jobs" def __init__(self): - self.client = udp_client.UDPClient(SonicPi.UDP_IP, SonicPi.UDP_PORT) - self.client_for_messages = udp_client.UDPClient(SonicPi.UDP_IP, SonicPi.UDP_PORT_OSC_MESSAGE) - - def sample(self, command): - self.run(command) + self.client = udp_client.UDPClient( + SonicPi.UDP_IP, + SonicPi.UDP_PORT + ) + self.client_for_messages = udp_client.UDPClient( + self.UDP_IP, + self.UDP_PORT_OSC_MESSAGE + ) def play(self, command): command = 'use_synth :{0}\n'.format(_current_synth.name) + command - self.run(command) + self.send(command) def synth(self, command): - self.run(command) - - def sleep(self, duration): - time.sleep(duration) + self.send(command) - def run(self, command): + def send(self, command): self.send_command(SonicPi.RUN_COMMAND, command) def stop(self): @@ -312,7 +226,6 @@ def send_command(self, address, argument=''): if argument != "": msg.add_arg(argument) msg = msg.build() - self.client.send(msg) def send_message(self,message, *parameters): @@ -322,16 +235,16 @@ def send_message(self,message, *parameters): msg = msg.build() self.client_for_messages.send(msg) -class SonicPiNew: - """ - Communiction to Sonic Pi - """ +class SonicPiNew(SonicPiCommon): + """Communiction to Sonic Pi""" - UDP_IP = "127.0.0.1" UDP_PORT = 4559 def __init__(self): - self.client = udp_client.UDPClient(SonicPiNew.UDP_IP, SonicPiNew.UDP_PORT) + self.client = udp_client.UDPClient( + self.UDP_IP, + self.UDP_PORT + ) self.commandServer = SonicPi() # x= 'live_loop :py do\n nv=sync "/SENDOSC"\n puts nv\n eval(nv[0])\nend' # self.commandServer.run(x) @@ -346,15 +259,9 @@ def send(self, address, *message): msg = msg.build() self.client.send(msg) - def sample(self, command): - self.send(command) - def play(self, command): self.send(command) - def sleep(self, duration): - time.sleep(duration) - synth_server = SonicPi() diff --git a/tests/test_psonic.py b/tests/test_psonic.py index a6e3231..24cb66f 100644 --- a/tests/test_psonic.py +++ b/tests/test_psonic.py @@ -1,8 +1,9 @@ from psonic import * def test_originl_behaviour(): - play(72) - play(G5) + print('starting tests') + notes_to_play = [72, G5, Fs5, Eb5] + (play(note) for note in notes_to_play) sleep(0.5) play(72,amp=2) play(74,pan=-1) @@ -32,6 +33,24 @@ def test_originl_behaviour(): detune = 0.7 synth(SQUARE, note = E4+detune) synth(GNOISE, release = 0.5, amp = 1, cutoff = 100) + sc = Ring(scale(E3, MINOR_PENTATONIC)) + play(next(sc), release= 0.1) + with Fx(SLICER): + synth(PROPHET,note=E2,release=8,cutoff=80) + synth(PROPHET,note=E2+4,release=8,cutoff=80) + print('ending tests') + +def test_imports(): + from psonic import( + SonicPi, + SonicPiNew, + ChordQuality, + Message, + Ring, + Fx, + Synth, + Sample, + ) From 246f4d21a5c6dcc4c13a4c419df2e1520fe8a3b6 Mon Sep 17 00:00:00 2001 From: Toomas Ormisson Date: Fri, 23 Feb 2018 18:01:23 +0200 Subject: [PATCH 16/49] Placeholder for synth_server --- psonic/psonic.py | 2 -- psonic/synth_server.py | 0 2 files changed, 2 deletions(-) create mode 100644 psonic/synth_server.py diff --git a/psonic/psonic.py b/psonic/psonic.py index 7c3885c..429f177 100644 --- a/psonic/psonic.py +++ b/psonic/psonic.py @@ -12,11 +12,9 @@ __debug = False - ## Module attributes ## _current_synth = BEEP - ## Module methodes ## def use_synth(synth): global _current_synth diff --git a/psonic/synth_server.py b/psonic/synth_server.py new file mode 100644 index 0000000..e69de29 From ba76e372f07223c70d4979be86238a973bfc7fbd Mon Sep 17 00:00:00 2001 From: Toomas Ormisson Date: Fri, 23 Feb 2018 18:23:13 +0200 Subject: [PATCH 17/49] Moved synth_server to separate file. --- psonic/api.py | 2 + psonic/psonic.py | 129 +++-------------------------------------- psonic/synth_server.py | 110 +++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 122 deletions(-) diff --git a/psonic/api.py b/psonic/api.py index 36bc542..5d7d4da 100644 --- a/psonic/api.py +++ b/psonic/api.py @@ -4,6 +4,7 @@ from .notes import * from .scales import * from .synthesizers import * +from .synth_server import * from .effects import * from .samples import * from .samples.loops import * @@ -19,6 +20,7 @@ from .internals.chords import * from .internals.scales import * + def in_thread(func): """Thread decorator""" diff --git a/psonic/psonic.py b/psonic/psonic.py index 429f177..ab64023 100644 --- a/psonic/psonic.py +++ b/psonic/psonic.py @@ -1,26 +1,16 @@ import random -import time -#TODO: Optimize imports from .samples import Sample -from .synthesizers import BEEP, SAW +from .synthesizers import SAW from .notes import C5 from .internals.chords import _CHORD_QUALITY from .internals.scales import _SCALE_MODE - -from pythonosc import osc_message_builder # osc support -from pythonosc import udp_client +from .synth_server import ( + SonicPi, + use_synth, +) __debug = False -## Module attributes ## -_current_synth = BEEP - -## Module methodes ## -def use_synth(synth): - global _current_synth - _current_synth = synth - - def synth(name, note=None, attack=None, decay=None, sustain_level=None, sustain=None, release=None, cutoff=None, cutoff_attack=None, amp=None, pan=None): @@ -35,7 +25,6 @@ def synth(name, note=None, attack=None, decay=None, _debug('synth command={}'.format(command)) synth_server.synth(command) - def play(note, attack=None, decay=None, sustain_level=None, sustain=None, release=None, cutoff=None, cutoff_attack=None, amp=None, pan=None): @@ -50,7 +39,6 @@ def play(note, attack=None, decay=None, _debug('play command={}'.format(command)) synth_server.play(command) - def play_pattern_timed(notes, times, release=None): """play notes :param notes: @@ -65,14 +53,12 @@ def play_pattern_timed(notes, times, release=None): play(i, release=release) sleep(t) - def play_pattern(notes): """:param notes: :return: """ play_pattern_timed(notes, 1) - def sample(sample, rate=None, attack=None, sustain=None, release=None, beat_stretch=None, start=None, finish=None, amp=None, pan=None): @@ -91,7 +77,6 @@ def sample(sample, rate=None, attack=None, sustain=None, _debug('sample command={}'.format(command)) synth_server.sample(command) - def sleep(duration): """the same as time.sleep :param duration: @@ -107,7 +92,6 @@ def sample_duration(sample): """ return sample.duration - def one_in(max): """random function returns True in one of max cases :param max: @@ -115,7 +99,6 @@ def one_in(max): """ return random.randint(1, max) == 1 - def chord(root_note, chord_quality): """Generates a list of notes of a chord :param root_note: @@ -133,7 +116,6 @@ def chord(root_note, chord_quality): return result - def scale(root_note, scale_mode, num_octaves=1): """Genarates a liste of notes of scale :param root_note: @@ -164,108 +146,11 @@ def stop(): def send_message(message, *parameter): synth_server.send_message(message, *parameter) -## Compound classes ## -class SonicPiCommon: - - UDP_IP = "127.0.0.1" - - def send(self, command): - pass - - def sleep(self, duration): - time.sleep(duration) - - def sample(self, command): - self.send(command) - - -## Connection classes ## -class SonicPi(SonicPiCommon): - """Communiction to Sonic Pi""" - - UDP_PORT = 4557 - UDP_PORT_OSC_MESSAGE = 4559 - GUI_ID = 'SONIC_PI_PYTHON' - - RUN_COMMAND = "/run-code" - STOP_COMMAND = "/stop-all-jobs" - - def __init__(self): - self.client = udp_client.UDPClient( - SonicPi.UDP_IP, - SonicPi.UDP_PORT - ) - self.client_for_messages = udp_client.UDPClient( - self.UDP_IP, - self.UDP_PORT_OSC_MESSAGE - ) - - def play(self, command): - command = 'use_synth :{0}\n'.format(_current_synth.name) + command - self.send(command) - - def synth(self, command): - self.send(command) - - def send(self, command): - self.send_command(SonicPi.RUN_COMMAND, command) - - def stop(self): - self.send_command(SonicPi.STOP_COMMAND) - - def test_connection(self): - # OSC::Server.new(PORT) - # abort("ERROR: Sonic Pi is not listening on #{PORT} - is it running?") - pass - - def send_command(self, address, argument=''): - msg = osc_message_builder.OscMessageBuilder(address=address) - msg.add_arg('SONIC_PI_PYTHON') - if argument != "": - msg.add_arg(argument) - msg = msg.build() - self.client.send(msg) - - def send_message(self,message, *parameters): - msg = osc_message_builder.OscMessageBuilder(message) - for p in parameters: - msg.add_arg(p) - msg = msg.build() - self.client_for_messages.send(msg) - -class SonicPiNew(SonicPiCommon): - """Communiction to Sonic Pi""" - - UDP_PORT = 4559 - - def __init__(self): - self.client = udp_client.UDPClient( - self.UDP_IP, - self.UDP_PORT - ) - self.commandServer = SonicPi() - # x= 'live_loop :py do\n nv=sync "/SENDOSC"\n puts nv\n eval(nv[0])\nend' - # self.commandServer.run(x) - - def set_OSC_receiver(self, source): - self.commandServer.run(source) - - def send(self, address, *message): - msg = osc_message_builder.OscMessageBuilder(address) - for m in message: - msg.add_arg(m) - msg = msg.build() - self.client.send(msg) - - def play(self, command): - self.send(command) - synth_server = SonicPi() -## system functions ## -def _debug(*allargs): # simple debug function for working in different environments - if __debug: print(allargs) +def _debug(*args): + if __debug: print(args) if __name__ == '__main__': diff --git a/psonic/synth_server.py b/psonic/synth_server.py index e69de29..9b63b92 100644 --- a/psonic/synth_server.py +++ b/psonic/synth_server.py @@ -0,0 +1,110 @@ +import time +from pythonosc import osc_message_builder # osc support +from pythonosc import udp_client +from .synthesizers import BEEP + +## Module attributes ## +_current_synth = BEEP + +## Module methodes ## +def use_synth(synth): + global _current_synth + _current_synth = synth + + +## Compound classes ## +class SonicPiCommon: + + UDP_IP = "127.0.0.1" + + def send(self, command): + pass + + def sleep(self, duration): + time.sleep(duration) + + def sample(self, command): + self.send(command) + + +## Connection classes ## +class SonicPi(SonicPiCommon): + """Communiction to Sonic Pi""" + + UDP_PORT = 4557 + UDP_PORT_OSC_MESSAGE = 4559 + GUI_ID = 'SONIC_PI_PYTHON' + + RUN_COMMAND = "/run-code" + STOP_COMMAND = "/stop-all-jobs" + + def __init__(self): + self.client = udp_client.UDPClient( + SonicPi.UDP_IP, + SonicPi.UDP_PORT + ) + self.client_for_messages = udp_client.UDPClient( + self.UDP_IP, + self.UDP_PORT_OSC_MESSAGE + ) + + def play(self, command): + command = 'use_synth :{0}\n'.format(_current_synth.name) + command + self.send(command) + + def synth(self, command): + self.send(command) + + def send(self, command): + self.send_command(SonicPi.RUN_COMMAND, command) + + def stop(self): + self.send_command(SonicPi.STOP_COMMAND) + + def test_connection(self): + # OSC::Server.new(PORT) + # abort("ERROR: Sonic Pi is not listening on #{PORT} - is it running?") + pass + + def send_command(self, address, argument=''): + msg = osc_message_builder.OscMessageBuilder(address=address) + msg.add_arg('SONIC_PI_PYTHON') + if argument != "": + msg.add_arg(argument) + msg = msg.build() + self.client.send(msg) + + def send_message(self,message, *parameters): + msg = osc_message_builder.OscMessageBuilder(message) + for p in parameters: + msg.add_arg(p) + msg = msg.build() + self.client_for_messages.send(msg) + +class SonicPiNew(SonicPiCommon): + """Communiction to Sonic Pi""" + + UDP_PORT = 4559 + + def __init__(self): + self.client = udp_client.UDPClient( + self.UDP_IP, + self.UDP_PORT + ) + self.commandServer = SonicPi() + # x= 'live_loop :py do\n nv=sync "/SENDOSC"\n puts nv\n eval(nv[0])\nend' + # self.commandServer.run(x) + + def set_OSC_receiver(self, source): + self.commandServer.run(source) + + def send(self, address, *message): + msg = osc_message_builder.OscMessageBuilder(address) + for m in message: + msg.add_arg(m) + msg = msg.build() + self.client.send(msg) + + def play(self, command): + self.send(command) + From 89b4b689efcdf2f7bd1c7f1052ba109c2174fb2f Mon Sep 17 00:00:00 2001 From: Toomas Ormisson Date: Sat, 24 Feb 2018 10:33:55 +0200 Subject: [PATCH 18/49] Fixed a bug that prevented playing samples --- psonic/api.py | 2 -- psonic/psonic.py | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/psonic/api.py b/psonic/api.py index 5d7d4da..987e8b6 100644 --- a/psonic/api.py +++ b/psonic/api.py @@ -30,7 +30,6 @@ def wrapper(): return wrapper - class Fx: """FX Effects""" @@ -65,7 +64,6 @@ def __next__(self): # return Iterator next element def choose(self): # random choose return random.choice(self.data) - # Not used anywhere class Message: """For sending messages between threads""" diff --git a/psonic/psonic.py b/psonic/psonic.py index ab64023..3fc9124 100644 --- a/psonic/psonic.py +++ b/psonic/psonic.py @@ -16,6 +16,7 @@ def synth(name, note=None, attack=None, decay=None, cutoff=None, cutoff_attack=None, amp=None, pan=None): arguments = locals() + arguments.pop('name') parameters = ['{0}: {1}'.format(k, v) for k, v in arguments.items() if v is not None] parameter = '' if len(parameters) > 0: parameter = ',' + ','.join(parameters) @@ -30,6 +31,7 @@ def play(note, attack=None, decay=None, cutoff=None, cutoff_attack=None, amp=None, pan=None): arguments = locals() + arguments.pop('note') parameters = ['{0}: {1}'.format(k, v) for k, v in arguments.items() if v is not None] parameter = '' if len(parameters) > 0: parameter = ',' + ','.join(parameters) @@ -64,6 +66,7 @@ def sample(sample, rate=None, attack=None, sustain=None, finish=None, amp=None, pan=None): arguments = locals() + arguments.pop('sample') parameters = ['{0}: {1}'.format(k, v) for k, v in arguments.items() if v is not None] parameter = '' command = '' @@ -73,7 +76,6 @@ def sample(sample, rate=None, attack=None, sustain=None, command = 'sample :{0}{1}'.format(sample.name, parameter) else: command = 'sample "{0}"{1}'.format(sample, parameter) - _debug('sample command={}'.format(command)) synth_server.sample(command) From a9075927125a36c4c80b13f40e70007de77b2c5e Mon Sep 17 00:00:00 2001 From: Toomas Ormisson Date: Sat, 24 Feb 2018 10:49:17 +0200 Subject: [PATCH 19/49] Increased version nr. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index de94cbb..48b8e82 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ setup( name='python-sonic', - version='0.3.1', + version='0.3.2', description='Programming Music with Sonic Pi or Supercollider', long_description=long_description, url='https://github.com/gkvoelkl/python-sonic', From e65fbf6b5d7070426bef504f235109516a803de3 Mon Sep 17 00:00:00 2001 From: Toomas Ormisson Date: Mon, 26 Feb 2018 19:27:11 +0200 Subject: [PATCH 20/49] improved gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 03fd1c5..b201f56 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ sdist/ var/ *.egg-info/ .pytest_cache/ +.ipynb_checkpoints/ .installed.cfg *.egg From 990a1fc13c8c705027803235a818dd0b9a849f0e Mon Sep 17 00:00:00 2001 From: Toomas Ormisson Date: Mon, 26 Feb 2018 20:12:35 +0200 Subject: [PATCH 21/49] removed reclessly added functionality, tried to clean up a little. --- psonic/__init__.py | 1 - psonic/api.py | 10 +++---- psonic/effects.py | 12 +++------ psonic/notes.py | 3 ++- psonic/samples/__init__.py | 5 +--- psonic/samples/ambient.py | 20 +++----------- psonic/samples/bass.py | 18 +++---------- psonic/samples/drums.py | 45 +++----------------------------- psonic/samples/electric.py | 34 +++--------------------- psonic/samples/guitars.py | 15 +++-------- psonic/samples/loops/__init__.py | 4 ++- psonic/samples/misc.py | 15 +++-------- psonic/samples/percussions.py | 17 +++--------- psonic/samples/tabla.py | 37 +++----------------------- psonic/samples/vinyl.py | 14 +++------- psonic/scales.py | 3 ++- psonic/synth_server.py | 3 ++- psonic/synthesizers.py | 9 ++----- 18 files changed, 50 insertions(+), 215 deletions(-) diff --git a/psonic/__init__.py b/psonic/__init__.py index 973a698..0a0e47b 100644 --- a/psonic/__init__.py +++ b/psonic/__init__.py @@ -1,2 +1 @@ from .api import * - diff --git a/psonic/api.py b/psonic/api.py index 987e8b6..910ad00 100644 --- a/psonic/api.py +++ b/psonic/api.py @@ -1,10 +1,14 @@ import random import threading +from .synth_server import ( + SonicPi, + SonicPiNew, + use_synth, +) from .psonic import * from .notes import * from .scales import * from .synthesizers import * -from .synth_server import * from .effects import * from .samples import * from .samples.loops import * @@ -78,7 +82,3 @@ def cue(self): def sync(self): with self._condition: self._condition.wait() # Wait for message - - - - diff --git a/psonic/effects.py b/psonic/effects.py index f673b7c..58fc901 100644 --- a/psonic/effects.py +++ b/psonic/effects.py @@ -1,12 +1,12 @@ +"""Effects""" + class FxName: - """ - FX name - """ + """FX name""" def __init__(self, name): self.name = name -## FX + BITCRUSHER = FxName('bitcrusher') COMPRESSOR = FxName('compressor') ECHO = FxName('echo') @@ -18,7 +18,3 @@ def __init__(self, name): REVERB = FxName('reverb') SLICER = FxName('slicer') WOBBLE = FxName('wobble') - - - - diff --git a/psonic/notes.py b/psonic/notes.py index 67f9fc0..13f8bbb 100644 --- a/psonic/notes.py +++ b/psonic/notes.py @@ -1,4 +1,5 @@ -## Notes ## +"""Notes""" + C2 = 36 Cs2 = 37 Db2 = Cs2 diff --git a/psonic/samples/__init__.py b/psonic/samples/__init__.py index 0c48619..b53120d 100644 --- a/psonic/samples/__init__.py +++ b/psonic/samples/__init__.py @@ -1,8 +1,5 @@ - class Sample(object): - """ - Sample - """ + """Sample""" def __init__(self, name, duration): self.name = name diff --git a/psonic/samples/ambient.py b/psonic/samples/ambient.py index 914495a..6a1bdb7 100644 --- a/psonic/samples/ambient.py +++ b/psonic/samples/ambient.py @@ -1,6 +1,8 @@ +"""Ambient sounds""" + from . import Sample -## Ambient Sounds + AMBI_CHOIR = Sample('ambi_choir', 1.5715419501133787) AMBI_DARK_WOOSH = Sample('ambi_dark_woosh', 3.7021315192743764) AMBI_DRONE = Sample('ambi_drone', 4.40843537414966) @@ -11,19 +13,3 @@ AMBI_PIANO = Sample('ambi_piano', 2.811746031746032) AMBI_SOFT_BUZZ = Sample('ambi_soft_buzz', 0.7821541950113379) AMBI_SWOOSH = Sample('ambi_swoosh', 1.8484580498866212) - -def get_sounds(): - - sounds = dict( - AMBI_CHOIR = AMBI_CHOIR, - AMBI_DARK_WOOSH = AMBI_DARK_WOOSH, - AMBI_DRONE = AMBI_DRONE, - AMBI_GLASS_HUM = AMBI_GLASS_HUM, - AMBI_GLASS_RUB = AMBI_GLASS_RUB, - AMBI_HAUNTED_HUM = AMBI_HAUNTED_HUM, - AMBI_LUNAR_LAND = AMBI_LUNAR_LAND, - AMBI_PIANO = AMBI_PIANO, - AMBI_SOFT_BUZZ = AMBI_SOFT_BUZZ, - AMBI_SWOOSH = AMBI_SWOOSH, - ) - return sounds diff --git a/psonic/samples/bass.py b/psonic/samples/bass.py index 868f07a..bd6a1c3 100644 --- a/psonic/samples/bass.py +++ b/psonic/samples/bass.py @@ -1,6 +1,8 @@ +"""Bass sounds""" + from . import Sample -## Bass Sounds + BASS_DNB_F = Sample('bass_dnb_f', 0.8705442176870748) BASS_DROP_C = Sample('bass_drop_c', 2.3589115646258505) BASS_HARD_C = Sample('bass_hard_c', 1.5) @@ -9,17 +11,3 @@ BASS_VOXY_C = Sample('bass_voxy_c', 6.23469387755102) BASS_VOXY_HIT_C = Sample('bass_voxy_hit_c', 0.457437641723356) BASS_WOODSY_C = Sample('bass_woodsy_c', 3.252267573696145) - - -def get_sounds(): - sounds = dict( - BASS_DNB_F = BASS_DNB_F, - BASS_DROP_C = BASS_DROP_C, - BASS_HARD_C = BASS_HARD_C, - BASS_HIT_C = BASS_HIT_C, - BASS_THICK_C = BASS_THICK_C, - BASS_VOXY_C = BASS_VOXY_C, - BASS_VOXY_HIT_C = BASS_VOXY_HIT_C, - BASS_WOODSY_C = BASS_WOODSY_C, - ) - return sounds diff --git a/psonic/samples/drums.py b/psonic/samples/drums.py index 6bb4b57..b73f0f5 100644 --- a/psonic/samples/drums.py +++ b/psonic/samples/drums.py @@ -1,5 +1,8 @@ +"""Drum sounds""" + from . import Sample + ## Drum Sounds DRUM_BASS_HARD = Sample('drum_bass_hard', 0.6951927437641723) DRUM_BASS_SOFT = Sample('drum_bass_soft', 0.5624489795918367) @@ -40,45 +43,3 @@ BD_TEK = Sample('bd_tek', 0.24024943310657595) BD_ZOME = Sample('bd_zome', 0.45972789115646256) BD_ZUM = Sample('bd_zum', 0.13158730158730159) - -def get_sounds(): - sounds = dict( - DRUM_BASS_HARD = DRUM_BASS_HARD, - DRUM_BASS_SOFT = DRUM_BASS_SOFT, - DRUM_COWBELL = DRUM_COWBELL, - DRUM_CYMBAL_CLOSED = DRUM_CYMBAL_CLOSED, - DRUM_CYMBAL_HARD = DRUM_CYMBAL_HARD, - DRUM_CYMBAL_OPEN = DRUM_CYMBAL_OPEN, - DRUM_CYMBAL_PEDAL = DRUM_CYMBAL_PEDAL, - DRUM_CYMBAL_SOFT = DRUM_CYMBAL_SOFT, - DRUM_HEAVY_KICK = DRUM_HEAVY_KICK, - DRUM_ROLL = DRUM_ROLL, - DRUM_SNARE_HARD = DRUM_SNARE_HARD, - DRUM_SNARE_SOFT = DRUM_SNARE_SOFT, - DRUM_SPLASH_HARD = DRUM_SPLASH_HARD, - DRUM_SPLASH_SOFT = DRUM_SPLASH_SOFT, - DRUM_TOM_HI_HARD = DRUM_TOM_HI_HARD, - DRUM_TOM_HI_SOFT = DRUM_TOM_HI_SOFT, - DRUM_TOM_LO_HARD = DRUM_TOM_LO_HARD, - DRUM_TOM_LO_SOFT = DRUM_TOM_LO_SOFT, - DRUM_TOM_MID_HARD = DRUM_TOM_MID_HARD, - DRUM_TOM_MID_SOFT = DRUM_TOM_MID_SOFT, - - SN_DOLF = SN_DOLF, - SN_DUB = SN_DUB, - SN_ZOME = SN_ZOME, - - BD_808 = BD_808, - BD_ADA = BD_ADA, - BD_BOOM = BD_BOOM, - BD_FAT = BD_FAT, - BD_GAS = BD_GAS, - BD_HAUS = BD_HAUS, - BD_KLUB = BD_KLUB, - BD_PURE = BD_PURE, - BD_SONE = BD_SONE, - BD_TEK = BD_TEK, - BD_ZOME = BD_ZOME, - BD_ZUM = BD_ZUM, - ) - return sounds diff --git a/psonic/samples/electric.py b/psonic/samples/electric.py index d305198..2af4640 100644 --- a/psonic/samples/electric.py +++ b/psonic/samples/electric.py @@ -1,6 +1,8 @@ +"""Electric Sounds""" + from . import Sample -## Electric Sounds + ELEC_BEEP = Sample('elec_beep', 0.10578231292517007) ELEC_BELL = Sample('elec_bell', 0.27825396825396825) ELEC_BLIP = Sample('elec_blip', 0.15816326530612246) @@ -26,33 +28,3 @@ ELEC_TWANG = Sample('elec_twang', 0.6243083900226757) ELEC_TWIP = Sample('elec_twip', 0.10140589569160997) ELEC_WOOD = Sample('elec_wood', 0.47811791383219954) - -def get_sounds(): - sounds = dict( - ELEC_BEEP = ELEC_BEEP, - ELEC_BELL = ELEC_BELL, - ELEC_BLIP = ELEC_BLIP, - ELEC_BLIP2 = ELEC_BLIP2, - ELEC_BLUP = ELEC_BLUP, - ELEC_BONG = ELEC_BONG, - ELEC_CHIME = ELEC_CHIME, - ELEC_CYMBAL = ELEC_CYMBAL, - ELEC_FILT_SNARE = ELEC_FILT_SNARE, - ELEC_FLIP = ELEC_FLIP, - ELEC_FUZZ_TOM = ELEC_FUZZ_TOM, - ELEC_HI_SNARE = ELEC_HI_SNARE, - ELEC_HOLLOW_KICK = ELEC_HOLLOW_KICK, - ELEC_LO_SNARE = ELEC_LO_SNARE, - ELEC_MID_SNARE = ELEC_MID_SNARE, - ELEC_PING = ELEC_PING, - ELEC_PLIP = ELEC_PLIP, - ELEC_POP = ELEC_POP, - ELEC_SNARE = ELEC_SNARE, - ELEC_SOFT_KICK = ELEC_SOFT_KICK, - ELEC_TICK = ELEC_TICK, - ELEC_TRIANGLE = ELEC_TRIANGLE, - ELEC_TWANG = ELEC_TWANG, - ELEC_TWIP = ELEC_TWIP, - ELEC_WOOD = ELEC_WOOD, - ) - return sounds diff --git a/psonic/samples/guitars.py b/psonic/samples/guitars.py index 9d54245..012de6a 100644 --- a/psonic/samples/guitars.py +++ b/psonic/samples/guitars.py @@ -1,18 +1,9 @@ +"""Sounds featuring guitars""" + from . import Sample -## Sounds featuring guitars + GUIT_E_FIFTHS = Sample('guit_e_fifths', 5.971791383219955) GUIT_E_SLIDE = Sample('guit_e_slide', 4.325192743764172) GUIT_EM9 = Sample('guit_em9', 9.972063492063493) GUIT_HARMONICS = Sample('guit_harmonics', 3.5322675736961453) - -def get_sounds(): - - sounds = dict( - GUIT_E_FIFTHS = GUIT_E_FIFTHS, - GUIT_E_SLIDE = GUIT_E_SLIDE, - GUIT_EM9 = GUIT_EM9, - GUIT_HARMONICS = GUIT_HARMONICS, - ) - return sounds - diff --git a/psonic/samples/loops/__init__.py b/psonic/samples/loops/__init__.py index 6ed41a3..514fbb5 100644 --- a/psonic/samples/loops/__init__.py +++ b/psonic/samples/loops/__init__.py @@ -1,6 +1,8 @@ +"""Sounds for Looping""" + from psonic.samples import Sample -## Sounds for Looping + LOOP_AMEN = Sample('loop_amen', 1.753310657596372) LOOP_AMEN_FULL = Sample('loop_amen_full', 6.857142857142857) LOOP_BREAKBEAT = Sample('loop_breakbeat', 1.9047619047619047) diff --git a/psonic/samples/misc.py b/psonic/samples/misc.py index b6783e3..6773be2 100644 --- a/psonic/samples/misc.py +++ b/psonic/samples/misc.py @@ -1,15 +1,8 @@ +"""Miscellaneous Sounds""" + from . import Sample -## Miscellaneous Sounds + + MISC_BURP = Sample('misc_burp', 0.7932879818594104) MISC_CINEBOOM = Sample('misc_cineboom', 7.922675736961451) MISC_CROW = Sample('misc_crow', 0.48063492063492064) - -def get_sounds(): - - sounds = dict( - MISC_BURP = MISC_BURP, - MISC_CINEBOOM = MISC_CINEBOOM, - MISC_CROW = MISC_CROW, - ) - return sounds - diff --git a/psonic/samples/percussions.py b/psonic/samples/percussions.py index a7f1c70..ad03a56 100644 --- a/psonic/samples/percussions.py +++ b/psonic/samples/percussions.py @@ -1,19 +1,10 @@ +"""Percurssive Sounds""" + from . import Sample -## Percurssive Sounds + + PERC_BELL = Sample('perc_bell', 6.719206349206349) PERC_SNAP = Sample('perc_snap', 0.30795918367346936) PERC_SNAP2 = Sample('perc_snap2', 0.17414965986394557) PERC_SWASH = Sample('perc_swash', 0.3195011337868481) PERC_TILL = Sample('perc_till', 2.665736961451247) - - -def get_sounds(): - - sounds = dict( - PERC_BELL = PERC_BELL, - PERC_SNAP = PERC_SNAP, - PERC_SNAP2 = PERC_SNAP2, - PERC_SWASH = PERC_SWASH, - PERC_TILL = PERC_TILL, - ) - return sounds diff --git a/psonic/samples/tabla.py b/psonic/samples/tabla.py index be35d8f..7bd64be 100644 --- a/psonic/samples/tabla.py +++ b/psonic/samples/tabla.py @@ -1,6 +1,8 @@ +"""Tabla""" + from . import Sample -## Tabla + TABLA_DHEC = Sample('tabla_dhec', 0.3250793650793651) TABLA_GHE1 = Sample('tabla_ghe1', 0.5912244897959184) TABLA_GHE2 = Sample('tabla_ghe2', 2.6607256235827665) @@ -27,36 +29,3 @@ TABLA_TUN1 = Sample('tabla_tun1', 2.3394104308390022) TABLA_TUN2 = Sample('tabla_tun2', 2.693514739229025) TABLA_TUN3 = Sample('tabla_tun3', 2.0956009070294783) - -def get_sounds(): - - sounds = dict( - TABLA_DHEC = TABLA_DHEC, - TABLA_GHE1 = TABLA_GHE1, - TABLA_GHE2 = TABLA_GHE2, - TABLA_GHE3 = TABLA_GHE3, - TABLA_GHE4 = TABLA_GHE4, - TABLA_GHE5 = TABLA_GHE5, - TABLA_GHE6 = TABLA_GHE6, - TABLA_GHE7 = TABLA_GHE7, - TABLA_GHE8 = TABLA_GHE8, - TABLA_KE1 = TABLA_KE1, - TABLA_KE2 = TABLA_KE2, - TABLA_KE3 = TABLA_KE3, - TABLA_NA = TABLA_NA, - TABLA_NA_O = TABLA_NA_O, - TABLA_NA_S = TABLA_NA_S, - TABLA_RE = TABLA_RE, - TABLA_TAS1 = TABLA_TAS1, - TABLA_TAS2 = TABLA_TAS2, - TABLA_TAS3 = TABLA_TAS3, - TABLA_TE1 = TABLA_TE1, - TABLA_TE2 = TABLA_TE2, - TABLA_TE_M = TABLA_TE_M, - TABLA_TE_NE = TABLA_TE_NE, - TABLA_TUN1 = TABLA_TUN1, - TABLA_TUN2 = TABLA_TUN2, - TABLA_TUN3 = TABLA_TUN3, - ) - return sounds - diff --git a/psonic/samples/vinyl.py b/psonic/samples/vinyl.py index c172a1d..e96ef67 100644 --- a/psonic/samples/vinyl.py +++ b/psonic/samples/vinyl.py @@ -1,16 +1,8 @@ +"""Vinyl""" from . import Sample -# Vinyl + + VINYL_BACKSPIN = Sample('vinyl_backspin', 1.06124716553288) VINYL_HISS = Sample('vinyl_hiss', 8.0) VINYL_REWIND = Sample('vinyl_rewind', 2.6804761904761905) VINYL_SCRATCH = Sample('vinyl_scratch', 0.27383219954648524) - -def get_sounds(): - - sounds = dict( - VINYL_BACKSPIN = VINYL_BACKSPIN, - VINYL_HISS = VINYL_HISS, - VINYL_REWIND = VINYL_REWIND, - VINYL_SCRATCH = VINYL_SCRATCH, - ) - return sounds diff --git a/psonic/scales.py b/psonic/scales.py index b4888ce..a1de503 100644 --- a/psonic/scales.py +++ b/psonic/scales.py @@ -1,4 +1,5 @@ -## Scale Mode (from sonic pi)## +"""Scale Mode (from sonic pi)""" + DIATONIC = 'diatonic' IONIAN = 'ionian' MAJOR = 'major' diff --git a/psonic/synth_server.py b/psonic/synth_server.py index 9b63b92..7a4c65b 100644 --- a/psonic/synth_server.py +++ b/psonic/synth_server.py @@ -1,3 +1,5 @@ +"""SonicPi synth server""" + import time from pythonosc import osc_message_builder # osc support from pythonosc import udp_client @@ -107,4 +109,3 @@ def send(self, address, *message): def play(self, command): self.send(command) - diff --git a/psonic/synthesizers.py b/psonic/synthesizers.py index bb3bc81..47be2cf 100644 --- a/psonic/synthesizers.py +++ b/psonic/synthesizers.py @@ -1,15 +1,12 @@ - +"""Synthesizer""" class Synth: - """ - Synthesizer - """ + """Synthesizer""" def __init__(self, name): self.name = name -## Synthezier ## DULL_BELL = Synth('dull_bell') PRETTY_BELL = Synth('pretty_bell') SINE = Synth('sine') @@ -52,5 +49,3 @@ def __init__(self, name): TECHSAWS = Synth('tech_saws') # Sonic Pi 2.11 SOUND_IN = Synth('sound_in') SOUND_IN_STEREO = Synth('sound_in_stereo') - - From f821645087d67cf07983fdf727ca864b23d925c2 Mon Sep 17 00:00:00 2001 From: Toomas Ormisson Date: Mon, 26 Feb 2018 20:27:44 +0200 Subject: [PATCH 22/49] Tried to clean up a little. --- setup.py | 5 +---- tests/test_psonic.py | 20 ++++++++------------ 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/setup.py b/setup.py index 48b8e82..a43b2f1 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -from setuptools import setup, find_packages +from setuptools import setup # To use a consistent encoding from codecs import open from os import path @@ -11,12 +11,10 @@ requirements = [ 'python-osc', - ] setup_requirements = [ 'pytest-runner', - ] test_requirements = [ @@ -67,4 +65,3 @@ tests_require=test_requirements, setup_requires=setup_requirements, ) - diff --git a/tests/test_psonic.py b/tests/test_psonic.py index 24cb66f..0bcd66a 100644 --- a/tests/test_psonic.py +++ b/tests/test_psonic.py @@ -1,12 +1,11 @@ from psonic import * def test_originl_behaviour(): - print('starting tests') notes_to_play = [72, G5, Fs5, Eb5] (play(note) for note in notes_to_play) sleep(0.5) - play(72,amp=2) - play(74,pan=-1) + play(72, amp=2) + play(74, pan=-1) use_synth(SAW) use_synth(PROPHET) play( @@ -15,7 +14,7 @@ def test_originl_behaviour(): ) sample( LOOP_AMEN, start=0.5, finish=0.8, - rate=-0.2,attack=0.3,release=1 + rate=-0.2, attack=0.3, release=1 ) sample( DRUM_CYMBAL_OPEN, attack=0.01, @@ -31,17 +30,15 @@ def test_originl_behaviour(): ) synth(TRI, note=D4, amp=0.4) detune = 0.7 - synth(SQUARE, note = E4+detune) - synth(GNOISE, release = 0.5, amp = 1, cutoff = 100) + synth(SQUARE, note=E4+detune) + synth(GNOISE, release=0.5, amp=1, cutoff=100) sc = Ring(scale(E3, MINOR_PENTATONIC)) - play(next(sc), release= 0.1) + play(next(sc), release=0.1) with Fx(SLICER): - synth(PROPHET,note=E2,release=8,cutoff=80) - synth(PROPHET,note=E2+4,release=8,cutoff=80) + synth(PROPHET, note=E2, release=8, cutoff=80) + synth(PROPHET, note=E2+4, release=8, cutoff=80) - print('ending tests') - def test_imports(): from psonic import( SonicPi, @@ -53,4 +50,3 @@ def test_imports(): Synth, Sample, ) - From 679daa24c2ab456ed2053b7740ea3ebaec87addb Mon Sep 17 00:00:00 2001 From: Toomas Ormisson Date: Sun, 4 Mar 2018 22:37:23 +0200 Subject: [PATCH 23/49] Fixed a bug stupidly introduced by refactoring, added test for it. --- psonic/synth_server.py | 2 +- tests/test_psonic.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/psonic/synth_server.py b/psonic/synth_server.py index 7a4c65b..95cf69a 100644 --- a/psonic/synth_server.py +++ b/psonic/synth_server.py @@ -57,7 +57,7 @@ def play(self, command): def synth(self, command): self.send(command) - def send(self, command): + def run(self, command): self.send_command(SonicPi.RUN_COMMAND, command) def stop(self): diff --git a/tests/test_psonic.py b/tests/test_psonic.py index 0bcd66a..7a85249 100644 --- a/tests/test_psonic.py +++ b/tests/test_psonic.py @@ -37,6 +37,11 @@ def test_originl_behaviour(): with Fx(SLICER): synth(PROPHET, note=E2, release=8, cutoff=80) synth(PROPHET, note=E2+4, release=8, cutoff=80) + run("""live_loop :foo do + use_real_time + a, b, c = sync "/osc/trigger/prophet" + synth :prophet, note: a, cutoff: b, sustain: c + end """) def test_imports(): From 2b01f52384d1ae574ee145b641f1788cedb6a343 Mon Sep 17 00:00:00 2001 From: Toomas Ormisson Date: Sun, 4 Mar 2018 23:14:30 +0200 Subject: [PATCH 24/49] minor fixes --- psonic/synth_server.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/psonic/synth_server.py b/psonic/synth_server.py index 95cf69a..6295322 100644 --- a/psonic/synth_server.py +++ b/psonic/synth_server.py @@ -42,8 +42,8 @@ class SonicPi(SonicPiCommon): def __init__(self): self.client = udp_client.UDPClient( - SonicPi.UDP_IP, - SonicPi.UDP_PORT + self.UDP_IP, + self.UDP_PORT ) self.client_for_messages = udp_client.UDPClient( self.UDP_IP, @@ -76,7 +76,7 @@ def send_command(self, address, argument=''): msg = msg.build() self.client.send(msg) - def send_message(self,message, *parameters): + def send_message(self, message, *parameters): msg = osc_message_builder.OscMessageBuilder(message) for p in parameters: msg.add_arg(p) From 464da72b4852e1c8711221b76596f35bc0d92cc2 Mon Sep 17 00:00:00 2001 From: Toomas Ormisson Date: Sun, 4 Mar 2018 23:18:06 +0200 Subject: [PATCH 25/49] Added missing osc communication tests. --- tests/test_psonic.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_psonic.py b/tests/test_psonic.py index 7a85249..8358379 100644 --- a/tests/test_psonic.py +++ b/tests/test_psonic.py @@ -42,7 +42,8 @@ def test_originl_behaviour(): a, b, c = sync "/osc/trigger/prophet" synth :prophet, note: a, cutoff: b, sustain: c end """) - + send_message('/trigger/prophet', 70, 100, 8) + stop() def test_imports(): from psonic import( From 59e0c52472590f5fe30809002503292848f39b3e Mon Sep 17 00:00:00 2001 From: gkvoelkl Date: Sun, 11 Mar 2018 16:45:06 +0100 Subject: [PATCH 26/49] Version 0.3.2 --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) mode change 100644 => 100755 README.rst diff --git a/README.rst b/README.rst old mode 100644 new mode 100755 index a8a3a8a..378e15d --- a/README.rst +++ b/README.rst @@ -45,6 +45,10 @@ Changelog +------------+---------------------------------------------------------------+ | 0.3.0 | OSC Communication | +------------+---------------------------------------------------------------+ +| 0.3.1. | Update, sort and duration of samples | ++------------+---------------------------------------------------------------+ +| 0.3.2. | Restructured | ++------------+---------------------------------------------------------------+ Examples -------- From a1c7035335d69a318c9408e26c7daa61277b109e Mon Sep 17 00:00:00 2001 From: gkvoelkl Date: Sun, 11 Mar 2018 16:48:01 +0100 Subject: [PATCH 27/49] Version 0.3.2 --- python-sonic.ipynb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) mode change 100644 => 100755 python-sonic.ipynb diff --git a/python-sonic.ipynb b/python-sonic.ipynb old mode 100644 new mode 100755 index 4aef40c..8aa4d2d --- a/python-sonic.ipynb +++ b/python-sonic.ipynb @@ -82,7 +82,9 @@ "|Version | |\n", "|--------------|------------------------------------------------------------------------------------------|\n", "| 0.2.0 | Some changes for Sonic Pi 2.11. Simpler multi-threading with decorator *@in_thread*. Messaging with *cue* and *sync*. |\n", - "| 0.3.0 | OSC Communication | " + "| 0.3.0 | OSC Communication | \n", + "| 0.3.1. | Update, sort and duration of samples |\n", + "| 0.3.2. | Restructured |" ] }, { @@ -1551,7 +1553,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.2" + "version": "3.6.4" } }, "nbformat": 4, From cdf41cfe02296d8a3b060156391e02b9c53f9af0 Mon Sep 17 00:00:00 2001 From: gkvoelkl Date: Tue, 3 Apr 2018 20:22:44 +0200 Subject: [PATCH 28/49] Version 0.3.2 --- psonic/psonic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 psonic/psonic.py diff --git a/psonic/psonic.py b/psonic/psonic.py old mode 100644 new mode 100755 index 3fc9124..26ba33a --- a/psonic/psonic.py +++ b/psonic/psonic.py @@ -113,8 +113,8 @@ def chord(root_note, chord_quality): half_tone_steps = _CHORD_QUALITY[chord_quality] for i in half_tone_steps: - n = n + i - result.append(n) + q = n + i + result.append(q) return result From 6b82331c53693f1b93480b73151d05e9d475cf4d Mon Sep 17 00:00:00 2001 From: gkvoelkl Date: Tue, 3 Apr 2018 20:23:18 +0200 Subject: [PATCH 29/49] Version 0.3.2 --- psonic/synth_server.py | 3 +++ 1 file changed, 3 insertions(+) mode change 100644 => 100755 psonic/synth_server.py diff --git a/psonic/synth_server.py b/psonic/synth_server.py old mode 100644 new mode 100755 index 6295322..e52b6f5 --- a/psonic/synth_server.py +++ b/psonic/synth_server.py @@ -60,6 +60,9 @@ def synth(self, command): def run(self, command): self.send_command(SonicPi.RUN_COMMAND, command) + def send(self,command): + self.run(command) + def stop(self): self.send_command(SonicPi.STOP_COMMAND) From 077cf9239338337607467e6182aff574e442e2c3 Mon Sep 17 00:00:00 2001 From: Chris Csikszentmihalyi Date: Sun, 5 Aug 2018 14:16:47 +0100 Subject: [PATCH 30/49] Update README.rst (#21) --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 378e15d..fecd1c4 100755 --- a/README.rst +++ b/README.rst @@ -716,7 +716,7 @@ Now a simple structure with four live loops live_thread_1 = Thread(name='producer', target=live_loop_1, args=(condition,stop_event)) live_thread_2 = Thread(name='consumer1', target=live_loop_2, args=(condition,stop_event)) live_thread_3 = Thread(name='consumer2', target=live_loop_3, args=(condition,stop_event)) - live_thread_4 = Thread(name='consumer3', target=live_loop_3, args=(condition,stop_event)) + live_thread_4 = Thread(name='consumer3', target=live_loop_4, args=(condition,stop_event)) live_thread_1.start() live_thread_2.start() @@ -751,8 +751,8 @@ After starting the loops you can change them .. code:: ipython3 def live_2(): - #sample(AMBI_CHOIR, rate=0.4) - #sleep(1) + sample(AMBI_CHOIR, rate=0.4) + sleep(1) pass .. code:: ipython3 From 82ae1f2a1a361517492d7c1af03608b8618415aa Mon Sep 17 00:00:00 2001 From: Frank Massi Date: Mon, 1 Oct 2018 10:20:45 -0500 Subject: [PATCH 31/49] Couple small updates (#22) * Expand notes to octaves 0 through 8 Also programatically set all notes beyond the 0 octave. * Add pitch inversions - Allow chords to take an inversion argument --- README.rst | 14 ++++++- psonic/notes.py | 95 ++++++++++++-------------------------------- psonic/psonic.py | 20 +++++++++- tests/test_psonic.py | 20 ++++++++++ 4 files changed, 78 insertions(+), 71 deletions(-) diff --git a/README.rst b/README.rst index fecd1c4..3992ea5 100755 --- a/README.rst +++ b/README.rst @@ -32,7 +32,6 @@ Limitations - You have to start *Sonic Pi* first before you can use it with python-sonic -- Only the notes from C2 to C6 Changelog --------- @@ -409,6 +408,19 @@ Play chords play(chord(E4, DOM7)) sleep(1) +Play chords with inversions + +.. code:: ipython3 + + play(chord(E4, MAJOR)) + sleep(1) + play(chord(E4, MAJOR, inversion=1)) + sleep(1) + play(chord(E4, MAJOR, 2)) + sleep(1) + play(chord(E3, MAJOR)) + sleep(1) + Play arpeggios .. code:: ipython3 diff --git a/psonic/notes.py b/psonic/notes.py index 13f8bbb..2cd84f0 100644 --- a/psonic/notes.py +++ b/psonic/notes.py @@ -1,73 +1,30 @@ """Notes""" -C2 = 36 -Cs2 = 37 -Db2 = Cs2 -D2 = 38 -Ds2 = 39 -Eb2 = Ds2 -E2 = 40 -F2 = 41 -Fs2 = 42 -Gb2 = Fs2 -G2 = 43 -Gs2 = 44 -Ab2 = Gs2 -A2 = 45 -As2 = 46 -Bb2 = As2 -B2 = 47 -C3 = 48 -Cs3 = 49 -Db3 = Cs3 -D3 = 50 -Ds3 = 51 -Eb3 = Ds3 -E3 = 52 -F3 = 53 -Fs3 = 54 -Gb3 = Fs3 -G3 = 55 -Gs3 = 56 -Ab3 = Gs3 -A3 = 57 -As3 = 58 -Bb3 = As3 -B3 = 59 -C4 = 60 -Cs4 = 61 -Db4 = Cs4 -D4 = 62 -Ds4 = 63 -Eb4 = Ds4 -E4 = 64 -F4 = 65 -Fs4 = 66 -Gb4 = Fs4 -G4 = 67 -Gs4 = 68 -Ab4 = Gs4 -A4 = 69 -As4 = 70 -Bb4 = As4 -B4 = 71 -C5 = 72 -Cs5 = 73 -Db5 = Cs5 -D5 = 74 -Ds5 = 75 -Eb5 = Ds5 -E5 = 76 -F5 = 77 -Fs5 = 78 -Gb5 = Fs5 -G5 = 79 -Gs5 = 80 -Ab5 = Gs5 -A5 = 81 -As5 = 82 -Bb5 = As5 -B5 = 83 -C6 = 84 +root_notes = { + "C0": 12, + "Cs0": 13, + "Db0": 13, + "D0": 14, + "Ds0": 15, + "Eb0": 15, + "E0": 16, + "F0": 17, + "Fs0": 18, + "Gb0": 18, + "G0": 19, + "Gs0": 20, + "Ab0": 20, + "A0": 21, + "As0": 22, + "Bb0": 22, + "B0": 23, +} + +notes = {} +for octave in range(9): + for key in root_notes: + notes[key.replace("0", str(octave))] = root_notes[key] + (12 * octave) + +globals().update(notes) R = 0 diff --git a/psonic/psonic.py b/psonic/psonic.py index 26ba33a..94d6057 100755 --- a/psonic/psonic.py +++ b/psonic/psonic.py @@ -101,10 +101,25 @@ def one_in(max): """ return random.randint(1, max) == 1 -def chord(root_note, chord_quality): +def invert_pitches(pitches, inversion): + """Inverts a list of pitches, wrapping the top pitches an octave below the root + :param pitches: list + :param inversion: int + :return: list + """ + + for i in range(1, (inversion % (len(pitches)))+1): + pitches[-i] = pitches[-i] - 12 + + pitches.sort() + + return pitches + +def chord(root_note, chord_quality, inversion=None): """Generates a list of notes of a chord :param root_note: :param chord_quality: + :param inversion: :return: list """ result = [] @@ -116,6 +131,9 @@ def chord(root_note, chord_quality): q = n + i result.append(q) + if inversion: + result = invert_pitches(result, inversion) + return result def scale(root_note, scale_mode, num_octaves=1): diff --git a/tests/test_psonic.py b/tests/test_psonic.py index 8358379..0cccef6 100644 --- a/tests/test_psonic.py +++ b/tests/test_psonic.py @@ -1,3 +1,4 @@ +import pytest from psonic import * def test_originl_behaviour(): @@ -45,6 +46,25 @@ def test_originl_behaviour(): send_message('/trigger/prophet', 70, 100, 8) stop() + +@pytest.mark.parametrize("root,quality,inversion,result", ( + (C4, MAJOR, None, [C4, E4, G4]), + (C4, MAJOR, 0, [C4, E4, G4]), + (C4, MAJOR, 1, [G3, C4, E4]), + (C4, MAJOR, 2, [E3, G3, C4]), + (C4, MAJOR, 3, [C4, E4, G4]), + (C4, MAJOR, 4, [G3, C4, E4]), + (C4, MAJOR7, 0, [C4, E4, G4, B4]), + (C4, MAJOR7, 1, [B3, C4, E4, G4]), + (C4, MAJOR7, 2, [G3, B3, C4, E4]), + (C4, MAJOR7, 3, [E3, G3, B3, C4]), + (C4, MAJOR7, 4, [C4, E4, G4, B4]), + (C4, MAJOR7, 5, [B3, C4, E4, G4]), +)) +def test_chord_inversions(root, quality, inversion, result): + assert chord(root, quality, inversion) == result + + def test_imports(): from psonic import( SonicPi, From 6968f07230459e085b79cc65078d7ca7ce006569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dar=C3=ADo=20Here=C3=B1=C3=BA?= Date: Mon, 3 Dec 2018 14:52:46 -0300 Subject: [PATCH 32/49] Fix typo on string #077 (#26) --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 3992ea5..a45ccd9 100755 --- a/README.rst +++ b/README.rst @@ -14,7 +14,7 @@ If you like it, use it. If you have some suggestions, tell me Installation ------------ -- First you need Python 3 (https://www.python.org, ) - Python 3.5 +- First you need Python 3 (https://www.python.org) - Python 3.5 should work, because it's the development environment - Then Sonic Pi (https://sonic-pi.net) - That makes the sound - Modul python-osc (https://pypi.python.org/pypi/python-osc) - @@ -74,7 +74,7 @@ Some more notes sleep(1) play(79) -In more tratitional music notation +In more traditional music notation .. code:: ipython3 From 012bce65ff7be3965aaa9fd487ff72e890135498 Mon Sep 17 00:00:00 2001 From: python-ninja-hebi Date: Thu, 15 Oct 2020 14:55:28 +0200 Subject: [PATCH 33/49] Version 0.4.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 setup.py diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 index a43b2f1..5381683 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ setup( name='python-sonic', - version='0.3.2', + version='0.4.0', description='Programming Music with Sonic Pi or Supercollider', long_description=long_description, url='https://github.com/gkvoelkl/python-sonic', From f7328041951ffabc43ac815138f069b2c370d806 Mon Sep 17 00:00:00 2001 From: python-ninja-hebi Date: Thu, 15 Oct 2020 15:07:57 +0200 Subject: [PATCH 34/49] Version 0.4.0 --- psonic/notes.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 psonic/notes.py diff --git a/psonic/notes.py b/psonic/notes.py old mode 100644 new mode 100755 From 55d90dd2af48a2077844a3bc32191327134a0a6b Mon Sep 17 00:00:00 2001 From: python-ninja-hebi Date: Thu, 15 Oct 2020 15:08:36 +0200 Subject: [PATCH 35/49] Version 0.4.0 --- tests/test_psonic.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 tests/test_psonic.py diff --git a/tests/test_psonic.py b/tests/test_psonic.py old mode 100644 new mode 100755 From a0e1b3dc2f08d64aa5d9867293c242aab41836ba Mon Sep 17 00:00:00 2001 From: python-ninja-hebi Date: Thu, 15 Oct 2020 15:37:46 +0200 Subject: [PATCH 36/49] Version 0.4.0 --- psonic/psonic.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/psonic/psonic.py b/psonic/psonic.py index 94d6057..d38e856 100755 --- a/psonic/psonic.py +++ b/psonic/psonic.py @@ -167,8 +167,23 @@ def send_message(message, *parameter): synth_server.send_message(message, *parameter) +def start_recording(): + synth_server.start_recording() + + +def stop_recording(): + synth_server.stop_recording() + + +def save_recording(name): + synth_server.save_recording(name) + synth_server = SonicPi() +def set_server_parameter(udp_ip="", udp_port=-1, udp_port_osc_message=-1): + synth_server.set_parameter(udp_ip, udp_port, udp_port_osc_message) + + def _debug(*args): if __debug: print(args) From d9b255e8c9ef6d3ba31d67840c4454b64896123a Mon Sep 17 00:00:00 2001 From: python-ninja-hebi Date: Thu, 15 Oct 2020 15:38:24 +0200 Subject: [PATCH 37/49] Version 0.4.0 --- psonic/synth_server.py | 50 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/psonic/synth_server.py b/psonic/synth_server.py index e52b6f5..cf9353e 100755 --- a/psonic/synth_server.py +++ b/psonic/synth_server.py @@ -19,6 +19,15 @@ class SonicPiCommon: UDP_IP = "127.0.0.1" + def __init__(self): + self.udp_ip = self.UDP_IP + + def set_parameter(self, udp_ip=""): + if udp_ip == "": + self.udp_ip = self.UDP_IP + else: + self.udp_ip = udp_ip + def send(self, command): pass @@ -28,28 +37,57 @@ def sleep(self, duration): def sample(self, command): self.send(command) +## Ports could be find in home ./sonic-pi/log/server-output.log +# Version 3.2.0 +# Listen port: 51235 4557 +# Scsynth port: 51237 4556 +# Scsynth send port: 51237 4556 +# OSC cues port: 4560 4559 +# Erlang port: 51240 4560 +# OSC MIDI out port: 51238 4561 +# OSC MIDI in port: 51239 4562 +# Websocket port: 51241 ## Connection classes ## class SonicPi(SonicPiCommon): """Communiction to Sonic Pi""" - UDP_PORT = 4557 - UDP_PORT_OSC_MESSAGE = 4559 + #UDP_PORT = 4557 + UDP_PORT = 51235 + + #UDP_PORT_OSC_MESSAGE = 4559 + UDP_PORT_OSC_MESSAGE = 4560 GUI_ID = 'SONIC_PI_PYTHON' RUN_COMMAND = "/run-code" STOP_COMMAND = "/stop-all-jobs" def __init__(self): + super().__init__() + self.udp_port = self.UDP_PORT + self.udp_port_osc_message = self.UDP_PORT_OSC_MESSAGE + + self._init_client() + + def _init_client(self): self.client = udp_client.UDPClient( - self.UDP_IP, - self.UDP_PORT + self.udp_ip, + self.udp_port ) self.client_for_messages = udp_client.UDPClient( - self.UDP_IP, - self.UDP_PORT_OSC_MESSAGE + self.udp_ip, + self.udp_port_osc_message ) + def set_parameter(self, udp_ip = "", udp_port=-1, udp_port_osc_message=-1): + super().set_parameter(udp_ip) + if udp_port == -1: udp_port = self.UDP_PORT + self.udp_port = udp_port + if udp_port_osc_message == -1: udp_port_osc_message = self.UDP_PORT_OSC_MESSAGE + self.udp_port_osc_message = udp_port_osc_message + + self._init_client() + def play(self, command): command = 'use_synth :{0}\n'.format(_current_synth.name) + command self.send(command) From f761621682e5d15248419c577b78e2e328f9fe8b Mon Sep 17 00:00:00 2001 From: python-ninja-hebi Date: Thu, 15 Oct 2020 15:39:01 +0200 Subject: [PATCH 38/49] Version 0.4.0 --- python-sonic.ipynb | 400 +++++++++++++++++++++++++++------------------ 1 file changed, 245 insertions(+), 155 deletions(-) diff --git a/python-sonic.ipynb b/python-sonic.ipynb index 8aa4d2d..f5bd8d0 100755 --- a/python-sonic.ipynb +++ b/python-sonic.ipynb @@ -39,9 +39,7 @@ }, { "cell_type": "raw", - "metadata": { - "collapsed": true - }, + "metadata": {}, "source": [ "$ pip install python-sonic" ] @@ -84,7 +82,31 @@ "| 0.2.0 | Some changes for Sonic Pi 2.11. Simpler multi-threading with decorator *@in_thread*. Messaging with *cue* and *sync*. |\n", "| 0.3.0 | OSC Communication | \n", "| 0.3.1. | Update, sort and duration of samples |\n", - "| 0.3.2. | Restructured |" + "| 0.3.2. | Restructured |\n", + "| 0.4.0 | Changes communication ports and recording |" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Communication" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The API *python-sonic* communications with *Sonic Pi* over UDP and two ports. One port is an internal *Sonic Pi* port and could be changed. \n", + "For older *Sonic Pi* Version you have to set the ports explicitly" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "from psonic import *\n", + "set_server_parameters('127.0.0.1',4557,4559)" ] }, { @@ -103,10 +125,8 @@ }, { "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, + "execution_count": 1, + "metadata": {}, "outputs": [], "source": [ "from psonic import *" @@ -121,10 +141,8 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, + "execution_count": 2, + "metadata": {}, "outputs": [], "source": [ "play(70) #play MIDI note 70" @@ -139,10 +157,8 @@ }, { "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true - }, + "execution_count": 3, + "metadata": {}, "outputs": [], "source": [ "play(72)\n", @@ -161,10 +177,8 @@ }, { "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": true - }, + "execution_count": 4, + "metadata": {}, "outputs": [], "source": [ "play(C5)\n", @@ -184,9 +198,7 @@ { "cell_type": "code", "execution_count": 5, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "play(Fs5)\n", @@ -204,9 +216,7 @@ { "cell_type": "code", "execution_count": 6, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "play(72,amp=2)\n", @@ -223,10 +233,8 @@ }, { "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, + "execution_count": 7, + "metadata": {}, "outputs": [], "source": [ "use_synth(SAW)\n", @@ -248,10 +256,8 @@ }, { "cell_type": "code", - "execution_count": 13, - "metadata": { - "collapsed": true - }, + "execution_count": 8, + "metadata": {}, "outputs": [], "source": [ "play (60, attack=0.5, decay=1, sustain_level=0.4, sustain=2, release=0.5) \n", @@ -268,9 +274,7 @@ { "cell_type": "code", "execution_count": 9, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "sample(AMBI_LUNAR_LAND, amp=0.5)" @@ -278,10 +282,8 @@ }, { "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, + "execution_count": 10, + "metadata": {}, "outputs": [], "source": [ "sample(LOOP_AMEN,pan=-1)\n", @@ -292,9 +294,7 @@ { "cell_type": "code", "execution_count": 11, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "sample(LOOP_AMEN,rate=0.5)" @@ -303,9 +303,7 @@ { "cell_type": "code", "execution_count": 12, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "sample(LOOP_AMEN,rate=1.5)" @@ -314,9 +312,7 @@ { "cell_type": "code", "execution_count": 13, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "sample(LOOP_AMEN,rate=-1)#back" @@ -325,9 +321,7 @@ { "cell_type": "code", "execution_count": 14, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "sample(DRUM_CYMBAL_OPEN,attack=0.01,sustain=0.3,release=0.1)" @@ -336,9 +330,7 @@ { "cell_type": "code", "execution_count": 15, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "sample(LOOP_AMEN,start=0.5,finish=0.8,rate=-0.2,attack=0.3,release=1)" @@ -354,9 +346,7 @@ { "cell_type": "code", "execution_count": 16, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "import random\n", @@ -369,9 +359,7 @@ { "cell_type": "code", "execution_count": 17, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "for i in range(3):\n", @@ -388,10 +376,8 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, + "execution_count": 18, + "metadata": {}, "outputs": [], "source": [ "from psonic import *\n", @@ -449,11 +435,21 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 22\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;32mwhile\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 24\u001b[0;31m \u001b[0;32mpass\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mKeyboardInterrupt\u001b[0m: " + ] + } + ], "source": [ "import random\n", "from psonic import *\n", @@ -494,10 +490,10 @@ "metadata": {}, "outputs": [ { - "name": "stdout", + "name": "stdin", "output_type": "stream", "text": [ - "Press Enter to continue...\n" + "Press Enter to continue... \n" ] }, { @@ -565,25 +561,15 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, "outputs": [ { - "name": "stdout", + "name": "stdin", "output_type": "stream", "text": [ - "Press Enter to continue...\n" + "Press Enter to continue... \n" ] - }, - { - "data": { - "text/plain": [ - "''" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ @@ -624,9 +610,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "from psonic import *\n", @@ -661,10 +645,8 @@ }, { "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, + "execution_count": 2, + "metadata": {}, "outputs": [], "source": [ "from psonic import *\n", @@ -685,9 +667,7 @@ { "cell_type": "code", "execution_count": 2, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "play(chord(E4, MINOR)) \n", @@ -710,9 +690,7 @@ { "cell_type": "code", "execution_count": 3, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "play_pattern( chord(E4, 'm7')) \n", @@ -729,10 +707,8 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, + "execution_count": 4, + "metadata": {}, "outputs": [], "source": [ "play_pattern_timed(scale(C3, MAJOR), 0.125, release = 0.1) \n", @@ -749,7 +725,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -758,7 +734,7 @@ "[48, 50, 52, 53, 55, 57, 59, 60]" ] }, - "execution_count": 9, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -773,10 +749,8 @@ }, { "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true - }, + "execution_count": 6, + "metadata": {}, "outputs": [], "source": [ "s.reverse()" @@ -784,10 +758,8 @@ }, { "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": true - }, + "execution_count": 7, + "metadata": {}, "outputs": [], "source": [ "\n", @@ -863,7 +835,10 @@ "cell_type": "code", "execution_count": 6, "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "outputs": [], "source": [ @@ -877,7 +852,10 @@ "cell_type": "code", "execution_count": 7, "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "outputs": [], "source": [ @@ -891,7 +869,10 @@ "cell_type": "code", "execution_count": 8, "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "outputs": [], "source": [ @@ -980,7 +961,10 @@ "cell_type": "code", "execution_count": 11, "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "outputs": [], "source": [ @@ -993,7 +977,10 @@ "cell_type": "code", "execution_count": 14, "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "outputs": [], "source": [ @@ -1076,7 +1063,10 @@ "cell_type": "code", "execution_count": 3, "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "outputs": [], "source": [ @@ -1085,9 +1075,7 @@ }, { "cell_type": "markdown", - "metadata": { - "collapsed": true - }, + "metadata": {}, "source": [ "More complex live loops" ] @@ -1096,7 +1084,10 @@ "cell_type": "code", "execution_count": 4, "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "outputs": [], "source": [ @@ -1211,7 +1202,10 @@ "cell_type": "code", "execution_count": 12, "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "outputs": [], "source": [ @@ -1225,7 +1219,10 @@ "cell_type": "code", "execution_count": 7, "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "outputs": [], "source": [ @@ -1239,7 +1236,10 @@ "cell_type": "code", "execution_count": 14, "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "outputs": [], "source": [ @@ -1253,7 +1253,10 @@ "cell_type": "code", "execution_count": 13, "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "outputs": [], "source": [ @@ -1275,7 +1278,10 @@ "cell_type": "code", "execution_count": 15, "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "outputs": [], "source": [ @@ -1294,6 +1300,9 @@ "execution_count": 4, "metadata": { "collapsed": true, + "jupyter": { + "outputs_hidden": true + }, "scrolled": true }, "outputs": [], @@ -1309,7 +1318,10 @@ "cell_type": "code", "execution_count": 5, "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "outputs": [], "source": [ @@ -1322,7 +1334,10 @@ "cell_type": "code", "execution_count": 12, "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "outputs": [], "source": [ @@ -1352,7 +1367,10 @@ "cell_type": "code", "execution_count": 2, "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "outputs": [], "source": [ @@ -1367,7 +1385,10 @@ "cell_type": "code", "execution_count": 3, "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "outputs": [], "source": [ @@ -1394,9 +1415,7 @@ { "cell_type": "code", "execution_count": 1, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "from psonic import *" @@ -1411,15 +1430,13 @@ }, { "cell_type": "code", - "execution_count": 3, - "metadata": { - "collapsed": true - }, + "execution_count": 2, + "metadata": {}, "outputs": [], "source": [ "run(\"\"\"live_loop :foo do\n", " use_real_time\n", - " a, b, c = sync \"/osc/trigger/prophet\"\n", + " a, b, c = sync \"/osc*/trigger/prophet\"\n", " synth :prophet, note: a, cutoff: b, sustain: c\n", "end \"\"\")" ] @@ -1433,10 +1450,8 @@ }, { "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, + "execution_count": 3, + "metadata": {}, "outputs": [], "source": [ "send_message('/trigger/prophet', 70, 100, 8)" @@ -1444,15 +1459,86 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, + "execution_count": 4, + "metadata": {}, "outputs": [], "source": [ "stop()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Recording" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With python-sonic you can record wave files." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from psonic import *" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# start recording\n", + "start_recording()\n", + "\n", + "play(chord(E4, MINOR)) \n", + "sleep(1)\n", + "play(chord(E4, MAJOR))\n", + "sleep(1)\n", + "play(chord(E4, MINOR7))\n", + "sleep(1)\n", + "play(chord(E4, DOM7))\n", + "sleep(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# stop recording\n", + "stop_recording" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# save file\n", + "save_recording('/Volumes/jupyter/python-sonic/test.wav')" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1464,7 +1550,10 @@ "cell_type": "code", "execution_count": 1, "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "outputs": [], "source": [ @@ -1475,7 +1564,10 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "outputs": [], "source": [ @@ -1507,9 +1599,7 @@ }, { "cell_type": "markdown", - "metadata": { - "collapsed": true - }, + "metadata": {}, "source": [ "## Sources" ] @@ -1553,9 +1643,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.4" + "version": "3.7.9" } }, "nbformat": 4, - "nbformat_minor": 1 + "nbformat_minor": 4 } From ad2563160a6edf6dce23fbfaff1070c87947a374 Mon Sep 17 00:00:00 2001 From: python-ninja-hebi Date: Thu, 15 Oct 2020 15:39:39 +0200 Subject: [PATCH 39/49] Version 0.4.0 --- README.rst | 150 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 101 insertions(+), 49 deletions(-) diff --git a/README.rst b/README.rst index a45ccd9..22403b1 100755 --- a/README.rst +++ b/README.rst @@ -1,4 +1,3 @@ - python-sonic - Programming Music with Python, Sonic Pi or Supercollider ======================================================================= @@ -14,8 +13,8 @@ If you like it, use it. If you have some suggestions, tell me Installation ------------ -- First you need Python 3 (https://www.python.org) - Python 3.5 - should work, because it's the development environment +- First you need Python 3 (https://www.python.org, ) - Python 3.5 + should work, because it’s the development environment - Then Sonic Pi (https://sonic-pi.net) - That makes the sound - Modul python-osc (https://pypi.python.org/pypi/python-osc) - Connection between Python and Sonic Pi Server @@ -32,22 +31,37 @@ Limitations - You have to start *Sonic Pi* first before you can use it with python-sonic +- Only the notes from C2 to C6 Changelog --------- -+------------+---------------------------------------------------------------+ -| Version | | -+============+===============================================================+ -| 0.2.0 | Some changes for Sonic Pi 2.11. Simpler multi-threading with | -| | decorator *@in\_thread*. Messaging with *cue* and *sync*. | -+------------+---------------------------------------------------------------+ -| 0.3.0 | OSC Communication | -+------------+---------------------------------------------------------------+ -| 0.3.1. | Update, sort and duration of samples | -+------------+---------------------------------------------------------------+ -| 0.3.2. | Restructured | -+------------+---------------------------------------------------------------+ ++--------+-------------------------------------------------------------+ +| V | | +| ersion | | ++========+=============================================================+ +| 0.2.0 | Some changes for Sonic Pi 2.11. Simpler multi-threading | +| | with decorator *@in_thread*. Messaging with *cue* and | +| | *sync*. | ++--------+-------------------------------------------------------------+ +| 0.3.0 | OSC Communication | ++--------+-------------------------------------------------------------+ +| 0.3.1. | Update, sort and duration of samples | ++--------+-------------------------------------------------------------+ +| 0.3.2. | Restructured | ++--------+-------------------------------------------------------------+ +| 0.4.0 | Changes communication ports and recording | ++--------+-------------------------------------------------------------+ + +Communication +------------- + +| The API *python-sonic* communications with *Sonic Pi* over UDP and two + ports. One port is an internal *Sonic Pi* port and could be changed. +| For older *Sonic Pi* Version you have to set the ports explicitly + +from psonic import * +set_server_parameters('127.0.0.1',4557,4559) Examples -------- @@ -74,7 +88,7 @@ Some more notes sleep(1) play(79) -In more traditional music notation +In more tratitional music notation .. code:: ipython3 @@ -249,8 +263,25 @@ If you want to hear more than one sound at a time, use Threads. while True: pass -Every function *bass\_sound* and *snare\_sound* have its own thread. -Your can hear them running. + +:: + + + --------------------------------------------------------------------------- + + KeyboardInterrupt Traceback (most recent call last) + + in + 22 + 23 while True: + ---> 24 pass + + + KeyboardInterrupt: + + +Every function *bass_sound* and *snare_sound* have its own thread. Your +can hear them running. .. code:: ipython3 @@ -293,7 +324,7 @@ Your can hear them running. .. parsed-literal:: - Press Enter to continue... + Press Enter to continue... @@ -309,7 +340,7 @@ you can use *Condition*. One function sends a message with *condition.notifyAll* the other waits until the message comes *condition.wait*. -More simple with decorator \_\_@in\_thread\_\_ +More simple with decorator \_\_@in_thread_\_ .. code:: ipython3 @@ -349,15 +380,7 @@ More simple with decorator \_\_@in\_thread\_\_ .. parsed-literal:: - Press Enter to continue... - - - - -.. parsed-literal:: - - '' - + Press Enter to continue... .. code:: ipython3 @@ -408,19 +431,6 @@ Play chords play(chord(E4, DOM7)) sleep(1) -Play chords with inversions - -.. code:: ipython3 - - play(chord(E4, MAJOR)) - sleep(1) - play(chord(E4, MAJOR, inversion=1)) - sleep(1) - play(chord(E4, MAJOR, 2)) - sleep(1) - play(chord(E3, MAJOR)) - sleep(1) - Play arpeggios .. code:: ipython3 @@ -473,7 +483,7 @@ Live Loop ~~~~~~~~~ One of the best in SONIC PI is the *Live Loop*. While a loop is playing -music you can change it and hear the change. Let's try it in Python, +music you can change it and hear the change. Let’s try it in Python, too. .. code:: ipython3 @@ -509,7 +519,7 @@ too. -Now change the function *my\_loop* und you can hear it. +Now change the function *my_loop* und you can hear it. .. code:: ipython3 @@ -533,7 +543,7 @@ Now change the function *my\_loop* und you can hear it. play(random.choice(chord(E3, MINOR)), release= 0.2, cutoff=random.randrange(60, 130)) sleep(0.25) -To stop the sound you have to end the kernel. In IPython with Kernel --> +To stop the sound you have to end the kernel. In IPython with Kernel –> Restart Now with two live loops which are synch. @@ -728,7 +738,7 @@ Now a simple structure with four live loops live_thread_1 = Thread(name='producer', target=live_loop_1, args=(condition,stop_event)) live_thread_2 = Thread(name='consumer1', target=live_loop_2, args=(condition,stop_event)) live_thread_3 = Thread(name='consumer2', target=live_loop_3, args=(condition,stop_event)) - live_thread_4 = Thread(name='consumer3', target=live_loop_4, args=(condition,stop_event)) + live_thread_4 = Thread(name='consumer3', target=live_loop_3, args=(condition,stop_event)) live_thread_1.start() live_thread_2.start() @@ -763,8 +773,8 @@ After starting the loops you can change them .. code:: ipython3 def live_2(): - sample(AMBI_CHOIR, rate=0.4) - sleep(1) + #sample(AMBI_CHOIR, rate=0.4) + #sleep(1) pass .. code:: ipython3 @@ -850,7 +860,7 @@ You can write it in th GUI or send one with Python. run("""live_loop :foo do use_real_time - a, b, c = sync "/osc/trigger/prophet" + a, b, c = sync "/osc*/trigger/prophet" synth :prophet, note: a, cutoff: b, sustain: c end """) @@ -864,6 +874,48 @@ Now send a message to Sonic Pi. stop() +Recording +--------- + +With python-sonic you can record wave files. + +.. code:: ipython3 + + from psonic import * + +.. code:: ipython3 + + # start recording + start_recording() + + play(chord(E4, MINOR)) + sleep(1) + play(chord(E4, MAJOR)) + sleep(1) + play(chord(E4, MINOR7)) + sleep(1) + play(chord(E4, DOM7)) + sleep(1) + +.. code:: ipython3 + + # stop recording + stop_recording + + + + +.. parsed-literal:: + + + + + +.. code:: ipython3 + + # save file + save_recording('/Volumes/jupyter/python-sonic/test.wav') + More Examples ------------- From 1af7f5ce390172270b81c4d14d2b3d6ac9362e16 Mon Sep 17 00:00:00 2001 From: python-ninja-hebi Date: Sun, 25 Oct 2020 15:33:04 +0100 Subject: [PATCH 40/49] Version 0.4.1 --- setup.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 5381683..caf3b62 100755 --- a/setup.py +++ b/setup.py @@ -1,12 +1,14 @@ from setuptools import setup # To use a consistent encoding +# python setup.py sdist bdist_wheel +# twine upload dist/* from codecs import open from os import path here = path.abspath(path.dirname(__file__)) # Get the long description from the README file -with open(path.join(here, 'README.rst'), encoding='utf-8') as f: +with open(path.join(here, 'README.txt'), encoding='utf-8') as f: long_description = f.read() requirements = [ @@ -24,9 +26,11 @@ setup( name='python-sonic', - version='0.4.0', + version='0.4.1', description='Programming Music with Sonic Pi or Supercollider', long_description=long_description, +# long_description_content_type='text/x-rst', + long_description_content_type='text/plain', url='https://github.com/gkvoelkl/python-sonic', author='gkvoelkl', author_email='gkvoelkl@nelson-games.de', @@ -50,6 +54,7 @@ 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7' ], keywords= [ 'music', From d0620f93b656fb37e1ab83f454a2aff0703dd85f Mon Sep 17 00:00:00 2001 From: python-ninja-hebi Date: Sun, 25 Oct 2020 15:35:05 +0100 Subject: [PATCH 41/49] Version 0.4.1 --- psonic/synth_server.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/psonic/synth_server.py b/psonic/synth_server.py index cf9353e..353e97f 100755 --- a/psonic/synth_server.py +++ b/psonic/synth_server.py @@ -61,6 +61,9 @@ class SonicPi(SonicPiCommon): RUN_COMMAND = "/run-code" STOP_COMMAND = "/stop-all-jobs" + START_RECORDING_COMMAND = "/start-recording" + STOP_RECORDING_COMMAND = "/stop-recording" + SAVE_RECORDING_COMMAND = "/save-recording" def __init__(self): super().__init__() @@ -98,6 +101,16 @@ def synth(self, command): def run(self, command): self.send_command(SonicPi.RUN_COMMAND, command) + def start_recording(self): + self.send_command(SonicPi.START_RECORDING_COMMAND) + + def stop_recording(self): + self.send_command(SonicPi.START_RECORDING_COMMAND) + + def save_recording(self, name): + self.send_command(SonicPi.SAVE_RECORDING_COMMAND,name) + + def send(self,command): self.run(command) From b17ded608556c62f55b7e40910c4856215f51133 Mon Sep 17 00:00:00 2001 From: python-ninja-hebi Date: Tue, 24 Nov 2020 16:58:22 +0100 Subject: [PATCH 42/49] Version 0.4.1 --- README.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 22403b1..edd4306 100755 --- a/README.rst +++ b/README.rst @@ -60,8 +60,10 @@ Communication ports. One port is an internal *Sonic Pi* port and could be changed. | For older *Sonic Pi* Version you have to set the ports explicitly -from psonic import * -set_server_parameters('127.0.0.1',4557,4559) +.. code:: ipython3 + + from psonic import * + set_server_parameter('127.0.0.1',4557,4559) Examples -------- From 7a31edc065494dfdb4938a49e2cb3c496b130956 Mon Sep 17 00:00:00 2001 From: Mike Roberts <2947595+m-roberts@users.noreply.github.com> Date: Wed, 4 Aug 2021 14:58:32 +0100 Subject: [PATCH 43/49] Fix `setup.py` (#35) * Fix README * Revert pytest-runner change --- setup.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index caf3b62..6120bd6 100755 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ here = path.abspath(path.dirname(__file__)) # Get the long description from the README file -with open(path.join(here, 'README.txt'), encoding='utf-8') as f: +with open(path.join(here, 'README.rst'), encoding='utf-8') as f: long_description = f.read() requirements = [ @@ -29,8 +29,7 @@ version='0.4.1', description='Programming Music with Sonic Pi or Supercollider', long_description=long_description, -# long_description_content_type='text/x-rst', - long_description_content_type='text/plain', + long_description_content_type='text/x-rst', url='https://github.com/gkvoelkl/python-sonic', author='gkvoelkl', author_email='gkvoelkl@nelson-games.de', From 2cee64a3dc7aefe1b7668f1f147ac514ea8240d1 Mon Sep 17 00:00:00 2001 From: python-ninja-hebi Date: Sun, 15 Aug 2021 16:37:51 +0200 Subject: [PATCH 44/49] Version 0.4.2 --- PREADME.txt | 1069 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1069 insertions(+) create mode 100755 PREADME.txt diff --git a/PREADME.txt b/PREADME.txt new file mode 100755 index 0000000..ec9f21d --- /dev/null +++ b/PREADME.txt @@ -0,0 +1,1069 @@ +== python-sonic - Programming Music with Python, Sonic Pi or Supercollider + +Python-Sonic is a simple Python interface for Sonic Pi, which is a real +great music software created by Sam Aaron (http://sonic-pi.net). + +At the moment Python-Sonic works with Sonic Pi. It is planned, that it +will work with Supercollider, too. + +If you like it, use it. If you have some suggestions, tell me +(gkvoelkl@nelson-games.de). + +== Installation + +* First you need Python 3 (https://www.python.org, ) - Python 3.5 should +work, because it’s the development environment +* Then Sonic Pi (https://sonic-pi.net) - That makes the sound +* Modul python-osc (https://pypi.python.org/pypi/python-osc) - +Connection between Python and Sonic Pi Server +* And this modul python-sonic - simply copy the source + +Or try +$ pip install python-sonic +That should work. + +== Limitations + +* You have to start _Sonic Pi_ first before you can use it with +python-sonic +* Only the notes from C2 to C6 + +== Changelog + +[width="100%",cols="14%,86%",options="header",] +|=== +|Version | +|0.2.0 |Some changes for Sonic Pi 2.11. Simpler multi-threading with +decorator _@in_thread_. Messaging with _cue_ and _sync_. + +|0.3.0 |OSC Communication + +|0.3.1. |Update, sort and duration of samples + +|0.3.2. |Restructured + +|0.4.0 |Changes communication ports and recording +|=== + +== Communication + +The API _python-sonic_ communications with _Sonic Pi_ over UDP and two +ports. One port is an internal _Sonic Pi_ port and could be changed. + +For older _Sonic Pi_ Version you have to set the ports explicitly +from psonic import * +set_server_parameters('127.0.0.1',4557,4559) +== Examples + +Many of the examples are inspired from the help menu in _Sonic Pi_. + + ++*In[1]:*+ +[source, ipython3] +---- +from psonic import * +---- + +The first sound + + ++*In[2]:*+ +[source, ipython3] +---- +play(70) #play MIDI note 70 +---- + +Some more notes + + ++*In[3]:*+ +[source, ipython3] +---- +play(72) +sleep(1) +play(75) +sleep(1) +play(79) +---- + +In more tratitional music notation + + ++*In[4]:*+ +[source, ipython3] +---- +play(C5) +sleep(0.5) +play(D5) +sleep(0.5) +play(G5) +---- + +Play sharp notes like _F#_ or dimished ones like _Eb_ + + ++*In[5]:*+ +[source, ipython3] +---- +play(Fs5) +sleep(0.5) +play(Eb5) +---- + +Play louder (parameter amp) or from a different direction (parameter +pan) + + ++*In[6]:*+ +[source, ipython3] +---- +play(72,amp=2) +sleep(0.5) +play(74,pan=-1) #left +---- + +Different synthesizer sounds + + ++*In[7]:*+ +[source, ipython3] +---- +use_synth(SAW) +play(38) +sleep(0.25) +play(50) +sleep(0.5) +use_synth(PROPHET) +play(57) +sleep(0.25) +---- + +ADSR _(Attack, Decay, Sustain and Release)_ Envelope + + ++*In[8]:*+ +[source, ipython3] +---- +play (60, attack=0.5, decay=1, sustain_level=0.4, sustain=2, release=0.5) +sleep(4) +---- + +Play some samples + + ++*In[9]:*+ +[source, ipython3] +---- +sample(AMBI_LUNAR_LAND, amp=0.5) +---- + + ++*In[10]:*+ +[source, ipython3] +---- +sample(LOOP_AMEN,pan=-1) +sleep(0.877) +sample(LOOP_AMEN,pan=1) +---- + + ++*In[11]:*+ +[source, ipython3] +---- +sample(LOOP_AMEN,rate=0.5) +---- + + ++*In[12]:*+ +[source, ipython3] +---- +sample(LOOP_AMEN,rate=1.5) +---- + + ++*In[13]:*+ +[source, ipython3] +---- +sample(LOOP_AMEN,rate=-1)#back +---- + + ++*In[14]:*+ +[source, ipython3] +---- +sample(DRUM_CYMBAL_OPEN,attack=0.01,sustain=0.3,release=0.1) +---- + + ++*In[15]:*+ +[source, ipython3] +---- +sample(LOOP_AMEN,start=0.5,finish=0.8,rate=-0.2,attack=0.3,release=1) +---- + +Play some random notes + + ++*In[16]:*+ +[source, ipython3] +---- +import random + +for i in range(5): + play(random.randrange(50, 100)) + sleep(0.5) +---- + + ++*In[17]:*+ +[source, ipython3] +---- +for i in range(3): + play(random.choice([C5,E5,G5])) + sleep(1) +---- + +Sample slicing + + ++*In[18]:*+ +[source, ipython3] +---- +from psonic import * + +number_of_pieces = 8 + +for i in range(16): + s = random.randrange(0,number_of_pieces)/number_of_pieces #sample starts at 0.0 and finishes at 1.0 + f = s + (1.0/number_of_pieces) + sample(LOOP_AMEN,beat_stretch=2,start=s,finish=f) + sleep(2.0/number_of_pieces) +---- + +An infinite loop and if + + ++*In[18]:*+ +[source, ipython3] +---- +while True: + if one_in(2): + sample(DRUM_HEAVY_KICK) + sleep(0.5) + else: + sample(DRUM_CYMBAL_CLOSED) + sleep(0.25) +---- + + ++*Out[18]:*+ +---- + + --------------------------------------------------------------------------- + + KeyboardInterrupt Traceback (most recent call last) + + in () + 5 else: + 6 sample(DRUM_CYMBAL_CLOSED) + ----> 7 sleep(0.25) + + + /mnt/jupyter/python-sonic/psonic.py in sleep(duration) + 587 :return: + 588 """ + --> 589 time.sleep(duration) + 590 _debug('sleep', duration) + 591 + + + KeyboardInterrupt: + +---- + +If you want to hear more than one sound at a time, use Threads. + + ++*In[19]:*+ +[source, ipython3] +---- +import random +from psonic import * +from threading import Thread + +def bass_sound(): + c = chord(E3, MAJOR7) + while True: + use_synth(PROPHET) + play(random.choice(c), release=0.6) + sleep(0.5) + +def snare_sound(): + while True: + sample(ELEC_SNARE) + sleep(1) + +bass_thread = Thread(target=bass_sound) +snare_thread = Thread(target=snare_sound) + +bass_thread.start() +snare_thread.start() + +while True: + pass +---- + + ++*Out[19]:*+ +---- + + --------------------------------------------------------------------------- + + KeyboardInterrupt Traceback (most recent call last) + + in + 22 + 23 while True: + ---> 24 pass + + + KeyboardInterrupt: + +---- + +Every function _bass_sound_ and _snare_sound_ have its own thread. Your +can hear them running. + + ++*In[1]:*+ +[source, ipython3] +---- +from psonic import * +from threading import Thread, Condition +from random import choice + +def random_riff(condition): + use_synth(PROPHET) + sc = scale(E3, MINOR) + while True: + s = random.choice([0.125,0.25,0.5]) + with condition: + condition.wait() #Wait for message + for i in range(8): + r = random.choice([0.125, 0.25, 1, 2]) + n = random.choice(sc) + co = random.randint(30,100) + play(n, release = r, cutoff = co) + sleep(s) + +def drums(condition): + while True: + with condition: + condition.notifyAll() #Message to threads + for i in range(16): + r = random.randrange(1,10) + sample(DRUM_BASS_HARD, rate=r) + sleep(0.125) + +condition = Condition() +random_riff_thread = Thread(name='consumer1', target=random_riff, args=(condition,)) +drums_thread = Thread(name='producer', target=drums, args=(condition,)) + +random_riff_thread.start() +drums_thread.start() + +input("Press Enter to continue...") +---- + + ++*Out[1]:*+ +---- +Press Enter to continue... +''---- + +To synchronize the thread, so that they play a note at the same time, +you can use _Condition_. One function sends a message with +_condition.notifyAll_ the other waits until the message comes +_condition.wait_. + +More simple with decorator __@in_thread__ + + ++*In[ ]:*+ +[source, ipython3] +---- +from psonic import * +from random import choice + +tick = Message() + +@in_thread +def random_riff(): + use_synth(PROPHET) + sc = scale(E3, MINOR) + while True: + s = random.choice([0.125,0.25,0.5]) + tick.sync() + for i in range(8): + r = random.choice([0.125, 0.25, 1, 2]) + n = random.choice(sc) + co = random.randint(30,100) + play(n, release = r, cutoff = co) + sleep(s) + +@in_thread +def drums(): + while True: + tick.cue() + for i in range(16): + r = random.randrange(1,10) + sample(DRUM_BASS_HARD, rate=r) + sleep(0.125) + +random_riff() +drums() + +input("Press Enter to continue...") +---- + + ++*Out[ ]:*+ +---- +Press Enter to continue... +---- + + ++*In[ ]:*+ +[source, ipython3] +---- +from psonic import * + +tick = Message() + +@in_thread +def metronom(): + while True: + tick.cue() + sleep(1) + +@in_thread +def instrument(): + while True: + tick.sync() + sample(DRUM_HEAVY_KICK) + +metronom() +instrument() + +while True: + pass +---- + +Play a list of notes + + ++*In[2]:*+ +[source, ipython3] +---- +from psonic import * + +play ([64, 67, 71], amp = 0.3) +sleep(1) +play ([E4, G4, B4]) +sleep(1) +---- + +Play chords + + ++*In[2]:*+ +[source, ipython3] +---- +play(chord(E4, MINOR)) +sleep(1) +play(chord(E4, MAJOR)) +sleep(1) +play(chord(E4, MINOR7)) +sleep(1) +play(chord(E4, DOM7)) +sleep(1) +---- + +Play arpeggios + + ++*In[3]:*+ +[source, ipython3] +---- +play_pattern( chord(E4, 'm7')) +play_pattern_timed( chord(E4, 'm7'), 0.25) +play_pattern_timed(chord(E4, 'dim'), [0.25, 0.5]) +---- + +Play scales + + ++*In[4]:*+ +[source, ipython3] +---- +play_pattern_timed(scale(C3, MAJOR), 0.125, release = 0.1) +play_pattern_timed(scale(C3, MAJOR, num_octaves = 2), 0.125, release = 0.1) +play_pattern_timed(scale(C3, MAJOR_PENTATONIC, num_octaves = 2), 0.125, release = 0.1) +---- + +The function _scale_ returns a list with all notes of a scale. So you +can use list methodes or functions. For example to play arpeggios +descending or shuffeld. + + ++*In[5]:*+ +[source, ipython3] +---- +import random +from psonic import * + +s = scale(C3, MAJOR) +s +---- + + ++*Out[5]:*+ +----[48, 50, 52, 53, 55, 57, 59, 60]---- + + ++*In[6]:*+ +[source, ipython3] +---- +s.reverse() +---- + + ++*In[7]:*+ +[source, ipython3] +---- + +play_pattern_timed(s, 0.125, release = 0.1) +random.shuffle(s) +play_pattern_timed(s, 0.125, release = 0.1) +---- + +== Live Loop + +One of the best in SONIC PI is the _Live Loop_. While a loop is playing +music you can change it and hear the change. Let’s try it in Python, +too. + + ++*In[5]:*+ +[source, ipython3] +---- +from psonic import * +from threading import Thread + +def my_loop(): + play(60) + sleep(1) + +def looper(): + while True: + my_loop() + +looper_thread = Thread(name='looper', target=looper) + +looper_thread.start() + +input("Press Enter to continue...") +---- + + ++*Out[5]:*+ +---- +Press Enter to continue...Y +'Y'---- + +Now change the function _my_loop_ und you can hear it. + + ++*In[6]:*+ +[source, ipython3] +---- +def my_loop(): + use_synth(TB303) + play (60, release= 0.3) + sleep (0.25) +---- + + ++*In[7]:*+ +[source, ipython3] +---- +def my_loop(): + use_synth(TB303) + play (chord(E3, MINOR), release= 0.3) + sleep(0.5) +---- + + ++*In[8]:*+ +[source, ipython3] +---- +def my_loop(): + use_synth(TB303) + sample(DRUM_BASS_HARD, rate = random.uniform(0.5, 2)) + play(random.choice(chord(E3, MINOR)), release= 0.2, cutoff=random.randrange(60, 130)) + sleep(0.25) +---- + +To stop the sound you have to end the kernel. In IPython with Kernel –> +Restart + +Now with two live loops which are synch. + + ++*In[10]:*+ +[source, ipython3] +---- +from psonic import * +from threading import Thread, Condition +from random import choice + +def loop_foo(): + play (E4, release = 0.5) + sleep (0.5) + + +def loop_bar(): + sample (DRUM_SNARE_SOFT) + sleep (1) + + +def live_loop_1(condition): + while True: + with condition: + condition.notifyAll() #Message to threads + loop_foo() + +def live_loop_2(condition): + while True: + with condition: + condition.wait() #Wait for message + loop_bar() + +condition = Condition() +live_thread_1 = Thread(name='producer', target=live_loop_1, args=(condition,)) +live_thread_2 = Thread(name='consumer1', target=live_loop_2, args=(condition,)) + +live_thread_1.start() +live_thread_2.start() + +input("Press Enter to continue...") +---- + + ++*Out[10]:*+ +---- +Press Enter to continue...y +'y'---- + + ++*In[11]:*+ +[source, ipython3] +---- +def loop_foo(): + play (A4, release = 0.5) + sleep (0.5) +---- + + ++*In[14]:*+ +[source, ipython3] +---- +def loop_bar(): + sample (DRUM_HEAVY_KICK) + sleep (0.125) +---- + +If would be nice if we can stop the loop with a simple command. With +stop event it works. + + ++*In[1]:*+ +[source, ipython3] +---- +from psonic import * +from threading import Thread, Condition, Event + +def loop_foo(): + play (E4, release = 0.5) + sleep (0.5) + + +def loop_bar(): + sample (DRUM_SNARE_SOFT) + sleep (1) + + +def live_loop_1(condition,stop_event): + while not stop_event.is_set(): + with condition: + condition.notifyAll() #Message to threads + loop_foo() + +def live_loop_2(condition,stop_event): + while not stop_event.is_set(): + with condition: + condition.wait() #Wait for message + loop_bar() + + + +condition = Condition() +stop_event = Event() +live_thread_1 = Thread(name='producer', target=live_loop_1, args=(condition,stop_event)) +live_thread_2 = Thread(name='consumer1', target=live_loop_2, args=(condition,stop_event)) + + +live_thread_1.start() +live_thread_2.start() + +input("Press Enter to continue...") +---- + + ++*Out[1]:*+ +---- +Press Enter to continue...y +'y'---- + + ++*In[3]:*+ +[source, ipython3] +---- +stop_event.set() +---- + +More complex live loops + + ++*In[4]:*+ +[source, ipython3] +---- +sc = Ring(scale(E3, MINOR_PENTATONIC)) + +def loop_foo(): + play (next(sc), release= 0.1) + sleep (0.125) + +sc2 = Ring(scale(E3,MINOR_PENTATONIC,num_octaves=2)) + +def loop_bar(): + use_synth(DSAW) + play (next(sc2), release= 0.25) + sleep (0.25) +---- + +Now a simple structure with four live loops + + ++*In[11]:*+ +[source, ipython3] +---- +import random +from psonic import * +from threading import Thread, Condition, Event + +def live_1(): + pass + +def live_2(): + pass + +def live_3(): + pass + +def live_4(): + pass + +def live_loop_1(condition,stop_event): + while not stop_event.is_set(): + with condition: + condition.notifyAll() #Message to threads + live_1() + +def live_loop_2(condition,stop_event): + while not stop_event.is_set(): + with condition: + condition.wait() #Wait for message + live_2() + +def live_loop_3(condition,stop_event): + while not stop_event.is_set(): + with condition: + condition.wait() #Wait for message + live_3() + +def live_loop_4(condition,stop_event): + while not stop_event.is_set(): + with condition: + condition.wait() #Wait for message + live_4() + +condition = Condition() +stop_event = Event() +live_thread_1 = Thread(name='producer', target=live_loop_1, args=(condition,stop_event)) +live_thread_2 = Thread(name='consumer1', target=live_loop_2, args=(condition,stop_event)) +live_thread_3 = Thread(name='consumer2', target=live_loop_3, args=(condition,stop_event)) +live_thread_4 = Thread(name='consumer3', target=live_loop_3, args=(condition,stop_event)) + +live_thread_1.start() +live_thread_2.start() +live_thread_3.start() +live_thread_4.start() + +input("Press Enter to continue...") +---- + + ++*Out[11]:*+ +---- +Press Enter to continue...y +'y'---- + +After starting the loops you can change them + + ++*In[12]:*+ +[source, ipython3] +---- +def live_1(): + sample(BD_HAUS,amp=2) + sleep(0.5) + pass +---- + + ++*In[7]:*+ +[source, ipython3] +---- +def live_2(): + #sample(AMBI_CHOIR, rate=0.4) + #sleep(1) + pass +---- + + ++*In[14]:*+ +[source, ipython3] +---- +def live_3(): + use_synth(TB303) + play(E2, release=4,cutoff=120,cutoff_attack=1) + sleep(4) +---- + + ++*In[13]:*+ +[source, ipython3] +---- +def live_4(): + notes = scale(E3, MINOR_PENTATONIC, num_octaves=2) + for i in range(8): + play(random.choice(notes),release=0.1,amp=1.5) + sleep(0.125) +---- + +And stop. + + ++*In[15]:*+ +[source, ipython3] +---- +stop_event.set() +---- + +== Creating Sound + + ++*In[4]:*+ +[source, ipython3] +---- +from psonic import * + +synth(SINE, note=D4) +synth(SQUARE, note=D4) +synth(TRI, note=D4, amp=0.4) +---- + + ++*In[5]:*+ +[source, ipython3] +---- +detune = 0.7 +synth(SQUARE, note = E4) +synth(SQUARE, note = E4+detune) +---- + + ++*In[12]:*+ +[source, ipython3] +---- +detune=0.1 # Amplitude shaping +synth(SQUARE, note = E2, release = 2) +synth(SQUARE, note = E2+detune, amp = 2, release = 2) +synth(GNOISE, release = 2, amp = 1, cutoff = 60) +synth(GNOISE, release = 0.5, amp = 1, cutoff = 100) +synth(NOISE, release = 0.2, amp = 1, cutoff = 90) +---- + +== Next Step + +Using FX _Not implemented yet_ + + ++*In[2]:*+ +[source, ipython3] +---- +from psonic import * + +with Fx(SLICER): + synth(PROPHET,note=E2,release=8,cutoff=80) + synth(PROPHET,note=E2+4,release=8,cutoff=80) +---- + + ++*In[3]:*+ +[source, ipython3] +---- +with Fx(SLICER, phase=0.125, probability=0.6,prob_pos=1): + synth(TB303, note=E2, cutoff_attack=8, release=8) + synth(TB303, note=E3, cutoff_attack=4, release=8) + synth(TB303, note=E4, cutoff_attack=2, release=8) +---- + +== OSC Communication (Sonic Pi Ver. 3.x or better) + +In Sonic Pi version 3 or better you can work with messages. + + ++*In[1]:*+ +[source, ipython3] +---- +from psonic import * +---- + +First you need a programm in the Sonic Pi server that receives messages. +You can write it in th GUI or send one with Python. + + ++*In[2]:*+ +[source, ipython3] +---- +run("""live_loop :foo do + use_real_time + a, b, c = sync "/osc*/trigger/prophet" + synth :prophet, note: a, cutoff: b, sustain: c +end """) +---- + +Now send a message to Sonic Pi. + + ++*In[3]:*+ +[source, ipython3] +---- +send_message('/trigger/prophet', 70, 100, 8) +---- + + ++*In[4]:*+ +[source, ipython3] +---- +stop() +---- + +== Recording + +With python-sonic you can record wave files. + + ++*In[1]:*+ +[source, ipython3] +---- +from psonic import * +---- + + ++*In[5]:*+ +[source, ipython3] +---- +# start recording +start_recording() + +play(chord(E4, MINOR)) +sleep(1) +play(chord(E4, MAJOR)) +sleep(1) +play(chord(E4, MINOR7)) +sleep(1) +play(chord(E4, DOM7)) +sleep(1) +---- + + ++*In[6]:*+ +[source, ipython3] +---- +# stop recording +stop_recording +---- + + ++*Out[6]:*+ +-------- + + ++*In[7]:*+ +[source, ipython3] +---- +# save file +save_recording('/Volumes/jupyter/python-sonic/test.wav') +---- + +== More Examples + + ++*In[1]:*+ +[source, ipython3] +---- +from psonic import * +---- + + ++*In[ ]:*+ +[source, ipython3] +---- +#Inspired by Steve Reich Clapping Music + +clapping = [1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0] + +for i in range(13): + for j in range(4): + for k in range(12): + if clapping[k] ==1 : sample(DRUM_SNARE_SOFT,pan=-0.5) + if clapping[(i+k)%12] == 1: sample(DRUM_HEAVY_KICK,pan=0.5) + sleep (0.25) +---- + +== Projects that use Python-Sonic + +Raspberry Pi sonic-track.py a Sonic-pi Motion Track Demo +https://github.com/pageauc/sonic-track + +== Sources + +Joe Armstrong: Connecting Erlang to the Sonic Pi +http://joearms.github.io/2015/01/05/Connecting-Erlang-to-Sonic-Pi.html + +Joe Armstrong: Controlling Sound with OSC Messages +http://joearms.github.io/2016/01/29/Controlling-Sound-with-OSC-Messages.html + +.. From 5d0a66c5618673d74daf063aa4ce4d8caf856c40 Mon Sep 17 00:00:00 2001 From: python-ninja-hebi Date: Sun, 15 Aug 2021 16:38:48 +0200 Subject: [PATCH 45/49] Version 0.4.2 --- setup.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index caf3b62..a6383c9 100755 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ here = path.abspath(path.dirname(__file__)) # Get the long description from the README file -with open(path.join(here, 'README.txt'), encoding='utf-8') as f: +with open(path.join(here, 'PREADME.txt'), encoding='utf-8') as f: long_description = f.read() requirements = [ @@ -26,10 +26,9 @@ setup( name='python-sonic', - version='0.4.1', + version='0.4.2', description='Programming Music with Sonic Pi or Supercollider', long_description=long_description, -# long_description_content_type='text/x-rst', long_description_content_type='text/plain', url='https://github.com/gkvoelkl/python-sonic', author='gkvoelkl', @@ -54,7 +53,9 @@ 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7' + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9' ], keywords= [ 'music', From 7c83394cdff625664bc3d1aa69e53ec1274c0a24 Mon Sep 17 00:00:00 2001 From: Mike Roberts <2947595+m-roberts@users.noreply.github.com> Date: Sun, 22 Aug 2021 14:48:57 +0100 Subject: [PATCH 46/49] Fix README for PyPI (#37) --- PREADME.txt | 1069 --------------------------------------------------- README.rst | 124 +++--- setup.py | 3 +- 3 files changed, 63 insertions(+), 1133 deletions(-) delete mode 100755 PREADME.txt diff --git a/PREADME.txt b/PREADME.txt deleted file mode 100755 index ec9f21d..0000000 --- a/PREADME.txt +++ /dev/null @@ -1,1069 +0,0 @@ -== python-sonic - Programming Music with Python, Sonic Pi or Supercollider - -Python-Sonic is a simple Python interface for Sonic Pi, which is a real -great music software created by Sam Aaron (http://sonic-pi.net). - -At the moment Python-Sonic works with Sonic Pi. It is planned, that it -will work with Supercollider, too. - -If you like it, use it. If you have some suggestions, tell me -(gkvoelkl@nelson-games.de). - -== Installation - -* First you need Python 3 (https://www.python.org, ) - Python 3.5 should -work, because it’s the development environment -* Then Sonic Pi (https://sonic-pi.net) - That makes the sound -* Modul python-osc (https://pypi.python.org/pypi/python-osc) - -Connection between Python and Sonic Pi Server -* And this modul python-sonic - simply copy the source - -Or try -$ pip install python-sonic -That should work. - -== Limitations - -* You have to start _Sonic Pi_ first before you can use it with -python-sonic -* Only the notes from C2 to C6 - -== Changelog - -[width="100%",cols="14%,86%",options="header",] -|=== -|Version | -|0.2.0 |Some changes for Sonic Pi 2.11. Simpler multi-threading with -decorator _@in_thread_. Messaging with _cue_ and _sync_. - -|0.3.0 |OSC Communication - -|0.3.1. |Update, sort and duration of samples - -|0.3.2. |Restructured - -|0.4.0 |Changes communication ports and recording -|=== - -== Communication - -The API _python-sonic_ communications with _Sonic Pi_ over UDP and two -ports. One port is an internal _Sonic Pi_ port and could be changed. + -For older _Sonic Pi_ Version you have to set the ports explicitly -from psonic import * -set_server_parameters('127.0.0.1',4557,4559) -== Examples - -Many of the examples are inspired from the help menu in _Sonic Pi_. - - -+*In[1]:*+ -[source, ipython3] ----- -from psonic import * ----- - -The first sound - - -+*In[2]:*+ -[source, ipython3] ----- -play(70) #play MIDI note 70 ----- - -Some more notes - - -+*In[3]:*+ -[source, ipython3] ----- -play(72) -sleep(1) -play(75) -sleep(1) -play(79) ----- - -In more tratitional music notation - - -+*In[4]:*+ -[source, ipython3] ----- -play(C5) -sleep(0.5) -play(D5) -sleep(0.5) -play(G5) ----- - -Play sharp notes like _F#_ or dimished ones like _Eb_ - - -+*In[5]:*+ -[source, ipython3] ----- -play(Fs5) -sleep(0.5) -play(Eb5) ----- - -Play louder (parameter amp) or from a different direction (parameter -pan) - - -+*In[6]:*+ -[source, ipython3] ----- -play(72,amp=2) -sleep(0.5) -play(74,pan=-1) #left ----- - -Different synthesizer sounds - - -+*In[7]:*+ -[source, ipython3] ----- -use_synth(SAW) -play(38) -sleep(0.25) -play(50) -sleep(0.5) -use_synth(PROPHET) -play(57) -sleep(0.25) ----- - -ADSR _(Attack, Decay, Sustain and Release)_ Envelope - - -+*In[8]:*+ -[source, ipython3] ----- -play (60, attack=0.5, decay=1, sustain_level=0.4, sustain=2, release=0.5) -sleep(4) ----- - -Play some samples - - -+*In[9]:*+ -[source, ipython3] ----- -sample(AMBI_LUNAR_LAND, amp=0.5) ----- - - -+*In[10]:*+ -[source, ipython3] ----- -sample(LOOP_AMEN,pan=-1) -sleep(0.877) -sample(LOOP_AMEN,pan=1) ----- - - -+*In[11]:*+ -[source, ipython3] ----- -sample(LOOP_AMEN,rate=0.5) ----- - - -+*In[12]:*+ -[source, ipython3] ----- -sample(LOOP_AMEN,rate=1.5) ----- - - -+*In[13]:*+ -[source, ipython3] ----- -sample(LOOP_AMEN,rate=-1)#back ----- - - -+*In[14]:*+ -[source, ipython3] ----- -sample(DRUM_CYMBAL_OPEN,attack=0.01,sustain=0.3,release=0.1) ----- - - -+*In[15]:*+ -[source, ipython3] ----- -sample(LOOP_AMEN,start=0.5,finish=0.8,rate=-0.2,attack=0.3,release=1) ----- - -Play some random notes - - -+*In[16]:*+ -[source, ipython3] ----- -import random - -for i in range(5): - play(random.randrange(50, 100)) - sleep(0.5) ----- - - -+*In[17]:*+ -[source, ipython3] ----- -for i in range(3): - play(random.choice([C5,E5,G5])) - sleep(1) ----- - -Sample slicing - - -+*In[18]:*+ -[source, ipython3] ----- -from psonic import * - -number_of_pieces = 8 - -for i in range(16): - s = random.randrange(0,number_of_pieces)/number_of_pieces #sample starts at 0.0 and finishes at 1.0 - f = s + (1.0/number_of_pieces) - sample(LOOP_AMEN,beat_stretch=2,start=s,finish=f) - sleep(2.0/number_of_pieces) ----- - -An infinite loop and if - - -+*In[18]:*+ -[source, ipython3] ----- -while True: - if one_in(2): - sample(DRUM_HEAVY_KICK) - sleep(0.5) - else: - sample(DRUM_CYMBAL_CLOSED) - sleep(0.25) ----- - - -+*Out[18]:*+ ----- - - --------------------------------------------------------------------------- - - KeyboardInterrupt Traceback (most recent call last) - - in () - 5 else: - 6 sample(DRUM_CYMBAL_CLOSED) - ----> 7 sleep(0.25) - - - /mnt/jupyter/python-sonic/psonic.py in sleep(duration) - 587 :return: - 588 """ - --> 589 time.sleep(duration) - 590 _debug('sleep', duration) - 591 - - - KeyboardInterrupt: - ----- - -If you want to hear more than one sound at a time, use Threads. - - -+*In[19]:*+ -[source, ipython3] ----- -import random -from psonic import * -from threading import Thread - -def bass_sound(): - c = chord(E3, MAJOR7) - while True: - use_synth(PROPHET) - play(random.choice(c), release=0.6) - sleep(0.5) - -def snare_sound(): - while True: - sample(ELEC_SNARE) - sleep(1) - -bass_thread = Thread(target=bass_sound) -snare_thread = Thread(target=snare_sound) - -bass_thread.start() -snare_thread.start() - -while True: - pass ----- - - -+*Out[19]:*+ ----- - - --------------------------------------------------------------------------- - - KeyboardInterrupt Traceback (most recent call last) - - in - 22 - 23 while True: - ---> 24 pass - - - KeyboardInterrupt: - ----- - -Every function _bass_sound_ and _snare_sound_ have its own thread. Your -can hear them running. - - -+*In[1]:*+ -[source, ipython3] ----- -from psonic import * -from threading import Thread, Condition -from random import choice - -def random_riff(condition): - use_synth(PROPHET) - sc = scale(E3, MINOR) - while True: - s = random.choice([0.125,0.25,0.5]) - with condition: - condition.wait() #Wait for message - for i in range(8): - r = random.choice([0.125, 0.25, 1, 2]) - n = random.choice(sc) - co = random.randint(30,100) - play(n, release = r, cutoff = co) - sleep(s) - -def drums(condition): - while True: - with condition: - condition.notifyAll() #Message to threads - for i in range(16): - r = random.randrange(1,10) - sample(DRUM_BASS_HARD, rate=r) - sleep(0.125) - -condition = Condition() -random_riff_thread = Thread(name='consumer1', target=random_riff, args=(condition,)) -drums_thread = Thread(name='producer', target=drums, args=(condition,)) - -random_riff_thread.start() -drums_thread.start() - -input("Press Enter to continue...") ----- - - -+*Out[1]:*+ ----- -Press Enter to continue... -''---- - -To synchronize the thread, so that they play a note at the same time, -you can use _Condition_. One function sends a message with -_condition.notifyAll_ the other waits until the message comes -_condition.wait_. - -More simple with decorator __@in_thread__ - - -+*In[ ]:*+ -[source, ipython3] ----- -from psonic import * -from random import choice - -tick = Message() - -@in_thread -def random_riff(): - use_synth(PROPHET) - sc = scale(E3, MINOR) - while True: - s = random.choice([0.125,0.25,0.5]) - tick.sync() - for i in range(8): - r = random.choice([0.125, 0.25, 1, 2]) - n = random.choice(sc) - co = random.randint(30,100) - play(n, release = r, cutoff = co) - sleep(s) - -@in_thread -def drums(): - while True: - tick.cue() - for i in range(16): - r = random.randrange(1,10) - sample(DRUM_BASS_HARD, rate=r) - sleep(0.125) - -random_riff() -drums() - -input("Press Enter to continue...") ----- - - -+*Out[ ]:*+ ----- -Press Enter to continue... ----- - - -+*In[ ]:*+ -[source, ipython3] ----- -from psonic import * - -tick = Message() - -@in_thread -def metronom(): - while True: - tick.cue() - sleep(1) - -@in_thread -def instrument(): - while True: - tick.sync() - sample(DRUM_HEAVY_KICK) - -metronom() -instrument() - -while True: - pass ----- - -Play a list of notes - - -+*In[2]:*+ -[source, ipython3] ----- -from psonic import * - -play ([64, 67, 71], amp = 0.3) -sleep(1) -play ([E4, G4, B4]) -sleep(1) ----- - -Play chords - - -+*In[2]:*+ -[source, ipython3] ----- -play(chord(E4, MINOR)) -sleep(1) -play(chord(E4, MAJOR)) -sleep(1) -play(chord(E4, MINOR7)) -sleep(1) -play(chord(E4, DOM7)) -sleep(1) ----- - -Play arpeggios - - -+*In[3]:*+ -[source, ipython3] ----- -play_pattern( chord(E4, 'm7')) -play_pattern_timed( chord(E4, 'm7'), 0.25) -play_pattern_timed(chord(E4, 'dim'), [0.25, 0.5]) ----- - -Play scales - - -+*In[4]:*+ -[source, ipython3] ----- -play_pattern_timed(scale(C3, MAJOR), 0.125, release = 0.1) -play_pattern_timed(scale(C3, MAJOR, num_octaves = 2), 0.125, release = 0.1) -play_pattern_timed(scale(C3, MAJOR_PENTATONIC, num_octaves = 2), 0.125, release = 0.1) ----- - -The function _scale_ returns a list with all notes of a scale. So you -can use list methodes or functions. For example to play arpeggios -descending or shuffeld. - - -+*In[5]:*+ -[source, ipython3] ----- -import random -from psonic import * - -s = scale(C3, MAJOR) -s ----- - - -+*Out[5]:*+ -----[48, 50, 52, 53, 55, 57, 59, 60]---- - - -+*In[6]:*+ -[source, ipython3] ----- -s.reverse() ----- - - -+*In[7]:*+ -[source, ipython3] ----- - -play_pattern_timed(s, 0.125, release = 0.1) -random.shuffle(s) -play_pattern_timed(s, 0.125, release = 0.1) ----- - -== Live Loop - -One of the best in SONIC PI is the _Live Loop_. While a loop is playing -music you can change it and hear the change. Let’s try it in Python, -too. - - -+*In[5]:*+ -[source, ipython3] ----- -from psonic import * -from threading import Thread - -def my_loop(): - play(60) - sleep(1) - -def looper(): - while True: - my_loop() - -looper_thread = Thread(name='looper', target=looper) - -looper_thread.start() - -input("Press Enter to continue...") ----- - - -+*Out[5]:*+ ----- -Press Enter to continue...Y -'Y'---- - -Now change the function _my_loop_ und you can hear it. - - -+*In[6]:*+ -[source, ipython3] ----- -def my_loop(): - use_synth(TB303) - play (60, release= 0.3) - sleep (0.25) ----- - - -+*In[7]:*+ -[source, ipython3] ----- -def my_loop(): - use_synth(TB303) - play (chord(E3, MINOR), release= 0.3) - sleep(0.5) ----- - - -+*In[8]:*+ -[source, ipython3] ----- -def my_loop(): - use_synth(TB303) - sample(DRUM_BASS_HARD, rate = random.uniform(0.5, 2)) - play(random.choice(chord(E3, MINOR)), release= 0.2, cutoff=random.randrange(60, 130)) - sleep(0.25) ----- - -To stop the sound you have to end the kernel. In IPython with Kernel –> -Restart - -Now with two live loops which are synch. - - -+*In[10]:*+ -[source, ipython3] ----- -from psonic import * -from threading import Thread, Condition -from random import choice - -def loop_foo(): - play (E4, release = 0.5) - sleep (0.5) - - -def loop_bar(): - sample (DRUM_SNARE_SOFT) - sleep (1) - - -def live_loop_1(condition): - while True: - with condition: - condition.notifyAll() #Message to threads - loop_foo() - -def live_loop_2(condition): - while True: - with condition: - condition.wait() #Wait for message - loop_bar() - -condition = Condition() -live_thread_1 = Thread(name='producer', target=live_loop_1, args=(condition,)) -live_thread_2 = Thread(name='consumer1', target=live_loop_2, args=(condition,)) - -live_thread_1.start() -live_thread_2.start() - -input("Press Enter to continue...") ----- - - -+*Out[10]:*+ ----- -Press Enter to continue...y -'y'---- - - -+*In[11]:*+ -[source, ipython3] ----- -def loop_foo(): - play (A4, release = 0.5) - sleep (0.5) ----- - - -+*In[14]:*+ -[source, ipython3] ----- -def loop_bar(): - sample (DRUM_HEAVY_KICK) - sleep (0.125) ----- - -If would be nice if we can stop the loop with a simple command. With -stop event it works. - - -+*In[1]:*+ -[source, ipython3] ----- -from psonic import * -from threading import Thread, Condition, Event - -def loop_foo(): - play (E4, release = 0.5) - sleep (0.5) - - -def loop_bar(): - sample (DRUM_SNARE_SOFT) - sleep (1) - - -def live_loop_1(condition,stop_event): - while not stop_event.is_set(): - with condition: - condition.notifyAll() #Message to threads - loop_foo() - -def live_loop_2(condition,stop_event): - while not stop_event.is_set(): - with condition: - condition.wait() #Wait for message - loop_bar() - - - -condition = Condition() -stop_event = Event() -live_thread_1 = Thread(name='producer', target=live_loop_1, args=(condition,stop_event)) -live_thread_2 = Thread(name='consumer1', target=live_loop_2, args=(condition,stop_event)) - - -live_thread_1.start() -live_thread_2.start() - -input("Press Enter to continue...") ----- - - -+*Out[1]:*+ ----- -Press Enter to continue...y -'y'---- - - -+*In[3]:*+ -[source, ipython3] ----- -stop_event.set() ----- - -More complex live loops - - -+*In[4]:*+ -[source, ipython3] ----- -sc = Ring(scale(E3, MINOR_PENTATONIC)) - -def loop_foo(): - play (next(sc), release= 0.1) - sleep (0.125) - -sc2 = Ring(scale(E3,MINOR_PENTATONIC,num_octaves=2)) - -def loop_bar(): - use_synth(DSAW) - play (next(sc2), release= 0.25) - sleep (0.25) ----- - -Now a simple structure with four live loops - - -+*In[11]:*+ -[source, ipython3] ----- -import random -from psonic import * -from threading import Thread, Condition, Event - -def live_1(): - pass - -def live_2(): - pass - -def live_3(): - pass - -def live_4(): - pass - -def live_loop_1(condition,stop_event): - while not stop_event.is_set(): - with condition: - condition.notifyAll() #Message to threads - live_1() - -def live_loop_2(condition,stop_event): - while not stop_event.is_set(): - with condition: - condition.wait() #Wait for message - live_2() - -def live_loop_3(condition,stop_event): - while not stop_event.is_set(): - with condition: - condition.wait() #Wait for message - live_3() - -def live_loop_4(condition,stop_event): - while not stop_event.is_set(): - with condition: - condition.wait() #Wait for message - live_4() - -condition = Condition() -stop_event = Event() -live_thread_1 = Thread(name='producer', target=live_loop_1, args=(condition,stop_event)) -live_thread_2 = Thread(name='consumer1', target=live_loop_2, args=(condition,stop_event)) -live_thread_3 = Thread(name='consumer2', target=live_loop_3, args=(condition,stop_event)) -live_thread_4 = Thread(name='consumer3', target=live_loop_3, args=(condition,stop_event)) - -live_thread_1.start() -live_thread_2.start() -live_thread_3.start() -live_thread_4.start() - -input("Press Enter to continue...") ----- - - -+*Out[11]:*+ ----- -Press Enter to continue...y -'y'---- - -After starting the loops you can change them - - -+*In[12]:*+ -[source, ipython3] ----- -def live_1(): - sample(BD_HAUS,amp=2) - sleep(0.5) - pass ----- - - -+*In[7]:*+ -[source, ipython3] ----- -def live_2(): - #sample(AMBI_CHOIR, rate=0.4) - #sleep(1) - pass ----- - - -+*In[14]:*+ -[source, ipython3] ----- -def live_3(): - use_synth(TB303) - play(E2, release=4,cutoff=120,cutoff_attack=1) - sleep(4) ----- - - -+*In[13]:*+ -[source, ipython3] ----- -def live_4(): - notes = scale(E3, MINOR_PENTATONIC, num_octaves=2) - for i in range(8): - play(random.choice(notes),release=0.1,amp=1.5) - sleep(0.125) ----- - -And stop. - - -+*In[15]:*+ -[source, ipython3] ----- -stop_event.set() ----- - -== Creating Sound - - -+*In[4]:*+ -[source, ipython3] ----- -from psonic import * - -synth(SINE, note=D4) -synth(SQUARE, note=D4) -synth(TRI, note=D4, amp=0.4) ----- - - -+*In[5]:*+ -[source, ipython3] ----- -detune = 0.7 -synth(SQUARE, note = E4) -synth(SQUARE, note = E4+detune) ----- - - -+*In[12]:*+ -[source, ipython3] ----- -detune=0.1 # Amplitude shaping -synth(SQUARE, note = E2, release = 2) -synth(SQUARE, note = E2+detune, amp = 2, release = 2) -synth(GNOISE, release = 2, amp = 1, cutoff = 60) -synth(GNOISE, release = 0.5, amp = 1, cutoff = 100) -synth(NOISE, release = 0.2, amp = 1, cutoff = 90) ----- - -== Next Step - -Using FX _Not implemented yet_ - - -+*In[2]:*+ -[source, ipython3] ----- -from psonic import * - -with Fx(SLICER): - synth(PROPHET,note=E2,release=8,cutoff=80) - synth(PROPHET,note=E2+4,release=8,cutoff=80) ----- - - -+*In[3]:*+ -[source, ipython3] ----- -with Fx(SLICER, phase=0.125, probability=0.6,prob_pos=1): - synth(TB303, note=E2, cutoff_attack=8, release=8) - synth(TB303, note=E3, cutoff_attack=4, release=8) - synth(TB303, note=E4, cutoff_attack=2, release=8) ----- - -== OSC Communication (Sonic Pi Ver. 3.x or better) - -In Sonic Pi version 3 or better you can work with messages. - - -+*In[1]:*+ -[source, ipython3] ----- -from psonic import * ----- - -First you need a programm in the Sonic Pi server that receives messages. -You can write it in th GUI or send one with Python. - - -+*In[2]:*+ -[source, ipython3] ----- -run("""live_loop :foo do - use_real_time - a, b, c = sync "/osc*/trigger/prophet" - synth :prophet, note: a, cutoff: b, sustain: c -end """) ----- - -Now send a message to Sonic Pi. - - -+*In[3]:*+ -[source, ipython3] ----- -send_message('/trigger/prophet', 70, 100, 8) ----- - - -+*In[4]:*+ -[source, ipython3] ----- -stop() ----- - -== Recording - -With python-sonic you can record wave files. - - -+*In[1]:*+ -[source, ipython3] ----- -from psonic import * ----- - - -+*In[5]:*+ -[source, ipython3] ----- -# start recording -start_recording() - -play(chord(E4, MINOR)) -sleep(1) -play(chord(E4, MAJOR)) -sleep(1) -play(chord(E4, MINOR7)) -sleep(1) -play(chord(E4, DOM7)) -sleep(1) ----- - - -+*In[6]:*+ -[source, ipython3] ----- -# stop recording -stop_recording ----- - - -+*Out[6]:*+ --------- - - -+*In[7]:*+ -[source, ipython3] ----- -# save file -save_recording('/Volumes/jupyter/python-sonic/test.wav') ----- - -== More Examples - - -+*In[1]:*+ -[source, ipython3] ----- -from psonic import * ----- - - -+*In[ ]:*+ -[source, ipython3] ----- -#Inspired by Steve Reich Clapping Music - -clapping = [1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0] - -for i in range(13): - for j in range(4): - for k in range(12): - if clapping[k] ==1 : sample(DRUM_SNARE_SOFT,pan=-0.5) - if clapping[(i+k)%12] == 1: sample(DRUM_HEAVY_KICK,pan=0.5) - sleep (0.25) ----- - -== Projects that use Python-Sonic - -Raspberry Pi sonic-track.py a Sonic-pi Motion Track Demo -https://github.com/pageauc/sonic-track - -== Sources - -Joe Armstrong: Connecting Erlang to the Sonic Pi -http://joearms.github.io/2015/01/05/Connecting-Erlang-to-Sonic-Pi.html - -Joe Armstrong: Controlling Sound with OSC Messages -http://joearms.github.io/2016/01/29/Controlling-Sound-with-OSC-Messages.html - -.. diff --git a/README.rst b/README.rst index edd4306..949f371 100755 --- a/README.rst +++ b/README.rst @@ -60,7 +60,7 @@ Communication ports. One port is an internal *Sonic Pi* port and could be changed. | For older *Sonic Pi* Version you have to set the ports explicitly -.. code:: ipython3 +.. code-block:: python from psonic import * set_server_parameter('127.0.0.1',4557,4559) @@ -70,19 +70,19 @@ Examples Many of the examples are inspired from the help menu in *Sonic Pi*. -.. code:: ipython3 +.. code-block:: python from psonic import * The first sound -.. code:: ipython3 +.. code-block:: python play(70) #play MIDI note 70 Some more notes -.. code:: ipython3 +.. code-block:: python play(72) sleep(1) @@ -92,7 +92,7 @@ Some more notes In more tratitional music notation -.. code:: ipython3 +.. code-block:: python play(C5) sleep(0.5) @@ -102,7 +102,7 @@ In more tratitional music notation Play sharp notes like *F#* or dimished ones like *Eb* -.. code:: ipython3 +.. code-block:: python play(Fs5) sleep(0.5) @@ -111,7 +111,7 @@ Play sharp notes like *F#* or dimished ones like *Eb* Play louder (parameter amp) or from a different direction (parameter pan) -.. code:: ipython3 +.. code-block:: python play(72,amp=2) sleep(0.5) @@ -119,7 +119,7 @@ pan) Different synthesizer sounds -.. code:: ipython3 +.. code-block:: python use_synth(SAW) play(38) @@ -132,46 +132,46 @@ Different synthesizer sounds ADSR *(Attack, Decay, Sustain and Release)* Envelope -.. code:: ipython3 +.. code-block:: python play (60, attack=0.5, decay=1, sustain_level=0.4, sustain=2, release=0.5) sleep(4) Play some samples -.. code:: ipython3 +.. code-block:: python sample(AMBI_LUNAR_LAND, amp=0.5) -.. code:: ipython3 +.. code-block:: python sample(LOOP_AMEN,pan=-1) sleep(0.877) sample(LOOP_AMEN,pan=1) -.. code:: ipython3 +.. code-block:: python sample(LOOP_AMEN,rate=0.5) -.. code:: ipython3 +.. code-block:: python sample(LOOP_AMEN,rate=1.5) -.. code:: ipython3 +.. code-block:: python sample(LOOP_AMEN,rate=-1)#back -.. code:: ipython3 +.. code-block:: python sample(DRUM_CYMBAL_OPEN,attack=0.01,sustain=0.3,release=0.1) -.. code:: ipython3 +.. code-block:: python sample(LOOP_AMEN,start=0.5,finish=0.8,rate=-0.2,attack=0.3,release=1) Play some random notes -.. code:: ipython3 +.. code-block:: python import random @@ -179,7 +179,7 @@ Play some random notes play(random.randrange(50, 100)) sleep(0.5) -.. code:: ipython3 +.. code-block:: python for i in range(3): play(random.choice([C5,E5,G5])) @@ -187,7 +187,7 @@ Play some random notes Sample slicing -.. code:: ipython3 +.. code-block:: python from psonic import * @@ -201,7 +201,7 @@ Sample slicing An infinite loop and if -.. code:: ipython3 +.. code-block:: python while True: if one_in(2): @@ -238,7 +238,7 @@ An infinite loop and if If you want to hear more than one sound at a time, use Threads. -.. code:: ipython3 +.. code-block:: python import random from psonic import * @@ -285,7 +285,7 @@ If you want to hear more than one sound at a time, use Threads. Every function *bass_sound* and *snare_sound* have its own thread. Your can hear them running. -.. code:: ipython3 +.. code-block:: python from psonic import * from threading import Thread, Condition @@ -344,7 +344,7 @@ you can use *Condition*. One function sends a message with More simple with decorator \_\_@in_thread_\_ -.. code:: ipython3 +.. code-block:: python from psonic import * from random import choice @@ -385,7 +385,7 @@ More simple with decorator \_\_@in_thread_\_ Press Enter to continue... -.. code:: ipython3 +.. code-block:: python from psonic import * @@ -411,7 +411,7 @@ More simple with decorator \_\_@in_thread_\_ Play a list of notes -.. code:: ipython3 +.. code-block:: python from psonic import * @@ -422,7 +422,7 @@ Play a list of notes Play chords -.. code:: ipython3 +.. code-block:: python play(chord(E4, MINOR)) sleep(1) @@ -435,7 +435,7 @@ Play chords Play arpeggios -.. code:: ipython3 +.. code-block:: python play_pattern( chord(E4, 'm7')) play_pattern_timed( chord(E4, 'm7'), 0.25) @@ -443,7 +443,7 @@ Play arpeggios Play scales -.. code:: ipython3 +.. code-block:: python play_pattern_timed(scale(C3, MAJOR), 0.125, release = 0.1) play_pattern_timed(scale(C3, MAJOR, num_octaves = 2), 0.125, release = 0.1) @@ -453,7 +453,7 @@ The function *scale* returns a list with all notes of a scale. So you can use list methodes or functions. For example to play arpeggios descending or shuffeld. -.. code:: ipython3 +.. code-block:: python import random from psonic import * @@ -470,11 +470,11 @@ descending or shuffeld. -.. code:: ipython3 +.. code-block:: python s.reverse() -.. code:: ipython3 +.. code-block:: python play_pattern_timed(s, 0.125, release = 0.1) @@ -488,7 +488,7 @@ One of the best in SONIC PI is the *Live Loop*. While a loop is playing music you can change it and hear the change. Let’s try it in Python, too. -.. code:: ipython3 +.. code-block:: python from psonic import * from threading import Thread @@ -523,21 +523,21 @@ too. Now change the function *my_loop* und you can hear it. -.. code:: ipython3 +.. code-block:: python def my_loop(): use_synth(TB303) play (60, release= 0.3) sleep (0.25) -.. code:: ipython3 +.. code-block:: python def my_loop(): use_synth(TB303) play (chord(E3, MINOR), release= 0.3) sleep(0.5) -.. code:: ipython3 +.. code-block:: python def my_loop(): use_synth(TB303) @@ -550,7 +550,7 @@ Restart Now with two live loops which are synch. -.. code:: ipython3 +.. code-block:: python from psonic import * from threading import Thread, Condition @@ -601,13 +601,13 @@ Now with two live loops which are synch. -.. code:: ipython3 +.. code-block:: python def loop_foo(): play (A4, release = 0.5) sleep (0.5) -.. code:: ipython3 +.. code-block:: python def loop_bar(): sample (DRUM_HEAVY_KICK) @@ -616,7 +616,7 @@ Now with two live loops which are synch. If would be nice if we can stop the loop with a simple command. With stop event it works. -.. code:: ipython3 +.. code-block:: python from psonic import * from threading import Thread, Condition, Event @@ -670,13 +670,13 @@ stop event it works. -.. code:: ipython3 +.. code-block:: python stop_event.set() More complex live loops -.. code:: ipython3 +.. code-block:: python sc = Ring(scale(E3, MINOR_PENTATONIC)) @@ -693,7 +693,7 @@ More complex live loops Now a simple structure with four live loops -.. code:: ipython3 +.. code-block:: python import random from psonic import * @@ -765,28 +765,28 @@ Now a simple structure with four live loops After starting the loops you can change them -.. code:: ipython3 +.. code-block:: python def live_1(): sample(BD_HAUS,amp=2) sleep(0.5) pass -.. code:: ipython3 +.. code-block:: python def live_2(): #sample(AMBI_CHOIR, rate=0.4) #sleep(1) pass -.. code:: ipython3 +.. code-block:: python def live_3(): use_synth(TB303) play(E2, release=4,cutoff=120,cutoff_attack=1) sleep(4) -.. code:: ipython3 +.. code-block:: python def live_4(): notes = scale(E3, MINOR_PENTATONIC, num_octaves=2) @@ -796,14 +796,14 @@ After starting the loops you can change them And stop. -.. code:: ipython3 +.. code-block:: python stop_event.set() Creating Sound ~~~~~~~~~~~~~~ -.. code:: ipython3 +.. code-block:: python from psonic import * @@ -811,13 +811,13 @@ Creating Sound synth(SQUARE, note=D4) synth(TRI, note=D4, amp=0.4) -.. code:: ipython3 +.. code-block:: python detune = 0.7 synth(SQUARE, note = E4) synth(SQUARE, note = E4+detune) -.. code:: ipython3 +.. code-block:: python detune=0.1 # Amplitude shaping synth(SQUARE, note = E2, release = 2) @@ -831,7 +831,7 @@ Next Step Using FX *Not implemented yet* -.. code:: ipython3 +.. code-block:: python from psonic import * @@ -839,7 +839,7 @@ Using FX *Not implemented yet* synth(PROPHET,note=E2,release=8,cutoff=80) synth(PROPHET,note=E2+4,release=8,cutoff=80) -.. code:: ipython3 +.. code-block:: python with Fx(SLICER, phase=0.125, probability=0.6,prob_pos=1): synth(TB303, note=E2, cutoff_attack=8, release=8) @@ -851,14 +851,14 @@ OSC Communication (Sonic Pi Ver. 3.x or better) In Sonic Pi version 3 or better you can work with messages. -.. code:: ipython3 +.. code-block:: python from psonic import * First you need a programm in the Sonic Pi server that receives messages. You can write it in th GUI or send one with Python. -.. code:: ipython3 +.. code-block:: python run("""live_loop :foo do use_real_time @@ -868,11 +868,11 @@ You can write it in th GUI or send one with Python. Now send a message to Sonic Pi. -.. code:: ipython3 +.. code-block:: python send_message('/trigger/prophet', 70, 100, 8) -.. code:: ipython3 +.. code-block:: python stop() @@ -881,11 +881,11 @@ Recording With python-sonic you can record wave files. -.. code:: ipython3 +.. code-block:: python from psonic import * -.. code:: ipython3 +.. code-block:: python # start recording start_recording() @@ -899,7 +899,7 @@ With python-sonic you can record wave files. play(chord(E4, DOM7)) sleep(1) -.. code:: ipython3 +.. code-block:: python # stop recording stop_recording @@ -913,7 +913,7 @@ With python-sonic you can record wave files. -.. code:: ipython3 +.. code-block:: python # save file save_recording('/Volumes/jupyter/python-sonic/test.wav') @@ -921,11 +921,11 @@ With python-sonic you can record wave files. More Examples ------------- -.. code:: ipython3 +.. code-block:: python from psonic import * -.. code:: ipython3 +.. code-block:: python #Inspired by Steve Reich Clapping Music diff --git a/setup.py b/setup.py index a6383c9..505e1bd 100755 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ here = path.abspath(path.dirname(__file__)) # Get the long description from the README file -with open(path.join(here, 'PREADME.txt'), encoding='utf-8') as f: +with open(path.join(here, 'README.rst'), encoding='utf-8') as f: long_description = f.read() requirements = [ @@ -29,7 +29,6 @@ version='0.4.2', description='Programming Music with Sonic Pi or Supercollider', long_description=long_description, - long_description_content_type='text/plain', url='https://github.com/gkvoelkl/python-sonic', author='gkvoelkl', author_email='gkvoelkl@nelson-games.de', From 18165fa7a86fd63e0192628e514d9eb90bee42c8 Mon Sep 17 00:00:00 2001 From: python-ninja-hebi Date: Mon, 30 Aug 2021 19:01:35 +0200 Subject: [PATCH 47/49] Version 0.4.3 --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 505e1bd..60250e6 100755 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup # To use a consistent encoding # python setup.py sdist bdist_wheel -# twine upload dist/* +# python -m twine upload dist/* from codecs import open from os import path @@ -26,7 +26,7 @@ setup( name='python-sonic', - version='0.4.2', + version='0.4.3', description='Programming Music with Sonic Pi or Supercollider', long_description=long_description, url='https://github.com/gkvoelkl/python-sonic', From 97dba79b9185e88152c8c21b6eccb97448bc1af7 Mon Sep 17 00:00:00 2001 From: Mickey Li Date: Sun, 19 Jan 2025 19:25:10 +0000 Subject: [PATCH 48/49] Support Sonic-Pi v4+ (#43) * Updated to support sonic-pi >v4 * Minor README fixes * Minor README fixes * Removes minor debugging changes * Added line about example in README --------- Co-authored-by: mickey li --- README.rst | 53 ++++++++++++++++++++++++++++++++++++++---- psonic/psonic.py | 30 ++++++++++++++++++++++-- psonic/synth_server.py | 26 ++++++++++----------- psonic_example.py | 6 +++++ setup.py | 2 +- 5 files changed, 96 insertions(+), 21 deletions(-) create mode 100644 psonic_example.py diff --git a/README.rst b/README.rst index 949f371..b83cecd 100755 --- a/README.rst +++ b/README.rst @@ -7,6 +7,9 @@ great music software created by Sam Aaron (http://sonic-pi.net). At the moment Python-Sonic works with Sonic Pi. It is planned, that it will work with Supercollider, too. +This version supports Sonic Pi versions > 4 when OSC run-code security +was added. + If you like it, use it. If you have some suggestions, tell me (gkvoelkl@nelson-games.de). @@ -26,6 +29,10 @@ $ pip install python-sonic That should work. +For local development you might want to locally install using + +$ pip install -e . + Limitations ----------- @@ -52,18 +59,56 @@ Changelog +--------+-------------------------------------------------------------+ | 0.4.0 | Changes communication ports and recording | +--------+-------------------------------------------------------------+ +| 0.4.4 | Enables GUI Token | ++--------+-------------------------------------------------------------+ Communication ------------- -| The API *python-sonic* communications with *Sonic Pi* over UDP and two - ports. One port is an internal *Sonic Pi* port and could be changed. -| For older *Sonic Pi* Version you have to set the ports explicitly +The API *python-sonic* communications with *Sonic Pi* over UDP and two +ports. One port is an internal *Sonic Pi* GUI port and the second is the +external OSC cue port. + +For >v4 a Token is needed to communicate with Sonic Pi. This token is generated +randomly at runtime when Sonic-Pi is started. The Token is extracted from the sonic-pi log files. +Similarly, the GUI udp port is now randomised at start and must be read from the log file too. +In order to play notes (and not just send OSC cues), a connection must be made to the GUI udp port +using the Token as the first argument in the message. The Token is automatically used on send. .. code-block:: python from psonic import * - set_server_parameter('127.0.0.1',4557,4559) + set_server_parameter('127.0.0.1', -2005799440, 30129, 4560) + + +These values are found from the file `~/.sonic-pi/log/spider.log` +:: + Sonic Pi Spider Server booting... + The time is 2023-10-01 11:01:41 +0100 + Using primary protocol: udp + Detecting port numbers... + Ports: {:server_port=>30129, :gui_port=>30130, :scsynth_port=>30131, :scsynth_send_port=>30131, :osc_cues_port=>4560, :tau_port=>30132, :listen_to_tau_port=>30136} + Token: -2005799440 + Opening UDP Server to listen to GUI on port: 30129 + Spider - Pulling in modules... + Spider - Starting Runtime Server + ... + +This can be automated by using the function `set_server_parameter_from_log` + +.. code-block:: python + + from psonic import * + set_server_parameter_from_log('127.0.0.1') + set_server_parameter_from_log('127.0.0.1', "path-to-log-file") + +Note if the `set_server_parameter` functions are not used, a default connection is created which will not work. + +There is a simple example file `psonic_example.py` which you can run to check that things work. First open sonic-pi, then run the following: + +$ python psonic_example.py + +and a note should be played. Examples -------- diff --git a/psonic/psonic.py b/psonic/psonic.py index d38e856..5606525 100755 --- a/psonic/psonic.py +++ b/psonic/psonic.py @@ -1,3 +1,5 @@ +import os +import re import random from .samples import Sample from .synthesizers import SAW @@ -180,9 +182,33 @@ def save_recording(name): synth_server = SonicPi() -def set_server_parameter(udp_ip="", udp_port=-1, udp_port_osc_message=-1): - synth_server.set_parameter(udp_ip, udp_port, udp_port_osc_message) +def set_server_parameter(udp_ip, token, udp_port=-1, udp_port_osc_message=-1): + synth_server.set_parameter(udp_ip, token, udp_port, udp_port_osc_message) +def set_server_parameter_from_log(udp_ip, log_file=None): + if log_file is None: + # Automatically find the log file + home = os.path.expanduser("~") + log_file = os.path.join(home, ".sonic-pi", "log", "spider.log") + print(f"Reading Parameters From Log File: {log_file}") + + with open(log_file, 'r') as f: + text = f.read() + + # Use regular expressions to extract the values + token_match = re.search(r'Token: (-?\d+)', text) + server_port_match = re.search(r':server_port=>(\d+)', text) + osc_cues_port_match = re.search(r':osc_cues_port=>(\d+)', text) + + token = int(token_match.group(1)) if token_match else None + udp_port = int(server_port_match.group(1)) if server_port_match else None + udp_port_osc_message = int(osc_cues_port_match.group(1)) if osc_cues_port_match else None + + if token and udp_port and udp_port_osc_message: + print(f"Reading Parameters Succeeded. ip: {udp_ip}, token: \"{token}\", server_port: \"{udp_port}\", osc_port: {udp_port_osc_message}") + synth_server.set_parameter(udp_ip, token, udp_port, udp_port_osc_message) + else: + raise RuntimeError(f"Reading Parameters Failed. token: {token}, server_port: {udp_port}, osc_port: {udp_port_osc_message}") def _debug(*args): if __debug: print(args) diff --git a/psonic/synth_server.py b/psonic/synth_server.py index 353e97f..732a104 100755 --- a/psonic/synth_server.py +++ b/psonic/synth_server.py @@ -37,16 +37,11 @@ def sleep(self, duration): def sample(self, command): self.send(command) -## Ports could be find in home ./sonic-pi/log/server-output.log -# Version 3.2.0 -# Listen port: 51235 4557 -# Scsynth port: 51237 4556 -# Scsynth send port: 51237 4556 -# OSC cues port: 4560 4559 -# Erlang port: 51240 4560 -# OSC MIDI out port: 51238 4561 -# OSC MIDI in port: 51239 4562 -# Websocket port: 51241 +## Sonic Pi version 4.X: Ports can be found in +# Windows: C:/Users//.sonic-pi/log/spider.log +# Linux: ~/.sonic-pi/log/spider.log +# Both the server_port and Token are required +# Default OSC Cues Port is still 4560 ## Connection classes ## class SonicPi(SonicPiCommon): @@ -57,7 +52,6 @@ class SonicPi(SonicPiCommon): #UDP_PORT_OSC_MESSAGE = 4559 UDP_PORT_OSC_MESSAGE = 4560 - GUI_ID = 'SONIC_PI_PYTHON' RUN_COMMAND = "/run-code" STOP_COMMAND = "/stop-all-jobs" @@ -69,6 +63,7 @@ def __init__(self): super().__init__() self.udp_port = self.UDP_PORT self.udp_port_osc_message = self.UDP_PORT_OSC_MESSAGE + self.token=None self._init_client() @@ -82,17 +77,18 @@ def _init_client(self): self.udp_port_osc_message ) - def set_parameter(self, udp_ip = "", udp_port=-1, udp_port_osc_message=-1): + def set_parameter(self, udp_ip = "", token = "", udp_port=-1, udp_port_osc_message=-1): super().set_parameter(udp_ip) if udp_port == -1: udp_port = self.UDP_PORT self.udp_port = udp_port + self.token=token if udp_port_osc_message == -1: udp_port_osc_message = self.UDP_PORT_OSC_MESSAGE self.udp_port_osc_message = udp_port_osc_message self._init_client() def play(self, command): - command = 'use_synth :{0}\n'.format(_current_synth.name) + command + command = f'use_synth :{_current_synth.name}\n{command}' self.send(command) def synth(self, command): @@ -124,7 +120,9 @@ def test_connection(self): def send_command(self, address, argument=''): msg = osc_message_builder.OscMessageBuilder(address=address) - msg.add_arg('SONIC_PI_PYTHON') + if self.token is None: + raise RuntimeError("No token specified, please set token from file or manually before sending a command") + msg.add_arg(self.token) if argument != "": msg.add_arg(argument) msg = msg.build() diff --git a/psonic_example.py b/psonic_example.py new file mode 100644 index 0000000..c702942 --- /dev/null +++ b/psonic_example.py @@ -0,0 +1,6 @@ +from psonic import * + +set_server_parameter_from_log("127.0.0.1") +run("play 60") + + diff --git a/setup.py b/setup.py index 60250e6..597a890 100755 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ setup( name='python-sonic', - version='0.4.3', + version='0.4.4', description='Programming Music with Sonic Pi or Supercollider', long_description=long_description, url='https://github.com/gkvoelkl/python-sonic', From 5a0e873c532a3847e59b15e1f7041f8511e462ac Mon Sep 17 00:00:00 2001 From: Tristram Oaten Date: Sun, 19 Jan 2025 19:25:36 +0000 Subject: [PATCH 49/49] Formatted install instructions (#28) The install instructions seemed confusing to me. I also did a few driveby spelling fixes. Thanks! --- README.rst | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/README.rst b/README.rst index b83cecd..1fa44ea 100755 --- a/README.rst +++ b/README.rst @@ -21,13 +21,7 @@ Installation - Then Sonic Pi (https://sonic-pi.net) - That makes the sound - Modul python-osc (https://pypi.python.org/pypi/python-osc) - Connection between Python and Sonic Pi Server -- And this modul python-sonic - simply copy the source - -Or try - -$ pip install python-sonic - -That should work. +- Add the module `python-sonic` - simply copy the source, or try `pip install python-sonic` That should work. For local development you might want to locally install using