├── .build.yml ├── .gitattributes ├── .gitignore ├── LICENSE.txt ├── README.md ├── bugs └── hanging_fork.py ├── buildDocs ├── docs ├── _static │ ├── KadenzeThumbnail.png │ ├── ScampLogo.svg │ └── css │ │ └── custom.css ├── _templates │ └── autosummary │ │ ├── base.rst │ │ ├── class.rst │ │ └── module.rst ├── conf.py ├── index.rst ├── makePackageRSTs.py └── narrative │ ├── AlternateVersionAbjad.png │ ├── InstallingSCAMP.png │ ├── KadenzeThumbnail.png │ ├── LilyWarning.png │ ├── OpenLilyPondAnyway.png │ ├── PropertiesExample.png │ ├── SecuritySettings.png │ ├── SelectVersionAbjad.png │ ├── VoicesExample.png │ ├── WindowsInstallSCAMP.png │ ├── WindowsInstallingThonny.png │ ├── WindowsInstallingThonny2.png │ ├── WindowsLilypondUnnerving.png │ ├── WindowsManagePackages.png │ ├── easy_setup.rst │ ├── experienced_setup.rst │ ├── forum.rst │ ├── kadenze.rst │ ├── music.rst │ ├── note_properties.rst │ ├── support.rst │ ├── tutorial_videos.rst │ ├── type_aliases.rst │ └── youtube.rst ├── examples ├── AssortedHaphazard │ ├── GuitarArpeggios.py │ ├── NotationVSPerformance.py │ ├── OldTutorialExamples │ │ ├── articulations.py │ │ ├── hello_world.py │ │ ├── multipleTempos.py │ │ ├── note_spelling.py │ │ └── simple_polytempo.py │ ├── SavedFiles │ │ ├── perfShakoboe.json │ │ ├── quantized_glisses.json │ │ ├── savedEnsemble.json │ │ └── shakEnsemble.json │ ├── bananaphone.py │ ├── bunchStuff.py │ ├── chords_example.py │ ├── conway.py │ ├── double_score.py │ ├── ensembleExample.py │ ├── fifteen_sixteen.py │ ├── glissTest2.py │ ├── glissando_test.py │ ├── indispensibility.py │ ├── karlclock.py │ ├── karlclock_interactive.py │ ├── key_clocks.py │ ├── key_sig.py │ ├── load_and_play_performance.py │ ├── load_performance_and_export_midi.py │ ├── midi_channel_manager.py │ ├── midi_export.mid │ ├── multiple_score_example.py │ ├── osc │ │ ├── OSCListenerPython.scd │ │ └── osc_instrument_example.py │ ├── qt_interactive.py │ ├── quantization.py │ ├── repeated_notes.py │ ├── save_performance.py │ ├── tempo_test.py │ ├── ticker_test.py │ ├── voice-leading.py │ └── voice-leading_weird.py ├── Demos │ ├── KeyboardMapper │ │ └── KeyboardMapper.py │ ├── ScampCooking │ │ ├── Crackler.scd │ │ ├── ScampCooking.py │ │ ├── definitions.py │ │ └── formal_parameters.py │ └── ScampToMax │ │ ├── Monophonic │ │ ├── MonoMain.maxpat │ │ ├── MonoSender.py │ │ ├── MonoSynth.maxpat │ │ └── scampreceive.maxpat │ │ └── Polyphonic │ │ ├── PolyMain.maxpat │ │ ├── PolySender.py │ │ ├── PolySynth.maxpat │ │ └── scampreceive.maxpat ├── Reconstructions │ └── piano_phase.py ├── Tutorial │ ├── 01_hello_world.py │ ├── 02_tempo_change.py │ ├── 03_blocking_false.py │ ├── 04_random_durations_and_rests.py │ ├── 05_notation.py │ ├── 06_time_signature.py │ ├── 07_random_quantized.py │ ├── 08_simplified_quantization.py │ ├── 09_multi_part.py │ ├── 10_multi_tempo.py │ ├── 11_record_on_clock.py │ ├── 12_envelopes_basic.py │ ├── 13_envelopes_advanced.py │ ├── 14_gliss_from_envelope.py │ ├── 15_envelope_shorthand.py │ ├── 16_start_and_end_note.py │ ├── 17_volume_envelope.py │ ├── 18_microtonal.py │ ├── 19_playback_implementations.py │ ├── 20_note_properties.py │ ├── 21_pitch_spelling.py │ ├── 22_staff_text.py │ ├── 23_special_notations.py │ ├── 24_osc_to_supercollider.py │ ├── 24_osc_to_supercollider.scd │ ├── 25_MIDI_in_out.py │ ├── 26_keyboard_input.py │ ├── 27_mouse_input.py │ ├── 28a_osc_listener.py │ ├── 28b_osc_sender.py │ └── 29_spanners.py └── marc │ ├── barlicity.py │ └── lunar_trajectories.py ├── roadmap ├── Roadmap 1.0.txt └── Roadmap 2.0.txt ├── scamp ├── __init__.py ├── __pyinstaller │ ├── __init__.py │ └── hook-scamp.py ├── _dependencies.py ├── _engraving_translations.py ├── _metric_structure.py ├── _midi.py ├── _parsing.py ├── _soundfont_host.py ├── _thirdparty │ ├── __init__.py │ ├── fluidsynth.py │ ├── mac_libs │ │ ├── libFLAC.8.dylib │ │ ├── libfluidsynth.3.0.1.dylib │ │ ├── libglib-2.0.0.dylib │ │ ├── libgthread-2.0.0.dylib │ │ ├── libintl.8.dylib │ │ ├── libogg.0.dylib │ │ ├── libopus.0.dylib │ │ ├── libpcre.1.dylib │ │ ├── libportaudio.2.dylib │ │ ├── libreadline.8.dylib │ │ ├── libsndfile.1.dylib │ │ ├── libvorbis.0.dylib │ │ └── libvorbisenc.2.dylib │ ├── sf2or3utils │ │ ├── __init__.py │ │ ├── bag.py │ │ ├── generator.py │ │ ├── instrument.py │ │ ├── modulator.py │ │ ├── preset.py │ │ ├── riffparser.py │ │ ├── sample.py │ │ ├── sf2parse.py │ │ └── tests │ │ │ ├── __init__.py │ │ │ └── test_parse.py │ └── windows_libs │ │ ├── libfluidsynth.dll │ │ └── libfluidsynth64.dll ├── instruments.py ├── lilypond │ └── scamp_template.ly ├── note_properties.py ├── performance.py ├── playback_adjustments.py ├── playback_implementations.py ├── quantization.py ├── score.py ├── session.py ├── settings.py ├── soundfonts │ └── Merlin.sf2 ├── spanners.py ├── spelling.py ├── test_run.py ├── text.py ├── transcriber.py └── utilities.py ├── setup.cfg ├── setup.py └── test ├── example_tests ├── AssortedHaphazard │ ├── bananaphone.json │ ├── bananaphone.py │ ├── chords_example.json │ ├── chords_example.py │ ├── double_score.json │ ├── double_score.py │ ├── fifteen_sixteen.json │ └── fifteen_sixteen.py ├── Test │ ├── clock_order.json │ └── clock_order.py └── Tutorial │ ├── 10_multi_tempo.json │ ├── 10_multi_tempo.py │ ├── 11_record_on_clock.json │ ├── 11_record_on_clock.py │ ├── 12_envelopes_basic.json │ ├── 12_envelopes_basic.py │ ├── 13_envelopes_advanced.json │ ├── 13_envelopes_advanced.py │ ├── 14_gliss_from_envelope.json │ ├── 14_gliss_from_envelope.py │ ├── 15_envelope_shorthand.json │ ├── 15_envelope_shorthand.py │ ├── 16_start_and_end_note.json │ ├── 16_start_and_end_note.py │ ├── 17_volume_envelope.json │ ├── 17_volume_envelope.py │ ├── 18_microtonal.json │ ├── 18_microtonal.py │ ├── 19_playback_implementations.json │ ├── 19_playback_implementations.py │ ├── 1_hello_world.json │ ├── 1_hello_world.py │ ├── 20_note_properties.json │ ├── 20_note_properties.py │ ├── 21_pitch_spelling.json │ ├── 21_pitch_spelling.py │ ├── 22_staff_text.json │ ├── 22_staff_text.py │ ├── 23_special_notations.json │ ├── 23_special_notations.py │ ├── 24_osc_to_supercollider.json │ ├── 24_osc_to_supercollider.py │ ├── 29_spanners.json │ ├── 29_spanners.py │ ├── 2_tempo_change.json │ ├── 2_tempo_change.py │ ├── 3_blocking_false.json │ ├── 3_blocking_false.py │ ├── 4_random_durations_and_rests.json │ ├── 4_random_durations_and_rests.py │ ├── 5_notation.json │ ├── 5_notation.py │ ├── 6_time_signature.json │ ├── 6_time_signature.py │ ├── 7_random_quantized.json │ ├── 7_random_quantized.py │ ├── 8_simplified_quantization.json │ ├── 8_simplified_quantization.py │ ├── 9_multi_part.json │ └── 9_multi_part.py └── test_examples.py /.build.yml: -------------------------------------------------------------------------------- 1 | image: debian/stable 2 | sources: 3 | - https://git.sr.ht/~marcevanstein/scamp 4 | environment: 5 | GIT_SSH_COMMAND: "ssh -o StrictHostKeyChecking=no" 6 | secrets: 7 | - 9daf0a17-ac84-4185-8851-3464007a3615 8 | tasks: 9 | - mirror: | 10 | cd scamp 11 | git remote add github git@github.com:MarcTheSpark/scamp.git 12 | git push github --all --follow-tags -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sf* filter=lfs diff=lfs merge=lfs -text 2 | *.tar.gz filter=lfs diff=lfs merge=lfs -text 3 | *.sf2 filter=lfs diff=lfs merge=lfs -text 4 | *.dll filter=lfs diff=lfs merge=lfs -text 5 | *.jpg filter=lfs diff=lfs merge=lfs -text 6 | *.png filter=lfs diff=lfs merge=lfs -text 7 | *.dylib filter=lfs diff=lfs merge=lfs -text 8 | *.svg filter=lfs diff=lfs merge=lfs -text 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .idea 3 | build 4 | dist 5 | *.egg-info 6 | *__pycache__ 7 | docs/build 8 | docs/make.bat 9 | docs/Makefile 10 | docs/scamp.rst 11 | docs/clockblocks.rst 12 | docs/expenvelope.rst 13 | docs/pymusicxml.rst 14 | docs/scamp 15 | docs/clockblocks 16 | docs/expenvelope 17 | docs/pymusicxml 18 | docs/scamp_extensions.* -------------------------------------------------------------------------------- /bugs/hanging_fork.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | 19 | s = Session() 20 | 21 | clarinet = s.new_part("clarinet") 22 | 23 | 24 | def outer_fork(): 25 | def inner_fork(): 26 | clarinet.play_note(60, [0.5, 1, 0], 3) 27 | fork(inner_fork) 28 | # wait_for_children_to_finish() # works with this 29 | wait(0.5) # doesn't behave properly with this 30 | 31 | 32 | fork(outer_fork) 33 | 34 | wait_for_children_to_finish() 35 | -------------------------------------------------------------------------------- /buildDocs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # remove everything we're about to build 4 | find docs/* ! \( -iname "conf.py" -or -iname "makePackageRSTs.py" -or -path "*_static/*" -or -path "*_templates/*" -or -path "*narrative/*" -or -path "*index.rst" \) -delete 5 | 6 | cd docs 7 | python3 ./makePackageRSTs.py 8 | 9 | sphinx-build . build 10 | 11 | firefox build/index.html 12 | -------------------------------------------------------------------------------- /docs/_static/KadenzeThumbnail.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:552f53adbd9442fab20bc3e78f2739d8c2f3bfeeddb1be47e960eaf3373d81fa 3 | size 35161 4 | -------------------------------------------------------------------------------- /docs/_static/ScampLogo.svg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:b666cc37927affc772461f453cc2e74ef28a78608bf9bdb93ac0746ec64fc0f0 3 | size 18138 4 | -------------------------------------------------------------------------------- /docs/_static/css/custom.css: -------------------------------------------------------------------------------- 1 | @media screen and (min-width: 1100px) { 2 | .wy-nav-content { 3 | max-width: none; 4 | } 5 | } 6 | 7 | .youtube { 8 | margin: 0px auto 20px auto; 9 | width: 640px; 10 | max-width: 70vw; 11 | height: 360px; 12 | max-height: 39.375vw; 13 | display: block; 14 | } 15 | 16 | .soundcloud { 17 | margin: 0px auto 20px auto; 18 | width: 640px; 19 | max-width: 70vw; 20 | height: 166px; 21 | max-height: 39.375vw; 22 | display: block; 23 | } -------------------------------------------------------------------------------- /docs/_templates/autosummary/base.rst: -------------------------------------------------------------------------------- 1 | {{ fullname | escape | underline}} 2 | 3 | .. currentmodule:: {{ module }} 4 | 5 | .. auto{{ objtype }}:: {{ objname }} 6 | -------------------------------------------------------------------------------- /docs/_templates/autosummary/class.rst: -------------------------------------------------------------------------------- 1 | {{ fullname | escape | underline}} 2 | 3 | .. currentmodule:: {{ module }} 4 | 5 | .. autoclass:: {{ objname }} 6 | :members: 7 | :inherited-members: 8 | :undoc-members: 9 | 10 | 11 | {% block methods %} 12 | {% if methods %} 13 | 14 | {% if methods | reject('equalto', '__init__') | difference(inherited_members) %} 15 | .. rubric:: Methods 16 | .. autosummary:: 17 | {% endif %} 18 | 19 | {% for item in methods | reject('equalto', '__init__') | difference(inherited_members) %} 20 | ~{{ name }}.{{ item }} 21 | {%- endfor %} 22 | 23 | {% if methods | reject('equalto', '__init__') | intersect(inherited_members) %} 24 | .. rubric:: Inherited Methods 25 | .. autosummary:: 26 | {% endif %} 27 | 28 | {% for item in methods | reject('equalto', '__init__') | intersect(inherited_members) %} 29 | ~{{ name }}.{{ item }} 30 | {%- endfor %} 31 | 32 | {% endif %} 33 | {% endblock %} 34 | 35 | {% block attributes %} 36 | {% if attributes %} 37 | .. rubric:: Attributes 38 | 39 | .. autosummary:: 40 | {% for item in attributes %} 41 | ~{{ name }}.{{ item }} 42 | {%- endfor %} 43 | {% endif %} 44 | {% endblock %} 45 | -------------------------------------------------------------------------------- /docs/_templates/autosummary/module.rst: -------------------------------------------------------------------------------- 1 | {{ fullname | escape | underline }} 2 | 3 | .. currentmodule:: {{ fullname }} 4 | 5 | .. automodule:: {{ fullname }} 6 | 7 | {% set manual_classes = members | pick_classes_manually(fullname) %} 8 | {% if classes or manual_classes %} 9 | .. rubric:: Classes 10 | 11 | .. autosummary:: 12 | :toctree: . 13 | {% for class in classes %} 14 | {{ class }} 15 | {% endfor %} 16 | {% for class in manual_classes %} 17 | {{ class }} 18 | {% endfor %} 19 | {% endif %} 20 | 21 | {% set manual_functions = members | pick_functions_manually(fullname) %} 22 | {% if functions or manual_functions %} 23 | .. rubric:: Functions 24 | 25 | .. autosummary:: 26 | :toctree: . 27 | {% for function in functions %} 28 | {{ function }} 29 | {% endfor %} 30 | {% for function in manual_functions %} 31 | {{ function }} 32 | {% endfor %} 33 | {% endif %} 34 | 35 | {% set attributes = members | pick_attributes_manually(fullname) %} 36 | {% if attributes %} 37 | .. rubric:: Attributes 38 | 39 | .. autosummary:: 40 | {% for item in attributes %} 41 | ~{{ fullname }}.{{ item }} 42 | {%- endfor %} 43 | {% endif %} 44 | 45 | {% if exceptions %} 46 | .. rubric:: Exceptions 47 | 48 | .. autosummary:: 49 | {% for item in exceptions %} 50 | ~{{ fullname }}.{{ item }} 51 | {%- endfor %} 52 | {% endif %} -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | SCAMP (Suite for Computer-Assisted Music in Python) |release| 2 | ============================================================= 3 | 4 | `Source Code `_ | 5 | `PyPI `_ | 6 | `Paper `_ | 7 | `Forum `_ | 8 | `Patreon`_ | 9 | `Liberapay`_ 10 | 11 | .. _Patreon: https://www.patreon.com/marcevanstein 12 | 13 | .. _Liberapay: https://liberapay.com/marcevanstein/donate 14 | 15 | SCAMP is a computer-assisted composition framework in Python designed to act as a hub, flexibly connecting the 16 | composer-programmer to a variety of resources for playback and notation. SCAMP provides functionality to manage the 17 | flow of musical time, play back notes via SoundFonts or MIDI or OSC messages to an external synthesizer, and quantizes 18 | and exports the result to music notation in the form of MusicXML or LilyPond. 19 | 20 | Below, you will find instructions for getting SCAMP up and running on your computer, as well as complete API Documentation. 21 | Narrative documentation is available in the form of these :ref:`video tutorials`. If you're looking for more of an overview, 22 | the following video provides a good introduction to the framework: 23 | 24 | .. raw:: html 25 | 26 | 27 | 28 | You can also read the `paper introducing the framework `_, 29 | and check out the "tutorial" examples found `here `_. 30 | 31 | .. toctree:: 32 | :maxdepth: 2 33 | :caption: Set Up and Installation: 34 | 35 | narrative/easy_setup 36 | 37 | narrative/experienced_setup 38 | 39 | .. toctree:: 40 | :maxdepth: 2 41 | :caption: Learning Resources: 42 | 43 | narrative/kadenze 44 | narrative/tutorial_videos 45 | narrative/note_properties 46 | 47 | 48 | .. toctree:: 49 | :maxdepth: 2 50 | :caption: Community 51 | 52 | narrative/music 53 | narrative/forum 54 | narrative/youtube 55 | narrative/support 56 | 57 | 58 | .. toctree:: 59 | :maxdepth: 2 60 | :caption: API Reference: 61 | 62 | scamp 63 | 64 | clockblocks 65 | 66 | expenvelope 67 | 68 | pymusicxml 69 | 70 | scamp_extensions 71 | 72 | 73 | Indices and tables 74 | ================== 75 | 76 | * :ref:`genindex` 77 | * :ref:`modindex` 78 | * :ref:`search` 79 | -------------------------------------------------------------------------------- /docs/narrative/AlternateVersionAbjad.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:aaea6d0615ccca6764f9b3ff3f92c9221eba10b92baffaa7377bb73150dda5cd 3 | size 134127 4 | -------------------------------------------------------------------------------- /docs/narrative/InstallingSCAMP.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:93eb1d6a47dcdf6213b65d59f9bfbac2ae58c1efb02d3af97da4477145d6f510 3 | size 170777 4 | -------------------------------------------------------------------------------- /docs/narrative/KadenzeThumbnail.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:552f53adbd9442fab20bc3e78f2739d8c2f3bfeeddb1be47e960eaf3373d81fa 3 | size 35161 4 | -------------------------------------------------------------------------------- /docs/narrative/LilyWarning.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9077c898c5110ba704c1a748e1d2a0f7a0dd749cd7865db78b3abfc115707ca1 3 | size 58059 4 | -------------------------------------------------------------------------------- /docs/narrative/OpenLilyPondAnyway.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:277c8771aa426a520cc4675bb9b1177927eab88a59e65be4068dea3094e8e16b 3 | size 83177 4 | -------------------------------------------------------------------------------- /docs/narrative/PropertiesExample.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:cda03b655afe42d95e41bded8ee78a99584382f4dcb491673c305fd2d2abc8c7 3 | size 23990 4 | -------------------------------------------------------------------------------- /docs/narrative/SecuritySettings.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:890cd684462c3739e773bce772fa82cc69d381e8f0cb585e70a974ea52e547c5 3 | size 143978 4 | -------------------------------------------------------------------------------- /docs/narrative/SelectVersionAbjad.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:24f804fbb713b1200ffa93074faf3e4ce9ad780f7e4849dc2c8f8be1d7d5e77e 3 | size 141898 4 | -------------------------------------------------------------------------------- /docs/narrative/VoicesExample.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c850266b6646d19fefc99899d43f5b574af01fd4fe05fcc276f3fd19bec51f55 3 | size 18202 4 | -------------------------------------------------------------------------------- /docs/narrative/WindowsInstallSCAMP.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:dd3bc2ae155494b72e3e2ecde6f7fc1599dc1e3823dc9f093074295f60fbf174 3 | size 139532 4 | -------------------------------------------------------------------------------- /docs/narrative/WindowsInstallingThonny.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:552d508705a58eba6c59fac98fccb1731638f9df687936d00ffeb2d68a22afb2 3 | size 60282 4 | -------------------------------------------------------------------------------- /docs/narrative/WindowsInstallingThonny2.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:55381f796a542112065df331796a661c9d3cc73bfd1fd054a25246205d4a25e1 3 | size 64424 4 | -------------------------------------------------------------------------------- /docs/narrative/WindowsLilypondUnnerving.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:549e4e7152bedf6576262b2f1bac67627fc99905b8e38ec6d51f96df3f75ed28 3 | size 172605 4 | -------------------------------------------------------------------------------- /docs/narrative/WindowsManagePackages.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:e8a95cafa7af4f086a07bde025936de95690e4c16d3b2a3a9369314cb9373051 3 | size 116511 4 | -------------------------------------------------------------------------------- /docs/narrative/forum.rst: -------------------------------------------------------------------------------- 1 | Forum 2 | ===== 3 | 4 | Join the forum at https://scampsters.marcevanstein.com/, where you can ask questions, share your creations, 5 | make suggestions for the development of the framework, and generally take part in the wider SCAMP community. The forum 6 | is open-registration, and runs on `Discourse `_, an open-source discussion platform. -------------------------------------------------------------------------------- /docs/narrative/kadenze.rst: -------------------------------------------------------------------------------- 1 | Kadenze Course 2 | ============== 3 | 4 | If you're new to Python, or making music in Python, a great place to get started is with my 5 | `Kadenze Course `_. Not only is it a great, 6 | beginner-friendly introduction to Python and SCAMP, but the registration fee also helps support the work that I do! 7 | 8 | 9 | .. raw:: html 10 | 11 | -------------------------------------------------------------------------------- /docs/narrative/music.rst: -------------------------------------------------------------------------------- 1 | Music Created using SCAMP 2 | ========================= 3 | 4 | Here are a few examples of pieces of music recently created in SCAMP. More coming soon! 5 | 6 | 7 | Marc Evanstein: `Searsville 1891-2015, for open ensemble with flute and clarinet solo `_ 8 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 9 | 10 | .. raw:: html 11 | 12 | 13 | 14 | 15 | Alex Stephenson: `An Elemental Music `_ 16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 17 | 18 | According to Alex, "I used [SCAMP] quite a bit to model the canonic passages in the second movement. Listen `here `_. 19 | 20 | Stewart Engart: `A More Sound Outlook `_ 21 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 22 | 23 | .. raw:: html 24 | 25 | 26 | -------------------------------------------------------------------------------- /docs/narrative/support.rst: -------------------------------------------------------------------------------- 1 | Support SCAMP! 2 | ============== 3 | 4 | Although SCAMP will always be free to use and completely open source, a great deal of time and energy has gone into its 5 | development. If you'd like to support that development, consider becoming a patron on `Patreon`_ or `Liberapay`_! Your 6 | support not allows me to devote time and energy into SCAMP, but also into my YouTube videos and other educational 7 | outreach. 8 | 9 | Another great way to supporting my work (and learning something in the process!) is to `purchase my Kadenze course `_. 10 | 11 | 12 | .. _Patreon: https://www.patreon.com/marcevanstein 13 | 14 | .. _Liberapay: https://liberapay.com/marcevanstein/donate 15 | -------------------------------------------------------------------------------- /docs/narrative/type_aliases.rst: -------------------------------------------------------------------------------- 1 | SCAMP Type Aliases 2 | ================== 3 | 4 | .. csv-table:: Type aliases used in SCAMP 5 | :header: "Alias Name", "Alias meaning" 6 | :widths: 10, 30 7 | 8 | "_`NotePropertiesCompatible`", "str | dict | NoteProperty | NoteProperties | Sequence['NotePropertiesCompatible']" 9 | "_`PitchCompatible`", "float | Envelope | Sequence[float] | Sequence[Sequence[float]]" 10 | "_`VolumeCompatible`", "float | Envelope | Sequence[float] | Sequence[Sequence[float]]" 11 | "_`DurationCompatible`", "float | tuple[float, ...]" -------------------------------------------------------------------------------- /docs/narrative/youtube.rst: -------------------------------------------------------------------------------- 1 | YouTube 2 | ------- 3 | 4 | For content at the intersection of Python and music, subscribe to `my YouTube Channel, music.py `_. 5 | I post code, compositions, and a weekly series of Python Music Shorts to spark your imagination. Here's the first 6 | Python Music Short, to whet your appetite: 7 | 8 | .. raw:: html 9 | 10 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/GuitarArpeggios.py: -------------------------------------------------------------------------------- 1 | from scamp import * 2 | 3 | s = Session() 4 | 5 | guitar = s.new_part("guitar") 6 | 7 | 8 | def play_held_arpeggio(inst, pitches, volumes, note_length, total_length): 9 | t = 0 10 | for p, v in zip(pitches, volumes): 11 | inst.play_note(p, v, total_length - t, blocking=False) 12 | wait(note_length) 13 | t += note_length 14 | wait(total_length - t) 15 | 16 | 17 | while True: 18 | play_held_arpeggio(guitar, [45, 52, 59, 57, 60], [0.9, 0.6, 0.8, 0.5, 0.8], 0.25, 3.0) 19 | play_held_arpeggio(guitar, [43, 52, 59], [0.9, 0.6, 0.8], 0.25, 2.0) 20 | play_held_arpeggio(guitar, [41, 50, 59, 57, 60], [0.9, 0.6, 0.8, 0.5, 0.8], 0.25, 3.0) 21 | play_held_arpeggio(guitar, [40, 47, 56, 62], [0.9, 0.6, 0.6, 0.8], 1/3, 2.0) 22 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/NotationVSPerformance.py: -------------------------------------------------------------------------------- 1 | from scamp import * 2 | 3 | s = Session() 4 | 5 | Steel_drums=s.new_part("Steel Drum") 6 | Steel_drums_notation=s.new_silent_part("Steel Drum (clean notation)") 7 | 8 | straight_performance = s.start_transcribing(Steel_drums_notation) 9 | wonky_performance = s.start_transcribing(Steel_drums) 10 | 11 | 12 | def _play_straight_beat(pitches): 13 | for pitch, dur in zip(pitches, [0.25] * 4): 14 | Steel_drums_notation.play_note(pitch, 0.5, dur) 15 | 16 | def play_one_beat_drums(pitches, subdivision_durs=(0.26, 0.23, 0.24, 0.27)): 17 | fork(_play_straight_beat, args=[pitches]) 18 | for pitch, dur in zip(pitches, subdivision_durs): 19 | Steel_drums.play_note(pitch, 0.5, dur) 20 | 21 | 22 | for _ in range(4): # do it 4 times 23 | play_one_beat_drums([60, 61, 62, 63]) 24 | 25 | 26 | s.stop_transcribing(straight_performance) 27 | s.stop_transcribing(wonky_performance) 28 | wonky_performance.export_to_midi_file("wonky.mid") 29 | # If you check out the score, it takes into acount the intern elasticity produced by apply_rate_function 30 | partition = straight_performance.to_score(title="Elasticity", time_signature="4/4", composer="decomposer & Co") 31 | partition.show() -------------------------------------------------------------------------------- /examples/AssortedHaphazard/OldTutorialExamples/articulations.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | 19 | session = Session() 20 | piano = session.new_part("piano") 21 | 22 | session.start_transcribing() 23 | 24 | for _ in range(2): 25 | piano.play_note(65, 0.5, 1.25, "accent") 26 | piano.play_note(67, 0.5, 0.25, "staccato") 27 | piano.play_note(68, 0.5, 1.25, "staccato") 28 | piano.play_note(70, 0.5, 0.25, "staccato") 29 | piano.play_note(72, 0.5, 0.25, "staccatissimo, marcato") 30 | piano.play_note(70, 0.5, 0.25, "staccato") 31 | piano.play_note(68, 0.5, 0.25, "staccato") 32 | piano.play_note(67, 0.5, 0.25, "staccato") 33 | 34 | piano.play_chord([65, 77], 0.5, 2, "tenuto") 35 | 36 | session.stop_transcribing().to_score().show() 37 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/OldTutorialExamples/hello_world.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | 19 | session = Session() 20 | 21 | piano = session.new_part("piano") 22 | 23 | session.start_transcribing() 24 | 25 | pitches = [60, 62, 64, 65, 67, 69, 71, 72] 26 | durations = [0.5, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 2.0] 27 | 28 | for pitch, duration in zip(pitches, durations): 29 | piano.play_note(pitch, 1.0, duration) 30 | 31 | performance = session.stop_transcribing() 32 | 33 | performance.to_score().show() 34 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/OldTutorialExamples/simple_polytempo.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | 19 | s = Session() 20 | 21 | trumpet = s.new_part("trumpet") 22 | trombone = s.new_part("trombone") 23 | 24 | 25 | def trumpet_part(clock: Clock): 26 | while s.beat() < 3: 27 | trumpet.play_note(67, 1, 0.5) 28 | clock.set_rate_target(0.5, 6, duration_units="time") 29 | while s.beat() < 12: 30 | trumpet.play_note(67, 1, 0.5) 31 | 32 | 33 | s.set_tempo_target(100, 9) 34 | trumpet_clock = s.fork(trumpet_part) 35 | trombone_based_performance = s.start_transcribing() 36 | trumpet_based_performance = s.start_transcribing(clock=trumpet_clock) 37 | 38 | while s.beat() < 12: 39 | trombone.play_note(60, 1, 1) 40 | 41 | s.stop_transcribing(trombone_based_performance) 42 | s.stop_transcribing(trumpet_based_performance) 43 | 44 | trombone_based_performance.to_score("3/4", title="Trombone Clock").show_xml() 45 | trumpet_based_performance.to_score("3/4", title="Trumpet Clock").show_xml() -------------------------------------------------------------------------------- /examples/AssortedHaphazard/SavedFiles/savedEnsemble.json: -------------------------------------------------------------------------------- 1 | { 2 | "_type": "Ensemble", 3 | "default_audio_driver": "default", 4 | "default_midi_output_device": "default", 5 | "default_soundfont": "default", 6 | "default_spelling_policy": null, 7 | "instruments": [ 8 | { 9 | "_type": "ScampInstrument", 10 | "clef_preference": "from_name", 11 | "default_spelling_policy": null, 12 | "name": "piano", 13 | "playback_implementations": [ 14 | { 15 | "_type": "SoundfontPlaybackImplementation", 16 | "audio_driver": "default", 17 | "bank_and_preset": [ 18 | 0, 19 | 0 20 | ], 21 | "max_pitch_bend": 48, 22 | "num_channels": 8, 23 | "soundfont": "general_midi" 24 | } 25 | ] 26 | }, 27 | { 28 | "_type": "ScampInstrument", 29 | "clef_preference": "from_name", 30 | "default_spelling_policy": null, 31 | "name": "flute", 32 | "playback_implementations": [ 33 | { 34 | "_type": "SoundfontPlaybackImplementation", 35 | "audio_driver": "default", 36 | "bank_and_preset": [ 37 | 0, 38 | 73 39 | ], 40 | "max_pitch_bend": 48, 41 | "num_channels": 8, 42 | "soundfont": "general_midi" 43 | } 44 | ] 45 | }, 46 | { 47 | "_type": "ScampInstrument", 48 | "clef_preference": "from_name", 49 | "default_spelling_policy": null, 50 | "name": "strings", 51 | "playback_implementations": [ 52 | { 53 | "_type": "SoundfontPlaybackImplementation", 54 | "audio_driver": "default", 55 | "bank_and_preset": [ 56 | 0, 57 | 40 58 | ], 59 | "max_pitch_bend": 48, 60 | "num_channels": 8, 61 | "soundfont": "general_midi" 62 | } 63 | ] 64 | } 65 | ] 66 | } -------------------------------------------------------------------------------- /examples/AssortedHaphazard/SavedFiles/shakEnsemble.json: -------------------------------------------------------------------------------- 1 | { 2 | "_type": "Session", 3 | "default_audio_driver": "default", 4 | "default_soundfont": "default", 5 | "default_spelling_policy": null, 6 | "instruments": [ 7 | { 8 | "_type": "ScampInstrument", 9 | "clef_preference": "from_name", 10 | "default_spelling_policy": null, 11 | "name": "shakuhachi", 12 | "playback_implementations": [ 13 | { 14 | "_type": "SoundfontPlaybackImplementation", 15 | "audio_driver": "default", 16 | "bank_and_preset": [ 17 | 0, 18 | 77 19 | ], 20 | "max_pitch_bend": 48, 21 | "num_channels": 8, 22 | "soundfont": "general_midi" 23 | } 24 | ] 25 | }, 26 | { 27 | "_type": "ScampInstrument", 28 | "clef_preference": "from_name", 29 | "default_spelling_policy": null, 30 | "name": "oboe", 31 | "playback_implementations": [ 32 | { 33 | "_type": "SoundfontPlaybackImplementation", 34 | "audio_driver": "default", 35 | "bank_and_preset": [ 36 | 0, 37 | 68 38 | ], 39 | "max_pitch_bend": 48, 40 | "num_channels": 8, 41 | "soundfont": "general_midi" 42 | } 43 | ] 44 | } 45 | ], 46 | "tempo": 60.0 47 | } -------------------------------------------------------------------------------- /examples/AssortedHaphazard/bunchStuff.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | import random 19 | 20 | s = Session() 21 | 22 | # OSC to an external SuperCollider synth 23 | sine_waves = s.new_osc_part("vibrato", 57120) 24 | # basic soundfont-based rendering 25 | piano = s.new_part("piano") 26 | s.start_transcribing() # start transcribing the music 27 | 28 | 29 | def piano_part(): 30 | while True: 31 | # play random notes 0.25 seconds long between middle C and A440 (microtonal) 32 | piano.play_note(random.uniform(60, 69), 1.0, 0.25, "staccato") 33 | 34 | 35 | # start the piano as a parallel process 36 | s.fork(piano_part) 37 | 38 | while s.time() < 20: # for twenty seconds... 39 | # play random sine-wave glissandi via the SuperCollider synth with varying vibrato 40 | sine_waves.play_note( 41 | [random.uniform(70, 96), random.uniform(70, 96)], # pitch is a glissando between two values 42 | 1.0, random.uniform(1, 4), # volume is 1, duration is 1-4 seconds 43 | {"param_vibFreq": random.uniform(1, 20), 44 | "param_vibWidth": random.uniform(0.5, 3)} 45 | ) 46 | 47 | s.stop_transcribing().to_score(time_signature="3/4").show() 48 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/chords_example.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | 19 | s = Session() 20 | 21 | piano = s.new_part("piano") 22 | 23 | s.start_transcribing() 24 | 25 | # this makes the whole chord diamond noteheads 26 | handle = piano.start_chord(([60, 56], 64, 69), 0.5, "notehead: diamond") 27 | 28 | handle.change_pitch((62, 58, 60), (1/3, 1/3, 1/3), transition_curve_shape_or_shapes=(5, 5, 5)) 29 | 30 | engraving_settings.glissandi.control_point_policy = "split" 31 | 32 | wait(2) 33 | handle.end() 34 | engraving_settings.glissandi.consider_non_extrema_control_points = True 35 | 36 | # this gives separate noteheads for separate pitches 37 | piano.play_chord((60, 64, 69), 0.5, 2.0, "noteheads: diamond / normal / cross") 38 | 39 | # noteheads are assigned in the order of the pitch tuple given, not in order from low to high 40 | # so here, middle C has a normal notehead, E has a diamond, and A has a cross 41 | piano.play_chord((64, 60, 69), 0.5, 2.0, "noteheads: diamond / normal / cross") 42 | 43 | # This line would throw an error, because the wrong number of noteheads is given for the chord 44 | # piano.play_chord((64, 60, 69), 0.5, 2.0, "noteheads: diamond / normal") 45 | 46 | s.wait(0) 47 | s.stop_transcribing().to_score().show() 48 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/ensembleExample.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import Ensemble 18 | 19 | 20 | def construct_ensemble(): 21 | global piano, flute, strings, ensemble 22 | ensemble = Ensemble() 23 | 24 | ensemble.print_default_soundfont_presets() 25 | 26 | piano = ensemble.new_part("piano") 27 | flute = ensemble.new_part("flute") 28 | strings = ensemble.new_part("strings", (0, 40)) 29 | 30 | 31 | def play_some_stuff(): 32 | while True: 33 | piano.play_note(65, 0.5, 1.0) 34 | flute.play_note(70, 0.5, 0.25) 35 | strings.play_note([75, 73], 0.5, 1.0, blocking=True) 36 | 37 | 38 | construct_ensemble() 39 | 40 | # # ------- Use this line to save the Ensemble so that it can be reloaded ------- 41 | # ensemble.save_to_json("SavedFiles/savedEnsemble.json") 42 | 43 | # # ------- Use this line to reloaded the Ensemble from the saved file ------- 44 | # ensemble = Ensemble.load_from_json("SavedFiles/savedEnsemble.json") 45 | # piano, flute, strings = ensemble.instruments 46 | 47 | play_some_stuff() 48 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/fifteen_sixteen.py: -------------------------------------------------------------------------------- 1 | """ 2 | A rhythm quantized with a 15/16 time signature 3 | """ 4 | 5 | from scamp import * 6 | 7 | MeasureQuantizationScheme.from_time_signature("15/16") 8 | # engraving_settings.allow_duple_tuplets_in_compound_time = False 9 | dur_list=[1.0, 0.5, 0.5, 0.75, 0.75, 1.5, 1.5, 0.75, 0.75, 1.0, 1.25] 10 | 11 | time_sig_list=['7/8', '15/16', '3/4'] 12 | s = Session() 13 | s.fast_forward_in_time(1000) 14 | piano1 = s.new_part("Piano1") 15 | s.start_transcribing() 16 | for d in dur_list: 17 | piano1.play_note(67, 1, d) 18 | s.wait_for_children_to_finish() 19 | performance = s.stop_transcribing() 20 | performance.to_score(time_signature=time_sig_list, simplicity_preference=2).show() 21 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/glissTest2.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | import random 19 | 20 | session = Session() 21 | 22 | piano = session.new_part("violin") 23 | 24 | random.seed(1) 25 | 26 | session.start_transcribing() 27 | 28 | piano.play_note([60, 70, 50, 65, 55, 62, 58, 60], 1.0, 4.0) 29 | piano.play_chord([[60, 70, 50, 65, 55, 62, 58, 60], [67, 77, 57, 72, 62, 69, 65, 67]], 1.0, 4.0) 30 | 31 | performance = session.stop_transcribing() 32 | engraving_settings.glissandi.inner_grace_relevance_threshold = 0 33 | engraving_settings.glissandi.control_point_policy = "grace" 34 | performance.to_score().show() 35 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/glissando_test.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | import random 19 | 20 | session = Session() 21 | 22 | piano = session.new_part("piano") 23 | piano.set_max_pitch_bend(20) 24 | 25 | random.seed(1) 26 | 27 | session.start_transcribing() 28 | 29 | while session.time() < 12: 30 | gliss = Envelope( 31 | [random.random() * 20 + 60, random.random() * 20 + 60, random.random() * 20 + 60, random.random() * 20 + 60], 32 | [random.random()+0.5, random.random()+0.5, random.random()+0.5] 33 | ) 34 | 35 | if random.random() < 0.5: 36 | piano.play_note(gliss, 1.0, random.random()*2 + 0.5) 37 | else: 38 | piano.play_chord([gliss, gliss+4], 1.0, random.random()*2 + 0.5) 39 | if random.random() < 0.5: 40 | session.wait(random.random() * 2) 41 | 42 | # # Thia line causes the turn-around points of the glissandi to be rendered differently 43 | # engraving_settings.glissandi.control_point_policy = "grace" 44 | performance = session.stop_transcribing() 45 | 46 | performance.quantize(QuantizationScheme.from_time_signature("5/4")) 47 | performance.save_to_json("SavedFiles/quantized_glisses.json") 48 | 49 | session.wait(2) 50 | print("playing quantized") 51 | performance.play(clock=session) 52 | 53 | performance.to_score().show() 54 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/karlclock.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import Session 18 | import random 19 | 20 | s = Session() 21 | piano = s.new_part("piano") 22 | 23 | 24 | def do_chords(): 25 | while True: 26 | piano.play_chord([random.random()*24 + 60, random.random()*24 + 60], 1.0, 1.0) 27 | 28 | 29 | def do_fast_notes(): 30 | while True: 31 | piano.play_note(random.random()*24 + 80, 1.0, 0.1) 32 | 33 | 34 | s.fork(do_chords) 35 | s.fork(do_fast_notes) 36 | s.wait_forever() 37 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/karlclock_interactive.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import Session 18 | import random 19 | from pynput.keyboard import Listener 20 | 21 | rate = 1 22 | 23 | 24 | def on_press(key): 25 | try: 26 | global rate 27 | rate = 1 + int(str(key).replace("\'", "")) 28 | print("New rate is ", rate) 29 | except ValueError: 30 | # ignore key presses that don't correspond to number keys 31 | pass 32 | 33 | 34 | # Collect events until released 35 | Listener(on_press=on_press).start() 36 | 37 | 38 | s = Session() 39 | 40 | piano = s.new_part("piano", num_channels=40) 41 | 42 | 43 | def do_chords(clock): 44 | while True: 45 | if rate != clock.rate: 46 | clock.rate = rate 47 | piano.play_chord([random.random()*24 + 60, random.random()*24 + 60], 1.0, 1.0) 48 | 49 | 50 | def do_fast_notes(clock): 51 | while True: 52 | if rate != clock.rate: 53 | clock.rate = rate 54 | piano.play_note(random.random()*24 + 80, 1.0, 0.5) 55 | 56 | 57 | s.fork(do_chords) 58 | s.fork(do_fast_notes) 59 | s.wait_forever() 60 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/key_clocks.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | import math 19 | 20 | s = Session() 21 | piano = s.new_part("piano") 22 | 23 | 24 | def child(clock: Clock): 25 | clock.apply_tempo_function(lambda b: 90 + 60 * math.sin(b / 5)) 26 | global grand_child_clock 27 | grand_child_clock = clock.fork(grand_child) 28 | while True: 29 | wait(1) 30 | 31 | 32 | def grand_child(clock: Clock): 33 | clock.rate = 2 34 | while True: 35 | piano.play_note(103, 1, 1) 36 | piano.play_note(103, 1, 1) 37 | piano.play_note(103, 1, 1) 38 | piano.play_note(103, 1, 1) 39 | clock.rate = 1 / clock.rate 40 | 41 | 42 | def play_wiggle(p, transposition=0): 43 | for _ in range(2): 44 | piano.play_note(p + transposition, 0.5, 0.25) 45 | piano.play_note(p+1 + transposition, 0.5, 0.25) 46 | piano.play_note(p + transposition, 0.5, 0.25) 47 | piano.play_note(p-1 + transposition, 0.5, 0.25) 48 | 49 | 50 | def key_down(name, number): 51 | if 20 < number < 110: 52 | grand_child_clock.fork(play_wiggle, args=(number, )) 53 | 54 | 55 | grand_child_clock = None 56 | child_clock = s.fork(child) 57 | s.register_keyboard_listener(on_press=key_down, suppress=True) 58 | 59 | while True: 60 | piano.play_note(108, 1.0, 1) 61 | s.wait(1) 62 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/key_sig.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | 19 | s = Session(tempo=120) 20 | 21 | violin = s.new_part("violin") 22 | s.start_transcribing() 23 | 24 | for _ in range(3): 25 | for p in [62, 65, 63, 67, 68, 72, 70]: 26 | violin.play_note(p, 0.7, 0.5, "key: Eb") 27 | 28 | pymusicxml_score = s.stop_transcribing().to_score().to_music_xml() 29 | pymusicxml_part = pymusicxml_score.contents[0] 30 | pymusicxml_part.measures[0].key = "Eb" 31 | 32 | pymusicxml_score.export_to_file("KeySig.musicxml") 33 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/load_and_play_performance.py: -------------------------------------------------------------------------------- 1 | """ 2 | To be run after "load_and_play_performance.py", since this loads up that performance as well as the Ensemble. 3 | """ 4 | 5 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 6 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 7 | # Copyright © 2020 Marc Evanstein . # 8 | # # 9 | # This program is free software: you can redistribute it and/or modify it under the terms of # 10 | # the GNU General Public License as published by the Free Software Foundation, either version # 11 | # 3 of the License, or (at your option) any later version. # 12 | # # 13 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 14 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 15 | # See the GNU General Public License for more details. # 16 | # # 17 | # You should have received a copy of the GNU General Public License along with this program. # 18 | # If not, see . # 19 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 20 | 21 | from scamp import * 22 | 23 | session = Session.load_from_json("SavedFiles/shakEnsemble.json") 24 | 25 | performance = Performance.load_from_json("SavedFiles/perfShakoboe.json") 26 | 27 | session.tempo = 30 28 | session.set_tempo_target(150, 40, duration_units="time") 29 | 30 | while True: 31 | performance.play(ensemble=session, clock=session, blocking=True) 32 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/load_performance_and_export_midi.py: -------------------------------------------------------------------------------- 1 | """ 2 | To be run after "load_and_play_performance.py", since this loads up that performance as well as the Ensemble. 3 | """ 4 | 5 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 6 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 7 | # Copyright © 2020 Marc Evanstein . # 8 | # # 9 | # This program is free software: you can redistribute it and/or modify it under the terms of # 10 | # the GNU General Public License as published by the Free Software Foundation, either version # 11 | # 3 of the License, or (at your option) any later version. # 12 | # # 13 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 14 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 15 | # See the GNU General Public License for more details. # 16 | # # 17 | # You should have received a copy of the GNU General Public License along with this program. # 18 | # If not, see . # 19 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 20 | 21 | import random 22 | from scamp import * 23 | 24 | 25 | random.seed(0) 26 | s = Session() 27 | s.set_rate_target(2, 10, duration_units="time") 28 | s.fast_forward_to_beat(float("inf")) 29 | clar = s.new_part("clarinet") 30 | piano = s.new_part("piano") 31 | 32 | performance = s.start_transcribing() 33 | 34 | 35 | def piano_part(): 36 | while True: 37 | piano.play_note(random.randint(40, 58), random.uniform(0.3, 0.8), 0.5, "staccato") 38 | 39 | 40 | fork(piano_part) 41 | 42 | for p in [random.randint(50, 80) for _ in range(30)]: 43 | clar.play_note([p, p + 1, p - 2], Envelope([0.8, 0.1, 1.0], [0.1, 1.0]), random.uniform(0.1, 2), 44 | f"param_10: {random.uniform(0, 1)}" if random.random() < 0.5 else "param_10: [0, 1]") 45 | 46 | s.stop_transcribing().export_to_midi_file("midi_export.mid") -------------------------------------------------------------------------------- /examples/AssortedHaphazard/midi_channel_manager.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | from scamp._midi import MIDIChannelManager 17 | import time 18 | 19 | mcm = MIDIChannelManager(5) 20 | print("A", mcm.assign_note_to_channel(101, 64, 0, {})) # channel 0: 101 21 | time.sleep(1) 22 | print("B", mcm.assign_note_to_channel(102, 70, 0, {})) # channel 0: 101, 102 23 | time.sleep(0.3) 24 | mcm.end_note(101) # channel 0: 102 25 | print("C", mcm.assign_note_to_channel(103, 79, "variable", "variable")) # channel 1 (variable): 103 26 | print("D", mcm.assign_note_to_channel(104, 72, 0.5, {})) # channel 2 (pitch bend 0.5): 104 27 | print("D2", mcm.assign_note_to_channel(105, 72, 0, {})) # channel 0: 102, 105 28 | mcm.end_note(102) # channel 0: 105 29 | print("E", mcm.assign_note_to_channel(106, 72, 0.5, {})) # channel 3 (conflicting pitch): 106 30 | time.sleep(0.5) 31 | mcm.end_note(105) # channel 0: empty, but should need ring time 32 | time.sleep(0.6) 33 | print("F", mcm.assign_note_to_channel(107, 72, 0.5, {})) # channel 0 34 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/midi_export.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcTheSpark/scamp/beb920a7cd86feb48047be01a1181b057a3b2455/examples/AssortedHaphazard/midi_export.mid -------------------------------------------------------------------------------- /examples/AssortedHaphazard/multiple_score_example.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | import random 17 | import time 18 | from scamp import * 19 | 20 | s = Session().run_as_server() 21 | 22 | piano = s.new_part("piano") 23 | 24 | 25 | def some_music(): 26 | for _ in range(6): 27 | piano.play_note(random.randint(60, 70), 1.0, random.choice([0.5, 1.0, 1.5])) 28 | 29 | 30 | while True: 31 | # wait some random length of time 32 | time.sleep(random.uniform(1, 3)) 33 | s.start_transcribing() 34 | s.fork(some_music) 35 | time.sleep(8) 36 | performance = s.stop_transcribing() 37 | performance.to_score().show() 38 | 39 | 40 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/osc/OSCListenerPython.scd: -------------------------------------------------------------------------------- 1 | NetAddr.langPort; // Run this line to get the port to send to from Python 2 | MarcUtilities.setOutputLimiter(0.4, 0.4) 3 | ( // Define the Synths 4 | 5 | // Simple FM Sine thing 6 | SynthDef(\testSynth, { |out=0, freq=440, gain=0.1, modulationFreq=20, gate=1| 7 | var synth = EnvGen.ar(Env.asr(releaseTime:0.5), gate, doneAction: 2) * (SinOsc.ar(freq) * SinOsc.ar(modulationFreq) * gain); 8 | Out.ar(out, synth ! 2); 9 | }).add; 10 | 11 | // (from https://sccode.org/1-54H) 12 | SynthDef("hihat", {arg out = 0, gain = 0.5, att = 0.01, rel = 0.2, ffreq = 6000; 13 | var env, snd; 14 | env = Env.perc(att, rel, gain).kr(doneAction: 2); 15 | snd = WhiteNoise.ar; 16 | snd = HPF.ar(in: snd, freq: ffreq, mul: env); 17 | Out.ar(out, snd ! 2); 18 | }).add; 19 | ) 20 | 21 | ( // Set up the OSC Listeners 22 | 23 | // -------------------- FM SINES ----------------------- 24 | 25 | ~notesPlaying = Dictionary(); 26 | 27 | // START NOTE 28 | o = OSCFunc({ arg msg, time, addr, recvPort; 29 | var id = msg[1], pitch = msg[2], volume = msg[3]; 30 | ~notesPlaying.put(id, Synth(\testSynth, [\freq, pitch.midicps, \gain, (-40 * (1-volume)).dbamp])); 31 | }, '/fm_sines/start_note'); 32 | 33 | // END NOTE 34 | o = OSCFunc({ arg msg, time, addr, recvPort; 35 | var id = msg[1]; 36 | ~notesPlaying[id].set(\gate, 0); 37 | }, '/fm_sines/end_note'); 38 | 39 | // CHANGE PITCH 40 | o = OSCFunc({ arg msg, time, addr, recvPort; 41 | var id = msg[1], pitch = msg[2]; 42 | ~notesPlaying[id].set(\freq, pitch.midicps); 43 | }, '/fm_sines/change_pitch'); 44 | 45 | // CHANGE VOLUME 46 | o = OSCFunc({ arg msg, time, addr, recvPort; 47 | var id = msg[1], volume = msg[2]; 48 | ~notesPlaying[id].set(\gain, (-40 * (1-volume)).dbamp); 49 | }, '/fm_sines/change_volume'); 50 | 51 | // CHANGE THE MODULATION FREQUENCY 52 | o = OSCFunc({ arg msg, time, addr, recvPort; 53 | var id = msg[1], fm = msg[2]; 54 | ~notesPlaying[id].set(\modulationFreq, fm); 55 | }, '/fm_sines/change_parameter/fm'); 56 | 57 | 58 | // -------------------- HiHat ----------------------- 59 | 60 | // START NOTE 61 | o = OSCFunc({ arg msg, time, addr, recvPort; 62 | var id = msg[1], pitch = msg[2], volume = msg[3]; 63 | ~notesPlaying.put(id, Synth(\hihat, [\ffreq, pitch.midicps, \gain, (-40 * (1-volume)).dbamp])); 64 | }, '/hihat/start_note'); 65 | 66 | // CHANGE PITCH 67 | o = OSCFunc({ arg msg, time, addr, recvPort; 68 | var id = msg[1], pitch = msg[2]; 69 | ~notesPlaying[id].set(\ffreq, pitch.midicps); 70 | }, '/hihat/change_pitch'); 71 | ) 72 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/osc/osc_instrument_example.py: -------------------------------------------------------------------------------- 1 | """ 2 | Uses an OSCPlaycorderInstrument to send messages to a running SuperCollider script at OSCListenerPython.scd 3 | To test out, run all the code blocks in OSCListenerPython.scd, make sure that the port below matches the 4 | result of NetAddr.langPort, and then run this script. 5 | """ 6 | 7 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 8 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 9 | # Copyright © 2020 Marc Evanstein . # 10 | # # 11 | # This program is free software: you can redistribute it and/or modify it under the terms of # 12 | # the GNU General Public License as published by the Free Software Foundation, either version # 13 | # 3 of the License, or (at your option) any later version. # 14 | # # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 16 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 17 | # See the GNU General Public License for more details. # 18 | # # 19 | # You should have received a copy of the GNU General Public License along with this program. # 20 | # If not, see . # 21 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 22 | 23 | from scamp import * 24 | import random 25 | 26 | 27 | session = Session() 28 | 29 | # The port here must match the result of NetAddr.langPort in SuperCollider 30 | fm_sines = session.new_osc_part("fm_sines", 57120) 31 | hihat = session.new_osc_part("hihat", 57120) 32 | 33 | 34 | def fm_sines_part(): 35 | while True: 36 | fm_sines.play_note([random.random() * 20 + 60, random.random() * 20 + 60], 37 | [[1, 0], [1], [-2]], random.random() * 3 + 0.3, 38 | {"fm_param": [random.random() * 30, random.random() * 30]}, 39 | blocking=False) 40 | wait(random.random() * 3) 41 | 42 | 43 | def hihat_part(): 44 | while True: 45 | hihat.play_note(80 + random.random() * 40, random.random(), 0.25) 46 | 47 | 48 | session.fork(fm_sines_part) 49 | session.fork(hihat_part) 50 | session.wait_forever() 51 | 52 | 53 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/qt_interactive.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | 19 | from PyQt5 import QtCore, QtWidgets 20 | 21 | 22 | class MainWindow(QtWidgets.QMainWindow): 23 | def __init__(self, parent=None): 24 | super(MainWindow, self).__init__(parent) 25 | scene = QtWidgets.QGraphicsScene(self) 26 | view = QtWidgets.QGraphicsView(scene) 27 | # view.setSceneRect(QtCore.QRectF(0, 0, 500, 500)) 28 | scene.setSceneRect(QtCore.QRectF(0, 0, 800, 800)) 29 | 30 | self.setCentralWidget(view) 31 | 32 | self.rect_item = QtWidgets.QGraphicsRectItem(QtCore.QRectF(0, 0, 100, 100)) 33 | self.rect_item.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True) 34 | scene.addItem(self.rect_item) 35 | 36 | 37 | if __name__ == '__main__': 38 | import sys 39 | app = QtWidgets.QApplication(sys.argv) 40 | w = MainWindow() 41 | w.setFixedSize(QtCore.QSize(800, 800)) 42 | w.show() 43 | 44 | s = Session().run_as_server() 45 | piano = s.new_part("piano") 46 | s.start_transcribing() 47 | 48 | speed_response = Envelope([2, 0.1], [800], [-5]) 49 | 50 | def play_notes(): 51 | while True: 52 | piano.play_note((1-w.rect_item.y()/800) * 40 + 60, 1.0, speed_response.value_at(w.rect_item.x())) 53 | 54 | s.fork(play_notes) 55 | 56 | app.exec_() 57 | 58 | s.stop_transcribing().to_score().show() 59 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/quantization.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | from random import random, seed 19 | 20 | session = Session() 21 | drum = session.new_part("metronome", (0, 116)) 22 | piano = session.new_part("piano") 23 | 24 | recording = True 25 | 26 | seed(4) 27 | 28 | 29 | def piano_part(): 30 | while recording: 31 | if random() < 0.5: 32 | piano.play_note(50 + random()*20, 0.5, random() * 1.5) 33 | else: 34 | piano.play_chord([50 + random()*20, 50 + random()*20], 0.5, random() * 1.5) 35 | 36 | 37 | session.fork(piano_part) 38 | 39 | print("Making Recording...", end="") 40 | session.start_transcribing() 41 | for _ in range(8): 42 | drum.play_note(80, 1, 1) 43 | 44 | recording = False 45 | performance = session.stop_transcribing() 46 | quantized_performance = performance.quantized( 47 | QuantizationScheme([MeasureQuantizationScheme.from_time_signature("4/4", max_divisor=5, max_indigestibility=3)]) 48 | ) 49 | 50 | print("Done") 51 | 52 | while True: 53 | print("Replaying recording with quantization") 54 | session.wait(1) 55 | quantized_performance.play(clock=session) 56 | 57 | print("Replaying recording without quantization") 58 | session.wait(1) 59 | performance.play(clock=session) 60 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/repeated_notes.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | 19 | s = Session() 20 | 21 | # flute = s.new_part("flute", num_channels=3) 22 | 23 | # while True: 24 | # flute.play_note([60, 70, 60], [0.7, 0.4, 0.7], 3, blocking=False) 25 | # wait(1) 26 | # flute.play_note([70, 80, 70], [0.7, 0.4, 0.7], 3, blocking=False) 27 | # wait(1) 28 | # flute.play_note([80, 90, 80], [0.7, 0.4, 0.7], 3, blocking=False) 29 | # wait(1) 30 | 31 | piano = s.new_part("piano", num_channels=4) 32 | 33 | while True: 34 | piano.play_note(60, 0.8, 0.15, blocking=False) 35 | piano.play_note(62.5, 0.8, 0.15, blocking=False) 36 | piano.play_note(63.5, 0.8, 0.15, blocking=False) 37 | piano.play_note(67.5, 0.8, 0.15, blocking=False) 38 | wait(0.1) 39 | 40 | 41 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/save_performance.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | import random 19 | import math 20 | 21 | 22 | session = Session() 23 | 24 | shaku = session.new_part("shakuhachi") 25 | oboe = session.new_part("oboe") 26 | 27 | session.save_to_json("SavedFiles/shakEnsemble.json") 28 | 29 | 30 | def oboe_part(clock): 31 | while True: 32 | oboe.play_note(75 + random.random() * 7 + 15 * math.sin(clock.beat() / 10), 0.4, 0.25) 33 | 34 | 35 | def shaku_part(clock): 36 | assert isinstance(clock, Clock) 37 | pentatonic = [0, 2, 4, 7, 9] 38 | while True: 39 | if random.random() < 0.5: 40 | shaku.play_note(66 + random.choice(pentatonic) + 12*random.randint(0, 2), 41 | Envelope([1.0, 0.2, 1.0, 1.0], [0.15, 0.85, 0.15], [0, 2, 0]), 42 | 2.5, blocking=True) 43 | clock.wait(0.5 + random.choice([0, 0.5])) 44 | else: 45 | shaku.play_note(66 + random.choice(pentatonic) + 12*random.randint(0, 2), 1.0, 0.2*(1+random.random()*0.3)) 46 | clock.wait(random.choice([1, 2, 3])) 47 | 48 | 49 | session.fork(oboe_part) 50 | session.fork(shaku_part) 51 | 52 | session.set_tempo_target(300, 30, duration_units="time") 53 | 54 | session.wait(15) 55 | session.start_transcribing() 56 | print("Starting transcription...") 57 | session.wait(15) 58 | performance = session.stop_transcribing() 59 | print("Finished. Saving transcription.") 60 | 61 | performance.save_to_json("SavedFiles/perfShakoboe.json") 62 | 63 | session.wait_forever() 64 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/tempo_test.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | 19 | session = Session() 20 | session.fast_forward_in_time(100) 21 | 22 | engraving_settings.tempo.include_guide_marks = False 23 | 24 | violin = session.new_part("violin") 25 | 26 | session.set_tempo_target(100, 5) 27 | session.set_tempo_target(135, 26/3, truncate=False) 28 | session.set_tempo_target(135, 14, truncate=False) 29 | session.set_tempo_target(40, 18, truncate=False) 30 | session.set_tempo_target(100, 18, truncate=False) 31 | session.set_tempo_target(89, 27, truncate=False) 32 | 33 | session.start_transcribing() 34 | 35 | while session.beat() < 30: 36 | violin.play_note(70 + (session.beat() * 3) % 7, 1.0, 0.25) 37 | 38 | performance = session.stop_transcribing() 39 | 40 | # performance.to_score(time_signature="5/8").print_lilypond(True) 41 | bob = [1.5] 42 | import random 43 | while bob[-1] < 50: 44 | bob.append(bob[-1] + random.randint(1, 30) * 0.25) 45 | 46 | print(bob) 47 | performance.to_score(bar_line_locations=bob).print_lilypond(True) 48 | performance.to_score(bar_line_locations=bob).show() 49 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/ticker_test.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from clockblocks import Clock 18 | import threading 19 | import time 20 | 21 | bob = Clock("master") 22 | 23 | 24 | def wakethread(): 25 | time.sleep(1.5) 26 | ticker_clock.tempo = 10 27 | 28 | 29 | def ticker(clock): 30 | while True: 31 | print(clock.beat()) 32 | clock.wait(0.1) 33 | 34 | 35 | ticker_clock = bob.fork(ticker) 36 | threading.Thread(target=wakethread).start() 37 | bob.wait(1) 38 | bob.wait(4) 39 | 40 | print("done") 41 | -------------------------------------------------------------------------------- /examples/AssortedHaphazard/voice-leading.py: -------------------------------------------------------------------------------- 1 | from scamp import * 2 | from scamp_extensions.pitch import Scale 3 | 4 | s = Session() 5 | 6 | piano_treb = s.new_part("piano", clef_preference="treble", default_spelling_policy="E major") 7 | piano_bass = s.new_part("piano", clef_preference="bass", default_spelling_policy="E major") 8 | 9 | scl = Scale.major(64) 10 | 11 | bass_line = [-7, -6, -5, -9, -10, -12, -13, -10, -14] 12 | 13 | upper_parts = [-3, 0, 2] 14 | 15 | last_bass_degree = None 16 | 17 | def check_chord_tone(interval_with_bass): 18 | return interval_with_bass % 7 in (0, 2, 4) 19 | 20 | def move_upper_parts(old_bass_note, new_bass_note): 21 | bass_motion = new_bass_note - old_bass_note 22 | bass_motion_mod = bass_motion % 7 23 | print(bass_motion, bass_motion_mod) 24 | if bass_motion_mod == 0: 25 | return 26 | elif bass_motion_mod in [1, 6]: 27 | # stepwise motion 28 | # move to nearest consonance in opposite motion to bass 29 | direction_to_move = -1 if bass_motion > 0 else 1 30 | elif bass_motion_mod in [2, 4]: 31 | direction_to_move = -1 32 | else: 33 | direction_to_move = 1 34 | 35 | for i, p in enumerate(upper_parts): 36 | while not check_chord_tone(p - new_bass_note): 37 | p += direction_to_move 38 | upper_parts[i] = p 39 | 40 | s.start_transcribing() 41 | for bass_degree in bass_line: 42 | if last_bass_degree is not None: 43 | move_upper_parts(last_bass_degree, bass_degree) 44 | last_bass_degree = bass_degree 45 | piano_bass.play_note(scl.degree_to_pitch(bass_degree), 1, 1, blocking=False) 46 | piano_treb.play_chord(scl.degree_to_pitch(upper_parts), 1, 1) 47 | 48 | s.stop_transcribing().to_score().show() -------------------------------------------------------------------------------- /examples/AssortedHaphazard/voice-leading_weird.py: -------------------------------------------------------------------------------- 1 | from scamp import * 2 | from scamp_extensions.pitch import Scale 3 | import random 4 | 5 | s = Session() 6 | s.tempo = 200 7 | 8 | piano_treb = s.new_part("piano", clef_preference="treble", default_spelling_policy="E major") 9 | piano_bass = s.new_part("piano", clef_preference="bass", default_spelling_policy="E major") 10 | 11 | scl = Scale.major(64) 12 | 13 | bass_degree = -7 14 | 15 | upper_parts = [4, 7, 9] 16 | 17 | last_bass_degree = None 18 | 19 | def check_chord_tone(interval_with_bass): 20 | return interval_with_bass % 7 in (0, 2, 4) 21 | 22 | def move_upper_parts(old_bass_note, new_bass_note): 23 | bass_motion = new_bass_note - old_bass_note 24 | bass_motion_mod = bass_motion % 7 25 | print(bass_motion, bass_motion_mod) 26 | if bass_motion_mod == 0: 27 | return 28 | elif bass_motion_mod in [1, 6]: 29 | # stepwise motion 30 | # move to nearest consonance in opposite motion to bass 31 | direction_to_move = -1 if bass_motion > 0 else 1 32 | elif bass_motion_mod in [2, 4]: 33 | direction_to_move = -1 34 | else: 35 | direction_to_move = 1 36 | 37 | for i, p in enumerate(upper_parts): 38 | while not check_chord_tone(p - new_bass_note): 39 | p += direction_to_move 40 | upper_parts[i] = p 41 | 42 | s.start_transcribing() 43 | 44 | while s.beat() < 12: 45 | if last_bass_degree is not None: 46 | move_upper_parts(last_bass_degree, bass_degree) 47 | last_bass_degree = bass_degree 48 | piano_bass.play_note(scl.degree_to_pitch(bass_degree), 1, 0.5, "staccato", blocking=False) 49 | piano_treb.play_chord(scl.degree_to_pitch(upper_parts), 1, 0.5, "staccato") 50 | bass_degree += random.randint(-5, 5) 51 | 52 | s.stop_transcribing().to_score().show() -------------------------------------------------------------------------------- /examples/Demos/ScampCooking/Crackler.scd: -------------------------------------------------------------------------------- 1 | MarcUtilities.setOutputLimiter(0.5, globalMul: 0.3) 2 | 3 | ( 4 | ScampUtils.instrumentFromSynthDef( 5 | SynthDef(\superInst, { |freq=440, volume=0, gate=1, crackliness=0, spread=0, pan=0| 6 | var synth = Mix.fill(5, { |k| 7 | Pan2.ar( 8 | SinOsc.ar( 9 | freq * spread.linlin(0, 1, 1.0, 0.8).pow(k-2) * LFNoise1.ar(crackliness.linlin(0, 1, 30, 100), crackliness.linlin(0, 1, 0, 0.2), 1), 10 | crackliness.linexp(0, 1, 1, 300) 11 | ) * EnvGen.kr(Env.perc( 12 | attackTime: crackliness.linexp(0, 1, 0.02, 0.01), 13 | releaseTime:crackliness.linlin(0, 1, 0.2, 0.01)), 14 | gate: Impulse.kr(40)), 15 | k.linlin(0, 5, -1, 1) 16 | ) 17 | }); 18 | var panned_out = volume * synth * EnvGen.ar(Env.adsr, gate: gate, doneAction: 2) * Pan2.ar(DC.ar(1), pan); 19 | Out.ar(0, panned_out); 20 | }); 21 | ) 22 | ) 23 | -------------------------------------------------------------------------------- /examples/Demos/ScampCooking/definitions.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | 19 | # playback_settings.adjustments.set("staccato", "length * 0.3") 20 | 21 | forte_piano = Envelope( 22 | [0.8, 0.4, 1.0], [0.2, 0.8], curve_shapes=[0, 3] 23 | ) 24 | 25 | diminuendo = Envelope.from_levels([0.8, 0.3]) 26 | 27 | 28 | def wrap_in_range(value, low, high): 29 | return (value - low) % (high - low) + low 30 | 31 | 32 | bar_lines = [] 33 | 34 | 35 | def do_bar_line(beat): 36 | beats_since_last_bar_line = beat - bar_lines[-1] if len(bar_lines) > 0 else beat 37 | if beats_since_last_bar_line % 1 == 0 and beats_since_last_bar_line < 5: 38 | bar_lengths = [beats_since_last_bar_line] 39 | else: 40 | num_bars_to_add = 2 41 | while beats_since_last_bar_line / num_bars_to_add > 4: 42 | num_bars_to_add += 1 43 | last_bar_length = round(beats_since_last_bar_line / num_bars_to_add) 44 | remaining_bars_length = beats_since_last_bar_line - last_bar_length 45 | if remaining_bars_length <= 5: 46 | bar_lengths = [remaining_bars_length, last_bar_length] 47 | else: 48 | first_bar_length = remaining_bars_length % 3 + 3 if remaining_bars_length % 3 < 2 else remaining_bars_length % 3 49 | bar_lengths = [first_bar_length] + [3] * int((remaining_bars_length - first_bar_length) / 3) + [last_bar_length] 50 | for bar_length in bar_lengths: 51 | bar_lines.append(bar_lines[-1] + bar_length if len(bar_lines) > 0 else bar_length) 52 | 53 | -------------------------------------------------------------------------------- /examples/Demos/ScampCooking/formal_parameters.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | 19 | TOTAL_LENGTH = 180 20 | 21 | # golden ratio durations 22 | gr, one_minus_gr = TOTAL_LENGTH * 0.618, TOTAL_LENGTH * (1 - 0.618) 23 | 24 | cello_continue_probability = Envelope([0.4, 1.0, 0], [gr, one_minus_gr]) 25 | cello_staccato_probability = Envelope.from_levels([0, 1], length=TOTAL_LENGTH) 26 | pianoteq_gesture_length = Envelope.from_levels([0, 0, 10, 0], length=TOTAL_LENGTH) 27 | pianoteq_gesture_rit_factor = Envelope([0.25, 0.8, 0.1], [gr, one_minus_gr]) 28 | pianoteq_filler_probability = Envelope([0.3, 0.97, 0.3], [gr, one_minus_gr]) 29 | crackliness = Envelope([0.6, 1.0, 0], [gr, one_minus_gr]) 30 | sc_gesture_length = Envelope([5, 2, 5], [gr, one_minus_gr]) 31 | sc_register = Envelope([72, 72, 60, 60, 48, 48, 36, 36 ], 32 | [TOTAL_LENGTH * 0.7, 0, TOTAL_LENGTH * 0.1, 0, TOTAL_LENGTH * 0.1, 0, TOTAL_LENGTH * 0.1]) 33 | 34 | 35 | if __name__ == "__main__": 36 | cello_continue_probability.show_plot("Cello continue probability") 37 | cello_staccato_probability.show_plot("Cello staccato probability") 38 | pianoteq_gesture_length.show_plot("Pianoteq gesture length") 39 | pianoteq_gesture_rit_factor.show_plot("Pianoteq rit factor") 40 | pianoteq_filler_probability.show_plot("Pianoteq filler probability") 41 | crackliness.show_plot("SC instrument crackliness") 42 | sc_gesture_length.show_plot("SC instrument gesture length") 43 | sc_register.show_plot("SC instrument register") 44 | -------------------------------------------------------------------------------- /examples/Demos/ScampToMax/Monophonic/MonoSender.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | from random import random 19 | 20 | s = Session() 21 | 22 | maxinst = s.new_osc_part("maxinst", 9000) 23 | 24 | while True: 25 | for p in range(65, 80): 26 | maxinst.play_note(p, [0.2, 1.0], 1.0, "param_overdrive:[0, 0.8, 0]") 27 | if random() < 0.5: 28 | wait(random()) 29 | -------------------------------------------------------------------------------- /examples/Demos/ScampToMax/Polyphonic/PolySender.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | import random 19 | s = Session() 20 | 21 | maxinst = s.new_osc_part("maxinst", 9000) 22 | 23 | while True: 24 | maxinst.play_note( 25 | [random.uniform(60, 80), random.uniform(60, 80)], 26 | [0.2, 1.0], 27 | 5.0, 28 | "param_overdrive:[0, 0.8, 0]", 29 | blocking=False 30 | ) 31 | wait(random.uniform(0, 5)) 32 | -------------------------------------------------------------------------------- /examples/Reconstructions/piano_phase.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from scamp import * 18 | 19 | session = Session() 20 | 21 | piano1 = session.new_part("Piano") 22 | piano2 = session.new_part("Piano") 23 | 24 | pitches = [64, 66, 71, 73, 74, 66, 64, 73, 71, 66, 74, 73] 25 | 26 | 27 | def piano_part(which_piano): 28 | while True: 29 | for pitch in pitches: 30 | which_piano.play_note(pitch, 1.0, 0.25) 31 | 32 | 33 | clock1 = session.fork(piano_part, args=(piano1,), initial_tempo=100) 34 | clock2 = session.fork(piano_part, args=(piano2,), initial_tempo=98) 35 | 36 | session.start_transcribing(clock=clock1) 37 | session.wait(30) 38 | 39 | performance = session.stop_transcribing() 40 | performance.to_score(QuantizationScheme.from_time_signature("3/4", 16)).show() 41 | -------------------------------------------------------------------------------- /examples/Tutorial/01_hello_world.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Hello World 3 | 4 | Plays a C major arpeggio. 5 | """ 6 | 7 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 8 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 9 | # Copyright © 2020 Marc Evanstein . # 10 | # # 11 | # This program is free software: you can redistribute it and/or modify it under the terms of # 12 | # the GNU General Public License as published by the Free Software Foundation, either version # 13 | # 3 of the License, or (at your option) any later version. # 14 | # # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 16 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 17 | # See the GNU General Public License for more details. # 18 | # # 19 | # You should have received a copy of the GNU General Public License along with this program. # 20 | # If not, see . # 21 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 22 | 23 | # import the scamp namespace 24 | from scamp import * 25 | # construct a session object 26 | s = Session() 27 | # add a new s part to the session 28 | violin = s.new_part("Violin") 29 | # looping through the MIDI pitches 30 | # of a C major arpeggio... 31 | for pitch in [60, 64, 67, 72]: 32 | # play each pitch sequentially 33 | # with volume of 1 (full volume) 34 | # and duration of half a beat 35 | violin.play_note(pitch, 1, 0.5) 36 | -------------------------------------------------------------------------------- /examples/Tutorial/02_tempo_change.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Tempo Change 3 | 4 | Same as Hello World example, but at half-tempo. 5 | """ 6 | 7 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 8 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 9 | # Copyright © 2020 Marc Evanstein . # 10 | # # 11 | # This program is free software: you can redistribute it and/or modify it under the terms of # 12 | # the GNU General Public License as published by the Free Software Foundation, either version # 13 | # 3 of the License, or (at your option) any later version. # 14 | # # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 16 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 17 | # See the GNU General Public License for more details. # 18 | # # 19 | # You should have received a copy of the GNU General Public License along with this program. # 20 | # If not, see . # 21 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 22 | 23 | # import the scamp namespace 24 | from scamp import * 25 | # construct a session object 26 | s = Session() 27 | # change to half of default tempo 28 | s.tempo = 30 29 | 30 | # add a new violin part to the session 31 | violin = s.new_part("Violin") 32 | # looping through the MIDI pitches 33 | # of a C major arpeggio... 34 | for pitch in [60, 64, 67, 72]: 35 | # play each pitch sequentially 36 | # with volume of 1 (full volume) 37 | # and duration of half a beat 38 | violin.play_note(pitch, 1, 0.5) 39 | -------------------------------------------------------------------------------- /examples/Tutorial/03_blocking_false.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Blocking False 3 | 4 | Demonstrating the ability to have non-blocking calls to play_note. This plays two notes that each last 5 | for two beats, but overlapping by one beat. 6 | """ 7 | 8 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 9 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 10 | # Copyright © 2020 Marc Evanstein . # 11 | # # 12 | # This program is free software: you can redistribute it and/or modify it under the terms of # 13 | # the GNU General Public License as published by the Free Software Foundation, either version # 14 | # 3 of the License, or (at your option) any later version. # 15 | # # 16 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 17 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 18 | # See the GNU General Public License for more details. # 19 | # # 20 | # You should have received a copy of the GNU General Public License along with this program. # 21 | # If not, see . # 22 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 23 | 24 | # import the scamp namespace 25 | from scamp import * 26 | # construct a session object 27 | s = Session() 28 | # add a new violin part to the session 29 | violin = s.new_part("Violin") 30 | 31 | # start playing a 2-beat C, but return immediately 32 | violin.play_note(60, 1, 2, blocking=False) 33 | # wait for only one beat 34 | wait(1) 35 | # start playing a 2-beat E, blocking this time 36 | violin.play_note(64, 1, 2) 37 | -------------------------------------------------------------------------------- /examples/Tutorial/04_random_durations_and_rests.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Random Durations and Rests 3 | 4 | Loops a C major arpeggio twice, but with random, floating-point durations. 5 | Also sometimes adds a rest of up to a second between notes. 6 | """ 7 | 8 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 9 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 10 | # Copyright © 2020 Marc Evanstein . # 11 | # # 12 | # This program is free software: you can redistribute it and/or modify it under the terms of # 13 | # the GNU General Public License as published by the Free Software Foundation, either version # 14 | # 3 of the License, or (at your option) any later version. # 15 | # # 16 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 17 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 18 | # See the GNU General Public License for more details. # 19 | # # 20 | # You should have received a copy of the GNU General Public License along with this program. # 21 | # If not, see . # 22 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 23 | 24 | # import the scamp namespace 25 | from scamp import * 26 | # import the random module from 27 | # the python standard library 28 | import random 29 | 30 | # construct a session object 31 | s = Session() 32 | # add a new violin part to the session 33 | violin = s.new_part("Violin") 34 | 35 | for _ in range(2): # loop twice 36 | for pitch in [60, 64, 67, 72]: 37 | # pick a duration between 0.5 to 1.5 38 | dur = random.uniform(0.5, 1.5) 39 | # play a note of that duration 40 | violin.play_note(pitch, 1, dur) 41 | # with probability of one half, wait for between 0 and 1 seconds 42 | if random.random() < 0.5: 43 | # note that "wait" is the same here as "s.wait". What "wait" does it find the 44 | # clock operating on the given thread and call wait on that. In this case, that 45 | # clock is the Session as a whole. 46 | wait(random.random()) 47 | -------------------------------------------------------------------------------- /examples/Tutorial/06_time_signature.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Time Signatures 3 | 4 | Plays a simple C Major arpeggio, and generates notation for it in three different ways: 5 | - with a 3/8 time signature 6 | - with a 3/8 time signature for the first measure followed by 2/4 7 | - with alternating 3/8 and 2/4 time signatures 8 | - with a list of bar lengths determining time signatures 9 | """ 10 | 11 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 12 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 13 | # Copyright © 2020 Marc Evanstein . # 14 | # # 15 | # This program is free software: you can redistribute it and/or modify it under the terms of # 16 | # the GNU General Public License as published by the Free Software Foundation, either version # 17 | # 3 of the License, or (at your option) any later version. # 18 | # # 19 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 20 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 21 | # See the GNU General Public License for more details. # 22 | # # 23 | # You should have received a copy of the GNU General Public License along with this program. # 24 | # If not, see . # 25 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 26 | 27 | from scamp import * 28 | 29 | s = Session() 30 | violin = s.new_part("Violin") 31 | 32 | # begin recording (defaults to transcribing 33 | # all instruments within the session) 34 | s.start_transcribing() 35 | for _ in range(4): 36 | for pitch in [60, 64, 67, 72]: 37 | violin.play_note(pitch, 1, 0.5) 38 | # stop the recording and save the recorded 39 | # note events as a performance 40 | 41 | performance = s.stop_transcribing() 42 | # quantize and convert the performance to a Score object and open it as a PDF, 43 | # this time imposing a time signature of 3/8 44 | performance.to_score(time_signature="3/8").show() 45 | 46 | # a list is interpreted as a (non-repeating) sequence of time signatures 47 | performance.to_score(time_signature=["3/8", "2/4"]).show() 48 | 49 | # if a repeating sequence of time signatures is desires, end the list with "loop" 50 | performance.to_score(time_signature=["3/8", "2/4", "loop"]).show() 51 | 52 | # Alternatively, bar line locations can be given and sensible time signatures will be selected 53 | performance.to_score(bar_line_locations=[1.5, 3.5, 8]).show() 54 | -------------------------------------------------------------------------------- /examples/Tutorial/07_random_quantized.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Quantization of Random Floating-Point Durations 3 | 4 | Similar to the "Random Durations and Rests" example, except that now we generate 5 | notation. Since the lengths are floating point, quantization occurs in the call to "to_score" 6 | """ 7 | 8 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 9 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 10 | # Copyright © 2020 Marc Evanstein . # 11 | # # 12 | # This program is free software: you can redistribute it and/or modify it under the terms of # 13 | # the GNU General Public License as published by the Free Software Foundation, either version # 14 | # 3 of the License, or (at your option) any later version. # 15 | # # 16 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 17 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 18 | # See the GNU General Public License for more details. # 19 | # # 20 | # You should have received a copy of the GNU General Public License along with this program. # 21 | # If not, see . # 22 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 23 | 24 | from scamp import * 25 | import random 26 | 27 | s = Session() 28 | violin = s.new_part("Violin") 29 | 30 | # begin recording 31 | s.start_transcribing() 32 | 33 | for _ in range(2): # loop twice 34 | for pitch in [60, 64, 67, 72]: 35 | dur = random.uniform(0.5, 1.5) 36 | violin.play_note(pitch, 1, dur) 37 | if random.random() < 0.5: 38 | # note that "wait" is the same here as "s.wait". What "wait" does it find the 39 | # clock operating on the given thread and call wait on that. In this case, that 40 | # clock is the Session as a whole. 41 | wait(random.random()) 42 | 43 | performance = s.stop_transcribing() 44 | 45 | # quantize and convert the score in 3/4 46 | performance.to_score(time_signature="3/4").show() 47 | -------------------------------------------------------------------------------- /examples/Tutorial/08_simplified_quantization.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Simplified Quantization 3 | 4 | Same as last example except that a restriction on the max beat divisor is 5 | imposed on the quantization, rendering simpler -- if somewhat less accurate -- results. 6 | """ 7 | 8 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 9 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 10 | # Copyright © 2020 Marc Evanstein . # 11 | # # 12 | # This program is free software: you can redistribute it and/or modify it under the terms of # 13 | # the GNU General Public License as published by the Free Software Foundation, either version # 14 | # 3 of the License, or (at your option) any later version. # 15 | # # 16 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 17 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 18 | # See the GNU General Public License for more details. # 19 | # # 20 | # You should have received a copy of the GNU General Public License along with this program. # 21 | # If not, see . # 22 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 23 | 24 | from scamp import * 25 | import random 26 | 27 | s = Session() 28 | violin = s.new_part("Violin") 29 | 30 | # begin recording 31 | s.start_transcribing() 32 | 33 | for _ in range(2): # loop twice 34 | for pitch in [60, 64, 67, 72]: 35 | dur = random.uniform(0.5, 1.5) 36 | violin.play_note(pitch, 1, dur) 37 | if random.random() < 0.5: 38 | # note that "wait" is the same here as "s.wait". What "wait" does it find the 39 | # clock operating on the given thread and call wait on that. In this case, that 40 | # clock is the Session as a whole. 41 | wait(random.random()) 42 | 43 | performance = s.stop_transcribing() 44 | 45 | # first show the score with the default quantization settings 46 | performance.to_score(time_signature="3/4", title="Default Quantization").show() 47 | # now try it again imposing a max divisor of 4 this time 48 | performance.to_score(time_signature="3/4", max_divisor=4, title="Max Divisor of 4").show() 49 | # finally, try it with a higher max divisor, but a strong simplicity_preference 50 | performance.to_score(time_signature="3/4", simplicity_preference=3, title="Strong Simplicity Preference").show() 51 | -------------------------------------------------------------------------------- /examples/Tutorial/09_multi_part.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Multi-Part Music 3 | 4 | Plays two coordinated but independent parallel parts, one for oboe and one for bassoon. 5 | """ 6 | 7 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 8 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 9 | # Copyright © 2020 Marc Evanstein . # 10 | # # 11 | # This program is free software: you can redistribute it and/or modify it under the terms of # 12 | # the GNU General Public License as published by the Free Software Foundation, either version # 13 | # 3 of the License, or (at your option) any later version. # 14 | # # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 16 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 17 | # See the GNU General Public License for more details. # 18 | # # 19 | # You should have received a copy of the GNU General Public License along with this program. # 20 | # If not, see . # 21 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 22 | 23 | from scamp import * 24 | import random 25 | 26 | # one way of setting an initial tempo 27 | s = Session(tempo=100) 28 | 29 | oboe = s.new_part("oboe") 30 | bassoon = s.new_part("bassoon") 31 | 32 | 33 | # define a function for the oboe part 34 | def oboe_part(): 35 | # play random notes until we have 36 | # passed beat 7 in the session 37 | while s.beat() < 7: 38 | pitch = int(random.uniform(67, 79)) 39 | volume = random.uniform(0.5, 1) 40 | length = random.uniform(0.25, 1) 41 | oboe.play_note(pitch, volume, length) 42 | # end with a note of exactly the right 43 | # length to take us to the end of beat 8 44 | oboe.play_note(80, 1.0, 8 - s.beat()) 45 | 46 | 47 | # define a function for the bassoon part 48 | def bassoon_part(): 49 | # simply play quarter notes on random 50 | # pitches for 8 beats 51 | while s.beat() < 8: 52 | bassoon.play_note( 53 | random.randint(52, 59), 1, 1 54 | ) 55 | 56 | 57 | s.start_transcribing() 58 | # start the oboe and bassoon parts as two parallel child processes 59 | s.fork(oboe_part) 60 | s.fork(bassoon_part) 61 | # have the session wait for the child processes to finish (return) 62 | s.wait_for_children_to_finish() 63 | performance = s.stop_transcribing() 64 | performance.to_score().show() 65 | -------------------------------------------------------------------------------- /examples/Tutorial/12_envelopes_basic.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Envelopes (basic) 3 | 4 | Create and plot two simple envelopes, one with evenly-spaced linear segments, and one with uneven, curved segments. 5 | """ 6 | 7 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 8 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 9 | # Copyright © 2020 Marc Evanstein . # 10 | # # 11 | # This program is free software: you can redistribute it and/or modify it under the terms of # 12 | # the GNU General Public License as published by the Free Software Foundation, either version # 13 | # 3 of the License, or (at your option) any later version. # 14 | # # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 16 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 17 | # See the GNU General Public License for more details. # 18 | # # 19 | # You should have received a copy of the GNU General Public License along with this program. # 20 | # If not, see . # 21 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 22 | 23 | from scamp import Envelope 24 | 25 | e = Envelope.from_levels( 26 | [60, 72, 66, 70] 27 | ) 28 | e.show_plot("Evenly Spaced") 29 | 30 | e = Envelope( 31 | [60, 72, 66, 70], [2, 1, 1], 32 | curve_shapes=[2, -2, -2] 33 | ) 34 | e.show_plot("Uneven with Shaping") 35 | -------------------------------------------------------------------------------- /examples/Tutorial/14_gliss_from_envelope.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Glissando from Envelope 3 | 4 | Plays a glissando by passing an Envelope to the pitch parameter of play_note. 5 | """ 6 | 7 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 8 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 9 | # Copyright © 2020 Marc Evanstein . # 10 | # # 11 | # This program is free software: you can redistribute it and/or modify it under the terms of # 12 | # the GNU General Public License as published by the Free Software Foundation, either version # 13 | # 3 of the License, or (at your option) any later version. # 14 | # # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 16 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 17 | # See the GNU General Public License for more details. # 18 | # # 19 | # You should have received a copy of the GNU General Public License along with this program. # 20 | # If not, see . # 21 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 22 | 23 | from scamp import * 24 | 25 | s = Session() 26 | viola = s.new_part("viola") 27 | 28 | e = Envelope( 29 | [60, 72, 66, 70], [3 * 4/5, 1 * 4/5, 1 * 4/5], 30 | curve_shapes=[2, -2, -2] 31 | ) 32 | 33 | s.start_transcribing() 34 | 35 | viola.play_note(e, 1.0, 4) 36 | 37 | s.stop_transcribing().to_score().show() 38 | -------------------------------------------------------------------------------- /examples/Tutorial/15_envelope_shorthand.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Envelope Shorthand 3 | 4 | Plays two glissandi by passing lists instead of envelopes to the pitch argument of play_note. 5 | """ 6 | 7 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 8 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 9 | # Copyright © 2020 Marc Evanstein . # 10 | # # 11 | # This program is free software: you can redistribute it and/or modify it under the terms of # 12 | # the GNU General Public License as published by the Free Software Foundation, either version # 13 | # 3 of the License, or (at your option) any later version. # 14 | # # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 16 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 17 | # See the GNU General Public License for more details. # 18 | # # 19 | # You should have received a copy of the GNU General Public License along with this program. # 20 | # If not, see . # 21 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 22 | 23 | from scamp import * 24 | 25 | s = Session() 26 | viola = s.new_part("viola") 27 | 28 | s.start_transcribing() 29 | 30 | # a list of values results in evenly spaced glissando 31 | viola.play_note([60, 70, 55], 1.0, 4) 32 | 33 | # a list of lists can give values, durations, and (optionally) curve shapes 34 | # this results in segment durations of 2 and 1 and curve shapes of -2 and 0 35 | # NB: by default, envelopes created through list shorthand like this are 36 | # resized to the duration of the note, whereas Envelope objects passed directly 37 | # are not. So in this case, the durations of 2 and 1 get scaled up to 8/3 and 4/3 38 | # so that the whole envelope lasts 4 beats (the duration of the note). 39 | # You can change this behavior so that it resizes for Envelope objects as well, 40 | # or so that it never resizes, by altering playback_settings.resize_parameter_envelopes 41 | viola.play_note([[60, 70, 55], [2, 1], [-2, 0]], 1.0, 4) 42 | 43 | performance = s.stop_transcribing() 44 | performance.to_score().show() 45 | -------------------------------------------------------------------------------- /examples/Tutorial/16_start_and_end_note.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Start and End Note 3 | 4 | Plays notes by calling start_note (or start_chord) and then manipulating them afterward, instead of defining the course 5 | of the note from the beginning with "play_note". The "start_note" and "start_chord" functions return handles that can 6 | be used to change the pitch, volume or other parameters of the note after they have started, as well as end the note. 7 | """ 8 | 9 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 10 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 11 | # Copyright © 2020 Marc Evanstein . # 12 | # # 13 | # This program is free software: you can redistribute it and/or modify it under the terms of # 14 | # the GNU General Public License as published by the Free Software Foundation, either version # 15 | # 3 of the License, or (at your option) any later version. # 16 | # # 17 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 18 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 19 | # See the GNU General Public License for more details. # 20 | # # 21 | # You should have received a copy of the GNU General Public License along with this program. # 22 | # If not, see . # 23 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 24 | 25 | from scamp import * 26 | 27 | s = Session() 28 | 29 | violin = s.new_part("violin") 30 | 31 | s.start_transcribing() 32 | 33 | # start a violin note on middle C with volume 1 34 | note_handle1 = violin.start_note(60, 1.0) 35 | wait(1) 36 | # after 1 second, start a chord on Bb4 / D5 / F#5 with volume 1 37 | note_handle2 = violin.start_chord([70, 74, 78], 1.0) 38 | # change the pitch of the first note to F#3, glissing over the course of 2 seconds 39 | note_handle1.change_pitch(54, 2.0) 40 | wait(1) 41 | # one second later, start changing the pitch of the chord so that its first note (the Bb4) glisses up to E5 42 | # over the course of one second. The other notes of the chord will move in parallel to Ab5 and C6. 43 | note_handle2.change_pitch(76, 1.0) 44 | wait(2) 45 | # after 2 seconds, end the first note and start a fade out to volume 0 over the course of 2 seconds on the chord. 46 | note_handle1.end() 47 | note_handle2.change_volume(0, 2) 48 | # wait 2 seconds and then end the second chord 49 | wait(2) 50 | note_handle2.end() 51 | 52 | s.stop_transcribing().to_score().show() 53 | -------------------------------------------------------------------------------- /examples/Tutorial/17_volume_envelope.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Volume Envelope 3 | 4 | Plots an envelope representing a forte-piano-crescendo dynamic, and then uses it to affect the dynamics of a note's 5 | playback. This example shows that an Envelope can be passed to the volume argument of "play_note", just like it can 6 | to the pitch argument. (The list short-hand also works, by the way.) 7 | """ 8 | 9 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 10 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 11 | # Copyright © 2020 Marc Evanstein . # 12 | # # 13 | # This program is free software: you can redistribute it and/or modify it under the terms of # 14 | # the GNU General Public License as published by the Free Software Foundation, either version # 15 | # 3 of the License, or (at your option) any later version. # 16 | # # 17 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 18 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 19 | # See the GNU General Public License for more details. # 20 | # # 21 | # You should have received a copy of the GNU General Public License along with this program. # 22 | # If not, see . # 23 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 24 | 25 | from scamp import * 26 | 27 | s = Session() 28 | viola = s.new_part("viola") 29 | 30 | fp_cresc = Envelope( 31 | [0.8, 0.3, 1], 32 | [0.2, 3.8], 33 | curve_shapes=[2, 4] 34 | ) 35 | 36 | # plot the dynamic curve 37 | fp_cresc.show_plot("fp cresc.") 38 | 39 | # play a note with the dynamic curve 40 | viola.play_note(48, fp_cresc, 4) 41 | -------------------------------------------------------------------------------- /examples/Tutorial/18_microtonal.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Microtonal Playback and Notation 3 | 4 | Plays a few microtonal chords and notates them, turning on exact microtonal annotations. 5 | """ 6 | 7 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 8 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 9 | # Copyright © 2020 Marc Evanstein . # 10 | # # 11 | # This program is free software: you can redistribute it and/or modify it under the terms of # 12 | # the GNU General Public License as published by the Free Software Foundation, either version # 13 | # 3 of the License, or (at your option) any later version. # 14 | # # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 16 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 17 | # See the GNU General Public License for more details. # 18 | # # 19 | # You should have received a copy of the GNU General Public License along with this program. # 20 | # If not, see . # 21 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 22 | 23 | from scamp import * 24 | 25 | s = Session() 26 | piano = s.new_part("piano") 27 | 28 | s.start_transcribing() 29 | 30 | # this causes the true pitches to be written into the score by the notes, as midi pitch values. 31 | engraving_settings.show_microtonal_annotations = True 32 | # this can be used to control the max precision shown in the annotations (the default is 2 digits). 33 | # in this case, it doesn't really do anything, since the pitches are already rounded to 1 digit 34 | engraving_settings.microtonal_annotation_digits = 3 35 | 36 | # playing microtonal pitches is as simple as using floating-point values for pitch 37 | piano.play_chord([62.7, 71.3], 1.0, 1) 38 | piano.play_chord([65.2, 70.9], 1.0, 1) 39 | piano.play_chord([71.5, 74.3], 1.0, 1) 40 | 41 | s.stop_transcribing().to_score().show() 42 | -------------------------------------------------------------------------------- /examples/Tutorial/19_playback_implementations.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Playback Implementations 3 | 4 | Shows how to create parts that use different implementations for playback. This assumes that you are connecting to 5 | a midi device on port zero. 6 | """ 7 | 8 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 9 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 10 | # Copyright © 2020 Marc Evanstein . # 11 | # # 12 | # This program is free software: you can redistribute it and/or modify it under the terms of # 13 | # the GNU General Public License as published by the Free Software Foundation, either version # 14 | # 3 of the License, or (at your option) any later version. # 15 | # # 16 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 17 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 18 | # See the GNU General Public License for more details. # 19 | # # 20 | # You should have received a copy of the GNU General Public License along with this program. # 21 | # If not, see . # 22 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 23 | 24 | from scamp import * 25 | 26 | s = Session() 27 | # Calling "new_part" results in a default SoundfontPlaybackImplementation, and 28 | # add_streaming_midi_playback gives this a MIDIStreamPlaybackImplementation as well 29 | # (here, port 0 is used for output) 30 | piano = s.new_part("piano").add_streaming_midi_playback(0) 31 | # Calling "new_osc_part" gives the instrument an OSCPlaybackImplementation 32 | # This one is set up to communicate with the supercollider instrument in the osc_to_supercollider.scd example 33 | synth = s.new_osc_part("vibrato", ip_address="127.0.0.1", port=57120) 34 | # Calling "new_silent_part" results in an instrument with no PlaybackImplementation 35 | silent = s.new_silent_part("silent") 36 | 37 | s.start_transcribing() 38 | 39 | for _ in range(4): 40 | piano.play_note(60, 1, 0.5) 41 | synth.play_note(62, 1, 0.5) 42 | silent.play_note(63, 1, 0.5) 43 | 44 | s.stop_transcribing().to_score(time_signature="6/8").show() 45 | -------------------------------------------------------------------------------- /examples/Tutorial/24_osc_to_supercollider.scd: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------------------------------- 2 | // Corresponding SuperCollider Code for OSC-to-SuperCollider SCAMP example. Run this before the Python 3 | // script by booting the server and then running the code blocks below in order. 4 | // -------------------------------------------------------------------------------------------------- 5 | 6 | 7 | // If it's not working, make sure the port printed here matches the port that the Python file is sending to 8 | NetAddr.langPort.postln; 9 | 10 | 11 | ( 12 | // ----------------------------- 1) Define vib synth ---------------------------------- 13 | SynthDef(\vibSynth, { |out=0, freq=440, gain=0.1, vibFreq=20, vibWidth=0.5, gate=1| 14 | var envelope = EnvGen.ar(Env.asr(releaseTime:0.5), gate, doneAction: 2); 15 | var vibHalfSteps = SinOsc.ar(vibFreq) * vibWidth; 16 | var vibFreqMul = 2.pow(vibHalfSteps / 12); 17 | var vibSine = SinOsc.ar(freq * vibFreqMul) * gain; 18 | Out.ar(out, (envelope * vibSine / 5) ! 2); 19 | }, [\ir, 0.1, 0.1, 0.1, 0.1, \kr]).add; 20 | ) 21 | 22 | 23 | ( 24 | // ---------------------------- 2) Set up OSC Receivers ------------------------------- 25 | 26 | // This dictionary maps the note-id's sent by SCAMP to their associated Synths 27 | ~notesPlaying = Dictionary(); 28 | 29 | // START NOTE 30 | OSCFunc({ arg msg, time, addr, recvPort; 31 | var id = msg[1], pitch = msg[2], volume = msg[3]; 32 | // Start a new Synth and place it in the ~notesPlaying dictionary under its associated id 33 | ~notesPlaying.put(id, Synth(\vibSynth, 34 | [\freq, pitch.midicps, 35 | \gain, (-40 * (1-volume)).dbamp] 36 | )); 37 | }, '/vibrato/start_note'); 38 | 39 | // END NOTE 40 | OSCFunc({ arg msg, time, addr, recvPort; 41 | var id = msg[1]; 42 | ~notesPlaying[id].set(\gate, 0); 43 | }, '/vibrato/end_note'); 44 | 45 | // CHANGE PITCH 46 | OSCFunc({ arg msg, time, addr, recvPort; 47 | var id = msg[1], pitch = msg[2]; 48 | ~notesPlaying[id].set(\freq, pitch.midicps); 49 | }, '/vibrato/change_pitch'); 50 | 51 | // CHANGE VOLUME 52 | OSCFunc({ arg msg, time, addr, recvPort; 53 | var id = msg[1], volume = msg[2]; 54 | ~notesPlaying[id].set(\gain, 55 | (-40 * (1-volume)).dbamp); 56 | }, '/vibrato/change_volume'); 57 | 58 | // CHANGE VIBRATO FREQUENCY 59 | OSCFunc({ arg msg, time, addr, recvPort; 60 | var id = msg[1], freq = msg[2]; 61 | ~notesPlaying[id].set(\vibFreq, freq); 62 | }, '/vibrato/change_parameter/vibFreq'); 63 | 64 | // CHANGE VIBRATO WIDTH 65 | OSCFunc({ arg msg, time, addr, recvPort; 66 | var id = msg[1], width = msg[2]; 67 | ~notesPlaying[id].set(\vibWidth, width); 68 | }, '/vibrato/change_parameter/vibWidth'); 69 | ) 70 | -------------------------------------------------------------------------------- /examples/Tutorial/25_MIDI_in_out.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP EXAMPLE: Live MIDI input and output 3 | 4 | Demonstration of receiving and sending live midi input to and from a midi keyboard. Every note received by the keyboard 5 | is immediately sent back to the keyboard a perfect fifth higher. 6 | """ 7 | 8 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 9 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 10 | # Copyright © 2020 Marc Evanstein . # 11 | # # 12 | # This program is free software: you can redistribute it and/or modify it under the terms of # 13 | # the GNU General Public License as published by the Free Software Foundation, either version # 14 | # 3 of the License, or (at your option) any later version. # 15 | # # 16 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 17 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 18 | # See the GNU General Public License for more details. # 19 | # # 20 | # You should have received a copy of the GNU General Public License along with this program. # 21 | # If not, see . # 22 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 23 | 24 | from scamp import * 25 | 26 | s = Session() 27 | 28 | s.print_available_midi_output_devices() 29 | 30 | piano = s.new_midi_part("piano", midi_output_device=0, num_channels=15) 31 | 32 | # dictionary mapping keys that are down to the NoteHandles used to manipulate them. 33 | notes_started = {} 34 | 35 | 36 | def midi_callback(midi_message): 37 | code, pitch, volume = midi_message 38 | if volume > 0 and 144 <= code <= 159: 39 | notes_started[pitch] = piano.start_note(pitch + 7, volume/127) 40 | elif (volume == 0 and 144 <= code <= 159 or 128 <= code <= 143) and pitch in notes_started: 41 | notes_started[pitch].end() 42 | del notes_started[pitch] 43 | 44 | 45 | s.register_midi_listener(0, midi_callback) 46 | s.wait_forever() 47 | -------------------------------------------------------------------------------- /examples/Tutorial/26_keyboard_input.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP EXAMPLE: Computer Keyboard Input 3 | 4 | (WARNING: consumes key events and makes the keyboard otherwise unresponsive. To avoid this, you can remove the 5 | suppress=True flag under register_keyboard_listener) 6 | 7 | Demonstration of receiving computer keyboard events and using them to play notes based on the key number. Any key 8 | whose number code lies within a reasonable range triggers the playback of a note of that MIDI pitch. 9 | """ 10 | 11 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 12 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 13 | # Copyright © 2020 Marc Evanstein . # 14 | # # 15 | # This program is free software: you can redistribute it and/or modify it under the terms of # 16 | # the GNU General Public License as published by the Free Software Foundation, either version # 17 | # 3 of the License, or (at your option) any later version. # 18 | # # 19 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 20 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 21 | # See the GNU General Public License for more details. # 22 | # # 23 | # You should have received a copy of the GNU General Public License along with this program. # 24 | # If not, see . # 25 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 26 | 27 | from scamp import * 28 | 29 | s = Session() 30 | 31 | piano = s.new_part("piano") 32 | 33 | # dictionary mapping keys that are down to the NoteHandles used to manipulate them. 34 | notes_started = {} 35 | 36 | 37 | def key_down(name, number): 38 | if 20 < number < 110 and number not in notes_started: 39 | notes_started[number] = piano.start_note(number, 0.5) 40 | 41 | 42 | def key_up(name, number): 43 | if 20 < number < 110 and number in notes_started: 44 | notes_started[number].end() 45 | del notes_started[number] 46 | 47 | 48 | # note: suppress=True causes keyboard events to be consumed by this script, effectively disabling the keyboard 49 | s.register_keyboard_listener(on_press=key_down, on_release=key_up, suppress=True) 50 | s.wait_forever() 51 | -------------------------------------------------------------------------------- /examples/Tutorial/28a_osc_listener.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: OSC Listener 3 | 4 | Sets up an osc listener using Session.register_osc_listener, which takes in OSC messages and plays back notes and 5 | horrific bagpipe cluster. To run this example, first run this script, and then run 28b_osc_sender.py, which sends 6 | messages to trigger playback. (Of course, the real value of this is that incoming OSC messages can come from anywhere 7 | and can therefore be used to modify an ongoing SCAMP process.) 8 | """ 9 | 10 | 11 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 12 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 13 | # Copyright © 2020 Marc Evanstein . # 14 | # # 15 | # This program is free software: you can redistribute it and/or modify it under the terms of # 16 | # the GNU General Public License as published by the Free Software Foundation, either version # 17 | # 3 of the License, or (at your option) any later version. # 18 | # # 19 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 20 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 21 | # See the GNU General Public License for more details. # 22 | # # 23 | # You should have received a copy of the GNU General Public License along with this program. # 24 | # If not, see . # 25 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 26 | 27 | from scamp import * 28 | 29 | s = Session() 30 | piano = s.new_part("piano") 31 | flute = s.new_part("flute") 32 | bagpipe = s.new_part("bagpipe") 33 | 34 | 35 | def play_note_callback(osc_address, pitch, volume, length): 36 | if osc_address.split("/")[-1] == "piano": 37 | piano.play_note(pitch, volume, length, blocking=False) 38 | elif osc_address.split("/")[-1] == "flute": 39 | flute.play_note(pitch, volume, length, blocking=False) 40 | 41 | 42 | def bagpipe_callback(osc_address): 43 | bagpipe.play_chord([70, 71, 72, 73, 74, 75, 76], 0.5, 0.2, blocking=False) 44 | 45 | 46 | s.register_osc_listener(5995, "/play_note/*", play_note_callback) 47 | s.register_osc_listener(5995, "/play_bagpipe_cluster", bagpipe_callback) 48 | 49 | 50 | s.wait_forever() 51 | -------------------------------------------------------------------------------- /examples/Tutorial/28b_osc_sender.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: OSC Sender 3 | 4 | This doesn't really use SCAMP; it is merely a python script that sends out a few choice osc messages of the sort that 5 | 28a_osc_listener.py is designed to respond to. 6 | """ 7 | 8 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 9 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 10 | # Copyright © 2020 Marc Evanstein . # 11 | # # 12 | # This program is free software: you can redistribute it and/or modify it under the terms of # 13 | # the GNU General Public License as published by the Free Software Foundation, either version # 14 | # 3 of the License, or (at your option) any later version. # 15 | # # 16 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 17 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 18 | # See the GNU General Public License for more details. # 19 | # # 20 | # You should have received a copy of the GNU General Public License along with this program. # 21 | # If not, see . # 22 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 23 | 24 | from pythonosc import udp_client 25 | import time 26 | 27 | 28 | client = udp_client.SimpleUDPClient("127.0.0.1", 5995) 29 | 30 | # play a chromatic scale on the piano 31 | for x in range(65, 85): 32 | client.send_message("/play_note/piano", [x, 0.5, 0.1]) 33 | time.sleep(0.1) 34 | 35 | # play a horrifying bagpipe cluster 36 | client.send_message("/play_bagpipe_cluster", []) 37 | time.sleep(0.5) 38 | 39 | # play a chromatic scale on the flute 40 | for x in range(65, 85): 41 | client.send_message("/play_note/flute", [x, 0.5, 0.1]) 42 | time.sleep(0.1) 43 | 44 | # play another horrifying bagpipe cluster 45 | client.send_message("/play_bagpipe_cluster", []) 46 | time.sleep(0.5) 47 | 48 | # play a chromatic scale alternating between flute and piano 49 | for x in range(65, 85): 50 | client.send_message("/play_note/" + ("flute", "piano")[x % 2], [x, 0.5, 0.1]) 51 | time.sleep(0.1) 52 | -------------------------------------------------------------------------------- /roadmap/Roadmap 1.0.txt: -------------------------------------------------------------------------------- 1 | Documentation: 2 | - Point to the example scripts in the documentation 3 | - Point to the Forum in the tutorial 4 | - Music made with SCAMP in the documentation! 5 | - Add last tutorial video to tutorial videos page 6 | - Make a documentation page for note properties, including tables of all the possible options. 7 | - Add documentation for installing nicer soundfont 8 | - Maybe make a multi-tempo example that doesn't have nested tempi first. 9 | 10 | Testing: 11 | - Look into unittests 12 | 13 | Playback: 14 | - Make SCUtils receive start values for arbitrary parameters as well, and send those at note creation, 15 | not just afterwards. Then alter play_note to put the expected duration in properties.temp, add that to the 16 | note info dict, and have that get sent in the osc message as an extra parameter from the very beginning. 17 | - Also, alter ScampUtils so that the SynthDef doesn't need to have pitch, volume, or gate and just ignores it 18 | if it doesn't. 19 | - Can we put chords on the same clock?? 20 | - Have pitch and volume playback adjustments affect start_notes? 21 | - Add a soundfile playback implementation 22 | 23 | Engraving: 24 | - Clean up quantization: allow setting exact divisors. Remove max indigestibility in favor of setting exact divisors. 25 | - Engraving lines! 26 | - Fixing the wanderer class tempo stuff 27 | - Fix problem where it splits into two voices when you have notes that overlapped originally but then quantize 28 | in such a way that they would fit into the same voice. e.g. 0 to 0.7, 0.65 - 1.0, quantized into a triplet. 29 | - Two eighth note rests in a triplet shouldn't combine 30 | - Key signatures? 31 | - abjad: maybe try to implement outside-staff-priority on any situation with multiple attached notations, articulation, 32 | texts, etc. so that the first one attached comes closest in the score. 33 | - MusicXML tag 34 | - MusicXML "accidental" tag 35 | 36 | Assorted: 37 | - Clean up dependency tree so that it's not such a goddamn nigntmare 38 | - Make it so that text output can be controlled better 39 | 40 | Extensions: 41 | - MIDI file to Performance 42 | -------------------------------------------------------------------------------- /roadmap/Roadmap 2.0.txt: -------------------------------------------------------------------------------- 1 | Quantization: 2 | - Incorporate Paul Nauert's Q-Grid quantization ideas. 3 | 4 | Playback: 5 | - Switch to LinuxSampler as default, enabling use of .sfz files 6 | 7 | Engraving: 8 | - Attaching notations to the *instrument* (at the given time). `instrument.attach_notation`. This could use any 9 | of the direction types, and use pymusicxml's directions with displacements. This way we could put stuff on during 10 | rests. 11 | - Have a dynamic_envelope for each instrument (by voice? global?), which is an Envelope to which we could append 12 | a curve, or set its new value (in the case of a standard dynamic). Make it possible for the effect of a notation 13 | like a dynamic to set the value of the dynamic_envelope. Have this toggleable with something like 14 | `playback_settings.dynamic_playback_active`. 15 | - Have an adjustment stack of some sort, to allow lines/spanners to add to the instrument's adjustment stack, and 16 | then pop it off at the end of the line. E.g. 8va would add a pitch+12 to the adjustment stack and then pop it off 17 | at the end. 18 | - Engraving Dynamics at the phrase level. Perhaps do this by allowing the user to define 19 | dynamics notations along with their effect on playback. These would then affect both playback and notation. An 20 | other possibility might be to define DynamicEnvelope("fp. # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | import os 18 | 19 | 20 | def get_hook_dirs(): 21 | return [os.path.dirname(__file__)] 22 | 23 | 24 | def get_PyInstaller_tests(): 25 | return [os.path.dirname(__file__)] 26 | -------------------------------------------------------------------------------- /scamp/__pyinstaller/hook-scamp.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from PyInstaller.utils.hooks import collect_dynamic_libs 18 | from PyInstaller.compat import is_win, is_darwin 19 | 20 | if is_win: 21 | scamp_libs = [lib_info for lib_info in collect_dynamic_libs("scamp") 22 | if ".dll" in lib_info[0]] 23 | elif is_darwin: 24 | scamp_libs = [lib_info for lib_info in collect_dynamic_libs("scamp") 25 | if ".dylib" in lib_info[0]] 26 | else: 27 | scamp_libs = [] 28 | 29 | binaries = scamp_libs 30 | -------------------------------------------------------------------------------- /scamp/_thirdparty/__init__.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | -------------------------------------------------------------------------------- /scamp/_thirdparty/mac_libs/libFLAC.8.dylib: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:ee8ae18bda10fb3fe1866f149c2b2940d25cd3ef74d260968a06991c89d04d65 3 | size 216416 4 | -------------------------------------------------------------------------------- /scamp/_thirdparty/mac_libs/libfluidsynth.3.0.1.dylib: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:087b0f7baa4e31d39915d44518d334b7800ba049d955df99659e6ba1eb3c3910 3 | size 356524 4 | -------------------------------------------------------------------------------- /scamp/_thirdparty/mac_libs/libglib-2.0.0.dylib: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9ff9bb0a67a07d39ec0b5c81aafa776fc04dddf66db604d3d268402084de3fd6 3 | size 1201128 4 | -------------------------------------------------------------------------------- /scamp/_thirdparty/mac_libs/libgthread-2.0.0.dylib: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c0652fd3d51dbdafd0e3d521b6273ae4fb7269739d760f4620bec46c3e6b8a3e 3 | size 12648 4 | -------------------------------------------------------------------------------- /scamp/_thirdparty/mac_libs/libintl.8.dylib: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0a1fd4cab3cf4eb18a6ac0e6c5c74839d0f6c2400b533e585ea64b0317cb74e7 3 | size 54968 4 | -------------------------------------------------------------------------------- /scamp/_thirdparty/mac_libs/libogg.0.dylib: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a48fd370ad71e0552f749057884d9b895fa4c69383901afba198aaae1305769d 3 | size 28848 4 | -------------------------------------------------------------------------------- /scamp/_thirdparty/mac_libs/libopus.0.dylib: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:aec0a9c19454dbaa9220af7b619ea9d6e1af195f3e84d2d145c634cbfe7ce809 3 | size 331308 4 | -------------------------------------------------------------------------------- /scamp/_thirdparty/mac_libs/libpcre.1.dylib: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:90d5ae087e7c7dd3e5541a6722592cc0012c7dea71871ac2f91ce671441e1dcc 3 | size 423936 4 | -------------------------------------------------------------------------------- /scamp/_thirdparty/mac_libs/libportaudio.2.dylib: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8e45f9e83a209778a99dd98b2fb6709feb01957d75897270c2ad5f85b9c1031b 3 | size 88460 4 | -------------------------------------------------------------------------------- /scamp/_thirdparty/mac_libs/libreadline.8.dylib: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:e3a97589de475a130a1e4113fd9b5b8fed617b2d90950d3e92a0fbfabb3ca8ba 3 | size 253656 4 | -------------------------------------------------------------------------------- /scamp/_thirdparty/mac_libs/libsndfile.1.dylib: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:fa633e94587182d14986e77e2a6b396056ab208ceb481413eba43e4073aad0ee 3 | size 448336 4 | -------------------------------------------------------------------------------- /scamp/_thirdparty/mac_libs/libvorbis.0.dylib: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:bcb52091d5bfb839e67dcad6412eeec5ac422a5e04dfa08639b8a57fc632d623 3 | size 158396 4 | -------------------------------------------------------------------------------- /scamp/_thirdparty/mac_libs/libvorbisenc.2.dylib: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:af8ed6ae3c72f0e33317b4fb7643461ef39693c05970400ac571c9cbbf784ddb 3 | size 687336 4 | -------------------------------------------------------------------------------- /scamp/_thirdparty/sf2or3utils/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | This very slight modification of 3 | """ 4 | 5 | __all__ = ['sf2_parse'] 6 | __version__ = 0.9 7 | -------------------------------------------------------------------------------- /scamp/_thirdparty/sf2or3utils/instrument.py: -------------------------------------------------------------------------------- 1 | from .bag import Sf2Bag 2 | from .riffparser import from_cstr 3 | 4 | 5 | class Sf2Instrument(object): 6 | SENTINEL_NAME = "EOI" 7 | 8 | def __init__(self, hydra_header, idx, sf2parser): 9 | instrument_header = hydra_header['Inst'][idx] 10 | 11 | self.name = from_cstr(instrument_header.name) 12 | 13 | # don't process the sentinel item 14 | if self.name == self.SENTINEL_NAME: 15 | return 16 | 17 | self.hydra_header = hydra_header 18 | 19 | self.bag_idx = instrument_header.bag 20 | self.bag_size = hydra_header['Inst'][idx + 1].bag - self.bag_idx 21 | 22 | self.bags = self.build_bags() 23 | 24 | self.parent = sf2parser 25 | 26 | def build_bags(self): 27 | return [Sf2Bag(self.hydra_header, idx, self, 'Ibag', 'Imod', 'Igen') for idx in 28 | range(self.bag_idx, self.bag_idx + self.bag_size)] 29 | 30 | def pretty_print(self, prefix=u''): 31 | return u"\n".join([prefix + self.__unicode__()] + 32 | [bag.pretty_print(prefix + u'\t') for bag in self.bags if self.bags]) 33 | 34 | def is_sentinel(self): 35 | return self.name == self.SENTINEL_NAME 36 | 37 | def __unicode__(self): 38 | if self.is_sentinel(): 39 | return u"Instrument EOI" 40 | 41 | return u"Instrument {0.name} {0.bag_size} bag(s) from {0.bag_idx}".format(self) 42 | 43 | def __repr__(self): 44 | return self.__unicode__() 45 | -------------------------------------------------------------------------------- /scamp/_thirdparty/sf2or3utils/modulator.py: -------------------------------------------------------------------------------- 1 | class Sf2Mod(object): 2 | def __init__(self, mod_header): 3 | self.src_oper = mod_header.src_oper 4 | self.dest_oper = mod_header.dest_oper 5 | self.amount = mod_header.amount 6 | self.amount_src_oper = mod_header.amount_src_oper 7 | self.trans_oper = mod_header.trans_oper 8 | 9 | def build_link(self, parent_mods): 10 | """link mod if destination is another modulator""" 11 | # TODO indexing of destination mod is unclear and must be tested with an example 12 | if self.dest_oper & 0x8000: 13 | self.dest = parent_mods[self.dest_oper & 0x7FFF] 14 | 15 | def pretty_print(self, prefix=u''): 16 | return prefix + self.__unicode__() 17 | 18 | def __unicode__(self): 19 | return u"Modulation src oper {0.src_oper} dest oper {0.dest_oper} amount {0.amount} " \ 20 | u"amount_src_oper {0.amount_src_oper} trans_oper {0.trans_oper}".format(self) 21 | 22 | def __repr__(self): 23 | return self.__unicode__() 24 | -------------------------------------------------------------------------------- /scamp/_thirdparty/sf2or3utils/preset.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from .bag import Sf2Bag 4 | from .riffparser import from_cstr 5 | 6 | 7 | class Sf2Preset(object): 8 | def __init__(self, hydra_header, idx, sf2parser): 9 | preset_header = hydra_header['Phdr'][idx] 10 | 11 | self.name = from_cstr(preset_header.name) 12 | 13 | # don't process the sentinel item 14 | if self.name == 'EOP' or len(self.name) == 0: 15 | self.bags = [] 16 | return 17 | 18 | self.hydra_header = hydra_header 19 | 20 | self.preset = preset_header.preset 21 | self.bank = preset_header.bank 22 | 23 | self.bag_idx = preset_header.bag 24 | self.bag_size = hydra_header['Phdr'][idx + 1].bag - self.bag_idx 25 | 26 | if self.bank > 128: 27 | logging.warning("Bag %s has invalid bank number (%d while expected <= 128)", self.name, self.bank) 28 | 29 | self.bags = self.build_bags() 30 | 31 | self.sf2parser = sf2parser 32 | 33 | def build_bags(self): 34 | return [Sf2Bag(self.hydra_header, idx, self, 'Pbag', 'Pmod', 'Pgen') for idx in 35 | range(self.bag_idx, self.bag_idx + self.bag_size)] 36 | 37 | @property 38 | def gens(self): 39 | for bag in self.bags: 40 | for gen in bag.gens: 41 | yield gen 42 | 43 | @property 44 | def instruments(self): 45 | if len(self.bags) <= 0: 46 | yield None, None, None 47 | else: 48 | for bag in self.bags: 49 | yield bag.instrument 50 | 51 | def pretty_print(self, prefix=u''): 52 | return u"\n".join([prefix + self.__unicode__()] + 53 | ["{bag}\n{prefix}\tkeys: {key}\tvels: {vel}\n{instrument}".format( 54 | bag=bag.pretty_print(prefix + u'\t'), 55 | prefix=prefix, 56 | key=bag.key_range or "ALL", 57 | vel=bag.velocity_range or "ALL", 58 | instrument=bag.instrument.pretty_print( 59 | prefix + u'\t') if bag.instrument else prefix + "\tNo Instrument / Global") for bag in 60 | self.bags if self.bags]) 61 | 62 | def __unicode__(self): 63 | if self.name == "EOP" or len(self.name) == 0: 64 | return "Preset EOP" 65 | 66 | return u"Preset[{0.bank:03}:{0.preset:03}] {0.name} {0.bag_size} bag(s) from #{0.bag_idx}".format(self) 67 | 68 | def __repr__(self): 69 | return self.__unicode__() 70 | -------------------------------------------------------------------------------- /scamp/_thirdparty/sf2or3utils/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcTheSpark/scamp/beb920a7cd86feb48047be01a1181b057a3b2455/scamp/_thirdparty/sf2or3utils/tests/__init__.py -------------------------------------------------------------------------------- /scamp/_thirdparty/sf2or3utils/tests/test_parse.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from sf2utils.sf2parse import Sf2File 4 | 5 | 6 | def open_file(filename): 7 | return open(os.path.join(os.path.dirname(os.path.abspath(__file__)), filename), 'rb') 8 | 9 | 10 | def test_basic_parse(): 11 | """make sure that parsing a simple file loads the right amount of instruments, samples and presets""" 12 | with open_file('sf2utils_test.sf2') as file: 13 | sf2_file = Sf2File(file) 14 | assert len(sf2_file.instruments) == 3 15 | assert len(sf2_file.samples) == 3 16 | assert len(sf2_file.presets) == 2 17 | 18 | assert sf2_file.instruments[0].name == 'inst1' 19 | assert sf2_file.instruments[1].name == 'inst2' 20 | 21 | assert sf2_file.presets[0].bank == 13 22 | assert sf2_file.presets[0].preset == 37 23 | -------------------------------------------------------------------------------- /scamp/_thirdparty/windows_libs/libfluidsynth.dll: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:ac71859e276554c32a878aca4542fd05d53534b2f31cff3e1ef0927b6ff0af8a 3 | size 655360 4 | -------------------------------------------------------------------------------- /scamp/_thirdparty/windows_libs/libfluidsynth64.dll: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9d657c383945c8997ec615eb22d9eba11e04331226f57a55471c48d8f67df034 3 | size 1105408 4 | -------------------------------------------------------------------------------- /scamp/soundfonts/Merlin.sf2: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:a3ebced39a1264849616f160bd135d77efd19ddb2be129aa9b9d964ba391f979 3 | size 11583258 4 | -------------------------------------------------------------------------------- /scamp/test_run.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple module with a :func:`play` function that can be used to verify that SCAMP is installed successfully. 3 | """ 4 | 5 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 6 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 7 | # Copyright © 2020 Marc Evanstein . # 8 | # # 9 | # This program is free software: you can redistribute it and/or modify it under the terms of # 10 | # the GNU General Public License as published by the Free Software Foundation, either version # 11 | # 3 of the License, or (at your option) any later version. # 12 | # # 13 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 14 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 15 | # See the GNU General Public License for more details. # 16 | # # 17 | # You should have received a copy of the GNU General Public License along with this program. # 18 | # If not, see . # 19 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 20 | 21 | from . import Session 22 | 23 | 24 | def play(show_lilypond: bool = False, show_xml: bool = False) -> None: 25 | """ 26 | Simple method for determining if scamp was installed correctly. Should play a sequence of pitches telescoping 27 | towards middle C. 28 | 29 | :param show_lilypond: shows a PDF LilyPond rendering of demo played (requires abjad package). 30 | :param show_xml: opens up a MusicXML rendering of the music played 31 | """ 32 | s = Session() 33 | 34 | piano = s.new_part() 35 | 36 | if show_xml or show_lilypond: 37 | s.start_transcribing() 38 | 39 | s.set_rate_target(4, 10) 40 | 41 | for n in reversed(range(1, 40)): 42 | piano.play_note(60 + n * (-1) ** n, 1, 0.25) 43 | 44 | piano.play_note(60, 1.0, 6.25) 45 | 46 | if show_xml or show_lilypond: 47 | score = s.stop_transcribing().to_score() 48 | if show_lilypond: 49 | score.show() 50 | if show_xml: 51 | score.show_xml() 52 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = scamp 3 | description = A computer-assisted composition framework that manages the flow of musical time, plays back notes via SoundFonts, MIDI or OSC, and quantizes and saves the result to music notation. 4 | long_description = file: README.md 5 | long_description_content_type = text/markdown 6 | author = Marc Evanstein 7 | author_email = marc@marcevanstein.com 8 | url = http://scamp.marcevanstein.com 9 | license = GNU GPL Version 3 10 | version = 0.9.2.post2 11 | project_urls = 12 | Source Code = https://sr.ht/~marcevanstein/scamp/ 13 | Documentation = http://scamp.marcevanstein.com/ 14 | Forum = http://scampsters.marcevanstein.com/ 15 | classifier = 16 | Programming Language :: Python :: 3.10 17 | License :: OSI Approved :: GNU General Public License v3 (GPLv3) 18 | Operating System :: OS Independent 19 | 20 | [options] 21 | zip_safe = False 22 | packages = find: 23 | python_requires = >=3.10 24 | install_requires = 25 | pymusicxml >= 0.5.6 26 | expenvelope >= 0.7.2 27 | clockblocks >= 0.6.9 28 | python-osc 29 | arpeggio 30 | midiutil 31 | 32 | # Note: pyfluidsynth and sf2utils are also dependencies, but needed to be tweaked, 33 | # so they have been copied into the _third_party package 34 | 35 | [options.extras_require] 36 | all = abjad >= 3.17, <= 3.19; python-rtmidi; pynput 37 | 38 | [options.package_data] 39 | scamp = 40 | soundfonts/* 41 | lilypond/* 42 | _thirdparty/mac_libs/* 43 | _thirdparty/windows_libs/* 44 | 45 | [options.entry_points] 46 | pyinstaller40 = 47 | hook-dirs = scamp.__pyinstaller:get_hook_dirs 48 | tests = scamp.__pyinstaller:get_PyInstaller_tests 49 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 2 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 3 | # Copyright © 2020 Marc Evanstein . # 4 | # # 5 | # This program is free software: you can redistribute it and/or modify it under the terms of # 6 | # the GNU General Public License as published by the Free Software Foundation, either version # 7 | # 3 of the License, or (at your option) any later version. # 8 | # # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 10 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 11 | # See the GNU General Public License for more details. # 12 | # # 13 | # You should have received a copy of the GNU General Public License along with this program. # 14 | # If not, see . # 15 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 16 | 17 | from setuptools import setup 18 | 19 | setup() 20 | -------------------------------------------------------------------------------- /test/example_tests/AssortedHaphazard/fifteen_sixteen.py: -------------------------------------------------------------------------------- 1 | """ 2 | A rhythm quantized with a 15/16 time signature 3 | """ 4 | 5 | from scamp import * 6 | 7 | MeasureQuantizationScheme.from_time_signature("15/16") 8 | # engraving_settings.allow_duple_tuplets_in_compound_time = False 9 | dur_list=[1.0, 0.5, 0.5, 0.75, 0.75, 1.5, 1.5, 0.75, 0.75, 1.0, 1.25] 10 | 11 | time_sig_list=['7/8', '15/16', '3/4'] 12 | s = Session() 13 | s.fast_forward_in_time(1000) 14 | piano1 = s.new_part("Piano1") 15 | s.start_transcribing() 16 | for d in dur_list: 17 | piano1.play_note(67, 1, d) 18 | s.wait_for_children_to_finish() 19 | performance = s.stop_transcribing() 20 | 21 | s.kill() 22 | 23 | 24 | def test_results(): 25 | return ( 26 | performance, 27 | performance.to_score(time_signature=time_sig_list, simplicity_preference=2) 28 | ) 29 | -------------------------------------------------------------------------------- /test/example_tests/Test/clock_order.json: -------------------------------------------------------------------------------- 1 | [ 2 | "['A, 0', 'B, 0', 'A, 2.0', 'B, 2.0', 'A, 3.0', 'B, 4.0', 'A, 6.0', 'B, 6.0', 'A, 8.0', 'B, 8.0', 'A, 10.0', 'B, 10.0', 'B, 11.0', 'A, 13.0', 'A, 14.0', 'B, 14.0', 'B, 15.0', 'A, 16.0', 'B, 16.0', 'B, 18.0', 'A, 19.0', 'B, 21.0', 'A, 22.0', 'A, 23.0', 'B, 24.0', 'A, 25.0', 'B, 25.0', 'B, 26.0', 'A, 28.0', 'B, 29.0', 'A, 30.0', 'B, 31.0', 'B, 32.0', 'A, 33.0', 'B, 34.0', 'A, 35.0', 'B, 36.0', 'A, 38.0', 'A, 39.0', 'B, 39.0', 'B, 41.0', 'A, 42.0', 'B, 43.0', 'A, 45.0', 'B, 45.0', 'A, 46.0', 'A, 47.0', 'A, 48.0', 'B, 48.0', 'B, 50.0', 'A, 51.0', 'B, 53.0', 'A, 54.0', 'A, 55.0', 'B, 56.0', 'A, 58.0', 'B, 58.0', 'B, 59.0', 'A, 60.0', 'A, 62.0', 'B, 62.0', 'B, 63.0', 'B, 64.0', 'A, 65.0', 'A, 66.0', 'A, 67.0', 'B, 67.0', 'A, 68.0', 'A, 70.0', 'B, 70.0', 'A, 71.0', 'B, 71.0', 'A, 73.0', 'B, 74.0', 'A, 75.0', 'B, 75.0', 'A, 77.0', 'B, 78.0', 'A, 79.0', 'A, 80.0', 'B, 81.0', 'A, 83.0', 'B, 83.0', 'B, 84.0', 'A, 86.0', 'B, 87.0', 'A, 89.0', 'B, 90.0', 'A, 91.0', 'A, 92.0', 'B, 92.0', 'B, 94.0', 'A, 95.0', 'B, 96.0', 'B, 97.0', 'A, 98.0', 'A, 99.0', 'B, 99.0', 'A, 100.0', 'B, 100.0', 'B, 100.0', 'A, 100.0', 'A, 102.0', 'B, 103.0', 'B, 104.0', 'A, 104.0', 'B, 105.0', 'B, 106.0', 'B, 107.0', 'A, 107.0', 'B, 108.0', 'A, 108.0', 'B, 111.0', 'A, 111.0', 'A, 113.0', 'B, 114.0', 'A, 116.0', 'B, 117.0', 'A, 118.0', 'A, 119.0', 'B, 120.0', 'A, 120.0', 'B, 123.0', 'A, 123.0', 'B, 125.0', 'A, 126.0', 'B, 127.0', 'A, 128.0', 'B, 129.0', 'A, 131.0', 'B, 132.0', 'B, 134.0', 'A, 134.0', 'B, 135.0', 'A, 136.0', 'A, 137.0', 'B, 138.0', 'A, 139.0', 'B, 141.0', 'A, 142.0', 'B, 143.0', 'A, 143.0', 'B, 144.0', 'A, 144.0', 'A, 146.0', 'B, 147.0', 'A, 147.0', 'A, 148.0', 'B, 150.0', 'A, 150.0', 'B, 151.0', 'A, 152.0', 'B, 153.0', 'A, 153.0', 'B, 154.0', 'A, 154.0', 'A, 155.0', 'A, 156.0', 'B, 157.0', 'A, 159.0', 'B, 160.0', 'A, 162.0', 'B, 163.0', 'B, 164.0', 'B, 165.0', 'A, 165.0', 'B, 166.0', 'B, 167.0', 'A, 168.0', 'B, 170.0', 'B, 171.0', 'A, 171.0', 'A, 172.0', 'B, 173.0', 'B, 174.0', 'A, 174.0', 'B, 175.0', 'B, 176.0', 'B, 177.0', 'A, 177.0', 'B, 178.0', 'B, 179.0', 'A, 180.0', 'B, 181.0', 'A, 181.0', 'A, 182.0', 'B, 184.0', 'B, 185.0', 'A, 185.0', 'A, 187.0', 'B, 188.0', 'B, 189.0', 'A, 190.0', 'B, 191.0', 'A, 191.0', 'B, 192.0', 'A, 192.0', 'A, 194.0', 'B, 195.0', 'A, 196.0', 'B, 197.0', 'A, 197.0', 'B, 198.0', 'B, 200.0', 'A, 200.0']" 3 | ] -------------------------------------------------------------------------------- /test/example_tests/Test/clock_order.py: -------------------------------------------------------------------------------- 1 | from clockblocks import Clock, wait, fork 2 | import random 3 | 4 | random.seed(0) 5 | 6 | c = Clock() 7 | c.fast_forward_in_time(float("inf")) 8 | 9 | output = [] 10 | 11 | 12 | def part_a(): 13 | while True: 14 | output.append(f"A, {c.time()}") 15 | wait(random.choice([1, 2, 3])) 16 | 17 | 18 | def part_b(): 19 | while True: 20 | output.append(f"B, {c.time()}") 21 | wait(random.choice([1, 2, 3])) 22 | 23 | 24 | c1 = fork(part_a) 25 | c2 = fork(part_b) 26 | wait(100) 27 | c1.kill() 28 | c2.kill() 29 | c1 = fork(part_b) 30 | c2 = fork(part_a) 31 | wait(100) 32 | c1.kill() 33 | c2.kill() 34 | 35 | 36 | def test_results(): 37 | return [output] 38 | -------------------------------------------------------------------------------- /test/example_tests/Tutorial/12_envelopes_basic.json: -------------------------------------------------------------------------------- 1 | [ 2 | "{\n \"_type\": \"Envelope\",\n \"durations\": [\n 0.3333333333333333,\n 0.3333333333333333,\n 0.33333333333333337\n ],\n \"levels\": [\n 60,\n 72,\n 66,\n 70\n ]\n}", 3 | "{\n \"_type\": \"Envelope\",\n \"curve_shapes\": [\n 2,\n -2,\n -2\n ],\n \"durations\": [\n 2,\n 1,\n 1\n ],\n \"levels\": [\n 60,\n 72,\n 66,\n 70\n ]\n}" 4 | ] -------------------------------------------------------------------------------- /test/example_tests/Tutorial/12_envelopes_basic.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Envelopes (basic) 3 | 4 | Create and plot two simple envelopes, one with evenly-spaced linear segments, and one with uneven, curved segments. 5 | """ 6 | 7 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 8 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 9 | # Copyright © 2020 Marc Evanstein . # 10 | # # 11 | # This program is free software: you can redistribute it and/or modify it under the terms of # 12 | # the GNU General Public License as published by the Free Software Foundation, either version # 13 | # 3 of the License, or (at your option) any later version. # 14 | # # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 16 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 17 | # See the GNU General Public License for more details. # 18 | # # 19 | # You should have received a copy of the GNU General Public License along with this program. # 20 | # If not, see . # 21 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 22 | 23 | from scamp import Envelope 24 | 25 | e1 = Envelope.from_levels( 26 | [60, 72, 66, 70] 27 | ) 28 | 29 | e2 = Envelope( 30 | [60, 72, 66, 70], [2, 1, 1], 31 | curve_shapes=[2, -2, -2] 32 | ) 33 | 34 | 35 | def test_results(): 36 | return [e1.json_dumps(), e2.json_dumps()] 37 | -------------------------------------------------------------------------------- /test/example_tests/Tutorial/14_gliss_from_envelope.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Glissando from Envelope 3 | 4 | Plays a glissando by passing an Envelope to the pitch parameter of play_note. 5 | """ 6 | 7 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 8 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 9 | # Copyright © 2020 Marc Evanstein . # 10 | # # 11 | # This program is free software: you can redistribute it and/or modify it under the terms of # 12 | # the GNU General Public License as published by the Free Software Foundation, either version # 13 | # 3 of the License, or (at your option) any later version. # 14 | # # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 16 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 17 | # See the GNU General Public License for more details. # 18 | # # 19 | # You should have received a copy of the GNU General Public License along with this program. # 20 | # If not, see . # 21 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 22 | 23 | from scamp import * 24 | 25 | s = Session() 26 | s.fast_forward_in_beats(float("inf")) 27 | 28 | viola = s.new_part("viola") 29 | 30 | e = Envelope( 31 | [60, 72, 66, 70], [3 * 4/5, 1 * 4/5, 1 * 4/5], 32 | curve_shapes=[2, -2, -2] 33 | ) 34 | 35 | s.start_transcribing() 36 | 37 | viola.play_note(e, 1.0, 4) 38 | 39 | performance = s.stop_transcribing() 40 | 41 | s.kill() 42 | 43 | 44 | def test_results(): 45 | return ( 46 | performance, 47 | performance.to_score() 48 | ) 49 | -------------------------------------------------------------------------------- /test/example_tests/Tutorial/15_envelope_shorthand.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Envelope Shorthand 3 | 4 | Plays two glissandi by passing lists instead of envelopes to the pitch argument of play_note. 5 | """ 6 | 7 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 8 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 9 | # Copyright © 2020 Marc Evanstein . # 10 | # # 11 | # This program is free software: you can redistribute it and/or modify it under the terms of # 12 | # the GNU General Public License as published by the Free Software Foundation, either version # 13 | # 3 of the License, or (at your option) any later version. # 14 | # # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 16 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 17 | # See the GNU General Public License for more details. # 18 | # # 19 | # You should have received a copy of the GNU General Public License along with this program. # 20 | # If not, see . # 21 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 22 | 23 | from scamp import * 24 | 25 | s = Session() 26 | s.fast_forward_in_beats(float("inf")) 27 | 28 | viola = s.new_part("viola") 29 | 30 | s.start_transcribing() 31 | 32 | # a list of values results in evenly spaced glissando 33 | viola.play_note([60, 70, 55], 1.0, 4) 34 | 35 | # a list of lists can give values, durations, and (optionally) curve shapes 36 | # this results in segment durations of 2 and 1 and curve shapes of -2 and 0 37 | # NB: by default, envelopes created through list shorthand like this are 38 | # resized to the duration of the note, whereas Envelope objects passed directly 39 | # are not. So in this case, the durations of 2 and 1 get scaled up to 8/3 and 4/3 40 | # so that the whole envelope lasts 4 beats (the duration of the note). 41 | # You can change this behavior so that it resizes for Envelope objects as well, 42 | # or so that it never resizes, by altering playback_settings.resize_parameter_envelopes 43 | viola.play_note([[60, 70, 55], [2, 1], [-2, 0]], 1.0, 4) 44 | 45 | 46 | performance = s.stop_transcribing() 47 | 48 | s.kill() 49 | 50 | 51 | def test_results(): 52 | return ( 53 | performance, 54 | performance.to_score() 55 | ) 56 | -------------------------------------------------------------------------------- /test/example_tests/Tutorial/17_volume_envelope.json: -------------------------------------------------------------------------------- 1 | [ 2 | "True" 3 | ] -------------------------------------------------------------------------------- /test/example_tests/Tutorial/17_volume_envelope.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Volume Envelope 3 | 4 | Plots an envelope representing a forte-piano-crescendo dynamic, and then uses it to affect the dynamics of a note's 5 | playback. This example shows that an Envelope can be passed to the volume argument of "play_note", just like it can 6 | to the pitch argument. (The list short-hand also works, by the way.) 7 | """ 8 | 9 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 10 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 11 | # Copyright © 2020 Marc Evanstein . # 12 | # # 13 | # This program is free software: you can redistribute it and/or modify it under the terms of # 14 | # the GNU General Public License as published by the Free Software Foundation, either version # 15 | # 3 of the License, or (at your option) any later version. # 16 | # # 17 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 18 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 19 | # See the GNU General Public License for more details. # 20 | # # 21 | # You should have received a copy of the GNU General Public License along with this program. # 22 | # If not, see . # 23 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 24 | 25 | from scamp import * 26 | 27 | s = Session() 28 | viola = s.new_part("viola") 29 | s.fast_forward_in_beats(float("inf")) 30 | 31 | fp_cresc = Envelope( 32 | [0.8, 0.3, 1], 33 | [0.07, 0.93], 34 | curve_shapes=[2, 4] 35 | ) 36 | 37 | # play a note with the dynamic curve 38 | viola.play_note(48, fp_cresc, 4) 39 | 40 | s.kill() 41 | 42 | 43 | def test_results(): 44 | return [True] 45 | -------------------------------------------------------------------------------- /test/example_tests/Tutorial/18_microtonal.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Microtonal Playback and Notation 3 | 4 | Plays a few microtonal chords and notates them, turning on exact microtonal annotations. 5 | """ 6 | 7 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 8 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 9 | # Copyright © 2020 Marc Evanstein . # 10 | # # 11 | # This program is free software: you can redistribute it and/or modify it under the terms of # 12 | # the GNU General Public License as published by the Free Software Foundation, either version # 13 | # 3 of the License, or (at your option) any later version. # 14 | # # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 16 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 17 | # See the GNU General Public License for more details. # 18 | # # 19 | # You should have received a copy of the GNU General Public License along with this program. # 20 | # If not, see . # 21 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 22 | 23 | from scamp import * 24 | 25 | s = Session() 26 | s.fast_forward_in_beats(float("inf")) 27 | 28 | piano = s.new_part("piano") 29 | 30 | s.start_transcribing() 31 | 32 | # this causes the true pitches to be written into the score by the notes, as midi pitch values. 33 | engraving_settings.show_microtonal_annotations = True 34 | # playing microtonal pitches is as simple as using floating-point values for pitch 35 | piano.play_chord([62.7, 71.3], 1.0, 1) 36 | piano.play_chord([65.2, 70.9], 1.0, 1) 37 | piano.play_chord([71.5, 74.3], 1.0, 1) 38 | 39 | performance = s.stop_transcribing() 40 | 41 | s.kill() 42 | 43 | 44 | def test_results(): 45 | return ( 46 | performance, 47 | performance.to_score() 48 | ) 49 | -------------------------------------------------------------------------------- /test/example_tests/Tutorial/19_playback_implementations.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Playback Implementations 3 | 4 | Shows how to create parts that use different implementations for playback. This assumes that you are connecting to 5 | a midi device on port zero. 6 | """ 7 | 8 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 9 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 10 | # Copyright © 2020 Marc Evanstein . # 11 | # # 12 | # This program is free software: you can redistribute it and/or modify it under the terms of # 13 | # the GNU General Public License as published by the Free Software Foundation, either version # 14 | # 3 of the License, or (at your option) any later version. # 15 | # # 16 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 17 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 18 | # See the GNU General Public License for more details. # 19 | # # 20 | # You should have received a copy of the GNU General Public License along with this program. # 21 | # If not, see . # 22 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 23 | 24 | from scamp import * 25 | 26 | s = Session() 27 | s.fast_forward_in_beats(float("inf")) 28 | 29 | # Calling "new_part" results in a default SoundfontPlaybackImplementation, and 30 | # add_streaming_midi_playback gives this a MIDIStreamPlaybackImplementation as well 31 | # (here, port 0 is used for output) 32 | piano = s.new_part("piano").add_streaming_midi_playback(0) 33 | # Calling "new_osc_part" gives the instrument an OSCPlaybackImplementation 34 | # This one is set up to communicate with the supercollider instrument in the osc_to_supercollider.scd example 35 | synth = s.new_osc_part("vibrato", ip_address="127.0.0.1", port=57120) 36 | # Calling "new_silent_part" results in an instrument with no PlaybackImplementation 37 | silent = s.new_silent_part("silent") 38 | 39 | s.start_transcribing() 40 | 41 | for _ in range(4): 42 | piano.play_note(60, 1, 0.5) 43 | synth.play_note(62, 1, 0.5) 44 | silent.play_note(63, 1, 0.5) 45 | 46 | performance = s.stop_transcribing() 47 | 48 | s.kill() 49 | 50 | 51 | def test_results(): 52 | return ( 53 | performance, 54 | performance.to_score(time_signature="6/8") 55 | ) 56 | -------------------------------------------------------------------------------- /test/example_tests/Tutorial/1_hello_world.json: -------------------------------------------------------------------------------- 1 | [ 2 | "True" 3 | ] -------------------------------------------------------------------------------- /test/example_tests/Tutorial/1_hello_world.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Hello World 3 | 4 | Plays a C major arpeggio. 5 | """ 6 | 7 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 8 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 9 | # Copyright © 2020 Marc Evanstein . # 10 | # # 11 | # This program is free software: you can redistribute it and/or modify it under the terms of # 12 | # the GNU General Public License as published by the Free Software Foundation, either version # 13 | # 3 of the License, or (at your option) any later version. # 14 | # # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 16 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 17 | # See the GNU General Public License for more details. # 18 | # # 19 | # You should have received a copy of the GNU General Public License along with this program. # 20 | # If not, see . # 21 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 22 | 23 | # import the scamp namespace 24 | from scamp import * 25 | # construct a session object 26 | s = Session() 27 | s.fast_forward_in_beats(float("inf")) 28 | 29 | # add a new s part to the session 30 | violin = s.new_part("Violin") 31 | # looping through the MIDI pitches 32 | # of a C major arpeggio... 33 | for pitch in [60, 64, 67, 72]: 34 | # play each pitch sequentially 35 | # with volume of 1 (full volume) 36 | # and duration of half a beat 37 | violin.play_note(pitch, 1, 0.5) 38 | 39 | 40 | s.kill() 41 | 42 | 43 | def test_results(): 44 | return [True] 45 | -------------------------------------------------------------------------------- /test/example_tests/Tutorial/2_tempo_change.json: -------------------------------------------------------------------------------- 1 | [ 2 | "True" 3 | ] -------------------------------------------------------------------------------- /test/example_tests/Tutorial/2_tempo_change.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Tempo Change 3 | 4 | Same as Hello World example, but at half-tempo. 5 | """ 6 | 7 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 8 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 9 | # Copyright © 2020 Marc Evanstein . # 10 | # # 11 | # This program is free software: you can redistribute it and/or modify it under the terms of # 12 | # the GNU General Public License as published by the Free Software Foundation, either version # 13 | # 3 of the License, or (at your option) any later version. # 14 | # # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 16 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 17 | # See the GNU General Public License for more details. # 18 | # # 19 | # You should have received a copy of the GNU General Public License along with this program. # 20 | # If not, see . # 21 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 22 | 23 | # import the scamp namespace 24 | from scamp import * 25 | # construct a session object 26 | s = Session() 27 | s.fast_forward_in_beats(float("inf")) 28 | 29 | # change to half of default tempo 30 | s.tempo = 30 31 | 32 | # add a new violin part to the session 33 | violin = s.new_part("Violin") 34 | # looping through the MIDI pitches 35 | # of a C major arpeggio... 36 | for pitch in [60, 64, 67, 72]: 37 | # play each pitch sequentially 38 | # with volume of 1 (full volume) 39 | # and duration of half a beat 40 | violin.play_note(pitch, 1, 0.5) 41 | 42 | s.kill() 43 | 44 | 45 | def test_results(): 46 | return [True] 47 | -------------------------------------------------------------------------------- /test/example_tests/Tutorial/3_blocking_false.json: -------------------------------------------------------------------------------- 1 | [ 2 | "True" 3 | ] -------------------------------------------------------------------------------- /test/example_tests/Tutorial/3_blocking_false.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Blocking False 3 | 4 | Demonstrating the ability to have non-blocking calls to play_note. This plays two notes that each last 5 | for two beats, but overlapping by one beat. 6 | """ 7 | 8 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 9 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 10 | # Copyright © 2020 Marc Evanstein . # 11 | # # 12 | # This program is free software: you can redistribute it and/or modify it under the terms of # 13 | # the GNU General Public License as published by the Free Software Foundation, either version # 14 | # 3 of the License, or (at your option) any later version. # 15 | # # 16 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 17 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 18 | # See the GNU General Public License for more details. # 19 | # # 20 | # You should have received a copy of the GNU General Public License along with this program. # 21 | # If not, see . # 22 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 23 | 24 | # import the scamp namespace 25 | from scamp import * 26 | # construct a session object 27 | s = Session() 28 | s.fast_forward_in_beats(float("inf")) 29 | 30 | # add a new violin part to the session 31 | violin = s.new_part("Violin") 32 | 33 | # start playing a 2-beat C, but return immediately 34 | violin.play_note(60, 1, 2, blocking=False) 35 | # wait for only one beat 36 | wait(1) 37 | # start playing a 2-beat E, blocking this time 38 | violin.play_note(64, 1, 2) 39 | 40 | s.kill() 41 | 42 | 43 | def test_results(): 44 | return [True] -------------------------------------------------------------------------------- /test/example_tests/Tutorial/4_random_durations_and_rests.json: -------------------------------------------------------------------------------- 1 | [ 2 | "True" 3 | ] -------------------------------------------------------------------------------- /test/example_tests/Tutorial/4_random_durations_and_rests.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Random Durations and Rests 3 | 4 | Loops a C major arpeggio twice, but with random, floating-point durations. 5 | Also sometimes adds a rest of up to a second between notes. 6 | """ 7 | 8 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 9 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 10 | # Copyright © 2020 Marc Evanstein . # 11 | # # 12 | # This program is free software: you can redistribute it and/or modify it under the terms of # 13 | # the GNU General Public License as published by the Free Software Foundation, either version # 14 | # 3 of the License, or (at your option) any later version. # 15 | # # 16 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 17 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 18 | # See the GNU General Public License for more details. # 19 | # # 20 | # You should have received a copy of the GNU General Public License along with this program. # 21 | # If not, see . # 22 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 23 | 24 | # import the scamp namespace 25 | from scamp import * 26 | # import the random module from 27 | # the python standard library 28 | import random 29 | random.seed(0) 30 | 31 | # construct a session object 32 | s = Session() 33 | s.fast_forward_in_beats(float("inf")) 34 | 35 | # add a new violin part to the session 36 | violin = s.new_part("Violin") 37 | 38 | for _ in range(2): # loop twice 39 | for pitch in [60, 64, 67, 72]: 40 | # pick a duration between 0.5 to 1.5 41 | dur = random.uniform(0.5, 1.5) 42 | # play a note of that duration 43 | violin.play_note(pitch, 1, dur) 44 | # with probability of one half, wait for between 0 and 1 seconds 45 | if random.random() < 0.5: 46 | # note that "wait" is the same here as "s.wait". What "wait" does it find the 47 | # clock operating on the given thread and call wait on that. In this case, that 48 | # clock is the Session as a whole. 49 | wait(random.random()) 50 | 51 | s.kill() 52 | 53 | 54 | def test_results(): 55 | return [True] -------------------------------------------------------------------------------- /test/example_tests/Tutorial/5_notation.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Generating Notation 3 | 4 | Plays a simple C Major arpeggio, and generates notation for it. 5 | """ 6 | 7 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 8 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 9 | # Copyright © 2020 Marc Evanstein . # 10 | # # 11 | # This program is free software: you can redistribute it and/or modify it under the terms of # 12 | # the GNU General Public License as published by the Free Software Foundation, either version # 13 | # 3 of the License, or (at your option) any later version. # 14 | # # 15 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 16 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 17 | # See the GNU General Public License for more details. # 18 | # # 19 | # You should have received a copy of the GNU General Public License along with this program. # 20 | # If not, see . # 21 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 22 | 23 | from scamp import * 24 | 25 | s = Session() 26 | s.fast_forward_in_beats(float("inf")) 27 | violin = s.new_part("Violin") 28 | 29 | # begin recording (defaults to transcribing all instruments within the session) 30 | s.start_transcribing() 31 | for pitch in [60, 64, 67, 72]: 32 | violin.play_note(pitch, 1, 0.5) 33 | # stop the recording and save the recorded 34 | # note events as a performance 35 | 36 | # a Performance is essentially a note event list representing exactly 37 | # when and how notes were played back, in continuous time 38 | performance = s.stop_transcribing() 39 | 40 | s.kill() 41 | 42 | 43 | def test_results(): 44 | return ( 45 | performance, 46 | performance.to_score() 47 | ) 48 | -------------------------------------------------------------------------------- /test/example_tests/Tutorial/6_time_signature.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Time Signatures 3 | 4 | Plays a simple C Major arpeggio, and generates notation for it in three different ways: 5 | - with a 3/8 time signature 6 | - with a 3/8 time signature for the first measure followed by 2/4 7 | - with alternating 3/8 and 2/4 time signatures 8 | - with a list of bar lengths determining time signatures 9 | """ 10 | 11 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 12 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 13 | # Copyright © 2020 Marc Evanstein . # 14 | # # 15 | # This program is free software: you can redistribute it and/or modify it under the terms of # 16 | # the GNU General Public License as published by the Free Software Foundation, either version # 17 | # 3 of the License, or (at your option) any later version. # 18 | # # 19 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 20 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 21 | # See the GNU General Public License for more details. # 22 | # # 23 | # You should have received a copy of the GNU General Public License along with this program. # 24 | # If not, see . # 25 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 26 | 27 | from scamp import * 28 | 29 | s = Session() 30 | s.fast_forward_in_beats(float("inf")) 31 | violin = s.new_part("Violin") 32 | 33 | # begin recording (defaults to transcribing 34 | # all instruments within the session) 35 | s.start_transcribing() 36 | for _ in range(4): 37 | for pitch in [60, 64, 67, 72]: 38 | violin.play_note(pitch, 1, 0.5) 39 | # stop the recording and save the recorded 40 | # note events as a performance 41 | 42 | performance = s.stop_transcribing() 43 | 44 | s.kill() 45 | 46 | 47 | def test_results(): 48 | return ( 49 | performance, 50 | performance.to_score(time_signature="3/8"), 51 | performance.to_score(time_signature=["3/8", "2/4"]), 52 | performance.to_score(time_signature=["3/8", "2/4", "loop"]), 53 | performance.to_score(bar_line_locations=[1.5, 3.5, 8]) 54 | ) 55 | -------------------------------------------------------------------------------- /test/example_tests/Tutorial/7_random_quantized.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Quantization of Random Floating-Point Durations 3 | 4 | Similar to the "Random Durations and Rests" example, except that now we generate 5 | notation. Since the lengths are floating point, quantization occurs in the call to "to_score" 6 | """ 7 | 8 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 9 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 10 | # Copyright © 2020 Marc Evanstein . # 11 | # # 12 | # This program is free software: you can redistribute it and/or modify it under the terms of # 13 | # the GNU General Public License as published by the Free Software Foundation, either version # 14 | # 3 of the License, or (at your option) any later version. # 15 | # # 16 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 17 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 18 | # See the GNU General Public License for more details. # 19 | # # 20 | # You should have received a copy of the GNU General Public License along with this program. # 21 | # If not, see . # 22 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 23 | 24 | from scamp import * 25 | import random 26 | 27 | s = Session() 28 | s.fast_forward_in_beats(float("inf")) 29 | 30 | violin = s.new_part("Violin") 31 | 32 | # begin recording 33 | s.start_transcribing() 34 | 35 | for _ in range(2): # loop twice 36 | for pitch in [60, 64, 67, 72]: 37 | dur = random.uniform(0.5, 1.5) 38 | violin.play_note(pitch, 1, dur) 39 | if random.random() < 0.5: 40 | # note that "wait" is the same here as "s.wait". What "wait" does it find the 41 | # clock operating on the given thread and call wait on that. In this case, that 42 | # clock is the Session as a whole. 43 | wait(random.random()) 44 | 45 | performance = s.stop_transcribing() 46 | 47 | s.kill() 48 | 49 | 50 | def test_results(): 51 | return ( 52 | performance, 53 | performance.to_score(time_signature="3/4") 54 | ) 55 | -------------------------------------------------------------------------------- /test/example_tests/Tutorial/8_simplified_quantization.py: -------------------------------------------------------------------------------- 1 | """ 2 | SCAMP Example: Simplified Quantization 3 | 4 | Same as last example except that a restriction on the max beat divisor is 5 | imposed on the quantization, rendering simpler -- if somewhat less accurate -- results. 6 | """ 7 | 8 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 9 | # This file is part of SCAMP (Suite for Computer-Assisted Music in Python) # 10 | # Copyright © 2020 Marc Evanstein . # 11 | # # 12 | # This program is free software: you can redistribute it and/or modify it under the terms of # 13 | # the GNU General Public License as published by the Free Software Foundation, either version # 14 | # 3 of the License, or (at your option) any later version. # 15 | # # 16 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # 17 | # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # 18 | # See the GNU General Public License for more details. # 19 | # # 20 | # You should have received a copy of the GNU General Public License along with this program. # 21 | # If not, see . # 22 | # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # 23 | 24 | from scamp import * 25 | import random 26 | 27 | s = Session() 28 | s.fast_forward_in_beats(float("inf")) 29 | 30 | violin = s.new_part("Violin") 31 | 32 | # begin recording 33 | s.start_transcribing() 34 | 35 | for _ in range(2): # loop twice 36 | for pitch in [60, 64, 67, 72]: 37 | dur = random.uniform(0.5, 1.5) 38 | violin.play_note(pitch, 1, dur) 39 | if random.random() < 0.5: 40 | # note that "wait" is the same here as "s.wait". What "wait" does it find the 41 | # clock operating on the given thread and call wait on that. In this case, that 42 | # clock is the Session as a whole. 43 | wait(random.random()) 44 | 45 | performance = s.stop_transcribing() 46 | 47 | s.kill() 48 | 49 | 50 | def test_results(): 51 | return ( 52 | performance, 53 | performance.to_score(time_signature="3/4", title="Default Quantization"), 54 | performance.to_score(time_signature="3/4", max_divisor=4, title="Max Divisor of 4"), 55 | performance.to_score(time_signature="3/4", simplicity_preference=3, title="Strong Simplicity Preference") 56 | ) 57 | --------------------------------------------------------------------------------