{
"$type": "site.standard.document",
"content": "---\ntitle: \"Xinyu's Extempore tutorials\"\ndescription: \"PRIMM-style tutorials for learning sound and music in Extempore, from sine waves through to samplers and metronomes.\"\ntags:\n - livecoding\n - extempore\n---\n\nimport Picture from \"@/components/Picture.astro\";\n\nThese tutorials are part of Xinyu Hou's research project to create a set of\n[PRIMM](https://blogs.kcl.ac.uk/cser/2017/09/01/primm-a-structured-approach-to-teaching-programming/)-style\ntutorials for learning to make sound/music in Extempore.\n\nThey're currently a work-in-progress---when they're done they'll be hosted on\nthe main Extempore docs site, but for now they're here on my blog.\n\n[[toc]]\n\n## Preliminaries\n\n### Setting Up Environment\n\n#### Installing Extempore\n\nInstall Extempore by following the steps\n[here](https://extemporelang.github.io/docs/overview/install/).\n\n#### Installing VSCode\n\nInstall VSCode (a text editor) by following the steps\n[here](https://extemporelang.github.io/docs/overview/editor-support/).\n\n:::tip\n\nIf you want to use a different text editor,\n[there are other options as well](https://extemporelang.github.io/docs/overview/editor-support/).\n\n:::\n\n### Evaluating Extempore Code\n\nEvaluate the extempore code on VSCode by following the steps below:\n\n1. Start VSCode\n\n2. Go to **File** -> **Open**, then open your Extempore directory.\n\n3. In the VSCode built-in terminal window (**ctrl+\\`**) move into the extempore\n directory, and start extempore (type `./extempore` and hit return).\n\n4. Open or create an Extempore file (**.xtm** file)\n\n5. Connect to the Extempore process: you can do this in VSCode through the\n “**command palette**”, which can be brought up with **ctrl+Shift+P** on\n Windows/Linux or **cmd+Shift+P** on macOS.\n\n <Picture file=\"xinyu-tutorials/TO3.webp\" alt=\"the command palette\" />\n\n6. Type **Extempore Connect** in the opening command palette, the default host\n and port arguments will be `localhost` and `7099` respectively.\n\n:::tip[To evaluate Extempore code]\n\nMove the cursor into the code you want to evaluate and hit **cmd+enter** on\nmacOS or **ctrl+enter** on Windows/Linux.\n\n:::\n\nWhat you might see in the terminal window when you evaluate extempore code:\n\n<Picture file=\"xinyu-tutorials/TO4.webp\" alt=\"Extempore log view\" />\n\n> Note: you can use **ctrl+c** in the terminal where extempore is running to\n> kill the extempore process whenever you want.\n\n## Tutorial One\n\n### Predict and Run\n\nWork in pairs or small groups, look at the code below and predict what does this\ncode do:\n\n```xtlang\n(bind-val my-pi float 3.1415)\n```\n\n> Note: in Extempore, we generally use **kebab-case** case style `my-pi` instead\n> of using **camelCase** `myPi` or **PascalCase** `MyPi`or **snake_case** >\n> `my_pi`.\n\nRun the code above. Does the compiler print anything in the log? What does the\nprinted message mean?\n\n<Picture file=\"xinyu-tutorials/my-pi.webp\" alt=\"my-pi log view\" />\n\nNow, it's time to produce a sound in Extempore!\n\nWork in pairs or small groups again, look at the code below and predict/guess\nwhat might happen when it runs:\n\n```xtlang\n(bind-func dsp:DSP\n (lambda (in time chan dat)\n (let ((amplitude 0.1)\n (frequency 440.0)\n (two-pi (* 2.0 my-pi)))\n (* amplitude\n (sin (/ (* frequency\n two-pi\n (convert time)) SRf))))))\n\n(dsp:set! dsp)\n```\n\nDoes it work as predicted?\n\n> Hint: we set up the xtlang callback:\n>\n> ```xtlang\n> (bind-func dsp:DSP\n> (lambda (in time chan dat)\n> 0.0))\n> ```\n>\n> - **in:SAMPLE** sample from input device\n> - **time:i64** sample number\n> - **chan:i64** audio channel\n> - **dat:SAMPLE** user data\n> - **`<return>`:SAMPLE** sample at given channel and time\n> - sample value range from -1.0 to 1.0\n>\n> Note: In Extempore, we represent the mathematical operation _2 _ PI* as `(* 2\n> PI)`\n>\n> Note: `SRf` refers to the current sampling frequency\n>\n> Note: The form of a `let` statement is as follows:\n> `(let ((var_name_1 var_value_1) ... (var_n val_n)) (<body>))`\n>\n> Note: `convert` allows us to make a `SAMPLE` typed value from `time`.\n>\n> Note: We only can set `dsp` function once, but then redefine it as many times\n> as we want.\n\nWhat about white noise in Extempore?\n\n```xtlang\n(bind-func dsp:DSP\n (lambda (in time chan data)\n (* 0.2 (random))))\n```\n\nCan you explain what happened here?\n\n### Investigate\n\nWork in pairs or in small groups, work out the answers to the following\nquestions:\n\n1. In what order did you compile (execute) the code in this program?\n2. What happened after you evaluated each \"chunk\" of Extempore code?\n3. Can you guess what would happen if you compile the code in a different order?\n\n> Note: you can add\n> [comments](<https://en.wikipedia.org/wiki/Comment_(computer_programming)>)\n> (starting with **;;**) to the program to make some notes for you to understand\n> the code. The comments are generally ignored by compilers and interpreters.\n\n### Modify\n\n1. Can you play a sound with a frequency of 460.0?\n2. Can you make the sound louder by just changing the code? (don't use your\n volume buttons!)\n\n### Make\n\nNow, let’s move `amplitude`, `frequency` and `two-pi` outside of lambda to make\nour `dsp` function controllable outside as below:\n\n```xtlang\n(bind-func dsp:DSP\n (let ((amplitude 0.1)\n (frequency 440.0)\n (two-pi (* 2.0 my-pi)))\n (lambda (in time chan dat)\n (* amplitude\n (sin (/ (* frequency\n two-pi\n (convert time)) SRf))))))\n```\n\nCan you play a random frequency sound every time when you redefine the `dsp`\nfunction?\n\n> Note: You could use `(random 440.0 700.0)` to randomly generate a value\n> between 440.0 and 700.0\n\nCompare to the `(random)` in the white noise part above, does the `(random)`\nwork differently here? Can you explain how `(random)` can be either for\n\"control\" (e.g. see the above sine wave part) or for \"signal\" (e.g. see the\nwhite noise part)?\n\n## Tutorial Two\n\n### Predict and Run\n\nWork in pairs or small groups, look at the code below and predict what might\nhappen when it runs.\n\n```xtlang\n;; load the instruments file\n(sys:load \"libs/core/instruments.xtm\")\n\n;; define an fmsynth using the built-in components\n(make-instrument fmsynth fmsynth)\n\n;; add the instrument to the DSP output sink closure\n(bind-func dsp:DSP\n (lambda (in time chan dat)\n (fmsynth in time chan dat)))\n\n(dsp:set! dsp)\n```\n\n> Note: The dsp function takes as input:\n>\n> - **in**: the input audio sample, e.g. from the microphone.\n> - **time**: an `i64` representing the time.\n> - **chan**: another `i64` which represents the channel index (0 for L, 1 for\n> R, etc.). Extempore can handle any number of channels.\n> - **data**: this is a pointer to a `SAMPLE` type (which is float by default),\n> and can be used to pass arbitrary data into the dsp function.\n\nThink about what is the `i64` type in the above Note?\n\n> Primitive types in Extempore:\n>\n> Integers:\n>\n> - `i1`: (boolean) uses 1 bit.\n> - `i8`: (char) uses 8 bits (1 byte), which could store 2^8 = 256 unique\n> integer values, -128 ~ 127.\n> - `i32`: uses 32 bits, which could store 2^32 uniquew integer values, -2^31 ~\n> (2^31 - 1).\n> - `i64`: (default) uses 64 bits, which could store 2^63 uniquew integer\n> values, -2^63 ~ (2^63 - 1).\n>\n> Floats:\n>\n> - `float`: a single precision (32 bit) floating-point data type.\n> - `double`: (default), a double precision (64 bit) floating-point data type.\n\n> Moreover, for your interests:\n>\n> [Pointer](<https://en.wikipedia.org/wiki/Pointer_(computer_programming)>)\n> types:\n>\n> - `double*`: a pointer to a double.\n> - `i64*`: a pointer to a 64-bit integer.\n> - `i64**`: a pointer to a pointer to a 64-bit integer.\n\nNow, we could play a note by using the fmsynth instrument that we just defined:\n\n```xtlang\n;; play a note on our fmsynth\n(play-note (now) fmsynth (random 60 80) 80 *second*)\n```\n\n> Note: the parameters for `play-note` are\n> `play-note <start-time> <instrument> <note-frequency> <note-duration>`\n\nWhat would happend if you play the note for the second time (or multiple times)?\nWhy is the note different?\n\nIt's the time to make a loop in Extempore:\n\n```xtlang\n;; make a loop\n(define my-loop\n (lambda (time)\n (play-note time fmsynth (random 60 80) 80 *second*)\n (callback (+ time *second*) 'my-loop (+ time *second*))))\n\n(my-loop (now))\n```\n\nWe make a `loop` function above, but how does the `loop` work? let's see a\nsimpler example:\n\n```xtlang\n;; let's see a simpler loop example\n(define test-loop\n (lambda ()\n (println \"I'm looping...\")\n (callback (+ (now) 10000 *second*) 'test-loop ()))\n)\n\n(test-loop())\n```\n\n> Note: the parameters for `callback` are `callback <time> <func> <args>`\n\nCan you predict what will happen when you run the `test-loop` function? Run the\ncode to check your thoughts.\n\n> Note: do not forget to check your terminal panel.\n\n### Investigate\n\nWork in pairs or in small groups, work out the answers to the following\nquestions:\n\n1. What does `my-loop` do?\n2. What does the `callback` in the loop do?\n3. What is the difference between Scheme lambda expression and xtlang bind-func\n expression? More information click\n [here](https://digego.github.io/extempore/scheme-xtlang-interop.html).\n4. What is the difference between using **now** versus using **time**? More\n information click [here](https://digego.github.io/extempore/time.html).\n\n> Note: you can add\n> [comments](<https://en.wikipedia.org/wiki/Comment_(computer_programming)>)\n> (starting with `;;`) to the program to make some notes for you to understand\n> the code.\n\n### Modify\n\nWork in pairs or in small groups, can you modify the above code and make a loop\nwith random notes lengths and random notes frequencies.\n\n```xtlang\n;; hint\n;; make a loop with random note length and random note\n(define diff-length\n (lambda (time)\n (let ((note-length (random '(0.5 1.0 1.5 2.0)))\n (pitch (random '(60 63 65 68 70 73 75 78 80))))\n (play-note time fmsynth pitch 80 (* *second* note-length))\n (callback (+ time (* note-length *second*)) 'diff-length (+ time (* note-length *second*)))))))\n\n(diff-length (now))\n```\n\n### Make\n\nCan you make a chord (or play different notes at the same time) function by\nusing similar ideas to the previous program?\n\n## Tutorial Three\n\n### Predict and Run\n\n```xtlang\n;; loop major scale\n(let loop-white-keys((scale '(0 2 4 5 7 9 11 12 14 16 17 19 21 23))\n (time 0))\n (play-note (+ (now) time) synth (+ 60 (car scale)) 80 4000)\n (if (not (null? (cdr scale)))\n (loop-white-keys (cdr scale) (+ time 10000))))\n\n;; another loop for notes\n(let loop-black-keys ((scale '(1 3 6 8 10 13 15 18 20 22))\n (time 0))\n (play-note (+ (now) time) synth (+ 60 (car scale)) 80 4000)\n (if (not (null? (cdr scale)))\n (loop-black-keys (cdr scale) (+ time 10000))))\n```\n\n> Note:\n>\n> - `car` return the first element of a list.\n> - `cdr` return the rest of the elements in a list, that is, it returns the\n> part of the list that follows the first item.\n> - `if <test> <consequent> <alternate>`\n> - [more conditionals syntax](https://www.cs.cmu.edu/Groups/AI/html/r4rs/r4rs_6.html).\n\n> Note: the syntax for `let` is: `let <variable> <bindings> <body>`\n\n> Note: in the above examples, we used the named `let`. Therefore, the `<body>`\n> could be repeatly executed by invoking the `let` procedure named by the\n> `<variable>`.\n\nCan you predict what will happen? Then run the code to check your thoughts.\n\nBefore you run the code above, do not forget to load the libraries, define and\nadd an instrument `synth` to the `dsp` output sink closure:\n\n```xtlang\n;; load the instruments file\n(sys:load \"libs/core/instruments.xtm\")\n(sys:load \"libs/core/pc_ivl.xtm\")\n\n;; define a synth using the provided components\n(make-instrument synth fmsynth)\n\n;; add the instrument to the DSP output sink closure\n(bind-func dsp:DSP\n (lambda (in time chan dat)\n (synth in time chan dat)))\n\n(dsp:set! dsp)\n```\n\nNow, Let's play a chord on a list of frequencies! Can you predict what will\nhappen here:\n\n```xtlang\n;; play a chord\n(map (lambda (p)\n (play-note (now) synth p 80 44100))\n (list 72 76 79))\n```\n\n> Note: again, the parameters for `lambda` is: `lambda <formals> <body>`\n\n> Note: here we have two methods to express a `list`: For example,\n> `(list 72 76 79)` and `'(72 76 79)`.\n\nCan you guess what does `map` do?\n\nHere is another method to play the same chord that we ran above:\n\n```xtlang\n;; another method for play the same chord\n(define play-a-chord\n (lambda (time chord)\n (for-each (lambda (p)\n (play-note time synth p 80 44100))\n chord)\n ))\n\n(play-a-chord (now) '(72 76 79))\n```\n\nCan you guess what will happen after running `play-a-chord`?\n\nHow about more chords?\n\n```xtlang\n;; markov chord progression\n(define my-progression\n (lambda (time chords)\n (play-a-chord time (car chords))\n (define loop-chords '())\n (if (not (null? (cdr chords)))\n (set! loop-chords (cdr chords))\n (set! loop-chords '((72 76 79)(69 72 76)(65 69 72)(67 71 74))))\n (callback (+ time 40000) 'my-progression (+ time 44100) loop-chords))\n )\n\n(my-progression (now) '((72 76 79)(69 72 76)(65 69 72)(67 71 74)))\n```\n\n> Note: the syntax for `set!` is `set! <variable> <expression>`, which could\n> assign the `<expression>` to the `<variable>`\n\nRun the code. Dose it work as you predicted?\n\n### Investigate\n\nWork in pairs or in small groups, work out the answers to the following\nquestions:\n\n1. Currently, you have already seen `let`, `define` and `set!`. What are the\n differences between `let`, `define` and `set!`? E.g:\n - `set!` doesn't define the variable, rather it is used to assign the\n variable a new value.\n - The scope of variables defined by `let` are bound to the latter. `define`\n doesn't surround the body with parentheses.\n\n2. Here is another method to play the same chord that we predicted and ran\n before:\n\n```xtlang\n;; another method for play the same chord\n(define play-a-chord\n (lambda (time chord)\n (for-each (lambda (p)\n (play-note time synth p 80 44100))\n chord)\n ))\n\n(play-a-chord (now) '(72 76 79))\n```\n\nCompare to the `map` one, can you find what is the difference between `map` and\n`for-each`?\n\n- Evaluation order?\n- Return?\n\n3. We could use `do` to make a loop on playing the chord with the above\n `play-a-chord` method:\n\n```xtlang\n;; use do to loop the chord\n(do ((i 0 (+ i 1)))\n ((> i 3000) ())\n (play-a-chord (now) '(72 76 79)))\n```\n\nCompare to other `loop` or other iteration methods, can you find how does the\n`do` work here?\n\n> Hint: the syntax of `do` is:\n\n```xtlang\n(do ((<variable1> <init1> <step1>) ...)\n (<test> <expression> ...)\n <command> ...)\n```\n\n> - if the `<test>` result is `false`, then the sequence of `<command>` would be\n> evaluated.\n> - if the `<test>` result is `true`, the sequence of `<expression>` would be\n> evaluated from left to right, and the last `<expression>`'s values would be\n> returned at the end.\n> - the [boolean data type](https://en.wikipedia.org/wiki/Boolean_data_type) for\n> understanding `true` and `false`.\n\n### Modify\n\nNow, can you please modify the `my-progression` function so that it could loop\neach chord for four times before going to the next chord?\n\n```xtlang\n;; hint\n;; modified my-progression\n(define my-progression-modify\n (lambda (time chords n)\n\n (let loop ((loop-times 0))\n (if (= loop-times n)\n (display \"stopped\")\n (begin (play-a-chord (+ time (* loop-times 44100)) (car chords))\n (loop (+ loop-times 1)))))\n ; (loop-n-times n (play-a-chord time (car chords)))\n\n (define loop-chords-modify '())\n (if (not (null? (cdr chords)))\n (set! loop-chords-modify (cdr chords))\n (set! loop-chords-modify '((72 76 79)(69 72 76)(65 69 72)(67 71 74))))\n\n (callback (+ time (* n 40000)) 'my-progression-modify (+ time (* n 44100)) loop-chords-modify n))\n )\n\n(my-progression-modify (now) '((72 76 79)(69 72 76)(65 69 72)(67 71 74)) 4)\n```\n\n> Note: here we used the `begin` to do the sequencing:\n> `begin <expression 1> <expression 2> ...<expression n>`\n>\n> - the sequence of `<expression i>` will be evaluated sequentially from left to\n> right.\n> - `begin` will return the value of the last `<expression n>`.\n\n### Make\n\n1. Based on the previous code, can you make some creative melody by yourself?\n2. Can you play your melody and the chords at the same time?\n\n## Tutorial Four\n\n### Predict and Run\n\nIn this tutorial, we will learn how to do samplers in Extempore. Extempore\nprovides a built-in sampler in `libs/external/instruments_ext.xtm`, so let's\nload the libraries first:\n\n```xtlang\n;; load the libs\n(sys:load \"libs/external/instruments_ext.xtm\")\n(sys:load \"libs/core/instruments.xtm\")\n```\n\nWe are going to create a drum sampler, can you guess what is happening here:\n\n```xtlang\n;; TODO\n;; Set/Change the path of the subdirectory “OH” in your code.\n;; In my computer it’s like:\n(define drum-path \"/Users/apple/Downloads/COMP3740/salamanderDrumkit/OH/\")\n\n;; Load some wave files into drums sampler:\n(set-sampler-index drums (string-append drum-path \"kick_OH_F_9.wav\") *gm-kick* 0 0 0 1)\n(set-sampler-index drums (string-append drum-path \"snareStick_OH_F_9.wav\") *gm-side-stick* 0 0 0 1)\n(set-sampler-index drums (string-append drum-path \"snare_OH_FF_9.wav\") *gm-snare* 0 0 0 1)\n(set-sampler-index drums (string-append drum-path \"hihatClosed_OH_F_20.wav\") *gm-closed-hi-hat* 0 0 0 1)\n(set-sampler-index drums (string-append drum-path \"hihatFoot_OH_MP_12.wav\") *gm-pedal-hi-hat* 0 0 0 1)\n(set-sampler-index drums (string-append drum-path \"hihatOpen_OH_FF_6.wav\") *gm-open-hi-hat* 0 0 0 1)\n(set-sampler-index drums (string-append drum-path \"loTom_OH_FF_8.wav\") *gm-low-floor-tom* 0 0 0 1)\n(set-sampler-index drums (string-append drum-path \"hiTom_OH_FF_9.wav\") *gm-hi-floor-tom* 0 0 0 1)\n(set-sampler-index drums (string-append drum-path \"crash1_OH_FF_6.wav\") *gm-crash* 0 0 0 1)\n(set-sampler-index drums (string-append drum-path \"ride1_OH_FF_4.wav\") *gm-ride* 0 0 0 1)\n(set-sampler-index drums (string-append drum-path \"china1_OH_FF_8.wav\") *gm-chinese* 0 0 0 1)\n(set-sampler-index drums (string-append drum-path \"cowbell_FF_9.wav\") *gm-cowbell* 0 0 0 1)\n(set-sampler-index drums (string-append drum-path \"bellchime_F_3.wav\") *gm-open-triangle* 0 0 0 1)\n(set-sampler-index drums (string-append drum-path \"ride1Bell_OH_F_6.wav\") *gm-ride-bell* 0 0 0 1)\n\n```\n\nLet's look at the first row of `set-sampler-index`. Can you guess why do we need\n`*gm-kick*`? What does `string-append drum-path \"kick_OH_F_9.wav\"` do?\n\n> Note: How to load samples (you can also find more information from\n> [github](https://digego.github.io/extempore/sampler.html)).\n>\n> 1. Download the instrument samples from\n> [Salamader Drumkit](https://archive.org/download/SalamanderDrumkit/salamanderDrumkit.tar.bz2).\n> 2. Unzip the samples and put them wherever you like on your computer.\n> 3. Set/Change the path of the subdirectory \"OH\" in your code. In my computer\n> it’s like:\n> `define drum-path \"/Users/apple/Downloads/COMP3740/salamanderDrumkit/OH/\"`.\n\nBefore you run the code, do not forget to define and add an instrument `drums`\nto the `dsp` output sink closure, and set the `dsp` as well:\n\n```xtlang\n;; define a sampler (called drums) using the default sampler note kernel and effects\n(make-instrument drums sampler)\n;; define a synth using the provided components\n(make-instrument synth fmsynth)\n\n;; add the sampler to the dsp output callback\n(bind-func dsp:DSP\n (lambda (in time chan dat)\n (+ (synth in time chan dat)\n (drums in time chan dat))))\n\n(dsp:set! dsp)\n```\n\nSounds could be played by calling the below instructions, the similar things as\nwe did before:\n\n```xtlang\n;; evaluate\n(play-note (now) drums *gm-open-triangle* 80 44100)\n(play-note (now) drums *gm-snare* 80 44100)\n(play-note (now) drums *gm-closed-hi-hat* 80 44100)\n```\n\nCan you predict what will happen after you run above three lines respectively?\nThen run the code to check your thoughts.\n\n---\n\nNow, a cool thing we will do is to define a `metronome` to evenly distribute our\nbeats with respect to tempo changes.\n\nCan you predict what is happening here?\n\n```xtlang\n;; create a metronome starting at 120 bpm\n(define *metro1* (make-metro 120))\n\n;; beat loop along the metronome\n(define drum-loop\n (lambda (time duration drum)\n (println time duration)\n (play-note (*metro1* time) drums drum 80 (*metro1* 'dur duration))\n (callback (*metro1* (+ time (* .5 duration))) 'drum-loop (+ time duration)\n duration drum)))\n\n(drum-loop (*metro1* 'get-beat) 1 *gm-hi-floor-tom*)\n```\n\nLet's define another metronome `metronome2`:\n\n```xtlang\n;; create another metronome starting at 120 bpm\n(define *metro2* (make-metro 120))\n```\n\nCan you guess what will happen after you evaluate this `drum-loop-tempo-shift`?\nWhat does `println` will do?\n\n```xtlang\n;; set tempo shift\n(define drum-loop-tempo-shift\n (lambda (time duration drum)\n (*metro2* 'set-tempo (+ 20 (* 6. (cos (modulo time 9)))))\n (println (+ 20 (* 6. (cos (modulo time 9)))))\n (play-note (*metro2* time) drums drum 80 (*metro2* 'dur duration))\n (callback (*metro2* (+ time (* .5 duration))) 'drum-loop-tempo-shift (+ time duration)\n (random (list 0.5)) drum)))\n\n(drum-loop-tempo-shift (*metro2* 'get-beat) 0.5 *gm-cowbell*)\n```\n\n> Note: `println` calls the polymorphic function `print` for each supplied\n> argument, but `println` automatically provides both spaces and a carriage\n> return.\n\nRun the code. Did it work as you predicted?\n\n### Investigate\n\nWork in pairs or in small groups, work out the answers to the following\nquestions:\n\n1. What does the function `make-metro` do?\n2. What does `*metro* 'get-beat` do?\n3. What does `*metro* 'dur` do?\n4. What does `*metro* set-tempo` do?\n\n### Modify\n\nWork in pairs or in small groups, work out the answers to the following\nquestions:\n\n1. Can you load the piano samples as well?\n ([Salamander piano](https://download.linuxaudio.org/lau/SalamanderGrandPianoV2/SalamanderGrandPianoV2_44.1khz16bit.tar.bz2)).\n2. Can you pay multiple drums with different tempo at the same time?\n\n```xtlang\n;;hint\n(drum-loop (*metro1* 'get-beat) 1 *gm-hi-floor-tom*)\n(drum-loop (*metro1* 'get-beat) 0.3 *gm-open-triangle* )\n```\n\n### Make\n\nCombine the knowledge that you learned before. Now you can use drums, synth,\npiano and metronome to create your own music.\n\n## Going further\n\nAs well as the\n[Extempore documentation website](https://extemporelang.github.io), the best\nplace to start messing around is with more examples: e.g. the\n`examples/core/fmsynth.xtm` file in your Extempore folder.\n",
"createdAt": "2026-05-13T23:14:58.343Z",
"description": "PRIMM-style tutorials for learning sound and music in Extempore, from sine waves through to samplers and metronomes.",
"path": "/blog/2019/09/18/xinyus-extempore-tutorials",
"publishedAt": "2019-09-18T00:00:00.000Z",
"site": "at://did:plc:tevykrhi4kibtsipzci76d76/site.standard.publication/self",
"tags": [
"livecoding",
"extempore"
],
"textContent": "PRIMM-style tutorials for learning sound and music in Extempore, from sine waves through to samplers and metronomes.",
"title": "Xinyu's Extempore tutorials"
}