{ "info": { "author": "gkvoelkl", "author_email": "gkvoelkl@nelson-games.de", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Multimedia :: Sound/Audio" ], "description": "\npython-sonic - Programming Music with Python, Sonic Pi or Supercollider\n=======================================================================\n\nPython-Sonic is a simple Python interface for Sonic Pi, which is a real\ngreat music software created by Sam Aaron (http://sonic-pi.net).\n\nAt the moment Python-Sonic works with Sonic Pi. It is planned, that it\nwill work with Supercollider, too.\n\nIf you like it, use it. If you have some suggestions, tell me\n(gkvoelkl@nelson-games.de).\n\nInstallation\n------------\n\n- First you need Python 3 (https://www.python.org, ) - Python 3.5\n 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) -\n Connection between Python and Sonic Pi Server\n- And this modul python-sonic - simply copy the source\n\nOr try\n\n$ pip install python-sonic\n\nThat should work.\n\nLimitations\n-----------\n\n- You have to start *Sonic Pi* first before you can use it with\n python-sonic\n- Only the notes from C2 to C6\n\nChangelog\n---------\n\n+------------+---------------------------------------------------------------+\n| Version | |\n+============+===============================================================+\n| 0.2.0 | Some changes for Sonic Pi 2.11. Simpler multi-threading with |\n| | decorator *@in\\_thread*. Messaging with *cue* and *sync*. |\n+------------+---------------------------------------------------------------+\n| 0.3.0 | OSC Communication |\n+------------+---------------------------------------------------------------+\n\nExamples\n--------\n\nMany of the examples are inspired from the help menu in *Sonic Pi*.\n\n.. code:: ipython3\n\n from psonic import *\n\nThe first sound\n\n.. code:: ipython3\n\n play(70) #play MIDI note 70\n\nSome more notes\n\n.. code:: ipython3\n\n play(72)\n sleep(1)\n play(75)\n sleep(1)\n play(79) \n\nIn more tratitional music notation\n\n.. code:: ipython3\n\n play(C5)\n sleep(0.5)\n play(D5)\n sleep(0.5)\n play(G5) \n\nPlay sharp notes like *F#* or dimished ones like *Eb*\n\n.. code:: ipython3\n\n play(Fs5)\n sleep(0.5)\n play(Eb5)\n\nPlay louder (parameter amp) or from a different direction (parameter\npan)\n\n.. code:: ipython3\n\n play(72,amp=2)\n sleep(0.5)\n play(74,pan=-1) #left\n\nDifferent synthesizer sounds\n\n.. code:: ipython3\n\n use_synth(SAW)\n play(38)\n sleep(0.25)\n play(50)\n sleep(0.5)\n use_synth(PROPHET)\n play(57)\n sleep(0.25)\n\nADSR *(Attack, Decay, Sustain and Release)* Envelope\n\n.. code:: ipython3\n\n play (60, attack=0.5, decay=1, sustain_level=0.4, sustain=2, release=0.5) \n sleep(4)\n\nPlay some samples\n\n.. code:: ipython3\n\n sample(AMBI_LUNAR_LAND, amp=0.5)\n\n.. code:: ipython3\n\n sample(LOOP_AMEN,pan=-1)\n sleep(0.877)\n sample(LOOP_AMEN,pan=1)\n\n.. code:: ipython3\n\n sample(LOOP_AMEN,rate=0.5)\n\n.. code:: ipython3\n\n sample(LOOP_AMEN,rate=1.5)\n\n.. code:: ipython3\n\n sample(LOOP_AMEN,rate=-1)#back\n\n.. code:: ipython3\n\n sample(DRUM_CYMBAL_OPEN,attack=0.01,sustain=0.3,release=0.1)\n\n.. code:: ipython3\n\n sample(LOOP_AMEN,start=0.5,finish=0.8,rate=-0.2,attack=0.3,release=1)\n\nPlay some random notes\n\n.. code:: ipython3\n\n import random\n\n for i in range(5):\n play(random.randrange(50, 100))\n sleep(0.5)\n\n.. code:: ipython3\n\n for i in range(3):\n play(random.choice([C5,E5,G5]))\n sleep(1)\n\nSample slicing\n\n.. code:: ipython3\n\n 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)\n\nAn infinite loop and if\n\n.. code:: ipython3\n\n while True:\n if one_in(2):\n sample(DRUM_HEAVY_KICK)\n sleep(0.5)\n else:\n sample(DRUM_CYMBAL_CLOSED)\n sleep(0.25)\n\n\n::\n\n\n ---------------------------------------------------------------------------\n\n KeyboardInterrupt Traceback (most recent call last)\n\n in ()\n 5 else:\n 6 sample(DRUM_CYMBAL_CLOSED)\n ----> 7 sleep(0.25)\n\n\n /mnt/jupyter/python-sonic/psonic.py in sleep(duration)\n 587 :return:\n 588 \"\"\"\n --> 589 time.sleep(duration)\n 590 _debug('sleep', duration)\n 591 \n\n\n KeyboardInterrupt: \n\n\nIf you want to hear more than one sound at a time, use Threads.\n\n.. code:: ipython3\n\n import random\n from psonic import *\n from threading import Thread\n\n def bass_sound():\n c = chord(E3, MAJOR7)\n while True:\n use_synth(PROPHET)\n play(random.choice(c), release=0.6)\n sleep(0.5)\n\n def snare_sound():\n while True:\n sample(ELEC_SNARE)\n sleep(1)\n\n bass_thread = Thread(target=bass_sound)\n snare_thread = Thread(target=snare_sound)\n\n bass_thread.start()\n snare_thread.start()\n\n while True:\n pass\n\nEvery function *bass\\_sound* and *snare\\_sound* have its own thread.\nYour can hear them running.\n\n.. code:: ipython3\n\n from psonic import *\n from threading import Thread, Condition\n from random import choice\n\n def random_riff(condition):\n use_synth(PROPHET)\n sc = scale(E3, MINOR)\n while True:\n s = random.choice([0.125,0.25,0.5])\n with condition:\n condition.wait() #Wait for message\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 def drums(condition):\n while True:\n with condition:\n condition.notifyAll() #Message to threads\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 condition = Condition()\n random_riff_thread = Thread(name='consumer1', target=random_riff, args=(condition,))\n drums_thread = Thread(name='producer', target=drums, args=(condition,))\n\n random_riff_thread.start()\n drums_thread.start()\n\n input(\"Press Enter to continue...\")\n\n\n.. parsed-literal::\n\n Press Enter to continue...\n\n\n\n\n.. parsed-literal::\n\n ''\n\n\n\nTo synchronize the thread, so that they play a note at the same time,\nyou can use *Condition*. One function sends a message with\n*condition.notifyAll* the other waits until the message comes\n*condition.wait*.\n\nMore simple with decorator \\_\\_@in\\_thread\\_\\_\n\n.. code:: ipython3\n\n 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...\")\n\n\n.. parsed-literal::\n\n Press Enter to continue...\n\n\n\n\n.. parsed-literal::\n\n ''\n\n\n\n.. code:: ipython3\n\n 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\n\nPlay a list of notes\n\n.. code:: ipython3\n\n from psonic import *\n\n play ([64, 67, 71], amp = 0.3) \n sleep(1)\n play ([E4, G4, B4])\n sleep(1)\n\nPlay chords\n\n.. code:: ipython3\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)\n\nPlay arpeggios\n\n.. code:: ipython3\n\n play_pattern( chord(E4, 'm7')) \n play_pattern_timed( chord(E4, 'm7'), 0.25) \n play_pattern_timed(chord(E4, 'dim'), [0.25, 0.5]) \n\nPlay scales\n\n.. code:: ipython3\n\n play_pattern_timed(scale(C3, MAJOR), 0.125, release = 0.1) \n play_pattern_timed(scale(C3, MAJOR, num_octaves = 2), 0.125, release = 0.1) \n play_pattern_timed(scale(C3, MAJOR_PENTATONIC, num_octaves = 2), 0.125, release = 0.1)\n\nThe function *scale* returns a list with all notes of a scale. So you\ncan use list methodes or functions. For example to play arpeggios\ndescending or shuffeld.\n\n.. code:: ipython3\n\n import random\n from psonic import *\n\n s = scale(C3, MAJOR)\n s\n\n\n\n\n.. parsed-literal::\n\n [48, 50, 52, 53, 55, 57, 59, 60]\n\n\n\n.. code:: ipython3\n\n s.reverse()\n\n.. code:: ipython3\n\n\n play_pattern_timed(s, 0.125, release = 0.1)\n random.shuffle(s)\n play_pattern_timed(s, 0.125, release = 0.1)\n\nLive Loop\n~~~~~~~~~\n\nOne of the best in SONIC PI is the *Live Loop*. While a loop is playing\nmusic you can change it and hear the change. Let's try it in Python,\ntoo.\n\n.. code:: ipython3\n\n from psonic import *\n from threading import Thread\n\n def my_loop():\n play(60)\n sleep(1)\n\n def looper():\n while True:\n my_loop()\n\n looper_thread = Thread(name='looper', target=looper)\n\n looper_thread.start()\n\n input(\"Press Enter to continue...\")\n\n\n.. parsed-literal::\n\n Press Enter to continue...Y\n\n\n\n\n.. parsed-literal::\n\n 'Y'\n\n\n\nNow change the function *my\\_loop* und you can hear it.\n\n.. code:: ipython3\n\n def my_loop():\n use_synth(TB303)\n play (60, release= 0.3)\n sleep (0.25)\n\n.. code:: ipython3\n\n def my_loop():\n use_synth(TB303)\n play (chord(E3, MINOR), release= 0.3)\n sleep(0.5)\n\n.. code:: ipython3\n\n def my_loop():\n use_synth(TB303)\n sample(DRUM_BASS_HARD, rate = random.uniform(0.5, 2))\n play(random.choice(chord(E3, MINOR)), release= 0.2, cutoff=random.randrange(60, 130))\n sleep(0.25)\n\nTo stop the sound you have to end the kernel. In IPython with Kernel -->\nRestart\n\nNow with two live loops which are synch.\n\n.. code:: ipython3\n\n from psonic import *\n from threading import Thread, Condition\n from random import choice\n\n def loop_foo():\n play (E4, release = 0.5)\n sleep (0.5)\n\n\n def loop_bar():\n sample (DRUM_SNARE_SOFT)\n sleep (1)\n\n\n def live_loop_1(condition):\n while True:\n with condition:\n condition.notifyAll() #Message to threads\n loop_foo()\n\n def live_loop_2(condition):\n while True:\n with condition:\n condition.wait() #Wait for message\n loop_bar()\n\n condition = Condition()\n live_thread_1 = Thread(name='producer', target=live_loop_1, args=(condition,))\n live_thread_2 = Thread(name='consumer1', target=live_loop_2, args=(condition,))\n\n live_thread_1.start()\n live_thread_2.start()\n\n input(\"Press Enter to continue...\")\n\n\n.. parsed-literal::\n\n Press Enter to continue...y\n\n\n\n\n.. parsed-literal::\n\n 'y'\n\n\n\n.. code:: ipython3\n\n def loop_foo():\n play (A4, release = 0.5)\n sleep (0.5)\n\n.. code:: ipython3\n\n def loop_bar():\n sample (DRUM_HEAVY_KICK)\n sleep (0.125)\n\nIf would be nice if we can stop the loop with a simple command. With\nstop event it works.\n\n.. code:: ipython3\n\n from psonic import *\n from threading import Thread, Condition, Event\n\n def loop_foo():\n play (E4, release = 0.5)\n sleep (0.5)\n\n\n def loop_bar():\n sample (DRUM_SNARE_SOFT)\n sleep (1)\n\n\n def live_loop_1(condition,stop_event):\n while not stop_event.is_set():\n with condition:\n condition.notifyAll() #Message to threads\n loop_foo()\n\n def live_loop_2(condition,stop_event):\n while not stop_event.is_set():\n with condition:\n condition.wait() #Wait for message\n loop_bar()\n\n\n\n condition = Condition()\n stop_event = Event()\n live_thread_1 = Thread(name='producer', target=live_loop_1, args=(condition,stop_event))\n live_thread_2 = Thread(name='consumer1', target=live_loop_2, args=(condition,stop_event))\n\n\n live_thread_1.start()\n live_thread_2.start()\n\n input(\"Press Enter to continue...\")\n\n\n.. parsed-literal::\n\n Press Enter to continue...y\n\n\n\n\n.. parsed-literal::\n\n 'y'\n\n\n\n.. code:: ipython3\n\n stop_event.set()\n\nMore complex live loops\n\n.. code:: ipython3\n\n sc = Ring(scale(E3, MINOR_PENTATONIC))\n\n def loop_foo():\n play (next(sc), release= 0.1)\n sleep (0.125)\n\n sc2 = Ring(scale(E3,MINOR_PENTATONIC,num_octaves=2))\n\n def loop_bar():\n use_synth(DSAW)\n play (next(sc2), release= 0.25)\n sleep (0.25)\n\nNow a simple structure with four live loops\n\n.. code:: ipython3\n\n import random\n from psonic import *\n from threading import Thread, Condition, Event\n\n def live_1():\n pass\n\n def live_2():\n pass\n\n def live_3():\n pass\n\n def live_4():\n pass\n\n def live_loop_1(condition,stop_event):\n while not stop_event.is_set():\n with condition:\n condition.notifyAll() #Message to threads\n live_1()\n\n def live_loop_2(condition,stop_event):\n while not stop_event.is_set():\n with condition:\n condition.wait() #Wait for message\n live_2()\n\n def live_loop_3(condition,stop_event):\n while not stop_event.is_set():\n with condition:\n condition.wait() #Wait for message\n live_3()\n\n def live_loop_4(condition,stop_event):\n while not stop_event.is_set():\n with condition:\n condition.wait() #Wait for message\n live_4()\n\n condition = Condition()\n stop_event = Event()\n live_thread_1 = Thread(name='producer', target=live_loop_1, args=(condition,stop_event))\n live_thread_2 = Thread(name='consumer1', target=live_loop_2, args=(condition,stop_event))\n live_thread_3 = Thread(name='consumer2', target=live_loop_3, args=(condition,stop_event))\n live_thread_4 = Thread(name='consumer3', target=live_loop_3, args=(condition,stop_event))\n\n live_thread_1.start()\n live_thread_2.start()\n live_thread_3.start()\n live_thread_4.start()\n\n input(\"Press Enter to continue...\")\n\n\n.. parsed-literal::\n\n Press Enter to continue...y\n\n\n\n\n.. parsed-literal::\n\n 'y'\n\n\n\nAfter starting the loops you can change them\n\n.. code:: ipython3\n\n def live_1():\n sample(BD_HAUS,amp=2)\n sleep(0.5)\n pass\n\n.. code:: ipython3\n\n def live_2():\n #sample(AMBI_CHOIR, rate=0.4)\n #sleep(1)\n pass\n\n.. code:: ipython3\n\n def live_3():\n use_synth(TB303)\n play(E2, release=4,cutoff=120,cutoff_attack=1)\n sleep(4)\n\n.. code:: ipython3\n\n def live_4():\n notes = scale(E3, MINOR_PENTATONIC, num_octaves=2)\n for i in range(8):\n play(random.choice(notes),release=0.1,amp=1.5)\n sleep(0.125)\n\nAnd stop.\n\n.. code:: ipython3\n\n stop_event.set()\n\nCreating Sound\n~~~~~~~~~~~~~~\n\n.. code:: ipython3\n\n from psonic import *\n\n synth(SINE, note=D4)\n synth(SQUARE, note=D4)\n synth(TRI, note=D4, amp=0.4)\n\n.. code:: ipython3\n\n detune = 0.7\n synth(SQUARE, note = E4)\n synth(SQUARE, note = E4+detune)\n\n.. code:: ipython3\n\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)\n\nNext Step\n~~~~~~~~~\n\nUsing FX *Not implemented yet*\n\n.. code:: ipython3\n\n from psonic import *\n\n with Fx(SLICER):\n synth(PROPHET,note=E2,release=8,cutoff=80)\n synth(PROPHET,note=E2+4,release=8,cutoff=80)\n\n.. code:: ipython3\n\n 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)\n\nOSC Communication (Sonic Pi Ver. 3.x or better)\n-----------------------------------------------\n\nIn Sonic Pi version 3 or better you can work with messages.\n\n.. code:: ipython3\n\n from psonic import *\n\nFirst you need a programm in the Sonic Pi server that receives messages.\nYou can write it in th GUI or send one with Python.\n\n.. code:: ipython3\n\n 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 \"\"\")\n\nNow send a message to Sonic Pi.\n\n.. code:: ipython3\n\n send_message('/trigger/prophet', 70, 100, 8)\n\n.. code:: ipython3\n\n stop()\n\nMore Examples\n-------------\n\n.. code:: ipython3\n\n from psonic import *\n\n.. code:: ipython3\n\n #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)\n\nProjects that use Python-Sonic\n------------------------------\n\nRaspberry Pi sonic-track.py a Sonic-pi Motion Track Demo\nhttps://github.com/pageauc/sonic-track\n\nSources\n-------\n\nJoe Armstrong: Connecting Erlang to the Sonic Pi\nhttp://joearms.github.io/2015/01/05/Connecting-Erlang-to-Sonic-Pi.html\n\nJoe Armstrong: Controlling Sound with OSC Messages\nhttp://joearms.github.io/2016/01/29/Controlling-Sound-with-OSC-Messages.html\n\n..\n\n\n", "description_content_type": null, "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/gkvoelkl/python-sonic", "keywords": "music,sonic pi,raspberry pi,audio,music composition,scsynth,supercollider,synthesis", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "python-sonic", "package_url": "https://pypi.org/project/python-sonic/", "platform": "", "project_url": "https://pypi.org/project/python-sonic/", "project_urls": { "Homepage": "https://github.com/gkvoelkl/python-sonic" }, "release_url": "https://pypi.org/project/python-sonic/0.3.0/", "requires_dist": [ "python-osc" ], "requires_python": "", "summary": "Programming Music with Sonic Pi or Supercollider", "version": "0.3.0" }, "last_serial": 3078914, "releases": { "0.2.0": [ { "comment_text": "", "digests": { "md5": "37b30be0da8198538ec69d531d2d5109", "sha256": "7274a5cbc30ce05f255fbe0a07d262e56b898164ee1b554899b842df72c03e04" }, "downloads": -1, "filename": "python_sonic-0.2.0-py3-none-any.whl", "has_sig": false, "md5_digest": "37b30be0da8198538ec69d531d2d5109", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 16621, "upload_time": "2016-12-04T16:36:40", "url": "https://files.pythonhosted.org/packages/8e/64/f62de71f25b73a4cf4f7ecc32a25a1b1a5f57c6c0611eadc4f3757dd99d8/python_sonic-0.2.0-py3-none-any.whl" } ], "0.3.0": [ { "comment_text": "", "digests": { "md5": "c6d92797451e8b81780fcd21da5ca712", "sha256": "272c3c81f8ab0c2efcb1a6fcdafb5f8aa0a76472097df43c3354f7b656f2a5e5" }, "downloads": -1, "filename": "python_sonic-0.3.0-py3-none-any.whl", "has_sig": false, "md5_digest": "c6d92797451e8b81780fcd21da5ca712", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 18429, "upload_time": "2017-08-07T16:43:52", "url": "https://files.pythonhosted.org/packages/99/08/1b8efcf71866ac8e4585dd24594f9c92a6469358613592ce752e8fcb8188/python_sonic-0.3.0-py3-none-any.whl" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "c6d92797451e8b81780fcd21da5ca712", "sha256": "272c3c81f8ab0c2efcb1a6fcdafb5f8aa0a76472097df43c3354f7b656f2a5e5" }, "downloads": -1, "filename": "python_sonic-0.3.0-py3-none-any.whl", "has_sig": false, "md5_digest": "c6d92797451e8b81780fcd21da5ca712", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 18429, "upload_time": "2017-08-07T16:43:52", "url": "https://files.pythonhosted.org/packages/99/08/1b8efcf71866ac8e4585dd24594f9c92a6469358613592ce752e8fcb8188/python_sonic-0.3.0-py3-none-any.whl" } ] }