From aed8b6112f56375883cef7ca76b1c0004d5601e0 Mon Sep 17 00:00:00 2001 From: gesellkammer Date: Sat, 21 Dec 2024 13:11:25 +0100 Subject: [PATCH] forgot files --- examples/01 - Introduction.ipynb | 452 +++++++++++++++++++++ examples/02 - Threading.ipynb | 656 +++++++++++++++++++++++++++++++ 2 files changed, 1108 insertions(+) create mode 100644 examples/01 - Introduction.ipynb create mode 100644 examples/02 - Threading.ipynb diff --git a/examples/01 - Introduction.ipynb b/examples/01 - Introduction.ipynb new file mode 100644 index 0000000..6ed4e7f --- /dev/null +++ b/examples/01 - Introduction.ipynb @@ -0,0 +1,452 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## ctcsound7\n", + "\n", + "The Csound API is a set of C functions and C++ classes that expose to hosts programs the functionalities of Csound.\n", + "\n", + "**ctcsound7** is a python module wrapping the access to the Csound API using two Python classes: *Csound* and *CsoundPerformanceThread*. **ctcsound** uses the **ctypes** python module which is an FFI (Foreign Function Interface) allowing Python to access C shared libraries. " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import ctcsound7 as ct" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Csound Instance\n", + "\n", + "Creating a `Csound` instance spawns a csound process." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "cs = ct.Csound()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This object can return useful information about the underlying process" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "7000" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cs.version()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Version id: 7000, version tuple: 7.0.0\n", + "[OpcodeDef(name='ATSadd', outtypes='a', intypes='kkSiiopo', flags=0),\n", + " OpcodeDef(name='ATSadd', outtypes='a', intypes='kkiiiopo', flags=0),\n", + " OpcodeDef(name='ATSaddnz', outtypes='a', intypes='kSiop', flags=0),\n", + " OpcodeDef(name='ATSaddnz', outtypes='a', intypes='kiiop', flags=0),\n", + " OpcodeDef(name='ATSbufread', outtypes='', intypes='kkSiop', flags=0),\n", + " OpcodeDef(name='ATSbufread', outtypes='', intypes='kkiiop', flags=0),\n", + " OpcodeDef(name='ATScross', outtypes='a', intypes='kkSikkiopoo', flags=0),\n", + " OpcodeDef(name='ATScross', outtypes='a', intypes='kkiikkiopoo', flags=0),\n", + " OpcodeDef(name='ATSinfo', outtypes='i', intypes='Si', flags=0),\n", + " OpcodeDef(name='ATSinfo', outtypes='i', intypes='ii', flags=0),\n", + " OpcodeDef(name='ATSinterpread', outtypes='k', intypes='k', flags=0),\n", + " OpcodeDef(name='ATSpartialtap', outtypes='kk', intypes='i', flags=0),\n", + " OpcodeDef(name='ATSread', outtypes='kk', intypes='kSi', flags=0),\n", + " OpcodeDef(name='ATSread', outtypes='kk', intypes='kii', flags=0),\n", + " OpcodeDef(name='ATSreadnz', outtypes='k', intypes='kSi', flags=0),\n", + " OpcodeDef(name='ATSreadnz', outtypes='k', intypes='kii', flags=0),\n", + " OpcodeDef(name='ATSsinnoi', outtypes='a', intypes='kkkkSiop', flags=0),\n", + " OpcodeDef(name='ATSsinnoi', outtypes='a', intypes='kkkkiiop', flags=0),\n", + " OpcodeDef(name='K35_hpf', outtypes='a', intypes='axxOPo', flags=0),\n", + " OpcodeDef(name='K35_lpf', outtypes='a', intypes='axxOPo', flags=0)]\n", + "\n", + "--- 'osc' opcodes ---\n", + "\n", + "{'foscil',\n", + " 'foscili',\n", + " 'hsboscil',\n", + " 'loscil',\n", + " 'loscil3',\n", + " 'loscil3phs',\n", + " 'loscilphs',\n", + " 'loscilx',\n", + " 'lposcil',\n", + " 'lposcil3',\n", + " 'lposcila',\n", + " 'lposcilsa',\n", + " 'lposcilsa2',\n", + " 'moscil',\n", + " 'oscbnk',\n", + " 'oscil',\n", + " 'oscil1',\n", + " 'oscil1i',\n", + " 'oscil3',\n", + " 'oscili',\n", + " 'oscilikt',\n", + " 'osciliktp',\n", + " 'oscilikts',\n", + " 'osciln',\n", + " 'oscils',\n", + " 'oscilx',\n", + " 'poscil',\n", + " 'poscil3',\n", + " 'pvsosc',\n", + " 'temposcal'}\n" + ] + } + ], + "source": [ + "from pprint import pprint\n", + "\n", + "csoundversion = cs.version()\n", + "majorver = csoundversion // 1000\n", + "minorver = (csoundversion % 1000) // 100\n", + "patch = csoundversion % 10\n", + "\n", + "print(f\"Version id: {csoundversion}, version tuple: {majorver}.{minorver}.{patch}\")\n", + "\n", + "# On csound 6 there is an api version, this does not exist in csound 7\n", + "if csoundversion < 7000:\n", + " print(f\"API version: {cs.APIVersion()}\")\n", + "\n", + "# List of available opcodes, print only the beginning of the list\n", + "pprint(cs.getOpcodes()[:20])\n", + "\n", + "# Opcodes with 'osc' in the name\n", + "print(\"\\n--- 'osc' opcodes ---\\n\")\n", + "pprint(set(opc.name for opc in cs.getOpcodes() if \"osc\" in opc.name))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\t\t overall amps:\u001b[m 0.0\n", + "\t overall samples out of range:\u001b[m 0\u001b[m\n", + "0 errors in performance\n", + "\u001b[mElapsed time at end of performance: real: 2.530s, CPU: 0.152s\n", + "\u001b[m" + ] + } + ], + "source": [ + "del cs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Performing an orchestra\n", + "\n", + "The user has the option to perform an external .csd file, the text inside the .csd file or just pass csound code to the `Csound` instance to compile. \n", + "\n", + "In this case we will be performing in real-time, so we need to set the output to `dac`. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cs = ct.Csound()\n", + "cs.setOption('-odac')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we need to compile some actual code. Notice that settings like ksmps and 0dbfs need to be set at the first compile action. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cs.compileOrc(r'''\n", + "sr = 44100\n", + "ksmps = 64\n", + "0dbfs = 1\n", + "\n", + "instr 1\n", + " ; set default values\n", + " ; p4 p5 p6 p7\n", + " pset 0, 0, 0, 0.1, 60, 0.01, 0.2\n", + " iamp = p4\n", + " ipitch = p5\n", + " iattack = p6\n", + " irelease = p7\n", + " a0 = vco2(iamp, mtof(ipitch)) * linsegr:a(0, iattack, 1, irelease, 0)\n", + " outch 1, a0\n", + "endin\n", + "''')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "score = [\n", + " (1, 0, 10, 0.1, 60),\n", + " (1, 0.5, 10, 0.05, 62) \n", + "]\n", + "\n", + "for event in score:\n", + " cs.scoreEvent('i', event)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To actually perform the score we need to call `.perform()`, which executes all the events scheduled until now" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "--Csound version 7.0 (double samples) Dec 4 2024\n", + "[commit: ababd1a5e09ada51e5013f24732265a4273f9f09]\n", + "\u001b[mlibsndfile-1.2.2\n", + "\u001b[mgraphics suppressed, ascii substituted\n", + "sr = 44100.0,\u001b[m kr = 689.062,\u001b[m ksmps = 64\n", + "\u001b[m0dBFS level = 1.0,\u001b[m A4 tuning = 440.0\n", + "\u001b[morch now loaded\n", + "\u001b[maudio buffered in 256 sample-frame blocks\n", + "\u001b[mALSA output: total buffer size: 1024, period size: 256\n", + "writing 256 sample blks of 64-bit floats to dac\n", + "SECTION 1:\n", + "\u001b[mnew alloc for instr 1:\n", + "\t T 0.501 TT 0.501 M: 0.11661\n", + "new alloc for instr 1:\n", + "\u001b[m" + ] + } + ], + "source": [ + "cs.perform()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If some kind of interaction between the running csound process and python is needed, it is possible to use `.performKsmps()`, which processes one cycle of samples (determined by `ksmps`) and yields control to the user.\n", + "\n", + "In the example below we use this to schedule events by calculating time from python. This is just to show that any kind of process from python can be used to interact with a running csound instance" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Press I-I to interrupt\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "--Csound version 7.0 (double samples) Dec 4 2024\n", + "[commit: ababd1a5e09ada51e5013f24732265a4273f9f09]\n", + "\u001b[mlibsndfile-1.2.2\n", + "\u001b[mgraphics suppressed, ascii substituted\n", + "sr = 44100.0,\u001b[m kr = 689.062,\u001b[m ksmps = 64\n", + "\u001b[m0dBFS level = 1.0,\u001b[m A4 tuning = 440.0\n", + "\u001b[morch now loaded\n", + "\u001b[maudio buffered in 256 sample-frame blocks\n", + "\u001b[mALSA output: total buffer size: 1024, period size: 256\n", + "writing 512 sample blks of 64-bit floats to dac\n", + "SECTION 1:\n", + "\u001b[m" + ] + }, + { + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[69], line 40\u001b[0m\n\u001b[1;32m 38\u001b[0m scale \u001b[38;5;241m=\u001b[39m [\u001b[38;5;241m0\u001b[39m, \u001b[38;5;241m2\u001b[39m, \u001b[38;5;241m4\u001b[39m, \u001b[38;5;241m5\u001b[39m, \u001b[38;5;241m7\u001b[39m, \u001b[38;5;241m9\u001b[39m, \u001b[38;5;241m11\u001b[39m]\n\u001b[1;32m 39\u001b[0m pitches \u001b[38;5;241m=\u001b[39m [\u001b[38;5;241m60\u001b[39m \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m12\u001b[39m \u001b[38;5;241m*\u001b[39m octave \u001b[38;5;241m+\u001b[39m step \u001b[38;5;28;01mfor\u001b[39;00m octave \u001b[38;5;129;01min\u001b[39;00m [\u001b[38;5;241m0\u001b[39m, \u001b[38;5;241m1\u001b[39m] \u001b[38;5;28;01mfor\u001b[39;00m step \u001b[38;5;129;01min\u001b[39;00m scale]\n\u001b[0;32m---> 40\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[43mcs\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mperformKsmps\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;241m==\u001b[39m ct\u001b[38;5;241m.\u001b[39mCSOUND_SUCCESS:\n\u001b[1;32m 41\u001b[0m t1 \u001b[38;5;241m=\u001b[39m time\u001b[38;5;241m.\u001b[39mtime()\n\u001b[1;32m 42\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m t1 \u001b[38;5;241m-\u001b[39m t0 \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m1\u001b[39m\u001b[38;5;241m/\u001b[39m\u001b[38;5;241m8.\u001b[39m:\n", + "File \u001b[0;32m~/dev/python/ctcsound7/ctcsound7/api7.py:975\u001b[0m, in \u001b[0;36mCsound.performKsmps\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 973\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_started:\n\u001b[1;32m 974\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstart()\n\u001b[0;32m--> 975\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mbool\u001b[39m(\u001b[43mlibcsound\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcsoundPerformKsmps\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcs\u001b[49m\u001b[43m)\u001b[49m)\n", + "\u001b[0;31mKeyboardInterrupt\u001b[0m: " + ] + } + ], + "source": [ + "import random\n", + "import time\n", + "\n", + "# We need this in order to be able to use keyboard interrupt. Otherwise csound\n", + "# itself sets a signal handler and python looses control \n", + "ct.csoundInitialize(signalHandler=False, atExitHandler=False)\n", + "\n", + "cs = ct.Csound()\n", + "\n", + "# Realtime processing\n", + "cs.setOption('-odac')\n", + "\n", + "# Disable printing whenever a new event is scheduled\n", + "cs.setOption('-m128')\n", + "\n", + "# The orchestra\n", + "cs.compileOrc(r'''\n", + "sr = 44100\n", + "nchnls = 2\n", + "ksmps = 64\n", + "0dbfs = 1\n", + "\n", + "instr 1\n", + " iamp = p4\n", + " ipitch = p5\n", + " iattack = p6 > 0 ? p6 : 0.01\n", + " irelease = p7 > 0 ? p7 : 0.4\n", + " a0 = oscili(iamp, mtof(ipitch)) + oscili(iamp, mtof(ipitch+0.12))\n", + " outall a0 * linsegr:a(0, iattack, 1, iattack*2, 0.2, irelease, 0)\n", + "endin\n", + "''')\n", + "\n", + "print(\"\\nPress I-I to interrupt\\n\")\n", + "\n", + "# Schedule a note every 1/8 of a second. The pitch is chosen at random\n", + "# from a given scale, in this case a c-major scale\n", + "t0 = time.time()\n", + "scale = [0, 2, 4, 5, 7, 9, 11]\n", + "pitches = [60 + 12 * octave + step for octave in [0, 1] for step in scale]\n", + "while cs.performKsmps() == ct.CSOUND_SUCCESS:\n", + " t1 = time.time()\n", + " if t1 - t0 > 1/8.:\n", + " pitch = random.choice(pitches)\n", + " if pitch % 12 in (2, 4, 7):\n", + " dur = 1.5\n", + " else:\n", + " dur = 0.15\n", + " cs.scoreEvent('i', (1, 0, dur, 0.1, random.choice(pitches)))\n", + " t0 = t1\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The same can be achieved with csound counting time" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": {}, + "outputs": [ + { + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[70], line 4\u001b[0m\n\u001b[1;32m 1\u001b[0m sr \u001b[38;5;241m=\u001b[39m cs\u001b[38;5;241m.\u001b[39msr()\n\u001b[1;32m 2\u001b[0m t0 \u001b[38;5;241m=\u001b[39m cs\u001b[38;5;241m.\u001b[39mcurrentTimeSamples() \u001b[38;5;241m/\u001b[39m sr\n\u001b[0;32m----> 4\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[43mcs\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mperformKsmps\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;241m==\u001b[39m ct\u001b[38;5;241m.\u001b[39mCSOUND_SUCCESS:\n\u001b[1;32m 5\u001b[0m t1 \u001b[38;5;241m=\u001b[39m cs\u001b[38;5;241m.\u001b[39mcurrentTimeSamples() \u001b[38;5;241m/\u001b[39m sr\n\u001b[1;32m 6\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m t1 \u001b[38;5;241m-\u001b[39m t0 \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m1\u001b[39m\u001b[38;5;241m/\u001b[39m\u001b[38;5;241m8.\u001b[39m:\n", + "File \u001b[0;32m~/dev/python/ctcsound7/ctcsound7/api7.py:975\u001b[0m, in \u001b[0;36mCsound.performKsmps\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 973\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_started:\n\u001b[1;32m 974\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstart()\n\u001b[0;32m--> 975\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mbool\u001b[39m(\u001b[43mlibcsound\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcsoundPerformKsmps\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcs\u001b[49m\u001b[43m)\u001b[49m)\n", + "\u001b[0;31mKeyboardInterrupt\u001b[0m: " + ] + } + ], + "source": [ + "sr = cs.sr()\n", + "t0 = cs.currentTimeSamples() / sr\n", + "\n", + "while cs.performKsmps() == ct.CSOUND_SUCCESS:\n", + " t1 = cs.currentTimeSamples() / sr\n", + " if t1 - t0 > 1/8.:\n", + " pitch = random.choice(pitches)\n", + " if pitch % 12 in (2, 4, 7):\n", + " dur = 1.5\n", + " else:\n", + " dur = 0.15\n", + " cs.scoreEvent('i', (1, 0, dur, 0.1, random.choice(pitches)))\n", + " t0 = t1\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/02 - Threading.ipynb b/examples/02 - Threading.ipynb new file mode 100644 index 0000000..8bae6d3 --- /dev/null +++ b/examples/02 - Threading.ipynb @@ -0,0 +1,656 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Multithreading\n", + "\n", + "In the preceding examples, csound was executed within a performance loop and any python code needed to be run within that loop. To use Csound in a more flexible way it is advisable to run the performance loop in a dedicated thread\n", + "\n", + "Cound has a helper class called CsoundPerformanceThread, which creates a native thread and runs the performance loop of an existing csound instance on the background. The main Python thread is thus not blocked, allowing the user to interract with it, while the performance thread runs concurrently, outside of the GIL. The user can send messages to the performance thread to toggle pause, schedule input evets, etc.\n", + "\n", + "## Example\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import ctcsound7 as ct\n", + "cs = ct.Csound()\n", + "cs.setOption('-d -odac -m0')\n", + "cs.compileOrc(r'''\n", + "sr = 48000\n", + "ksmps = 64\n", + "nchnls = 2\n", + "0dbfs = 1\n", + "\n", + "instr 1\n", + " iamp, ipitch, iattack, idec, ipan passign 4\n", + " aenv = linen:a(1, iattack, p3, idec)\n", + " asig = poscil(iamp, mtof(ipitch)) * aenv\n", + " a1, a2 pan2 asig, ipan\n", + " outs a1, a2\n", + "endin\n", + "''')\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This creates a new thread with the existing csound process" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "--Csound version 7.0 (double samples) Dec 4 2024\n", + "[commit: ababd1a5e09ada51e5013f24732265a4273f9f09]\n", + "\u001b[mlibsndfile-1.2.2\n", + "\u001b[msr = 48000.0,\u001b[m kr = 750.000,\u001b[m ksmps = 64\n", + "\u001b[m0dBFS level = 1.0,\u001b[m A4 tuning = 440.0\n", + "\u001b[maudio buffered in 256 sample-frame blocks\n", + "\u001b[mwriting 512 sample blks of 64-bit floats to dac\n", + "SECTION 1:\n", + "\u001b[m" + ] + } + ], + "source": [ + "thread = cs.performanceThread()\n", + "thread.play()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, we can send messages to the performance thread:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "thread.scoreEvent(0, 'i', (1, 0, 1, 0.5, 60, 0.05, 0.3, 0.2))\n", + "thread.scoreEvent(0, 'i', (1, 0.5, 1, 0.5, 62, 0.05, 0.3, 0.8))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When we're done, we stop the performance thread:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\t\t overall amps:\u001b[m 0.62995 0.62998\n", + "\t overall samples out of range:\u001b[m 0\u001b[m 0\u001b[m\n", + "0 errors in performance\n", + "\u001b[m1066 512 sample blks of 64-bit floats written to dac\n" + ] + }, + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "thread.stop()\n", + "thread.join()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Process Callback\n", + "\n", + "A performance thread includes methods to control playback and schedule events. All other tasks must be performed via the csound instance. The problem is that when usign a performance thread, accessing the csound instance directly has an added latency. \n", + "\n", + "In order to access the csound instance while using the performance thread, it is advisable to set a process callback.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import queue\n", + "import time\n", + "import ctcsound7 as ct" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "rtaudio: JACK module enabled\n", + "--Csound version 7.0 (double samples) Dec 4 2024\n", + "[commit: ababd1a5e09ada51e5013f24732265a4273f9f09]\n", + "\u001b[mlibsndfile-1.2.2\n", + "\u001b[mgraphics suppressed, ascii substituted\n", + "sr = 48000.0,\u001b[m kr = 750.000,\u001b[m ksmps = 64\n", + "\u001b[m0dBFS level = 1.0,\u001b[m A4 tuning = 440.0\n", + "\u001b[morch now loaded\n", + "\u001b[maudio buffered in 256 sample-frame blocks\n", + "\u001b[msystem sr: 48000.000000\n", + "Jack output ports:\n", + " 0: dac0 (dac:Built-in Audio Analog Stereo:playback_FL)\n", + " 1: dac1 (dac:Built-in Audio Analog Stereo:playback_FR)\n", + "connecting channel 0 to Built-in Audio Analog Stereo:playback_FL\n", + "connecting channel 1 to Built-in Audio Analog Stereo:playback_FR\n", + "writing 512 sample blks of 64-bit floats to dac:Built-in\n", + "SECTION 1:\n", + "\u001b[mWARNING: \u001b[mrtjack: xrun in real time audio\u001b[m\n", + "\u001b[m" + ] + } + ], + "source": [ + "cs = ct.Csound()\n", + "\n", + "# Change as needed\n", + "# cs.setOption('-odac')\n", + "cs.setOption('-+rtaudio=jack -odac:Built-in' )\n", + "\n", + "cs.compileOrc(r'''\n", + "sr = 48000\n", + "ksmps = 64\n", + "nchnls = 2\n", + "0dbfs = 1\n", + "\n", + "instr 1\n", + " ipitch = p4\n", + " asig = poscil(0.1, mtof(ipitch)) \n", + " outch 1, asig * linen:a(1, 0.01, p3, 0.2)\n", + "endin\n", + "''')\n", + "\n", + "thread = cs.performanceThread()\n", + "thread.play()\n", + "\n", + "\n", + "class ProcessHandler:\n", + " def __init__(self, csound):\n", + " self.csound = csound\n", + " self.thread = csound.performanceThread()\n", + " self.q = queue.SimpleQueue()\n", + " self.thread.setProcessCallback(self.callback)\n", + " \n", + " def put(self, job):\n", + " self.q.put(job)\n", + " \n", + " def callback(self, data):\n", + " if self.q.qsize() == 0:\n", + " return\n", + " job = self.q.get_nowait()\n", + " job(self.csound, self.thread)\n", + "\n", + " def compile(self, code: str):\n", + " self.put(lambda cs, pt: cs.compileOrc(code))\n", + "\n", + " \n", + "proc = ProcessHandler(cs)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\t T 1.501 TT 1.501 M: 0.00000 0.00000\n", + "new alloc for instr 1:\n", + "\u001b[mnew alloc for instr 10:\n", + "WARNING: \u001b[mrtjack: xrun in real time audio\u001b[m\n", + "\u001b[m" + ] + } + ], + "source": [ + "proc.put(lambda cs, pt: cs.compileOrc(r'''\n", + "instr 10\n", + " ifreq = p4\n", + " outch 2, vco2:a(1, ifreq) \n", + "endin\n", + "'''))\n", + "\n", + "thread.scoreEvent(0, 'i', (1, 0, 1, 72))\n", + "# The code is compiled in the next process cycle, so it is possible to schedule\n", + "# an event right away\n", + "thread.scoreEvent(0, 'i', (10, 0, 3, 600))\n", + "\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "rtaudio: JACK module enabled\n", + "--Csound version 7.0 (double samples) Dec 4 2024\n", + "[commit: ababd1a5e09ada51e5013f24732265a4273f9f09]\n", + "\u001b[mlibsndfile-1.2.2\n", + "\u001b[mgraphics suppressed, ascii substituted\n", + "sr = 48000.0,\u001b[m kr = 750.000,\u001b[m ksmps = 64\n", + "\u001b[m0dBFS level = 1.0,\u001b[m A4 tuning = 440.0\n", + "\u001b[morch now loaded\n", + "\u001b[maudio buffered in 256 sample-frame blocks\n", + "\u001b[msystem sr: 48000.000000\n", + "Jack output ports:\n", + " 0: dac0 (dac:Built-in Audio Analog Stereo:playback_FL)\n", + " 1: dac1 (dac:Built-in Audio Analog Stereo:playback_FR)\n", + "connecting channel 0 to Built-in Audio Analog Stereo:playback_FL\n", + "connecting channel 1 to Built-in Audio Analog Stereo:playback_FR\n", + "writing 512 sample blks of 64-bit floats to dac:Built-in\n", + "SECTION 1:\n", + "\t T 0.001 TT 0.001 M: 0.00000 0.00000\n", + "new alloc for instr 1:\n", + "\u001b[mnew alloc for instr 10:\n", + "WARNING: \u001b[mrtjack: xrun in real time audio\u001b[m\n", + "\u001b[m" + ] + } + ], + "source": [ + "cs = ct.Csound()\n", + "\n", + "# Change as needed\n", + "# cs.setOption('-odac')\n", + "cs.setOption('-+rtaudio=jack -odac:Built-in' )\n", + "\n", + "cs.compileOrc(r'''\n", + "sr = 48000\n", + "ksmps = 64\n", + "nchnls = 2\n", + "0dbfs = 1\n", + "\n", + "instr 1\n", + " ipitch = p4\n", + " asig = poscil(0.1, mtof(ipitch)) \n", + " outch 1, asig * linen:a(1, 0.01, p3, 0.2)\n", + "endin\n", + "''')\n", + "\n", + "thread = cs.performanceThread(withProcessQueue=True)\n", + "thread.play()\n", + "\n", + "# time.sleep(2)\n", + "\n", + "thread.task(lambda cs, pt: cs.compileOrc(r'''\n", + "instr 10\n", + " ifreq = p4\n", + " outch 2, vco2:a(0.1, ifreq) \n", + "endin\n", + "'''))\n", + "\n", + "thread.scoreEvent(0, 'i', (1, 0, 1, 72))\n", + "thread.scoreEvent(0, 'i', (10, 0, 1, 600))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "rtaudio: JACK module enabled\n", + "--Csound version 7.0 (double samples) Dec 4 2024\n", + "[commit: ababd1a5e09ada51e5013f24732265a4273f9f09]\n", + "\u001b[mlibsndfile-1.2.2\n", + "\u001b[mgraphics suppressed, ascii substituted\n", + "sr = 48000.0,\u001b[m kr = 750.000,\u001b[m ksmps = 64\n", + "\u001b[m0dBFS level = 32768.0,\u001b[m A4 tuning = 440.0\n", + "\u001b[morch now loaded\n", + "\u001b[maudio buffered in 256 sample-frame blocks\n", + "\u001b[msystem sr: 48000.000000\n", + "Jack output ports:\n", + " 0: dac0 (dac:Built-in Audio Analog Stereo:playback_FL)\n", + " 1: dac1 (dac:Built-in Audio Analog Stereo:playback_FR)\n", + "connecting channel 0 to Built-in Audio Analog Stereo:playback_FL\n", + "writing 256 sample blks of 64-bit floats to dac\n", + "SECTION 1:\n", + "\u001b[mWARNING: \u001b[mrtjack: xrun in real time audio\u001b[m\n", + "\u001b[m" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "101.0 3.383636474609375\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "ftable 101:\n", + "ftable 101:\t1024 points, scalemax 0.000\n", + "inactive allocs returned to freespace\n", + "\t\t overall amps:\u001b[m 0.0\n", + "\t overall samples out of range:\u001b[m 0\u001b[m\n", + "0 errors in performance\n", + "\u001b[mElapsed time at end of performance: real: 0.560s, CPU: 0.078s\n", + "\u001b[m97 256 sample blks of 64-bit floats written to dac\n" + ] + } + ], + "source": [ + "import ctcsound7 as ct\n", + "import time\n", + "\n", + "cs = ct.Csound()\n", + "cs.setOption(\"-+rtaudio=jack -odac -B512 -b256\")\n", + "cs.compileOrc(r'''\n", + "sr = 48000\n", + "ksmps = 64\n", + "''')\n", + "\n", + "thread = cs.performanceThread(withProcessQueue=True)\n", + "thread.play()\n", + "bufsize = 1024\n", + "time.sleep(0.5)\n", + "\n", + "t0 = time.time()\n", + "tabnum = thread.evalCode(f'gi__tabnum ftgen 0, 0, {-bufsize}, -2, 0\\nreturn gi__tabnum')\n", + "t1 = time.time()\n", + "print(tabnum, (t1 - t0) * 1000)\n", + "\n", + "thread.stop()\n", + "thread.join()\n", + "del cs\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "rtaudio: JACK module enabled\n", + "--Csound version 7.0 (double samples) Dec 4 2024\n", + "[commit: ababd1a5e09ada51e5013f24732265a4273f9f09]\n", + "\u001b[mlibsndfile-1.2.2\n", + "\u001b[mgraphics suppressed, ascii substituted\n", + "sr = 48000.0,\u001b[m kr = 750.000,\u001b[m ksmps = 64\n", + "\u001b[m0dBFS level = 32768.0,\u001b[m A4 tuning = 440.0\n", + "\u001b[morch now loaded\n", + "\u001b[maudio buffered in 256 sample-frame blocks\n", + "\u001b[msystem sr: 48000.000000\n", + "Jack output ports:\n", + " 0: dac0 (dac:Built-in Audio Analog Stereo:playback_FL)\n", + " 1: dac1 (dac:Built-in Audio Analog Stereo:playback_FR)\n", + "connecting channel 0 to Built-in Audio Analog Stereo:playback_FL\n", + "writing 256 sample blks of 64-bit floats to dac\n", + "SECTION 1:\n", + "\u001b[mWARNING: \u001b[mrtjack: xrun in real time audio\u001b[m\n", + "\u001b[m" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "101.0 31.600475311279297\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "ftable 101:\n", + "ftable 101:\t1024 points, scalemax 0.000\n", + "inactive allocs returned to freespace\n", + "\t\t overall amps:\u001b[m 0.0\n", + "\t overall samples out of range:\u001b[m 0\u001b[m\n", + "0 errors in performance\n", + "\u001b[mElapsed time at end of performance: real: 0.588s, CPU: 0.050s\n", + "\u001b[m105 256 sample blks of 64-bit floats written to dac\n" + ] + } + ], + "source": [ + "import ctcsound7 as ct\n", + "import time\n", + "\n", + "cs = ct.Csound()\n", + "cs.setOption(\"-+rtaudio=jack -odac -b256\")\n", + "cs.compileOrc(r'''\n", + "sr = 48000\n", + "ksmps = 64\n", + "''')\n", + "\n", + "# csound.compileOrc(...)\n", + "thread = cs.performanceThread(withProcessQueue=False)\n", + "thread.play()\n", + "bufsize = 1024\n", + "time.sleep(0.5)\n", + "t0 = time.time()\n", + "\n", + "tabnum = cs.evalCode(f'gi__tabnum ftgen 0, 0, {-bufsize}, -2, 0\\nreturn gi__tabnum')\n", + "t1 = time.time()\n", + "print(tabnum, (t1 - t0) * 1000)\n", + "\n", + "thread.stop()\n", + "thread.join()\n", + "del cs\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "thread.compile(f'gi__tabnum ftgen 0, 0, {-bufsize}, -2, 0')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "error: \u001b[mget_arg_type2: Variable 'gi__tabnum' used before defined\n", + "Line 0" + ] + }, + { + "data": { + "text/plain": [ + "nan" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[m\n", + "\u001b[merror: \u001b[mVariable type for gi__tabnum could not be determined.\u001b[m\n", + "\u001b[mParsing failed due to syntax errors\n", + "\u001b[mStopping on parser failure\n", + "\u001b[m" + ] + } + ], + "source": [ + "cs.evalCode('return gi__tabnum')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "error: \u001b[mget_arg_type2: Variable 'i__tabnum' used before defined\n", + "Line 0\u001b[m\n", + "\u001b[merror: \u001b[mVariable type for i__tabnum could not be determined.\u001b[m\n", + "\u001b[mParsing failed due to syntax errors\n", + "\u001b[mStopping on parser failure\n", + "\u001b[m" + ] + }, + { + "data": { + "text/plain": [ + "nan" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cs.evalCode('return i__tabnum')" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "ftable 101:\n", + "graphics suppressed, ascii substituted\n", + "ftable 101:\t1024 points, scalemax 0.000\n" + ] + }, + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cs.compileOrc(f'gi__tabnum ftgen 0, 0, {-bufsize}, -2, 0')" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "101.0" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cs.evalCode('return gi__tabnum')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}