tidal-0.9.5/0000755000000000000000000000000013212576414011015 5ustar0000000000000000tidal-0.9.5/README.md0000644000000000000000000000056113212576414012276 0ustar0000000000000000 Tidal [![Build Status](https://travis-ci.org/tidalcycles/Tidal.svg?branch=1.0-dev)](https://travis-ci.org/tidalcycles/Tidal) ===== Language for live coding of pattern For documentation, mailing list and more info see here: http://tidal.lurk.org/ (c) Alex McLean and contributors, 2016 Distributed under the terms of the GNU Public license version 3 (or later). tidal-0.9.5/tidal.el0000644000000000000000000004025713212576414012444 0ustar0000000000000000;;; tidal.el --- Interact with TidalCycles for live coding patterns -*- lexical-binding: t; -*- ;; Copyright (C) 2012 alex@slab.org ;; Copyright (C) 2006-2008 rohan drape (hsc3.el) ;; Author: alex@slab.org ;; Homepage: https://github.com/tidalcycles/Tidal ;; Version: 0 ;; Keywords: tools ;; Package-Requires: ((haskell-mode "16") (emacs "24")) ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; notes from hsc3: ;; This mode is implemented as a derivation of `haskell' mode, ;; indentation and font locking is courtesy that mode. The ;; inter-process communication is courtesy `comint'. The symbol at ;; point acquisition is courtesy `thingatpt'. The directory search ;; facilities are courtesy `find-lisp'. ;;; Code: (require 'scheme) (require 'comint) (require 'thingatpt) (require 'find-lisp) (require 'pulse) (require 'haskell-mode) (defvar tidal-buffer "*tidal*" "*The name of the tidal process buffer (default=*tidal*).") (defvar tidal-interpreter "ghci" "*The haskell interpeter to use (default=ghci).") (defvar tidal-interpreter-arguments (list "-XOverloadedStrings" ) "*Arguments to the haskell interpreter (default=none).") (defvar tidal-literate-p t "*Flag to indicate if we are in literate mode (default=t).") (defvar tidal-modules nil "Additional module imports. See `tidal-run-region'.") (make-variable-buffer-local 'tidal-literate-p) (defun tidal-unlit (s) "Remove bird literate marks in S." (replace-regexp-in-string "^> " "" s)) (defun tidal-intersperse (e l) "Insert E between every element of list L." (when l (cons e (cons (car l) (tidal-intersperse e (cdr l)))))) (defun tidal-start-haskell () "Start haskell." (interactive) (if (comint-check-proc tidal-buffer) (error "A tidal process is already running") (apply 'make-comint "tidal" tidal-interpreter nil tidal-interpreter-arguments) (tidal-see-output)) (tidal-send-string ":set prompt \"\"") (tidal-send-string ":set prompt2 \"\"") (tidal-send-string ":module Sound.Tidal.Context") (tidal-send-string "import qualified Sound.Tidal.Scales as Scales") (tidal-send-string "import qualified Sound.Tidal.Chords as Chords") (tidal-send-string "(cps, nudger, getNow) <- cpsUtils'") (tidal-send-string "(d1,t1) <- superDirtSetters getNow") (tidal-send-string "(d2,t2) <- superDirtSetters getNow") (tidal-send-string "(d3,t3) <- superDirtSetters getNow") (tidal-send-string "(d4,t4) <- superDirtSetters getNow") (tidal-send-string "(d5,t5) <- superDirtSetters getNow") (tidal-send-string "(d6,t6) <- superDirtSetters getNow") (tidal-send-string "(d7,t7) <- superDirtSetters getNow") (tidal-send-string "(d8,t8) <- superDirtSetters getNow") (tidal-send-string "(d9,t9) <- superDirtSetters getNow") (tidal-send-string "(d10,t10) <- superDirtSetters getNow") (tidal-send-string "(c1,ct1) <- dirtSetters getNow") (tidal-send-string "(c2,ct2) <- dirtSetters getNow") (tidal-send-string "(c3,ct3) <- dirtSetters getNow") (tidal-send-string "(c4,ct4) <- dirtSetters getNow") (tidal-send-string "(c5,ct5) <- dirtSetters getNow") (tidal-send-string "(c6,ct6) <- dirtSetters getNow") (tidal-send-string "(c7,ct7) <- dirtSetters getNow") (tidal-send-string "(c8,ct8) <- dirtSetters getNow") (tidal-send-string "(c9,ct9) <- dirtSetters getNow") (tidal-send-string "(c10,ct10) <- dirtSetters getNow") (tidal-send-string "let bps x = cps (x/2)") (tidal-send-string "let hush = mapM_ ($ silence) [c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,d1,d2,d3,d4,d5,d6,d7,d8,d9,d10]") (tidal-send-string "let solo = (>>) hush") (tidal-send-string ":set prompt \"tidal> \"") ) (defun tidal-see-output () "Show haskell output." (interactive) (when (comint-check-proc tidal-buffer) (delete-other-windows) (split-window-vertically) (with-current-buffer tidal-buffer (let ((window (display-buffer (current-buffer)))) (goto-char (point-max)) (save-selected-window (set-window-point window (point-max))))))) (defun tidal-quit-haskell () "Quit haskell." (interactive) (kill-buffer tidal-buffer) (delete-other-windows)) (defun tidal-chunk-string (n s) "Split a string S into chunks of N characters." (let* ((l (length s)) (m (min l n)) (c (substring s 0 m))) (if (<= l n) (list c) (cons c (tidal-chunk-string n (substring s n)))))) (defun tidal-send-string (s) (if (comint-check-proc tidal-buffer) (let ((cs (tidal-chunk-string 64 (concat s "\n")))) (mapcar (lambda (c) (comint-send-string tidal-buffer c)) cs)) (error "no tidal process running?"))) (defun tidal-transform-and-store (f s) "Transform example text into compilable form." (with-temp-file f (mapc (lambda (module) (insert (concat module "\n"))) tidal-modules) (insert "main = do\n") (insert (if tidal-literate-p (tidal-unlit s) s)))) (defun tidal-get-now () "Store the current cycle position in a variable called 'now'." (interactive) (tidal-send-string "now' <- getNow") (tidal-send-string "let now = nextSam now'") (tidal-send-string "let retrig = (now `rotR`)") (tidal-send-string "let fadeOut n = spread' (_degradeBy) (retrig $ slow n $ envL)") (tidal-send-string "let fadeIn n = spread' (_degradeBy) (retrig $ slow n $ (1-) <$> envL)") ) (defun tidal-run-line () "Send the current line to the interpreter." (interactive) (tidal-get-now) (let* ((s (buffer-substring (line-beginning-position) (line-end-position))) (s* (if tidal-literate-p (tidal-unlit s) s))) (tidal-send-string s*)) (pulse-momentary-highlight-one-line (point)) (forward-line) ) (defun tidal-eval-multiple-lines () "Eval the current region in the interpreter as a single line." (tidal-get-now) (mark-paragraph) (let* ((s (buffer-substring-no-properties (region-beginning) (region-end))) (s* (if tidal-literate-p (tidal-unlit s) s))) (tidal-send-string ":{") (tidal-send-string s*) (tidal-send-string ":}") (mark-paragraph) (pulse-momentary-highlight-region (mark) (point)) ) ) (defun tidal-run-multiple-lines () "Send the current region to the interpreter as a single line." (interactive) (if (>= emacs-major-version 25) (save-mark-and-excursion (tidal-eval-multiple-lines)) (save-excursion (tidal-eval-multiple-lines)) ) ) (defun tidal-run-d1 () "Send the first instance of d1 to the interpreter as a single line." (interactive) (goto-char 0) (search-forward "d1" nil nil 1) (tidal-run-multiple-lines) ) (defun tidal-run-d2 () "Send the d2 to the interpreter as a single line." (interactive) (goto-char 0) (search-forward "d2" nil nil 1) (tidal-run-multiple-lines) ) (defun tidal-run-d3 () "Send the d3 to the interpreter as a single line." (interactive) (goto-char 0) (search-forward "d3" nil nil 1) (tidal-run-multiple-lines) ) (defun tidal-run-d4 () "Send the d4 to the interpreter as a single line." (interactive) (goto-char 0) (search-forward "d4" nil nil 1) (tidal-run-multiple-lines) ) (defun tidal-run-d5 () "Send the d5 to the interpreter as a single line." (interactive) (goto-char 0) (search-forward "d5" nil nil 1) (tidal-run-multiple-lines) ) (defun tidal-run-d6 () "Send the d6 to the interpreter as a single line." (interactive) (goto-char 0) (search-forward "d6" nil nil 1) (tidal-run-multiple-lines) ) (defun tidal-run-d7 () "Send the d7 to the interpreter as a single line." (interactive) (goto-char 0) (search-forward "d7" nil nil 1) (tidal-run-multiple-lines) ) (defun tidal-run-d8 () "Send the d9 to the interpreter as a single line." (interactive) (goto-char 0) (search-forward "d8" nil nil 1) (tidal-run-multiple-lines) ) (defun tidal-run-d9 () "Send the d9 to the interpreter as a single line." (interactive) (goto-char 0) (search-forward "d9" nil nil 1) (tidal-run-multiple-lines) ) (defun tidal-stop-d1 () "send d1 $ silence as a single line" (interactive) (tidal-send-string ":{") (tidal-send-string " mapM_ ($ silence) [d1]") (tidal-send-string ":}") ) (defun tidal-stop-d2 () "send d1 $ silence as a single line" (interactive) (tidal-send-string ":{") (tidal-send-string " mapM_ ($ silence) [d2]") (tidal-send-string ":}") ) (defun tidal-stop-d3 () "send d1 $ silence as a single line" (interactive) (tidal-send-string ":{") (tidal-send-string " mapM_ ($ silence) [d3]") (tidal-send-string ":}") ) (defun tidal-stop-d4 () "send d1 $ silence as a single line" (interactive) (tidal-send-string ":{") (tidal-send-string " mapM_ ($ silence) [d4]") (tidal-send-string ":}") ) (defun tidal-stop-d5 () "send d1 $ silence as a single line" (interactive) (tidal-send-string ":{") (tidal-send-string " mapM_ ($ silence) [d5]") (tidal-send-string ":}") ) (defun tidal-stop-d6 () "send d1 $ silence as a single line" (interactive) (tidal-send-string ":{") (tidal-send-string " mapM_ ($ silence) [d6]") (tidal-send-string ":}") ) (defun tidal-stop-d7 () "send d1 $ silence as a single line" (interactive) (tidal-send-string ":{") (tidal-send-string " mapM_ ($ silence) [d7]") (tidal-send-string ":}") ) (defun tidal-stop-d8 () "send d1 $ silence as a single line" (interactive) (tidal-send-string ":{") (tidal-send-string " mapM_ ($ silence) [d8]") (tidal-send-string ":}") ) (defun tidal-stop-d9 () "send d1 $ silence as a single line" (interactive) (tidal-send-string ":{") (tidal-send-string " mapM_ ($ silence) [d9]") (tidal-send-string ":}") ) (defun tidal-run-region () "Place the region in a do block and compile." (interactive) (tidal-transform-and-store "/tmp/tidal.hs" (buffer-substring-no-properties (region-beginning) (region-end))) (tidal-send-string ":load \"/tmp/tidal.hs\"") (tidal-send-string "main")) (defun tidal-load-buffer () "Load the current buffer." (interactive) (save-buffer) (tidal-send-string (format ":load \"%s\"" buffer-file-name))) (defun tidal-run-main () "Run current main." (interactive) (tidal-send-string "main")) (defun tidal-interrupt-haskell () (interactive) (if (comint-check-proc tidal-buffer) (with-current-buffer tidal-buffer (interrupt-process (get-buffer-process (current-buffer)))) (error "no tidal process running?"))) (defvar tidal-mode-map nil "Tidal keymap.") (defun tidal-mode-keybindings (map) "Haskell Tidal keybindings." (define-key map [?\C-c ?\C-s] 'tidal-start-haskell) (define-key map [?\C-c ?\C-v] 'tidal-see-output) (define-key map [?\C-c ?\C-q] 'tidal-quit-haskell) (define-key map [?\C-c ?\C-c] 'tidal-run-line) (define-key map [?\C-c ?\C-e] 'tidal-run-multiple-lines) (define-key map (kbd "") 'tidal-run-multiple-lines) (define-key map [?\C-c ?\C-r] 'tidal-run-region) (define-key map [?\C-c ?\C-l] 'tidal-load-buffer) (define-key map [?\C-c ?\C-i] 'tidal-interrupt-haskell) (define-key map [?\C-c ?\C-m] 'tidal-run-main) (define-key map [?\C-c ?\C-1] 'tidal-run-d1) (define-key map [?\C-c ?\C-2] 'tidal-run-d2) (define-key map [?\C-c ?\C-3] 'tidal-run-d3) (define-key map [?\C-c ?\C-4] 'tidal-run-d4) (define-key map [?\C-c ?\C-5] 'tidal-run-d5) (define-key map [?\C-c ?\C-6] 'tidal-run-d6) (define-key map [?\C-c ?\C-7] 'tidal-run-d7) (define-key map [?\C-c ?\C-8] 'tidal-run-d8) (define-key map [?\C-c ?\C-9] 'tidal-run-d9) (define-key map [?\C-v ?\C-1] 'tidal-stop-d1) (define-key map [?\C-v ?\C-2] 'tidal-stop-d2) (define-key map [?\C-v ?\C-3] 'tidal-stop-d3) (define-key map [?\C-v ?\C-4] 'tidal-stop-d4) (define-key map [?\C-v ?\C-5] 'tidal-stop-d5) (define-key map [?\C-v ?\C-6] 'tidal-stop-d6) (define-key map [?\C-v ?\C-7] 'tidal-stop-d7) (define-key map [?\C-v ?\C-8] 'tidal-stop-d8) (define-key map [?\C-v ?\C-9] 'tidal-stop-d9)) (defun turn-on-tidal-keybindings () "Haskell Tidal keybindings in the local map." (local-set-key [?\C-c ?\C-s] 'tidal-start-haskell) (local-set-key [?\C-c ?\C-v] 'tidal-see-output) (local-set-key [?\C-c ?\C-q] 'tidal-quit-haskell) (local-set-key [?\C-c ?\C-c] 'tidal-run-line) (local-set-key [?\C-c ?\C-e] 'tidal-run-multiple-lines) (local-set-key (kbd "") 'tidal-run-multiple-lines) (local-set-key [?\C-c ?\C-r] 'tidal-run-region) (local-set-key [?\C-c ?\C-l] 'tidal-load-buffer) (local-set-key [?\C-c ?\C-i] 'tidal-interrupt-haskell) (local-set-key [?\C-c ?\C-m] 'tidal-run-main) (local-set-key [?\C-c ?\C-1] 'tidal-run-d1) (local-set-key [?\C-c ?\C-2] 'tidal-run-d2) (local-set-key [?\C-c ?\C-3] 'tidal-run-d3) (local-set-key [?\C-c ?\C-4] 'tidal-run-d4) (local-set-key [?\C-c ?\C-5] 'tidal-run-d5) (local-set-key [?\C-c ?\C-6] 'tidal-run-d6) (local-set-key [?\C-c ?\C-7] 'tidal-run-d7) (local-set-key [?\C-c ?\C-8] 'tidal-run-d8) (local-set-key [?\C-c ?\C-9] 'tidal-run-d9) (local-set-key [?\C-v ?\C-1] 'tidal-stop-d1) (local-set-key [?\C-v ?\C-2] 'tidal-stop-d2) (local-set-key [?\C-v ?\C-3] 'tidal-stop-d3) (local-set-key [?\C-v ?\C-4] 'tidal-stop-d4) (local-set-key [?\C-v ?\C-5] 'tidal-stop-d5) (local-set-key [?\C-v ?\C-6] 'tidal-stop-d6) (local-set-key [?\C-v ?\C-7] 'tidal-stop-d7) (local-set-key [?\C-v ?\C-8] 'tidal-stop-d8) (local-set-key [?\C-v ?\C-9] 'tidal-stop-d9)) (defun tidal-mode-menu (map) "Haskell Tidal menu." (define-key map [menu-bar tidal] (cons "Haskell-Tidal" (make-sparse-keymap "Haskell-Tidal"))) (define-key map [menu-bar tidal help] (cons "Help" (make-sparse-keymap "Help"))) (define-key map [menu-bar tidal expression] (cons "Expression" (make-sparse-keymap "Expression"))) (define-key map [menu-bar tidal expression load-buffer] '("Load buffer" . tidal-load-buffer)) (define-key map [menu-bar tidal expression run-main] '("Run main" . tidal-run-main)) (define-key map [menu-bar tidal expression run-region] '("Run region" . tidal-run-region)) (define-key map [menu-bar tidal expression run-multiple-lines] '("Run multiple lines" . tidal-run-multiple-lines)) (define-key map [menu-bar tidal expression run-line] '("Run line" . tidal-run-line)) (define-key map [menu-bar tidal haskell] (cons "Haskell" (make-sparse-keymap "Haskell"))) (define-key map [menu-bar tidal haskell quit-haskell] '("Quit haskell" . tidal-quit-haskell)) (define-key map [menu-bar tidal haskell see-output] '("See output" . tidal-see-output)) (define-key map [menu-bar tidal haskell start-haskell] '("Start haskell" . tidal-start-haskell))) (unless tidal-mode-map (let ((map (make-sparse-keymap "Haskell-Tidal"))) (tidal-mode-keybindings map) (tidal-mode-menu map) (setq tidal-mode-map map))) ;;;###autoload (define-derived-mode literate-tidal-mode tidal-mode "Literate Haskell Tidal" "Major mode for interacting with an inferior haskell process." (set (make-local-variable 'paragraph-start) "\f\\|[ \t]*$") (set (make-local-variable 'paragraph-separate) "[ \t\f]*$") (setq tidal-literate-p t) (setq haskell-literate 'bird) (turn-on-font-lock)) ;;;###autoload (add-to-list 'auto-mode-alist '("\\.ltidal$" . literate-tidal-mode)) ;;(add-to-list 'load-path "/usr/share/emacs/site-lisp/haskell-mode/") ;required by olig1905 on linux ;;(require 'haskell-mode) ;required by olig1905 on linux ;;;###autoload (define-derived-mode tidal-mode haskell-mode "Haskell Tidal" "Major mode for interacting with an inferior haskell process." (set (make-local-variable 'paragraph-start) "\f\\|[ \t]*$") (set (make-local-variable 'paragraph-separate) "[ \t\f]*$") (setq tidal-literate-p nil) (turn-on-font-lock)) ;;;###autoload (add-to-list 'auto-mode-alist '("\\.tidal$" . tidal-mode)) (provide 'tidal) ;;; tidal.el ends here tidal-0.9.5/CHANGELOG.md0000644000000000000000000000361213212576414012630 0ustar0000000000000000# TidalCycles log of changes ## 0.9.5 ### Enhancements * Added `hurry` which both speeds up the sound and the pattern by the given amount. * Added `stripe` which repeats a pattern a given number of times per cycle, with random but contiguous durations. * Added continuous function `cosine` * Turned more pattern transformation parameters into patterns - spread', striateX, every', inside, outside, swing * Added experimental datatype for Xenakis sieves * Correctly parse negative rationals * Added `breakUp` that finds events that share the same timespan, and spreads them out during that timespan, so for example (breakUp "[bd,sn]") gets turned into the "bd sn" * Added `fill` which 'fills in' gaps in one pattern with events from another. ## 0.9.4 ### Fixes * Swapped `-` for `..` in ranges as quick fix for issue with parsing negative numbers * Removed overloaded list thingie for now, unsure whether it's worth the dependency ## 0.9.3 ### Enhancements * The sequence parser can now expand ranges, e.g. `"0-3 4-2"` is equivalent to `"[0 1 2 3] [4 3 2]"` * Sequences can now be described using list syntax, for example `sound ["bd", "sn"]` is equivalent to `sound "bd sn"`. They *aren't* lists though, so you can't for example do `sound (["bd", "sn"] ++ ["arpy", "cp"])` -- but can do `sound (append ["bd", "sn"] ["arpy", "cp"])` * New function `linger`, e.g. `linger (1/4)` will only play the first quarter of the given pattern, four times to fill the cycle. * `discretise` now takes time value as its first parameter, not a pattern of time, which was causing problems and needs some careful thought. * a `rel` alias for the `release` parameter, to match the `att` alias for `attack` * `_fast` alias for `_density` * The start of automatic testing for a holy bug-free future ### Fixes * Fixed bug that was causing events to double up or get lost, e.g. where `rev` was combined with certain other functions. tidal-0.9.5/LICENSE0000644000000000000000000010444613212576414012033 0ustar0000000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . tidal-0.9.5/Setup.hs0000644000000000000000000000005613212576414012452 0ustar0000000000000000import Distribution.Simple main = defaultMain tidal-0.9.5/tidal.cabal0000644000000000000000000000416213212576414013101 0ustar0000000000000000name: tidal version: 0.9.5 synopsis: Pattern language for improvised music -- description: homepage: http://tidalcycles.org/ license: GPL-3 license-file: LICENSE author: Alex McLean maintainer: Alex McLean , Mike Hodnick Stability: Experimental Copyright: (c) Tidal contributors, 2017 category: Sound build-type: Simple cabal-version: >=1.10 tested-with: GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.1 Extra-source-files: README.md CHANGELOG.md tidal.el doc/tidal.md Description: Tidal is a domain specific language for live coding pattern. library default-language: Haskell2010 Exposed-modules: Sound.Tidal.Bjorklund Sound.Tidal.Strategies Sound.Tidal.Dirt Sound.Tidal.Pattern Sound.Tidal.Stream Sound.Tidal.OscStream Sound.Tidal.Parse Sound.Tidal.Tempo Sound.Tidal.Time Sound.Tidal.Context Sound.Tidal.Utils Sound.Tidal.SuperCollider Sound.Tidal.Params Sound.Tidal.Transition Sound.Tidal.Scales Sound.Tidal.Chords Sound.Tidal.Sieve Sound.Tidal.Version -- Sound.Tidal.PatternList Build-depends: base < 5 , containers , hashable , colour , hosc > 0.13, hosc <= 0.15 , text , mersenne-random-pure64 , time , parsec , safe , websockets > 0.8 , mtl >= 2.1 source-repository head type: git location: https://github.com/tidalcycles/Tidal test-suite test default-language: Haskell2010 type: exitcode-stdio-1.0 hs-source-dirs: tests main-is: test.hs build-depends: base >= 4 && < 5 , tasty >= 0.11 , tasty-hunit , tidal tidal-0.9.5/tests/0000755000000000000000000000000013212576414012157 5ustar0000000000000000tidal-0.9.5/tests/test.hs0000644000000000000000000000215013212576414013470 0ustar0000000000000000{-# LANGUAGE OverloadedStrings #-} import Test.Tasty -- import Test.Tasty.SmallCheck as SC -- import Test.Tasty.QuickCheck as QC import Test.Tasty.HUnit import Data.List import Data.Ord import Sound.Tidal.Context main = defaultMain tests tests :: TestTree tests = testGroup "Tests" [basic1, patternsOfPatterns ] basic1 = testGroup "fast / slow" [ testCase "silence" $ same16 (fast 1.1 silence) (silence :: Pattern Double), testCase "fast" $ same16 silence (silence :: Pattern Double), testCase "fast" $ same16 "bd*128" (rep 128 "bd") ] patternsOfPatterns = testGroup "patterns of patterns" [ testCase "decimal density" $ same16 (_discretise 0.25 saw) (discretise 0.25 saw) ] rep :: Int -> String -> Pattern String rep n v = p $ intercalate " " $ take n $ repeat v sameN :: (Eq a, Show a) => String -> Time -> Pattern a -> Pattern a -> Assertion sameN s n a b = assertEqual s (arc a (0,n)) (arc b (0,n)) same16 :: (Eq a, Show a) => Pattern a -> Pattern a -> Assertion same16 = sameN "for 16 cycles," 16 tidal-0.9.5/Sound/0000755000000000000000000000000013212576414012105 5ustar0000000000000000tidal-0.9.5/Sound/Tidal/0000755000000000000000000000000013212576414013142 5ustar0000000000000000tidal-0.9.5/Sound/Tidal/Scales.hs0000644000000000000000000000663213212576414014717 0ustar0000000000000000module Sound.Tidal.Scales where -- five notes scales minPent :: Num a => [a] minPent = [0,3,5,7,10] majPent :: Num a => [a] majPent = [0,2,4,7,9] -- another mode of major pentatonic ritusen :: Num a => [a] ritusen = [0,2,5,7,9] -- another mode of major pentatonic egyptian :: Num a => [a] egyptian = [0,2,5,7,10] -- kumai :: Num a => [a] kumai = [0,2,3,7,9] hirajoshi :: Num a => [a] hirajoshi = [0,2,3,7,8] iwato :: Num a => [a] iwato = [0,1,5,6,10] chinese :: Num a => [a] chinese = [0,4,6,7,11] indian :: Num a => [a] indian = [0,4,5,7,10] pelog :: Num a => [a] pelog = [0,1,3,7,8] -- prometheus :: Num a => [a] prometheus = [0,2,4,6,11] scriabin :: Num a => [a] scriabin = [0,1,4,7,9] -- han chinese pentatonic scales gong :: Num a => [a] gong = [0,2,4,7,9] shang :: Num a => [a] shang = [0,2,5,7,10] jiao :: Num a => [a] jiao = [0,3,5,8,10] zhi :: Num a => [a] zhi = [0,2,5,7,9] yu :: Num a => [a] yu = [0,3,5,7,10] -- 6 note scales whole :: Num a => [a] whole = [0,2,4,6,8,10] augmented :: Num a => [a] augmented = [0,3,4,7,8,11] augmented2 :: Num a => [a] augmented2 = [0,1,4,5,8,9] -- hexatonic modes with no tritone hexMajor7 :: Num a => [a] hexMajor7 = [0,2,4,7,9,11] hexDorian :: Num a => [a] hexDorian = [0,2,3,5,7,10] hexPhrygian :: Num a => [a] hexPhrygian = [0,1,3,5,8,10] hexSus :: Num a => [a] hexSus = [0,2,5,7,9,10] hexMajor6 :: Num a => [a] hexMajor6 = [0,2,4,5,7,9] hexAeolian :: Num a => [a] hexAeolian = [0,3,5,7,8,10] -- 7 note scales major :: Num a => [a] major = [0,2,4,5,7,9,11] ionian :: Num a => [a] ionian = [0,2,4,5,7,9,11] dorian :: Num a => [a] dorian = [0,2,3,5,7,9,10] phrygian :: Num a => [a] phrygian = [0,1,3,5,7,8,10] lydian :: Num a => [a] lydian = [0,2,4,6,7,9,11] mixolydian :: Num a => [a] mixolydian = [0,2,4,5,7,9,10] aeolian :: Num a => [a] aeolian = [0,2,3,5,7,8,10] minor :: Num a => [a] minor = [0,2,3,5,7,8,10] locrian :: Num a => [a] locrian = [0,1,3,5,6,8,10] harmonicMinor :: Num a => [a] harmonicMinor = [0,2,3,5,7,8,11] harmonicMajor :: Num a => [a] harmonicMajor = [0,2,4,5,7,8,11] melodicMinor :: Num a => [a] melodicMinor = [0,2,3,5,7,9,11] melodicMinorDesc :: Num a => [a] melodicMinorDesc = [0,2,3,5,7,8,10] melodicMajor :: Num a => [a] melodicMajor = [0,2,4,5,7,8,10] bartok :: Num a => [a] bartok = [0,2,4,5,7,8,10] hindu :: Num a => [a] hindu = [0,2,4,5,7,8,10] -- raga modes todi :: Num a => [a] todi = [0,1,3,6,7,8,11] purvi :: Num a => [a] purvi = [0,1,4,6,7,8,11] marva :: Num a => [a] marva = [0,1,4,6,7,9,11] bhairav :: Num a => [a] bhairav = [0,1,4,5,7,8,11] ahirbhairav :: Num a => [a] ahirbhairav = [0,1,4,5,7,9,10] -- superLocrian :: Num a => [a] superLocrian = [0,1,3,4,6,8,10] romanianMinor :: Num a => [a] romanianMinor = [0,2,3,6,7,9,10] hungarianMinor :: Num a => [a] hungarianMinor = [0,2,3,6,7,8,11] neapolitanMinor :: Num a => [a] neapolitanMinor = [0,1,3,5,7,8,11] enigmatic :: Num a => [a] enigmatic = [0,1,4,6,8,10,11] spanish :: Num a => [a] spanish = [0,1,4,5,7,8,10] -- modes of whole tones with added note -> leadingWhole :: Num a => [a] leadingWhole = [0,2,4,6,8,10,11] lydianMinor :: Num a => [a] lydianMinor = [0,2,4,6,7,8,10] neapolitanMajor :: Num a => [a] neapolitanMajor = [0,1,3,5,7,9,11] locrianMajor :: Num a => [a] locrianMajor = [0,2,4,5,6,8,10] -- 8 note scales diminished :: Num a => [a] diminished = [0,1,3,4,6,7,9,10] diminished2 :: Num a => [a] diminished2 = [0,2,3,5,6,8,9,11] -- 12 note scales chromatic :: Num a => [a] chromatic = [0,1,2,3,4,5,6,7,8,9,10,11] tidal-0.9.5/Sound/Tidal/Version.hs0000644000000000000000000000007313212576414015123 0ustar0000000000000000 module Sound.Tidal.Version where tidal_version = "0.9.5" tidal-0.9.5/Sound/Tidal/OscStream.hs0000644000000000000000000000721213212576414015400 0ustar0000000000000000module Sound.Tidal.OscStream where import qualified Data.Map as Map import Data.Maybe import Sound.Tidal.Tempo (Tempo, cps) import Sound.Tidal.Stream import Sound.Tidal.Utils import GHC.Float (float2Double, double2Float) import Sound.OSC.FD import Sound.OSC.Datum import Sound.Tidal.Params data TimeStamp = BundleStamp | MessageStamp | NoStamp deriving Eq data OscSlang = OscSlang {path :: String, timestamp :: TimeStamp, namedParams :: Bool, preamble :: [Datum] } type OscMap = Map.Map Param Datum toOscDatum :: Value -> Datum toOscDatum (VF x) = float x toOscDatum (VI x) = int32 x toOscDatum (VS x) = string x toOscMap :: ParamMap -> OscMap toOscMap m = Map.map (toOscDatum) m -- constructs and sends an Osc Message according to the given slang -- and other params - this is essentially the same as the former -- toMessage in Stream.hs send :: (Integral a) => UDP -> OscSlang -> Shape -> Tempo -> a -> (Double, Double, OscMap) -> IO () send s slang shape change tick (on, off, m) = osc where osc | timestamp slang == BundleStamp = sendOSC s $ Bundle (ut_to_ntpr logicalOnset) [Message (path slang) oscdata] | timestamp slang == MessageStamp = sendOSC s $ Message (path slang) ((int32 sec):(int32 usec):oscdata) | otherwise = doAt logicalOnset $ sendOSC s $ Message (path slang) oscdata oscPreamble = cpsPrefix ++ preamble slang oscdata | namedParams slang = oscPreamble ++ (concatMap (\(k, v) -> [string (name k), v] ) $ Map.assocs m) | otherwise = oscPreamble ++ (catMaybes $ map (\x -> Map.lookup x m) (params shape)) cpsPrefix | cpsStamp shape && namedParams slang = [string "cps", float (cps change), string "delta", float (logicalOffset - logicalOnset), string "cycle", float cycle ] | cpsStamp shape = [float (cps change)] | otherwise = [] cycle = (on + fromIntegral tick) / (fromIntegral ticksPerCycle) _parameterise ds = mergelists (map (string . name) (params shape)) ds usec = floor $ 1000000 * (logicalOnset - (fromIntegral sec)) sec = floor logicalOnset logicalOnset = logicalOnset' change tick on ((latency shape) + nudge) logicalOffset = logicalOnset' change tick off ((latency shape) + nudge) nudge = maybe 0 (toF) (Map.lookup nudge_p (m :: OscMap)) toF (Float f) = float2Double f toF _ = 0 -- type OscMap = Map.Map Param (Maybe Datum) -- Returns a function that will convert a generic ParamMap into a specific Osc message and send it over UDP to the supplied server -- messages will be built according to the given OscSlang makeConnection :: String -> Int -> OscSlang -> IO (ToMessageFunc) makeConnection address port slang = do s <- openUDP address port return (\ shape change tick (on,off,m) -> do let m' = if (namedParams slang) then (Just m) else (applyShape' shape m) -- this might result in Nothing, make sure we do this first m'' <- fmap (toOscMap) m' -- to allow us to simplify `send` (no `do`) return $ send s slang shape change tick (on,off,m'') ) tidal-0.9.5/Sound/Tidal/Utils.hs0000644000000000000000000000550413212576414014602 0ustar0000000000000000{-| Module: Utils Description: Helper functions not directly specific to Tidal -} module Sound.Tidal.Utils where import Data.Maybe (listToMaybe) {- | enumerate a list of things >>> enumerate ["foo","bar","baz"] [(1,"foo"), (2,"bar"), (3,"baz")] -} enumerate :: [a] -> [(Int, a)] enumerate = zip [0..] -- | apply @f@ to the first element of a tuple mapFst :: (a -> b) -> (a, c) -> (b, c) mapFst f (x,y) = (f x,y) -- | apply function to the first value of each tuple in given list mapFsts :: (a -> b) -> [(a, c)] -> [(b, c)] mapFsts = map . mapFst -- | apply @f@ to the second element of a tuple mapSnd :: (a -> b) -> (c, a) -> (c, b) mapSnd f (x,y) = (x,f y) -- | apply function to the second value of each tuple in given list mapSnds :: (a -> b) -> [(c, a)] -> [(c, b)] mapSnds = fmap . mapSnd {- | split given list of @a@ by given single a, e.g. >>> wordsBy (== ':') "bd:3" ["bd", "3"] -} wordsBy :: (a -> Bool) -> [a] -> [[a]] wordsBy p s = case dropWhile p s of [] -> [] s':rest -> (s':w) : wordsBy p (drop 1 s'') where (w, s'') = break p rest maybeRead :: String -> Maybe Double maybeRead = fmap fst . listToMaybe . reads -- | shorthand for first element of triple fst' (a, _, _) = a -- | shorthand for second element of triple snd' (_, b, _) = b -- | shorthand for third element of triple thd' (_, _, c) = c -- | apply @f@ to the first element of a triple mapFst' :: (a -> x) -> (a, b, c) -> (x, b, c) mapFst' f (x,y,z) = (f x,y,z) -- | apply @f@ to the second element of a triple mapSnd' :: (b -> x) -> (a, b, c) -> (a, x, c) mapSnd' f (x,y,z) = (x,f y,z) -- | apply @f@ to the third element of a triple mapThd' :: (c -> x) -> (a, b, c) -> (a, b, x) mapThd' f (x,y,z) = (x,y,f z) -- | apply function to the second value of each triple in given list mapFsts' :: (a -> x) -> [(a, b, c)] -> [(x, b, c)] mapFsts' = fmap . mapFst' -- | apply function to the second value of each triple in given list mapSnds' :: (b -> x) -> [(a, b, c)] -> [(a, x, c)] mapSnds' = fmap . mapSnd' -- | apply function to the third value of each triple in given list mapThds' :: (c -> x) -> [(a, b, c)] -> [(a, b, x)] mapThds' = fmap . mapThd' -- | map @f@ over a given list of arcs mapArcs :: (a -> a) -> [(a, a, x)] -> [(a, a, x)] mapArcs f = (mapFsts' f) . (mapSnds' f) {- | combines two lists by interleaving them >>> mergelists [1,2,3] [9,8,7] [1,9,2,8,3,7] -} mergelists :: [a] -> [a] -> [a] mergelists xs [] = xs mergelists [] ys = ys mergelists (x:xs) (y:ys) = x : y : mergelists xs ys {- | like `!!` selects @n@th element from xs, but wraps over at the end of @xs@ >>> map ((!!!) [1,3,5]) [0,1,2,3,4,5] [1,3,5,1,3,5] -} (!!!) :: [a] -> Int -> a (!!!) xs n = xs !! (n `mod` length xs) accumulate :: Num t => [t] -> [t] accumulate = accumulate' 0 where accumulate' _ [] = [] accumulate' n (a:xs) = (n+a):(accumulate' (n+a) xs) tidal-0.9.5/Sound/Tidal/Context.hs0000644000000000000000000000126313212576414015124 0ustar0000000000000000module Sound.Tidal.Context (module C) where import Control.Concurrent as C import Data.List as C import Control.Applicative as C import Data.Ratio as C import Data.Monoid as C import Sound.Tidal.Parse as C import Sound.Tidal.Pattern as C -- import Sound.Tidal.PatternList as C import Sound.Tidal.Stream as C import Sound.Tidal.Dirt as C import Sound.Tidal.Strategies as C import Sound.Tidal.Tempo as C import Sound.Tidal.Time as C import Sound.Tidal.Sieve as C import Sound.Tidal.SuperCollider as C import Sound.Tidal.Params as C import Sound.Tidal.Transition as C import Sound.Tidal.Version as C import qualified Sound.Tidal.Scales as Scales import qualified Sound.Tidal.Chords as Chords tidal-0.9.5/Sound/Tidal/Pattern.hs0000644000000000000000000017343413212576414015127 0ustar0000000000000000{-# LANGUAGE DeriveDataTypeable #-} {-# OPTIONS_GHC -Wall -fno-warn-orphans -fno-warn-name-shadowing #-} module Sound.Tidal.Pattern where import Control.Applicative import Data.Monoid import Data.Fixed import Data.List import Data.Maybe import Data.Ord import Data.Ratio -- import Debug.Trace import Data.Typeable import Data.Function import System.Random.Mersenne.Pure64 -- import Data.Char import qualified Data.Text as T import Sound.Tidal.Time import Sound.Tidal.Utils import Sound.Tidal.Bjorklund import Text.Show.Functions () import qualified Control.Exception as E -- | The pattern datatype, a function from a time @Arc@ to @Event@ -- values. For discrete patterns, this returns the events which are -- active during that time. For continuous patterns, events with -- values for the midpoint of the given @Arc@ is returned. data Pattern a = Pattern {arc :: Arc -> [Event a]} deriving Typeable noOv :: String -> a noOv meth = error $ meth ++ ": No overloading" instance Eq (Pattern a) where (==) = noOv "(==)" instance Ord a => Ord (Pattern a) where min = liftA2 min max = liftA2 max instance Num a => Num (Pattern a) where negate = fmap negate (+) = liftA2 (+) (*) = liftA2 (*) fromInteger = pure . fromInteger abs = fmap abs signum = fmap signum instance Enum a => Enum (Pattern a) where succ = fmap succ pred = fmap pred toEnum = pure . toEnum fromEnum = noOv "fromEnum" enumFrom = noOv "enumFrom" enumFromThen = noOv "enumFromThen" enumFromTo = noOv "enumFromTo" enumFromThenTo = noOv "enumFromThenTo" instance (Num a, Ord a) => Real (Pattern a) where toRational = noOv "toRational" instance (Integral a) => Integral (Pattern a) where quot = liftA2 quot rem = liftA2 rem div = liftA2 div mod = liftA2 mod toInteger = noOv "toInteger" x `quotRem` y = (x `quot` y, x `rem` y) x `divMod` y = (x `div` y, x `mod` y) instance (Fractional a) => Fractional (Pattern a) where recip = fmap recip fromRational = pure . fromRational instance (Floating a) => Floating (Pattern a) where pi = pure pi sqrt = fmap sqrt exp = fmap exp log = fmap log sin = fmap sin cos = fmap cos asin = fmap asin atan = fmap atan acos = fmap acos sinh = fmap sinh cosh = fmap cosh asinh = fmap asinh atanh = fmap atanh acosh = fmap acosh instance (RealFrac a) => RealFrac (Pattern a) where properFraction = noOv "properFraction" truncate = noOv "truncate" round = noOv "round" ceiling = noOv "ceiling" floor = noOv "floor" instance (RealFloat a) => RealFloat (Pattern a) where floatRadix = noOv "floatRadix" floatDigits = noOv "floatDigits" floatRange = noOv "floatRange" decodeFloat = noOv "decodeFloat" encodeFloat = ((.).(.)) pure encodeFloat exponent = noOv "exponent" significand = noOv "significand" scaleFloat n = fmap (scaleFloat n) isNaN = noOv "isNaN" isInfinite = noOv "isInfinite" isDenormalized = noOv "isDenormalized" isNegativeZero = noOv "isNegativeZero" isIEEE = noOv "isIEEE" atan2 = liftA2 atan2 -- | @show (p :: Pattern)@ returns a text string representing the -- event values active during the first cycle of the given pattern. instance (Show a) => Show (Pattern a) where show p@(Pattern _) = intercalate " " $ map showEvent $ arc p (0, 1) -- | converts a ratio into human readable string, e.g. @1/3@ showTime :: (Show a, Integral a) => Ratio a -> String showTime t | denominator t == 1 = show (numerator t) | otherwise = show (numerator t) ++ ('/':show (denominator t)) -- | converts a time arc into human readable string, e.g. @1/3 3/4@ showArc :: Arc -> String showArc a = concat[showTime $ fst a, (' ':showTime (snd a))] -- | converts an event into human readable string, e.g. @("bd" 1/4 2/3)@ showEvent :: (Show a) => Event a -> String showEvent e@(_, b, v) = concat[on, show v, off, (' ':showArc b), "\n" ] where on | hasOnset e = "" | otherwise = ".." off | hasOffset e = "" | otherwise = ".." instance Functor Pattern where fmap f (Pattern a) = Pattern $ fmap (fmap (mapThd' f)) a -- | @pure a@ returns a pattern with an event with value @a@, which -- has a duration of one cycle, and repeats every cycle. instance Applicative Pattern where pure x = Pattern $ \(s, e) -> map (\t -> ((t%1, (t+1)%1), (t%1, (t+1)%1), x ) ) [floor s .. ((ceiling e) - 1)] (Pattern fs) <*> (Pattern xs) = Pattern $ \a -> concatMap applyX (fs a) where applyX ((s,e), (s', e'), f) = map (\(_, _, x) -> ((s,e), (s', e'), f x)) (filter (\(_, a', _) -> isIn a' s) (xs (s',e')) ) -- | @mempty@ is a synonym for @silence@. -- | @mappend@ is a synonym for @overlay@. instance Monoid (Pattern a) where mempty = silence mappend = overlay instance Monad Pattern where return = pure p >>= f = unwrap (f <$> p) unwrap :: Pattern (Pattern a) -> Pattern a unwrap p = Pattern $ \a -> concatMap (\(_, outerPart, p') -> catMaybes $ map (munge outerPart) $ arc p' a) (arc p a) where munge a (whole,part,v) = do part' <- subArc a part return (whole, part',v) -- | @atom@ is a synonym for @pure@. atom :: a -> Pattern a atom = pure -- | @silence@ returns a pattern with no events. silence :: Pattern a silence = Pattern $ const [] -- | @withQueryArc f p@ returns a new @Pattern@ with function @f@ -- applied to the @Arc@ values passed to the original @Pattern@ @p@. withQueryArc :: (Arc -> Arc) -> Pattern a -> Pattern a withQueryArc f p = Pattern $ \a -> arc p (f a) -- | @withQueryTime f p@ returns a new @Pattern@ with function @f@ -- applied to the both the start and end @Time@ of the @Arc@ passed to -- @Pattern@ @p@. withQueryTime :: (Time -> Time) -> Pattern a -> Pattern a withQueryTime = withQueryArc . mapArc -- | @withResultArc f p@ returns a new @Pattern@ with function @f@ -- applied to the @Arc@ values in the events returned from the -- original @Pattern@ @p@. withResultArc :: (Arc -> Arc) -> Pattern a -> Pattern a withResultArc f p = Pattern $ \a -> mapArcs f $ arc p a -- | @withResultTime f p@ returns a new @Pattern@ with function @f@ -- applied to the both the start and end @Time@ of the @Arc@ values in -- the events returned from the original @Pattern@ @p@. withResultTime :: (Time -> Time) -> Pattern a -> Pattern a withResultTime = withResultArc . mapArc -- | @withEvent f p@ returns a new @Pattern@ with events mapped over -- function @f@. withEvent :: (Event a -> Event b) -> Pattern a -> Pattern b withEvent f p = Pattern $ \a -> map f $ arc p a -- | @timedValues p@ returns a new @Pattern@ where values are turned -- into tuples of @Arc@ and value. timedValues :: Pattern a -> Pattern (Arc, a) timedValues = withEvent (\(a,a',v) -> (a,a',(a,v))) -- | @overlay@ combines two @Pattern@s into a new pattern, so that -- their events are combined over time. This is the same as the infix -- operator `<>`. overlay :: Pattern a -> Pattern a -> Pattern a overlay p p' = Pattern $ \a -> (arc p a) ++ (arc p' a) -- | @stack@ combines a list of @Pattern@s into a new pattern, so that -- their events are combined over time. stack :: [Pattern a] -> Pattern a stack ps = foldr overlay silence ps -- | @append@ combines two patterns @Pattern@s into a new pattern, so -- that the events of the second pattern are appended to those of the -- first pattern, within a single cycle append :: Pattern a -> Pattern a -> Pattern a append a b = fastcat [a,b] -- | @append'@ does the same as @append@, but over two cycles, so that -- the cycles alternate between the two patterns. append' :: Pattern a -> Pattern a -> Pattern a append' a b = slowcat [a,b] -- | @fastcat@ returns a new pattern which interlaces the cycles of the -- given patterns, within a single cycle. It's the equivalent of -- @append@, but with a list of patterns. fastcat :: [Pattern a] -> Pattern a fastcat ps = _density (fromIntegral $ length ps) $ slowcat ps splitAtSam :: Pattern a -> Pattern a splitAtSam p = splitQueries $ Pattern $ \(s,e) -> mapSnds' (trimArc (sam s)) $ arc p (s,e) where trimArc s' (s,e) = (max (s') s, min (s'+1) e) -- | @slowcat@ does the same as @fastcat@, but maintaining the duration of -- the original patterns. It is the equivalent of @append'@, but with -- a list of patterns. slowcat :: [Pattern a] -> Pattern a slowcat [] = silence slowcat ps = splitQueries $ Pattern f where ps' = map splitAtSam ps l = length ps' f (s,e) = arc (withResultTime (+offset) p) (s',e') where p = ps' !! n r = (floor s) :: Int n = (r `mod` l) :: Int offset = (fromIntegral $ r - ((r - n) `div` l)) :: Time (s', e') = (s-offset, e-offset) -- | @cat@ is an alias of @slowcat@ cat :: [Pattern a] -> Pattern a cat = slowcat -- | @listToPat@ turns the given list of values to a Pattern, which -- cycles through the list. listToPat :: [a] -> Pattern a listToPat = fastcat . map atom patToList :: Pattern a -> [a] patToList p = map (thd') $ sortBy (\a b -> compare (snd' a) (snd' b)) $ filter ((\x -> x >= 0 && x < 1) . fst . snd' ) (arc p (0,1)) -- | @maybeListToPat@ is similar to @listToPat@, but allows values to -- be optional using the @Maybe@ type, so that @Nothing@ results in -- gaps in the pattern. maybeListToPat :: [Maybe a] -> Pattern a maybeListToPat = fastcat . map f where f Nothing = silence f (Just x) = atom x -- | @run@ @n@ returns a pattern representing a cycle of numbers from @0@ to @n-1@. run :: (Enum a, Num a) => Pattern a -> Pattern a run tp = tp >>= _run _run :: (Enum a, Num a) => a -> Pattern a _run n = listToPat [0 .. n-1] scan :: (Enum a, Num a) => Pattern a -> Pattern a scan tp = tp >>= _scan _scan :: (Enum a, Num a) => a -> Pattern a _scan n = slowcat $ map _run [1 .. n] temporalParam :: (a -> Pattern b -> Pattern c) -> (Pattern a -> Pattern b -> Pattern c) temporalParam f tv p = unwrap $ (\v -> f v p) <$> tv temporalParam2 :: (a -> b -> Pattern c -> Pattern d) -> (Pattern a -> Pattern b -> Pattern c -> Pattern d) temporalParam2 f a b p = unwrap $ (\x y -> f x y p) <$> a <*> b temporalParam3 :: (a -> b -> c -> Pattern d -> Pattern e) -> (Pattern a -> Pattern b -> Pattern c -> Pattern d -> Pattern e) temporalParam3 f a b c p = unwrap $ (\x y z -> f x y z p) <$> a <*> b <*> c temporalParam' :: (a -> Pattern b -> Pattern c) -> (Pattern a -> Pattern b -> Pattern c) temporalParam' f tv p = unwrap' $ (\v -> f v p) <$> tv temporalParam2' :: (a -> b -> Pattern c -> Pattern d) -> (Pattern a -> Pattern b -> Pattern c -> Pattern d) temporalParam2' f a b p = unwrap' $ (\x y -> f x y p) <$> a <*> b temporalParam3' :: (a -> b -> c -> Pattern d -> Pattern e) -> (Pattern a -> Pattern b -> Pattern c -> Pattern d -> Pattern e) temporalParam3' f a b c p = unwrap' $ (\x y z -> f x y z p) <$> a <*> b <*> c -- | @fast@ (also known as @density@) returns the given pattern with speed -- (or density) increased by the given @Time@ factor. Therefore @fast 2 p@ -- will return a pattern that is twice as fast, and @fast (1/3) p@ -- will return one three times as slow. fast :: Pattern Time -> Pattern a -> Pattern a fast = temporalParam _density _fast :: Time -> Pattern a -> Pattern a _fast = _density fast' :: Pattern Time -> Pattern a -> Pattern a fast' = temporalParam' _density -- | @density@ is an alias of @fast@. @fast@ is quicker to type, but -- @density@ is its old name so is used in a lot of examples. density :: Pattern Time -> Pattern a -> Pattern a density = fast _density :: Time -> Pattern a -> Pattern a _density r p | r == 0 = silence | r < 0 = rev $ _density (0-r) p | otherwise = withResultTime (/ r) $ withQueryTime (* r) p -- | @fastGap@ (also known as @densityGap@ is similar to @fast@ but maintains its cyclic -- alignment. For example, @fastGap 2 p@ would squash the events in -- pattern @p@ into the first half of each cycle (and the second -- halves would be empty). fastGap :: Time -> Pattern a -> Pattern a fastGap 0 _ = silence fastGap r p = splitQueries $ withResultArc (\(s,e) -> (sam s + ((s - sam s)/r), (sam s + ((e - sam s)/r)))) $ Pattern (\a -> arc p $ mapArc (\t -> sam t + (min 1 (r * cyclePos t))) a) densityGap :: Time -> Pattern a -> Pattern a densityGap = fastGap -- | @slow@ does the opposite of @fast@, i.e. @slow 2 p@ will return a -- pattern that is half the speed. slow :: Pattern Time -> Pattern a -> Pattern a slow = temporalParam _slow sparsity :: Pattern Time -> Pattern a -> Pattern a sparsity = slow slow' :: Pattern Time -> Pattern a -> Pattern a slow' = temporalParam' _slow _slow :: Time -> Pattern a -> Pattern a _slow t p = _density (1/t) p -- | The @<~@ operator shifts (or rotates) a pattern to the left (or -- counter-clockwise) by the given @Time@ value. For example -- @(1%16) <~ p@ will return a pattern with all the events moved -- one 16th of a cycle to the left. rotL :: Time -> Pattern a -> Pattern a rotL t p = withResultTime (subtract t) $ withQueryTime (+ t) p (<~) :: Pattern Time -> Pattern a -> Pattern a (<~) = temporalParam rotL -- | The @~>@ operator does the same as @<~@ but shifts events to the -- right (or clockwise) rather than to the left. rotR :: Time -> Pattern a -> Pattern a rotR = (rotL) . (0-) (~>) :: Pattern Time -> Pattern a -> Pattern a (~>) = temporalParam rotR {- | (The above means that `brak` is a function from patterns of any type, to a pattern of the same type.) Make a pattern sound a bit like a breakbeat Example: @ d1 $ sound (brak "bd sn kurt") @ -} brak :: Pattern a -> Pattern a brak = when ((== 1) . (`mod` 2)) (((1%4) `rotR`) . (\x -> fastcat [x, silence])) {- | Divides a pattern into a given number of subdivisions, plays the subdivisions in order, but increments the starting subdivision each cycle. The pattern wraps to the first subdivision after the last subdivision is played. Example: @ d1 $ iter 4 $ sound "bd hh sn cp" @ This will produce the following over four cycles: @ bd hh sn cp hh sn cp bd sn cp bd hh cp bd hh sn @ There is also `iter'`, which shifts the pattern in the opposite direction. -} iter :: Pattern Int -> Pattern c -> Pattern c iter = temporalParam _iter _iter :: Int -> Pattern a -> Pattern a _iter n p = slowcat $ map (\i -> ((fromIntegral i)%(fromIntegral n)) `rotL` p) [0 .. (n-1)] -- | @iter'@ is the same as @iter@, but decrements the starting -- subdivision instead of incrementing it. iter' :: Pattern Int -> Pattern c -> Pattern c iter' = temporalParam _iter' _iter' :: Int -> Pattern a -> Pattern a _iter' n p = slowcat $ map (\i -> ((fromIntegral i)%(fromIntegral n)) `rotR` p) [0 .. (n-1)] -- | @rev p@ returns @p@ with the event positions in each cycle -- reversed (or mirrored). rev :: Pattern a -> Pattern a rev p = splitQueries $ Pattern $ \a -> map makeWholeAbsolute $ mapSnds' (mirrorArc (mid a)) $ map makeWholeRelative (arc p (mirrorArc (mid a) a)) where makeWholeRelative ((s,e), part@(s',e'), v) = ((s'-s, e-e'), part, v) makeWholeAbsolute ((s,e), part@(s',e'), v) = ((s'-e,e'+s), part, v) mid (s,e) = (sam s) + 0.5 -- | @palindrome p@ applies @rev@ to @p@ every other cycle, so that -- the pattern alternates between forwards and backwards. palindrome :: Pattern a -> Pattern a palindrome p = append' p (rev p) {-| Only `when` the given test function returns `True` the given pattern transformation is applied. The test function will be called with the current cycle as a number. @ d1 $ when ((elem '4').show) (striate 4) $ sound "hh hc" @ The above will only apply `striate 4` to the pattern if the current cycle number contains the number 4. So the fourth cycle will be striated and the fourteenth and so on. Expect lots of striates after cycle number 399. -} when :: (Int -> Bool) -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a when test f p = splitQueries $ Pattern apply where apply a | test (floor $ fst a) = (arc $ f p) a | otherwise = (arc p) a whenT :: (Time -> Bool) -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a whenT test f p = splitQueries $ Pattern apply where apply a | test (fst a) = (arc $ f p) a | otherwise = (arc p) a playWhen :: (Time -> Bool) -> Pattern a -> Pattern a playWhen test (Pattern f) = Pattern $ (filter (\e -> test (eventOnset e))) . f playFor :: Time -> Time -> Pattern a -> Pattern a playFor s e = playWhen (\t -> and [t >= s, t < e]) {- | The function @seqP@ allows you to define when a sound within a list starts and ends. The code below contains three separate patterns in a `stack`, but each has different start times (zero cycles, eight cycles, and sixteen cycles, respectively). All patterns stop after 128 cycles: @ d1 $ seqP [ (0, 128, sound "bd bd*2"), (8, 128, sound "hh*2 [sn cp] cp future*4"), (16, 128, sound (samples "arpy*8" (run 16))) ] @ -} seqP :: [(Time, Time, Pattern a)] -> Pattern a seqP ps = stack $ map (\(s, e, p) -> playFor s e ((sam s) `rotR` p)) ps -- | @every n f p@ applies the function @f@ to @p@, but only affects -- every @n@ cycles. every :: Pattern Int -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a every tp f p = tp >>= \t -> _every t f p _every :: Int -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a _every 0 _ p = p _every n f p = when ((== 0) . (`mod` n)) f p -- | @every n o f'@ is like @every n f@ with an offset of @o@ cycles every' :: Pattern Int -> Pattern Int -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a every' np op f p = do { n <- np; o <- op; _every' n o f p } _every' :: Int -> Int -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a _every' n o f = when ((== o) . (`mod` n)) f -- | @foldEvery ns f p@ applies the function @f@ to @p@, and is applied for -- each cycle in @ns@. foldEvery :: [Int] -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a foldEvery ns f p = foldr ($) p (map (\x -> _every x f) ns) -- | @sig f@ takes a function from time to values, and turns it into a -- @Pattern@. sig :: (Time -> a) -> Pattern a sig f = Pattern f' where f' (s,e) | s > e = [] | otherwise = [((s,e), (s,e), f s)] -- | @sinewave@ returns a @Pattern@ of continuous @Double@ values following a -- sinewave with frequency of one cycle, and amplitude from 0 to 1. sinewave :: Pattern Double sinewave = sig $ \t -> ((sin $ pi * 2 * (fromRational t)) + 1) / 2 -- | @sine@ is a synonym for @sinewave@. sine :: Pattern Double sine = sinewave -- | @sine@ is a synonym for @0.25 ~> sine@. cosine :: Pattern Double cosine = 0.25 ~> sine -- | @sinerat@ is equivalent to @sinewave@ for @Rational@ values, -- suitable for use as @Time@ offsets. sinerat :: Pattern Rational sinerat = fmap toRational sine -- | @ratsine@ is a synonym for @sinerat@. ratsine :: Pattern Rational ratsine = sinerat -- | @sineAmp d@ returns @sinewave@ with its amplitude offset by @d@. -- Deprecated, as these days you can simply do e.g. (sine + 0.5) sineAmp :: Double -> Pattern Double sineAmp offset = (+ offset) <$> sinewave1 -- | @sawwave@ is the equivalent of @sinewave@ for (ascending) sawtooth waves. sawwave :: Pattern Double sawwave = sig $ \t -> mod' (fromRational t) 1 -- | @saw@ is a synonym for @sawwave@. saw :: Pattern Double saw = sawwave -- | @sawrat@ is the same as @sawwave@ but returns @Rational@ values -- suitable for use as @Time@ offsets. sawrat :: Pattern Rational sawrat = fmap toRational saw -- | @triwave@ is the equivalent of @sinewave@ for triangular waves. triwave :: Pattern Double triwave = append sawwave1 (rev sawwave1) -- | @tri@ is a synonym for @triwave@. tri :: Pattern Double tri = triwave -- | @trirat@ is the same as @triwave@ but returns @Rational@ values -- suitable for use as @Time@ offsets. trirat :: Pattern Rational trirat = fmap toRational tri -- | @squarewave1@ is the equivalent of @sinewave@ for square waves. squarewave :: Pattern Double squarewave = sig $ \t -> fromIntegral $ ((floor $ (mod' (fromRational t :: Double) 1) * 2) :: Integer) -- | @square@ is a synonym for @squarewave@. square :: Pattern Double square = squarewave -- deprecated.. sinewave1 :: Pattern Double sinewave1 = sinewave sine1 :: Pattern Double sine1 = sinewave sinerat1 :: Pattern Rational sinerat1 = sinerat sineAmp1 :: Double -> Pattern Double sineAmp1 = sineAmp sawwave1 :: Pattern Double sawwave1 = sawwave saw1 :: Pattern Double saw1 = sawwave sawrat1 :: Pattern Rational sawrat1 = sawrat triwave1 :: Pattern Double triwave1 = triwave tri1 :: Pattern Double tri1 = triwave trirat1 :: Pattern Rational trirat1 = trirat squarewave1 :: Pattern Double squarewave1 = squarewave square1 :: Pattern Double square1 = square -- | @envL@ is a @Pattern@ of continuous @Double@ values, representing -- a linear interpolation between 0 and 1 during the first cycle, then -- staying constant at 1 for all following cycles. Possibly only -- useful if you're using something like the retrig function defined -- in tidal.el. envL :: Pattern Double envL = sig $ \t -> max 0 $ min (fromRational t) 1 -- like envL but reversed. envLR :: Pattern Double envLR = (1-) <$> envL -- 'Equal power' for gain-based transitions envEq :: Pattern Double envEq = sig $ \t -> sqrt (sin (pi/2 * (max 0 $ min (fromRational (1-t)) 1))) -- Equal power reversed envEqR :: Pattern Double envEqR = sig $ \t -> sqrt (cos (pi/2 * (max 0 $ min (fromRational (1-t)) 1))) fadeOut :: Time -> Pattern a -> Pattern a fadeOut n = spread' (_degradeBy) (_slow n $ envL) -- Alternate versions where you can provide the time from which the fade starts fadeOut' :: Time -> Time -> Pattern a -> Pattern a fadeOut' from dur p = spread' (_degradeBy) (from `rotR` _slow dur envL) p -- The 1 <~ is so fade ins and outs have different degredations fadeIn' :: Time -> Time -> Pattern a -> Pattern a fadeIn' from dur p = spread' (\n p -> 1 `rotL` _degradeBy n p) (from `rotR` _slow dur ((1-) <$> envL)) p fadeIn :: Time -> Pattern a -> Pattern a fadeIn n = spread' (_degradeBy) (_slow n $ (1-) <$> envL) {- | (The above is difficult to describe, if you don't understand Haskell, just ignore it and read the below..) The `spread` function allows you to take a pattern transformation which takes a parameter, such as `slow`, and provide several parameters which are switched between. In other words it 'spreads' a function across several values. Taking a simple high hat loop as an example: @ d1 $ sound "ho ho:2 ho:3 hc" @ We can slow it down by different amounts, such as by a half: @ d1 $ slow 2 $ sound "ho ho:2 ho:3 hc" @ Or by four thirds (i.e. speeding it up by a third; `4%3` means four over three): @ d1 $ slow (4%3) $ sound "ho ho:2 ho:3 hc" @ But if we use `spread`, we can make a pattern which alternates between the two speeds: @ d1 $ spread slow [2,4%3] $ sound "ho ho:2 ho:3 hc" @ Note that if you pass ($) as the function to spread values over, you can put functions as the list of values. For example: @ d1 $ spread ($) [density 2, rev, slow 2, striate 3, (# speed "0.8")] $ sound "[bd*2 [~ bd]] [sn future]*2 cp jvbass*4" @ Above, the pattern will have these transforms applied to it, one at a time, per cycle: * cycle 1: `density 2` - pattern will increase in speed * cycle 2: `rev` - pattern will be reversed * cycle 3: `slow 2` - pattern will decrease in speed * cycle 4: `striate 3` - pattern will be granualized * cycle 5: `(# speed "0.8")` - pattern samples will be played back more slowly After `(# speed "0.8")`, the transforms will repeat and start at `density 2` again. -} spread :: (a -> t -> Pattern b) -> [a] -> t -> Pattern b spread f xs p = slowcat $ map (\x -> f x p) xs slowspread :: (a -> t -> Pattern b) -> [a] -> t -> Pattern b slowspread = spread {- | @fastspread@ works the same as @spread@, but the result is squashed into a single cycle. If you gave four values to @spread@, then the result would seem to speed up by a factor of four. Compare these two: d1 $ spread chop [4,64,32,16] $ sound "ho ho:2 ho:3 hc" d1 $ fastspread chop [4,64,32,16] $ sound "ho ho:2 ho:3 hc" There is also @slowspread@, which is an alias of @spread@. -} fastspread :: (a -> t -> Pattern b) -> [a] -> t -> Pattern b fastspread f xs p = fastcat $ map (\x -> f x p) xs {- | There's a version of this function, `spread'` (pronounced "spread prime"), which takes a *pattern* of parameters, instead of a list: @ d1 $ spread' slow "2 4%3" $ sound "ho ho:2 ho:3 hc" @ This is quite a messy area of Tidal - due to a slight difference of implementation this sounds completely different! One advantage of using `spread'` though is that you can provide polyphonic parameters, e.g.: @ d1 $ spread' slow "[2 4%3, 3]" $ sound "ho ho:2 ho:3 hc" @ -} spread' :: Monad m => (a -> b -> m c) -> m a -> b -> m c spread' f vpat pat = vpat >>= \v -> f v pat {- | `spreadChoose f xs p` is similar to `slowspread` but picks values from `xs` at random, rather than cycling through them in order. It has a shorter alias `spreadr`. -} spreadChoose :: (t -> t1 -> Pattern b) -> [t] -> t1 -> Pattern b spreadChoose f vs p = do v <- discretise 1 (choose vs) f v p spreadr :: (t -> t1 -> Pattern b) -> [t] -> t1 -> Pattern b spreadr = spreadChoose filterValues :: (a -> Bool) -> Pattern a -> Pattern a filterValues f (Pattern x) = Pattern $ (filter (f . thd')) . x filterJust :: Pattern (Maybe a) -> Pattern a filterJust p = fromJust <$> (filterValues (isJust) p) -- Filter out events that have had their onsets cut off filterOnsets :: Pattern a -> Pattern a filterOnsets (Pattern f) = Pattern $ (filter (\e -> eventOnset e >= eventStart e)) . f -- Filter events which have onsets, which are within the given range -- TODO - what about < e ?? filterStartInRange :: Pattern a -> Pattern a filterStartInRange (Pattern f) = Pattern $ \(s,e) -> filter ((isIn (s,e)) . eventOnset) $ f (s,e) filterOnsetsInRange :: Pattern a -> Pattern a filterOnsetsInRange = filterOnsets . filterStartInRange -- Samples some events from a pattern, returning a list of onsets -- (relative to the given arc), deltas (durations) and values. seqToRelOnsetDeltas :: Arc -> Pattern a -> [(Double, Double, a)] seqToRelOnsetDeltas (s, e) p = map (\((s', e'), _, x) -> (fromRational $ (s'-s) / (e-s), fromRational $ (e'-s) / (e-s), x)) $ arc (filterOnsetsInRange p) (s, e) segment :: Pattern a -> Pattern [a] segment p = Pattern $ \(s,e) -> filter (\(_,(s',e'),_) -> s' < e && e' > s) $ groupByTime (segment' (arc p (s,e))) segment' :: [Event a] -> [Event a] segment' es = foldr split es pts where pts = nub $ points es split :: Time -> [Event a] -> [Event a] split _ [] = [] split t ((ev@(a,(s,e), v)):es) | t > s && t < e = (a,(s,t),v):(a,(t,e),v):(split t es) | otherwise = ev:split t es points :: [Event a] -> [Time] points [] = [] points ((_,(s,e), _):es) = s:e:(points es) groupByTime :: [Event a] -> [Event [a]] groupByTime es = map mrg $ groupBy ((==) `on` snd') $ sortBy (compare `on` snd') es where mrg es@((a, a', _):_) = (a, a', map thd' es) mrg _ = error "groupByTime" {-| Decide whether to apply one or another function depending on the result of a test function that is passed the current cycle as a number. @ d1 $ ifp ((== 0).(flip mod 2)) (striate 4) (# coarse "24 48") $ sound "hh hc" @ This will apply `striate 4` for every _even_ cycle and aply `# coarse "24 48"` for every _odd_. Detail: As you can see the test function is arbitrary and does not rely on anything tidal specific. In fact it uses only plain haskell functionality, that is: it calculates the modulo of 2 of the current cycle which is either 0 (for even cycles) or 1. It then compares this value against 0 and returns the result, which is either `True` or `False`. This is what the `ifp` signature's first part signifies `(Int -> Bool)`, a function that takes a whole number and returns either `True` or `False`. -} ifp :: (Int -> Bool) -> (Pattern a -> Pattern a) -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a ifp test f1 f2 p = splitQueries $ Pattern apply where apply a | test (floor $ fst a) = (arc $ f1 p) a | otherwise = (arc $ f2 p) a {-| `rand` generates a continuous pattern of (pseudo-)random, floating point numbers between `0` and `1`. @ d1 $ sound "bd*8" # pan rand @ pans bass drums randomly @ d1 $ sound "sn sn ~ sn" # gain rand @ makes the snares' randomly loud and quiet. Numbers coming from this pattern are random, but dependent on time. So if you reset time via `cps (-1)` the random pattern will emit the exact same _random_ numbers again. In cases where you need two different random patterns, you can shift one of them around to change the time from which the _random_ pattern is read, note the difference: @ d1 $ jux (|+| gain rand) $ sound "sn sn ~ sn" # gain rand @ and with the juxed version shifted backwards for 1024 cycles: @ d1 $ jux (|+| ((1024 <~) $ gain rand)) $ sound "sn sn ~ sn" # gain rand @ -} rand :: Pattern Double rand = Pattern $ \a -> [(a, a, timeToRand $ (midPoint a))] timeToRand :: RealFrac r => r -> Double timeToRand t = fst $ randomDouble $ pureMT $ floor $ (*1000000) t {- | Just like `rand` but for whole numbers, `irand n` generates a pattern of (pseudo-) random whole numbers between `0` to `n-1` inclusive. Notably used to pick a random samples from a folder: @ d1 $ n (irand 5) # sound "drum" @ -} irand :: Num a => Int -> Pattern a irand i = (fromIntegral . (floor :: Double -> Int) . (* (fromIntegral i))) <$> rand {- | Randomly picks an element from the given list @ d1 $ sound (samples "xx(3,8)" (tom $ choose ["a", "e", "g", "c"])) @ plays a melody randomly choosing one of the four notes \"a\", \"e\", \"g\", \"c\". -} choose :: [a] -> Pattern a choose [] = E.throw (E.ErrorCall "Empty list. Nothing to choose from.") choose xs = (xs !!) <$> (irand $ length xs) {- | Similar to `degrade` `degradeBy` allows you to control the percentage of events that are removed. For example, to remove events 90% of the time: @ d1 $ slow 2 $ degradeBy 0.9 $ sound "[[[feel:5*8,feel*3] feel:3*8], feel*4]" # accelerate "-6" # speed "2" @ -} degradeBy :: Pattern Double -> Pattern a -> Pattern a degradeBy = temporalParam _degradeBy _degradeBy :: Double -> Pattern a -> Pattern a _degradeBy x p = fmap fst $ filterValues ((> x) . snd) $ (,) <$> p <*> rand unDegradeBy :: Pattern Double -> Pattern a -> Pattern a unDegradeBy = temporalParam _unDegradeBy _unDegradeBy :: Double -> Pattern a -> Pattern a _unDegradeBy x p = fmap fst $ filterValues ((<= x) . snd) $ (,) <$> p <*> rand degradeOverBy :: Int -> Pattern Double -> Pattern a -> Pattern a degradeOverBy i tx p = unwrap $ (\x -> (fmap fst $ filterValues ((> x) . snd) $ (,) <$> p <*> repeatCycles i rand)) <$> (slow (fromIntegral i) tx) {- | Use @sometimesBy@ to apply a given function "sometimes". For example, the following code results in `density 2` being applied about 25% of the time: @ d1 $ sometimesBy 0.25 (density 2) $ sound "bd*8" @ There are some aliases as well: @ sometimes = sometimesBy 0.5 often = sometimesBy 0.75 rarely = sometimesBy 0.25 almostNever = sometimesBy 0.1 almostAlways = sometimesBy 0.9 @ -} sometimesBy :: Double -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a sometimesBy x f p = overlay (_degradeBy x p) (f $ _unDegradeBy x p) -- | @sometimes@ is an alias for sometimesBy 0.5. sometimes :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a sometimes = sometimesBy 0.5 -- | @often@ is an alias for sometimesBy 0.75. often :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a often = sometimesBy 0.75 -- | @rarely@ is an alias for sometimesBy 0.25. rarely :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a rarely = sometimesBy 0.25 -- | @almostNever@ is an alias for sometimesBy 0.1 almostNever :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a almostNever = sometimesBy 0.1 -- | @almostAlways@ is an alias for sometimesBy 0.9 almostAlways :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a almostAlways = sometimesBy 0.9 never :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a never = flip const always :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a always = id {- | @someCyclesBy@ is a cycle-by-cycle version of @sometimesBy@. It has a `someCycles = someCyclesBy 0.5` alias -} someCyclesBy :: Double -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a someCyclesBy x = when (test x) where test x c = (timeToRand (fromIntegral c :: Double)) < x somecyclesBy :: Double -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a somecyclesBy = someCyclesBy someCycles :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a someCycles = someCyclesBy 0.5 {- | `degrade` randomly removes events from a pattern 50% of the time: @ d1 $ slow 2 $ degrade $ sound "[[[feel:5*8,feel*3] feel:3*8], feel*4]" # accelerate "-6" # speed "2" @ The shorthand syntax for `degrade` is a question mark: `?`. Using `?` will allow you to randomly remove events from a portion of a pattern: @ d1 $ slow 2 $ sound "bd ~ sn bd ~ bd? [sn bd?] ~" @ You can also use `?` to randomly remove events from entire sub-patterns: @ d1 $ slow 2 $ sound "[[[feel:5*8,feel*3] feel:3*8]?, feel*4]" @ -} degrade :: Pattern a -> Pattern a degrade = _degradeBy 0.5 -- | @wedge t p p'@ combines patterns @p@ and @p'@ by squashing the -- @p@ into the portion of each cycle given by @t@, and @p'@ into the -- remainer of each cycle. wedge :: Time -> Pattern a -> Pattern a -> Pattern a wedge t p p' = overlay (densityGap (1/t) p) (t `rotR` densityGap (1/(1-t)) p') timeCat :: [(Time, Pattern a)] -> Pattern a timeCat tps = stack $ map (\(s,e,p) -> compress (s/total, e/total) p) $ arrange 0 tps where total = sum $ map fst tps arrange :: Time -> [(Time, Pattern a)] -> [(Time, Time, Pattern a)] arrange _ [] = [] arrange t ((t',p):tps) = (t,t+t',p):(arrange (t+t') tps) {- | @whenmod@ has a similar form and behavior to `every`, but requires an additional number. Applies the function to the pattern, when the remainder of the current loop number divided by the first parameter, is greater or equal than the second parameter. For example the following makes every other block of four loops twice as dense: @ d1 $ whenmod 8 4 (density 2) (sound "bd sn kurt") @ -} whenmod :: Int -> Int -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a whenmod a b = Sound.Tidal.Pattern.when ((\t -> (t `mod` a) >= b )) {- | @ superimpose f p = stack [p, f p] @ `superimpose` plays a modified version of a pattern at the same time as the original pattern, resulting in two patterns being played at the same time. @ d1 $ superimpose (density 2) $ sound "bd sn [cp ht] hh" d1 $ superimpose ((# speed "2") . (0.125 <~)) $ sound "bd sn cp hh" @ -} superimpose :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a superimpose f p = stack [p, f p] -- | @splitQueries p@ wraps `p` to ensure that it does not get -- queries that span arcs. For example `arc p (0.5, 1.5)` would be -- turned into two queries, `(0.5,1)` and `(1,1.5)`, and the results -- combined. Being able to assume queries don't span cycles often -- makes transformations easier to specify. splitQueries :: Pattern a -> Pattern a splitQueries p = Pattern $ \a -> concatMap (arc p) $ arcCycles a {- | @trunc@ truncates a pattern so that only a fraction of the pattern is played. The following example plays only the first quarter of the pattern: @ d1 $ trunc 0.25 $ sound "bd sn*2 cp hh*4 arpy bd*2 cp bd*2" @ -} trunc :: Pattern Time -> Pattern a -> Pattern a trunc = temporalParam _trunc _trunc :: Time -> Pattern a -> Pattern a _trunc t = compress (0,t) . zoom (0,t) {- | @linger@ is similar to `trunc` but the truncated part of the pattern loops until the end of the cycle @ d1 $ linger 0.25 $ sound "bd sn*2 cp hh*4 arpy bd*2 cp bd*2" @ -} linger :: Pattern Time -> Pattern a -> Pattern a linger = temporalParam _linger _linger :: Time -> Pattern a -> Pattern a _linger n p = _density (1/n) $ zoom (0,n) p {- | Plays a portion of a pattern, specified by a beginning and end arc of time. The new resulting pattern is played over the time period of the original pattern: @ d1 $ zoom (0.25, 0.75) $ sound "bd*2 hh*3 [sn bd]*2 drum" @ In the pattern above, `zoom` is used with an arc from 25% to 75%. It is equivalent to this pattern: @ d1 $ sound "hh*3 [sn bd]*2" @ -} zoom :: Arc -> Pattern a -> Pattern a zoom (s,e) p = splitQueries $ withResultArc (mapCycle ((/d) . (subtract s))) $ withQueryArc (mapCycle ((+s) . (*d))) p where d = e-s compress :: Arc -> Pattern a -> Pattern a compress (s,e) p | s >= e = silence | otherwise = s `rotR` densityGap (1/(e-s)) p sliceArc :: Arc -> Pattern a -> Pattern a sliceArc a@(s,e) p | s >= e = silence | otherwise = compress a $ zoom a p {- | Use `within` to apply a function to only a part of a pattern. For example, to apply `density 2` to only the first half of a pattern: @ d1 $ within (0, 0.5) (density 2) $ sound "bd*2 sn lt mt hh hh hh hh" @ Or, to apply `(# speed "0.5") to only the last quarter of a pattern: @ d1 $ within (0.75, 1) (# speed "0.5") $ sound "bd*2 sn lt mt hh hh hh hh" @ -} within :: Arc -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a within (s,e) f p = stack [playWhen (\t -> cyclePos t >= s && cyclePos t < e) $ f p, playWhen (\t -> not $ cyclePos t >= s && cyclePos t < e) $ p ] revArc :: Arc -> Pattern a -> Pattern a revArc a = within a rev {- | You can use the @e@ function to apply a Euclidean algorithm over a complex pattern, although the structure of that pattern will be lost: @ d1 $ e 3 8 $ sound "bd*2 [sn cp]" @ In the above, three sounds are picked from the pattern on the right according to the structure given by the `e 3 8`. It ends up picking two `bd` sounds, a `cp` and missing the `sn` entirely. These types of sequences use "Bjorklund's algorithm", which wasn't made for music but for an application in nuclear physics, which is exciting. More exciting still is that it is very similar in structure to the one of the first known algorithms written in Euclid's book of elements in 300 BC. You can read more about this in the paper [The Euclidean Algorithm Generates Traditional Musical Rhythms](http://cgm.cs.mcgill.ca/~godfried/publications/banff.pdf) by Toussaint. Some examples from this paper are included below, including rotation in some cases. @ - (2,5) : A thirteenth century Persian rhythm called Khafif-e-ramal. - (3,4) : The archetypal pattern of the Cumbia from Colombia, as well as a Calypso rhythm from Trinidad. - (3,5,2) : Another thirteenth century Persian rhythm by the name of Khafif-e-ramal, as well as a Rumanian folk-dance rhythm. - (3,7) : A Ruchenitza rhythm used in a Bulgarian folk-dance. - (3,8) : The Cuban tresillo pattern. - (4,7) : Another Ruchenitza Bulgarian folk-dance rhythm. - (4,9) : The Aksak rhythm of Turkey. - (4,11) : The metric pattern used by Frank Zappa in his piece titled Outside Now. - (5,6) : Yields the York-Samai pattern, a popular Arab rhythm. - (5,7) : The Nawakhat pattern, another popular Arab rhythm. - (5,8) : The Cuban cinquillo pattern. - (5,9) : A popular Arab rhythm called Agsag-Samai. - (5,11) : The metric pattern used by Moussorgsky in Pictures at an Exhibition. - (5,12) : The Venda clapping pattern of a South African children’s song. - (5,16) : The Bossa-Nova rhythm necklace of Brazil. - (7,8) : A typical rhythm played on the Bendir (frame drum). - (7,12) : A common West African bell pattern. - (7,16,14) : A Samba rhythm necklace from Brazil. - (9,16) : A rhythm necklace used in the Central African Republic. - (11,24,14) : A rhythm necklace of the Aka Pygmies of Central Africa. - (13,24,5) : Another rhythm necklace of the Aka Pygmies of the upper Sangha. @ -} e :: Int -> Int -> Pattern a -> Pattern a e n k p = (flip const) <$> (filterValues (== True) $ listToPat $ bjorklund (n,k)) <*> p e' :: Int -> Int -> Pattern a -> Pattern a e' n k p = fastcat $ map (\x -> if x then p else silence) (bjorklund (n,k)) index :: Real b => b -> Pattern b -> Pattern c -> Pattern c index sz indexpat pat = spread' (zoom' $ toRational sz) (toRational . (*(1-sz)) <$> indexpat) pat where zoom' sz start = zoom (start, start+sz) -- | @prrw f rot (blen, vlen) beatPattern valuePattern@: pattern rotate/replace. prrw :: (a -> b -> c) -> Int -> (Time, Time) -> Pattern a -> Pattern b -> Pattern c prrw f rot (blen, vlen) beatPattern valuePattern = let ecompare (_,e1,_) (_,e2,_) = compare (fst e1) (fst e2) beats = sortBy ecompare $ arc beatPattern (0, blen) values = fmap thd' . sortBy ecompare $ arc valuePattern (0, vlen) cycles = blen * (fromIntegral $ lcm (length beats) (length values) `div` (length beats)) in _slow cycles $ stack $ zipWith (\( _, (start, end), v') v -> (start `rotR`) $ densityGap (1 / (end - start)) $ pure (f v' v)) (sortBy ecompare $ arc (_density cycles $ beatPattern) (0, blen)) (drop (rot `mod` length values) $ cycle values) -- | @prr rot (blen, vlen) beatPattern valuePattern@: pattern rotate/replace. prr :: Int -> (Time, Time) -> Pattern String -> Pattern b -> Pattern b prr = prrw $ flip const {-| @preplace (blen, plen) beats values@ combines the timing of @beats@ with the values of @values@. Other ways of saying this are: * sequential convolution * @values@ quantized to @beats@. Examples: @ d1 $ sound $ preplace (1,1) "x [~ x] x x" "bd sn" d1 $ sound $ preplace (1,1) "x(3,8)" "bd sn" d1 $ sound $ "x(3,8)" <~> "bd sn" d1 $ sound "[jvbass jvbass:5]*3" |+| (shape $ "1 1 1 1 1" <~> "0.2 0.9") @ It is assumed the pattern fits into a single cycle. This works well with pattern literals, but not always with patterns defined elsewhere. In those cases use @preplace@ and provide desired pattern lengths: @ let p = slow 2 $ "x x x" d1 $ sound $ preplace (2,1) p "bd sn" @ -} preplace :: (Time, Time) -> Pattern String -> Pattern b -> Pattern b preplace = preplaceWith $ flip const -- | @prep@ is an alias for preplace. prep :: (Time, Time) -> Pattern String -> Pattern b -> Pattern b prep = preplace preplace1 :: Pattern String -> Pattern b -> Pattern b preplace1 = preplace (1, 1) preplaceWith :: (a -> b -> c) -> (Time, Time) -> Pattern a -> Pattern b -> Pattern c preplaceWith f (blen, plen) = prrw f 0 (blen, plen) prw :: (a -> b -> c) -> (Time, Time) -> Pattern a -> Pattern b -> Pattern c prw = preplaceWith preplaceWith1 :: (a -> b -> c) -> Pattern a -> Pattern b -> Pattern c preplaceWith1 f = prrw f 0 (1, 1) prw1 :: (a -> b -> c) -> Pattern a -> Pattern b -> Pattern c prw1 = preplaceWith1 (<~>) :: Pattern String -> Pattern b -> Pattern b (<~>) = preplace (1, 1) -- | @protate len rot p@ rotates pattern @p@ by @rot@ beats to the left. -- @len@: length of the pattern, in cycles. -- Example: @d1 $ every 4 (protate 2 (-1)) $ slow 2 $ sound "bd hh hh hh"@ protate :: Time -> Int -> Pattern a -> Pattern a protate len rot p = prrw (flip const) rot (len, len) p p prot :: Time -> Int -> Pattern a -> Pattern a prot = protate prot1 :: Int -> Pattern a -> Pattern a prot1 = protate 1 {-| The @<<~@ operator rotates a unit pattern to the left, similar to @<~@, but by events rather than linear time. The timing of the pattern remains constant: @ d1 $ (1 <<~) $ sound "bd ~ sn hh" -- will become d1 $ sound "sn ~ hh bd" @ -} (<<~) :: Int -> Pattern a -> Pattern a (<<~) = protate 1 -- | @~>>@ is like @<<~@ but for shifting to the right. (~>>) :: Int -> Pattern a -> Pattern a (~>>) = (<<~) . (0-) -- | @pequal cycles p1 p2@: quickly test if @p1@ and @p2@ are the same. pequal :: Ord a => Time -> Pattern a -> Pattern a -> Bool pequal cycles p1 p2 = (sort $ arc p1 (0, cycles)) == (sort $ arc p2 (0, cycles)) -- | @discretise n p@: 'samples' the pattern @p@ at a rate of @n@ -- events per cycle. Useful for turning a continuous pattern into a -- discrete one. discretise :: Time -> Pattern a -> Pattern a discretise n p = (_density n $ atom (id)) <*> p discretise' = discretise _discretise = discretise -- | @randcat ps@: does a @slowcat@ on the list of patterns @ps@ but -- randomises the order in which they are played. randcat :: [Pattern a] -> Pattern a randcat ps = spread' (rotL) (discretise 1 $ ((%1) . fromIntegral) <$> (irand (length ps) :: Pattern Int)) (slowcat ps) -- @fromNote p@: converts a pattern of human-readable pitch names -- into pitch numbers. For example, @"cs2"@ will be parsed as C Sharp -- in the 2nd octave with the result of @11@, and @"b-3"@ as -- @-25@. Pitches can be decorated using: -- -- * s = Sharp, a half-step above (@"gs-1"@) -- * f = Flat, a half-step below (@"gf-1"@) -- * n = Natural, no decoration (@"g-1" and "gn-1"@ are equivalent) -- * ss = Double sharp, a whole step above (@"gss-1"@) -- * ff = Double flat, a whole step below (@"gff-1"@) -- -- Note that TidalCycles now assumes that middle C is represented by -- the value 0, rather than the previous value of 60. This function -- is similar to previously available functions @tom@ and @toMIDI@, -- but the default octave is now 0 rather than 5. {- definition moved to Parse.hs .. toMIDI :: Pattern String -> Pattern Int toMIDI p = fromJust <$> (filterValues (isJust) (noteLookup <$> p)) where noteLookup :: String -> Maybe Int noteLookup [] = Nothing noteLookup s | not (last s `elem` ['0' .. '9']) = noteLookup (s ++ "0") | not (isLetter (s !! 1)) = noteLookup((head s):'n':(tail s)) | otherwise = parse s parse x = (\a b c -> a+b+c) <$> pc x <*> sym x <*> Just(12*digitToInt (last x)) pc x = lookup (head x) [('c',0),('d',2),('e',4),('f',5),('g',7),('a',9),('b',11)] sym x = lookup (init (tail x)) [("s",1),("f",-1),("n",0),("ss",2),("ff",-2)] -} -- @tom p@: Alias for @toMIDI@. -- tom = toMIDI {- | The `fit` function takes a pattern of integer numbers, which are used to select values from the given list. What makes this a bit strange is that only a given number of values are selected each cycle. For example: @ d1 $ sound (fit 3 ["bd", "sn", "arpy", "arpy:1", "casio"] "0 [~ 1] 2 1") @ The above fits three samples into the pattern, i.e. for the first cycle this will be `"bd"`, `"sn"` and `"arpy"`, giving the result `"bd [~ sn] arpy sn"` (note that we start counting at zero, so that `0` picks the first value). The following cycle the *next* three values in the list will be picked, i.e. `"arpy:1"`, `"casio"` and `"bd"`, giving the pattern `"arpy:1 [~ casio] bd casio"` (note that the list wraps round here). -} fit :: Int -> [a] -> Pattern Int -> Pattern a fit perCycle xs p = (xs !!!) <$> (Pattern $ \a -> map ((\e -> (mapThd' (+ (cyclePos perCycle e)) e))) (arc p a)) where cyclePos perCycle e = perCycle * (floor $ eventStart e) permstep :: RealFrac b => Int -> [a] -> Pattern b -> Pattern a permstep steps things p = unwrap $ (\n -> listToPat $ concatMap (\x -> replicate (fst x) (snd x)) $ zip (ps !! (floor (n * (fromIntegral $ (length ps - 1))))) things) <$> (discretise 1 p) where ps = permsort (length things) steps deviance avg xs = sum $ map (abs . (avg-) . fromIntegral) xs permsort n total = map fst $ sortBy (comparing snd) $ map (\x -> (x,deviance (fromIntegral total / (fromIntegral n :: Double)) x)) $ perms n total perms 0 _ = [] perms 1 n = [[n]] perms n total = concatMap (\x -> map (x:) $ perms (n-1) (total-x)) [1 .. (total-(n-1))] -- | @struct a b@: structures pattern @b@ in terms of @a@. struct :: Pattern String -> Pattern a -> Pattern a struct ps pv = (flip const) <$> ps <*> pv -- | @substruct a b@: similar to @struct@, but each event in pattern @a@ gets replaced with pattern @b@, compressed to fit the timespan of the event. substruct :: Pattern String -> Pattern b -> Pattern b substruct s p = Pattern $ f where f a = concatMap (\a' -> arc (compressTo a' p) a') $ (map fst' $ arc s a) compressTo :: Arc -> Pattern a -> Pattern a compressTo (s,e) p = compress (cyclePos s, e-(sam s)) p randArcs :: Int -> Pattern [Arc] randArcs n = do rs <- mapM (\x -> (pure $ (toRational x)/(toRational n)) <~ choose [1,2,3]) [0 .. (n-1)] let rats = map toRational rs total = sum rats pairs = pairUp $ accumulate $ map ((/total)) rats return $ pairs where pairUp [] = [] pairUp xs = (0,head xs):(pairUp' xs) pairUp' [] = [] pairUp' (a:[]) = [] pairUp' (a:b:[]) = [(a,1)] pairUp' (a:b:xs) = (a,b):(pairUp' (b:xs)) randStruct n = splitQueries $ Pattern f where f (s,e) = mapSnds' fromJust $ filter (\(_,x,_) -> isJust x) $ as where as = map (\(n, (s',e')) -> ((s' + sam s, e' + sam s), subArc (s,e) (s' + sam s, e' + sam s), n ) ) $ enumerate $ thd' $ head $ arc (randArcs n) (sam s, nextSam s) substruct' :: Pattern Int -> Pattern a -> Pattern a substruct' s p = Pattern $ \a -> concatMap (\(a', _, i) -> arc (compressTo a' (inside (pure $ 1/toRational(length (arc s (sam (fst a), nextSam (fst a))))) (rotR (toRational i)) p)) a') (arc s a) -- | @stripe n p@: repeats pattern @p@, @n@ times per cycle. So -- similar to @fast@, but with random durations. The repetitions will -- be continguous (touching, but not overlapping) and the durations -- will add up to a single cycle. @n@ can be supplied as a pattern of -- integers. stripe :: Pattern Int -> Pattern a -> Pattern a stripe = temporalParam _stripe _stripe :: Int -> Pattern a -> Pattern a _stripe = substruct' . randStruct -- | @slowstripe n p@: The same as @stripe@, but the result is also -- @n@ times slower, so that the mean average duration of the stripes -- is exactly one cycle, and every @n@th stripe starts on a cycle -- boundary (in indian classical terms, the @sam@). slowstripe :: Pattern Int -> Pattern a -> Pattern a slowstripe n = slow (toRational <$> n) . stripe n -- Lindenmayer patterns, these go well with the step sequencer -- general rule parser (strings map to strings) parseLMRule :: String -> [(String,String)] parseLMRule s = map (splitOn ':') (commaSplit s) where splitOn sep str = splitAt (fromJust $ elemIndex sep str) $ filter (/= sep) str commaSplit s = map T.unpack $ T.splitOn (T.pack ",") $ T.pack s -- specific parser for step sequencer (chars map to string) -- ruleset in form "a:b,b:ab" parseLMRule' :: String -> [(Char, String)] parseLMRule' str = map fixer $ parseLMRule str where fixer (c,r) = (head c, r) {- | returns the `n`th iteration of a [Lindenmayer System](https://en.wikipedia.org/wiki/L-system) with given start sequence. for example: @ lindenmayer 1 "a:b,b:ab" "ab" -> "bab" @ -} lindenmayer :: Int -> String -> String -> String lindenmayer _ _ [] = [] lindenmayer 1 r (c:cs) = (fromMaybe [c] $ lookup c $ parseLMRule' r) ++ (lindenmayer 1 r cs) lindenmayer n r s = iterate (lindenmayer 1 r) s !! n -- support for fit' unwrap' :: Pattern (Pattern a) -> Pattern a unwrap' pp = Pattern $ \a -> arc (stack $ map scalep (arc pp a)) a where scalep ev = compress (fst' ev) $ thd' ev {-| Removes events from second pattern that don't start during an event from first. Consider this, kind of messy rhythm without any rests. @ d1 $ sound (slowcat ["sn*8", "[cp*4 bd*4, hc*5]"]) # n (run 8) @ If we apply a mask to it @ d1 $ s (mask ("1 1 1 ~ 1 1 ~ 1" :: Pattern Bool) (slowcat ["sn*8", "[cp*4 bd*4, bass*5]"] )) # n (run 8) @ Due to the use of `slowcat` here, the same mask is first applied to `"sn*8"` and in the next cycle to `"[cp*4 bd*4, hc*5]". You could achieve the same effect by adding rests within the `slowcat` patterns, but mask allows you to do this more easily. It kind of keeps the rhythmic structure and you can change the used samples independently, e.g. @ d1 $ s (mask ("1 ~ 1 ~ 1 1 ~ 1" :: Pattern Bool) (slowcat ["can*8", "[cp*4 sn*4, jvbass*16]"] )) # n (run 8) @ Detail: It is currently needed to explicitly _tell_ Tidal that the mask itself is a `Pattern Bool` as it cannot infer this by itself, otherwise it will complain as it does not know how to interpret your input. -} mask :: Pattern a -> Pattern b -> Pattern b mask pa pb = Pattern $ \a -> concat [filterOns (subArc a $ eventArc i) (arc pb a) | i <- arc pa a] where filterOns Nothing _ = [] filterOns (Just arc) es = filter (onsetIn arc) es enclosingArc :: [Arc] -> Arc enclosingArc [] = (0,1) enclosingArc as = (minimum (map fst as), maximum (map snd as)) stretch :: Pattern a -> Pattern a stretch p = splitQueries $ Pattern $ \a@(s,_e) -> arc (zoom (enclosingArc $ map eventArc $ arc p (sam s,nextSam s)) p) a {- | `fit'` is a generalization of `fit`, where the list is instead constructed by using another integer pattern to slice up a given pattern. The first argument is the number of cycles of that latter pattern to use when slicing. It's easier to understand this with a few examples: @ d1 $ sound (fit' 1 2 "0 1" "1 0" "bd sn") @ So what does this do? The first `1` just tells it to slice up a single cycle of `"bd sn"`. The `2` tells it to select two values each cycle, just like the first argument to `fit`. The next pattern `"0 1"` is the "from" pattern which tells it how to slice, which in this case means `"0"` maps to `"bd"`, and `"1"` maps to `"sn"`. The next pattern `"1 0"` is the "to" pattern, which tells it how to rearrange those slices. So the final result is the pattern `"sn bd"`. A more useful example might be something like @ d1 $ fit' 1 4 (run 4) "[0 3*2 2 1 0 3*2 2 [1*8 ~]]/2" $ chop 4 $ (sound "breaks152" # unit "c") @ which uses `chop` to break a single sample into individual pieces, which `fit'` then puts into a list (using the `run 4` pattern) and reassembles according to the complicated integer pattern. -} fit' :: Pattern Time -> Int -> Pattern Int -> Pattern Int -> Pattern a -> Pattern a fit' cyc n from to p = unwrap' $ fit n (mapMasks n from' p') to where mapMasks n from p = [stretch $ mask (filterValues (== i) from) p | i <- [0..n-1]] p' = density cyc $ p from' = density cyc $ from {-| @chunk n f p@ treats the given pattern @p@ as having @n@ chunks, and applies the function @f@ to one of those sections per cycle, running from left to right. @ d1 $ chunk 4 (density 4) $ sound "cp sn arpy [mt lt]" @ -} chunk :: Integer -> (Pattern b -> Pattern b) -> Pattern b -> Pattern b chunk n f p = cat [within (i%(fromIntegral n),(i+1)%(fromIntegral n)) f p | i <- [0..n-1]] {- chunk n f p = do i <- _slow (toRational n) $ run (fromIntegral n) within (i%(fromIntegral n),(i+)1%(fromIntegral n)) f p -} -- deprecated (renamed to chunk) runWith :: Integer -> (Pattern b -> Pattern b) -> Pattern b -> Pattern b runWith = chunk {-| @chunk'@ works much the same as `chunk`, but runs from right to left. -} chunk' :: Integral a => a -> (Pattern b -> Pattern b) -> Pattern b -> Pattern b chunk' n f p = do i <- _slow (toRational n) $ rev $ run (fromIntegral n) within (i%(fromIntegral n),(i+)1%(fromIntegral n)) f p -- deprecated (renamed to chunk') runWith' :: Integral a => a -> (Pattern b -> Pattern b) -> Pattern b -> Pattern b runWith' = chunk' inside :: Pattern Time -> (Pattern a1 -> Pattern a) -> Pattern a1 -> Pattern a inside n f p = density n $ f (slow n p) outside :: Pattern Time -> (Pattern a1 -> Pattern a) -> Pattern a1 -> Pattern a outside n = inside (1/n) loopFirst :: Pattern a -> Pattern a loopFirst p = splitQueries $ Pattern f where f a@(s,_) = mapSnds' plus $ mapFsts' plus $ arc p (minus a) where minus = mapArc (subtract (sam s)) plus = mapArc (+ (sam s)) timeLoop :: Pattern Time -> Pattern a -> Pattern a timeLoop n = outside n loopFirst seqPLoop :: [(Time, Time, Pattern a)] -> Pattern a seqPLoop ps = timeLoop (pure $ maxT - minT) $ minT `rotL` seqP ps where minT = minimum $ map fst' ps maxT = maximum $ map snd' ps {- | @toScale@ lets you turn a pattern of notes within a scale (expressed as a list) to note numbers. For example `toScale [0, 4, 7] "0 1 2 3"` will turn into the pattern `"0 4 7 12"`. It assumes your scale fits within an octave; to change this use `toScale' size`. Example: `toScale' 24 [0,4,7,10,14,17] (run 8)` turns into `"0 4 7 10 14 17 24 28"` -} toScale' :: Int -> [Int] -> Pattern Int -> Pattern Int toScale' o s = fmap noteInScale where octave x = x `div` length s noteInScale x = (s !!! x) + o * octave x toScale :: [Int] -> Pattern Int -> Pattern Int toScale = toScale' 12 {- | `swingBy x n` divides a cycle into `n` slices and delays the notes in the second half of each slice by `x` fraction of a slice . @swing@ is an alias for `swingBy (1%3)` -} swingBy :: Pattern Time -> Pattern Time -> Pattern a -> Pattern a swingBy x n = inside n (within (0.5,1) (x ~>)) swing :: Pattern Time -> Pattern a -> Pattern a swing = swingBy (pure $ 1%3) {- | `cycleChoose` is like `choose` but only picks a new item from the list once each cycle -} cycleChoose::[a] -> Pattern a cycleChoose xs = Pattern $ \(s,e) -> [((s,e),(s,e), xs!!(floor $ (dlen xs)*(ctrand s) ))] where dlen xs = fromIntegral $ length xs ctrand s = (timeToRand :: Time -> Double) $ fromIntegral $ (floor :: Time -> Int) $ sam s {- | `shuffle n p` evenly divides one cycle of the pattern `p` into `n` parts, and returns a random permutation of the parts each cycle. For example, `shuffle 3 "a b c"` could return `"a b c"`, `"a c b"`, `"b a c"`, `"b c a"`, `"c a b"`, or `"c b a"`. But it will **never** return `"a a a"`, because that is not a permutation of the parts. -} shuffle::Int -> Pattern a -> Pattern a shuffle n = fit' 1 n (_run n) (randpat n) where randpat n = Pattern $ \(s,e) -> arc (p n $ sam s) (s,e) p n c = listToPat $ map snd $ sort $ zip [timeToRand (c+i/n') | i <- [0..n'-1]] [0..n-1] n' :: Time n' = fromIntegral n {- | `scramble n p` is like `shuffle` but randomly selects from the parts of `p` instead of making permutations. For example, `scramble 3 "a b c"` will randomly select 3 parts from `"a"` `"b"` and `"c"`, possibly repeating a single part. -} scramble::Int -> Pattern a -> Pattern a scramble n = fit' 1 n (_run n) (_density (fromIntegral n) $ liftA2 (+) (pure 0) $ irand n) ur :: Time -> Pattern String -> [Pattern a] -> Pattern a ur t outer_p ps = _slow t $ unwrap $ adjust <$> (timedValues $ (getPat . split) <$> outer_p) where split s = wordsBy (==':') s getPat (n:xs) = (ps' !!! read n, transform xs) ps' = map (_density t) ps adjust (a, (p, f)) = f a p transform (x:_) a = transform' x a transform _ _ = id transform' "in" (s,e) p = twiddle (fadeIn) (s,e) p transform' "out" (s,e) p = twiddle (fadeOut) (s,e) p transform' _ _ p = p twiddle f (s,e) p = s `rotR` (f (e-s) p) ur' :: Time -> Pattern String -> [(String, Pattern a)] -> [(String, Pattern a -> Pattern a)] -> Pattern a ur' t outer_p ps fs = _slow t $ unwrap $ adjust <$> (timedValues $ (getPat . split) <$> outer_p) where split s = wordsBy (==':') s getPat (s:xs) = (match s, transform xs) match s = fromMaybe silence $ lookup s ps' ps' = map (fmap (_density t)) ps adjust (a, (p, f)) = f a p transform (x:_) a = transform' x a transform _ _ = id transform' str (s,e) p = s `rotR` (inside (pure $ 1/(e-s)) (matchF str) p) matchF str = fromMaybe id $ lookup str fs inhabit :: [(String, Pattern a)] -> Pattern String -> Pattern a inhabit ps p = unwrap' $ (\s -> fromMaybe silence $ lookup s ps) <$> p repeatCycles :: Int -> Pattern a -> Pattern a repeatCycles n p = fastcat (replicate n p) {- | @spaceOut xs p@ repeats a pattern @p@ at different durations given by the list of time values in @xs@ -} spaceOut :: [Time] -> Pattern a -> Pattern a spaceOut xs p = _slow (toRational $ sum xs) $ stack $ map (\a -> compress a p) $ spaceArcs xs where markOut :: Time -> [Time] -> [(Time, Time)] markOut _ [] = [] markOut offset (x:xs) = (offset,offset+x):(markOut (offset+x) xs) spaceArcs xs = map (\(a,b) -> (a/s,b/s)) $ markOut 0 xs s = sum xs -- | @flatpat@ takes a Pattern of lists and pulls the list elements as -- separate Events flatpat :: Pattern [a] -> Pattern a flatpat p = Pattern $ \a -> (concatMap (\(b,b',xs) -> map (\x -> (b,b',x)) xs) $ arc p a) -- | @layer@ takes a Pattern of lists and pulls the list elements as -- separate Events layer :: [a -> Pattern b] -> a -> Pattern b layer fs p = stack $ map ($ p) fs -- | @breakUp@ finds events that share the same timespan, and spreads them out during that timespan, so for example @breakUp "[bd,sn]"@ gets turned into @"bd sn"@ breakUp :: Pattern a -> Pattern a breakUp p = Pattern $ \a -> munge $ arc p a where munge es = concatMap spreadOut (groupBy (\a b -> fst' a == fst' b) es) spreadOut xs = catMaybes $ map (\(n, x) -> shiftIt n (length xs) x) $ enumerate xs shiftIt n d ((s,e), a', v) = do a'' <- subArc (newS, newE) a' return ((newS, newE), a'', v) where newS = s + (dur*(fromIntegral n)) newE = newS + dur dur = (e - s) / (fromIntegral d) -- | @fill@ 'fills in' gaps in one pattern with events from another. For example @fill "bd" "cp ~ cp"@ would result in the equivalent of `"~ bd ~"`. This only finds gaps in a resulting pattern, in other words @"[bd ~, sn]"@ doesn't contain any gaps (because @sn@ covers it all), and @"bd ~ ~ sn"@ only contains a single gap that bridges two steps. fill :: Pattern a -> Pattern a -> Pattern a fill p' p = struct (splitQueries $ Pattern (f p)) p' where f p (s,e) = removeTolerance (s,e) $ invert (s-tolerance, e+tolerance) $ arc p (s-tolerance, e+tolerance) invert (s,e) es = map arcToEvent $ foldr remove [(s,e)] (map snd' es) remove (s,e) xs = concatMap (remove' (s, e)) xs remove' (s,e) (s',e') | s > s' && e < e' = [(s',s),(e,e')] -- inside | s > s' && s < e' = [(s',s)] -- cut off right | e > s' && e < e' = [(e,e')] -- cut off left | s <= s' && e >= e' = [] -- swallow | otherwise = [(s',e')] -- miss arcToEvent a = (a,a,"x") removeTolerance (s,e) es = concatMap (expand) $ mapSnds' f es where f (a) = concatMap (remove' (e,e+tolerance)) $ remove' (s-tolerance,s) a expand (a,xs,c) = map (\x -> (a,x,c)) xs tolerance = 0.01 tidal-0.9.5/Sound/Tidal/Tempo.hs0000644000000000000000000003314313212576414014566 0ustar0000000000000000{-# LANGUAGE ScopedTypeVariables #-} module Sound.Tidal.Tempo where import Data.Time (getCurrentTime, UTCTime, NominalDiffTime, diffUTCTime, addUTCTime) import Data.Time.Clock.POSIX import Control.Applicative ((<$>), (<*>)) import Control.Monad (forM_, forever, void) --import Control.Monad.IO.Class (liftIO) import Control.Concurrent (forkIO, threadDelay) import Control.Concurrent.MVar import Control.Monad.Trans (liftIO) import Data.Maybe (fromMaybe, maybe, isJust, fromJust) import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.IO as T import Data.Unique import qualified Network.WebSockets as WS import qualified Control.Exception as E import Safe (readNote) import System.Environment (lookupEnv) import qualified System.IO.Error as Error import GHC.Conc.Sync (ThreadId) import Sound.OSC.FD import Sound.Tidal.Utils data Tempo = Tempo {at :: UTCTime, beat :: Double, cps :: Double, paused :: Bool, clockLatency :: Double} type ClientState = [TConnection] data ServerMode = Master | Slave UDP instance Show ServerMode where show Master = "Master" show _ = "Slave" data TConnection = TConnection Unique WS.Connection wsConn :: TConnection -> WS.Connection wsConn (TConnection _ c) = c instance Eq TConnection where TConnection a _ == TConnection b _ = a == b instance Show Tempo where show x = show (at x) ++ "," ++ show (beat x) ++ "," ++ show (cps x) ++ "," ++ show (paused x) ++ "," ++ (show $ clockLatency x) getLatency :: IO Double getLatency = maybe 0.04 (readNote "latency parse") <$> lookupEnv "TIDAL_CLOCK_LATENCY" getClockIp :: IO String getClockIp = fromMaybe "127.0.0.1" <$> lookupEnv "TIDAL_TEMPO_IP" getServerPort :: IO Int getServerPort = maybe 9160 (readNote "port parse") <$> lookupEnv "TIDAL_TEMPO_PORT" getMasterPort :: IO Int getMasterPort = maybe 6042 (readNote "port parse") <$> lookupEnv "TIDAL_MASTER_PORT" getSlavePort :: IO Int getSlavePort = maybe 6043 (readNote "port parse") <$> lookupEnv "TIDAL_SLAVE_PORT" readTempo :: String -> Tempo readTempo x = Tempo (read a) (read b) (read c) (read d) (read e) where (a:b:c:d:e:_) = wordsBy (== ',') x logicalTime :: Tempo -> Double -> Double logicalTime t b = changeT + timeDelta where beatDelta = b - (beat t) timeDelta = beatDelta / (cps t) changeT = realToFrac $ utcTimeToPOSIXSeconds $ at t tempoMVar :: IO (MVar (Tempo)) tempoMVar = do now <- getCurrentTime l <- getLatency mv <- newMVar (Tempo now 0 0.5 False l) forkIO $ clocked $ f mv return mv where f mv change _ = do swapMVar mv change return () beatNow :: Tempo -> IO (Double) beatNow t = do now <- getCurrentTime let delta = realToFrac $ diffUTCTime now (at t) let beatDelta = cps t * delta return $ beat t + beatDelta clientApp :: MVar Tempo -> MVar Double -> MVar Double -> WS.ClientApp () clientApp mTempo mCps mNudge conn = do liftIO $ forkIO $ sendCps conn mCps liftIO $ forkIO $ sendNudge conn mNudge forever loop where loop = do msg <- WS.receiveData conn let s = T.unpack msg let tempo = readTempo $ s old <- liftIO $ tryTakeMVar mTempo -- putStrLn $ "from: " ++ show old -- putStrLn $ "to: " ++ show tempo liftIO $ putMVar mTempo tempo sendTempo :: [WS.Connection] -> Tempo -> IO () sendTempo conns t = mapM_ (\conn -> WS.sendTextData conn (T.pack $ show t)) conns sendCps :: WS.Connection -> MVar Double -> IO () sendCps conn mCps = forever $ do cps <- takeMVar mCps let m = "cps " ++ (show cps) WS.sendTextData conn (T.pack m) sendNudge :: WS.Connection -> MVar Double -> IO () sendNudge conn mNudge = forever $ do nudge <- takeMVar mNudge let m = "nudge " ++ (show nudge) WS.sendTextData conn (T.pack m) connectClient :: Bool -> String -> MVar Tempo -> MVar Double -> MVar Double -> IO () connectClient secondTry ip mTempo mCps mNudge = do let errMsg = "Failed to connect to tidal server. Try specifying a " ++ "different port (default is 9160) setting the " ++ "environment variable TIDAL_TEMPO_PORT" serverPort <- getServerPort WS.runClient ip serverPort "/tempo" (clientApp mTempo mCps mNudge) `E.catch` \(_ :: E.SomeException) -> do case secondTry of True -> error errMsg _ -> do res <- E.try (void startServer) case res of Left (_ :: E.SomeException) -> error errMsg Right _ -> do threadDelay 500000 connectClient True ip mTempo mCps mNudge runClient :: IO ((MVar Tempo, MVar Double, MVar Double)) runClient = do clockip <- getClockIp mTempo <- newEmptyMVar mCps <- newEmptyMVar mNudge <- newEmptyMVar forkIO $ connectClient False clockip mTempo mCps mNudge return (mTempo, mCps, mNudge) cpsUtils' :: IO ((Double -> IO (), (Double -> IO ()), IO Rational)) cpsUtils' = do (mTempo, mCps, mNudge) <- runClient let cpsSetter = putMVar mCps nudger = putMVar mNudge currentTime = do tempo <- readMVar mTempo now <- beatNow tempo return $ toRational now return (cpsSetter, nudger, currentTime) -- backward compatibility cpsUtils = do (cpsSetter, _, currentTime) <- cpsUtils' return (cpsSetter, currentTime) -- Backwards compatibility bpsUtils :: IO ((Double -> IO (), IO (Rational))) bpsUtils = cpsUtils cpsSetter :: IO (Double -> IO ()) cpsSetter = do (f, _) <- cpsUtils return f clocked :: (Tempo -> Int -> IO ()) -> IO () clocked = clockedTick 1 clockedTick :: Int -> (Tempo -> Int -> IO ()) -> IO () clockedTick tpb callback = do (mTempo, _, mCps) <- runClient t <- readMVar mTempo now <- getCurrentTime let delta = realToFrac $ diffUTCTime now (at t) beatDelta = cps t * delta nowBeat = beat t + beatDelta nextTick = ceiling (nowBeat * (fromIntegral tpb)) -- next4 = nextBeat + (4 - (nextBeat `mod` 4)) loop mTempo nextTick where loop mTempo tick = do tempo <- readMVar mTempo tick' <- doTick tempo tick loop mTempo tick' doTick tempo tick | paused tempo = do let pause = 0.01 -- TODO - do this via blocking read on the mvar somehow -- rather than polling threadDelay $ floor (pause * 1000000) -- reset tick to 0 if cps is negative return $ if cps tempo < 0 then 0 else tick | otherwise = do now <- getCurrentTime let tps = (fromIntegral tpb) * cps tempo delta = realToFrac $ diffUTCTime now (at tempo) actualTick = ((fromIntegral tpb) * beat tempo) + (tps * delta) -- only wait by up to two ticks tickDelta = min 2 $ (fromIntegral tick) - actualTick delay = tickDelta / tps -- putStrLn $ "tick delta: " ++ show tickDelta --putStrLn ("Delay: " ++ show delay ++ "s Beat: " ++ show (beat tempo)) threadDelay $ floor (delay * 1000000) callback tempo tick -- putStrLn $ "hmm diff: " ++ show (abs $ (floor actualTick) - tick) let newTick | (abs $ (floor actualTick) - tick) > 4 = floor actualTick | otherwise = tick + 1 return $ newTick updateTempo :: Tempo -> Double -> IO (Tempo) updateTempo t cps' | paused t == True && cps' > 0 = -- unpause do now <- getCurrentTime return $ t {at = addUTCTime (realToFrac $ clockLatency t) now, cps = cps', paused = False} | otherwise = do now <- getCurrentTime let delta = realToFrac $ diffUTCTime now (at t) beat' = (beat t) + ((cps t) * delta) beat'' = if cps' < 0 then 0 else beat' return $ t {at = now, beat = beat'', cps = cps', paused = (cps' <= 0)} nudgeTempo :: Tempo -> Double -> Tempo nudgeTempo t secs = t {at = addUTCTime (realToFrac secs) (at t)} removeClient :: TConnection -> ClientState -> ClientState removeClient client = filter (/= client) broadcast :: Text -> ClientState -> IO () broadcast message clients = do -- T.putStrLn message forM_ clients $ \conn -> WS.sendTextData (wsConn conn) $ message startServer :: IO (ThreadId) startServer = do serverPort <- getServerPort start <- getCurrentTime l <- getLatency tempoState <- newMVar (Tempo start 0 1 False l) clientState <- newMVar [] serverState <- newMVar Master --liftIO $ oscBridge clientState liftIO $ slave serverState clientState forkIO $ WS.runServer "0.0.0.0" serverPort $ serverApp tempoState serverState clientState serverApp :: MVar Tempo -> MVar ServerMode -> MVar ClientState -> WS.ServerApp serverApp tempoState serverState clientState pending = do conn <- TConnection <$> newUnique <*> WS.acceptRequest pending tempo <- liftIO $ readMVar tempoState liftIO $ WS.sendTextData (wsConn conn) $ T.pack $ show tempo clients <- liftIO $ readMVar clientState liftIO $ modifyMVar_ clientState $ return . (conn:) serverLoop conn tempoState serverState clientState slave :: MVar ServerMode -> MVar ClientState -> IO () slave serverState clientState = do slavePort <- getSlavePort slaveSock <- udpServer "127.0.0.1" (fromIntegral slavePort) _ <- forkIO $ loop slaveSock return () where loop slaveSock = do ms <- recvMessages slaveSock mapM_ (\m -> slaveAct (messageAddress m) serverState clientState m) ms loop slaveSock slaveAct :: String -> MVar ServerMode -> MVar ClientState -> Message -> IO () slaveAct "/tempo" serverState clientState m | isJust t = do clients <- readMVar clientState setSlave serverState sendTempo (map wsConn clients) (fromJust t) | otherwise = return () where t = do beat' <- datum_floating $ (messageDatum m) !! 2 cps' <- datum_floating $ (messageDatum m) !! 3 return $ Tempo {at = ut, beat = beat', cps = cps', paused = False, clockLatency = 0 } ut = addUTCTime (realToFrac $ dsec) ut_epoch sec = fromJust $ datum_int32 $ (messageDatum m) !! 0 usec = fromJust $ datum_int32 $ (messageDatum m) !! 1 dsec = ((fromIntegral sec) + ((fromIntegral usec) / 1000000)) :: Double setSlave :: MVar ServerMode -> IO () setSlave serverState = do s <- takeMVar serverState s' <- updateState s putMVar serverState s' return () where updateState Master = do putStrLn "Slaving tempo.." masterPort <- getMasterPort sock <- openUDP "127.0.0.1" (fromIntegral masterPort) return (Slave sock) -- already slaving.. updateState s = return s serverLoop :: TConnection -> MVar Tempo -> MVar ServerMode -> MVar ClientState -> IO () serverLoop conn tempoState serverState clientState = E.handle catchDisconnect $ forever $ do msg <- WS.receiveData $ wsConn conn --liftIO $ updateTempo tempoState $ maybeRead $ T.unpack msg mode <- readMVar serverState serverAct (T.unpack msg) mode tempoState clientState -- --tempo <- liftIO $ readMVar tempoState -- liftIO $ readMVar clientState >>= broadcast (T.pack $ show tempo) where catchDisconnect e = case E.fromException e of Just WS.ConnectionClosed -> liftIO $ modifyMVar_ clientState $ \s -> do let s' = removeClient conn s return s' _ -> return () serverAct :: String -> ServerMode -> MVar Tempo -> MVar ClientState -> IO () serverAct ('c':'p':'s':' ':n) mode tempoState clientState = setCps (read n) mode tempoState clientState serverAct ('n':'u':'d':'g':'e':' ':n) mode tempoState clientState = setNudge (read n) mode tempoState clientState serverAct s _ _ _ = do putStrLn $ "tempo server received unknown message " ++ s return () setCps :: Double -> ServerMode -> MVar Tempo -> MVar ClientState -> IO () setCps n Master tempoState clientState = do tempo <- takeMVar tempoState tempo' <- updateTempo tempo (n :: Double) clients <- readMVar clientState sendTempo (map wsConn clients) (tempo') putMVar tempoState tempo' return () setCps n (Slave sock) tempoState clientState = sendOSC sock $ Message "/cps" [Float (realToFrac n)] setNudge :: Double -> ServerMode -> MVar Tempo -> MVar ClientState -> IO () setNudge n Master tempoState clientState = do tempo <- takeMVar tempoState let tempo' = nudgeTempo tempo n clients <- readMVar clientState sendTempo (map wsConn clients) (tempo') putMVar tempoState tempo' return () setNudge n (Slave sock) tempoState clientState = sendOSC sock $ Message "/nudge" [Float (realToFrac n)] tidal-0.9.5/Sound/Tidal/Parse.hs0000644000000000000000000003314413212576414014555 0ustar0000000000000000{-# LANGUAGE OverloadedStrings, TypeSynonymInstances, FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} module Sound.Tidal.Parse where import Text.ParserCombinators.Parsec import qualified Text.ParserCombinators.Parsec.Token as P import Text.ParserCombinators.Parsec.Language ( haskellDef ) import Data.Ratio import Data.Colour import Data.Colour.Names import Data.Colour.SRGB import GHC.Exts( IsString(..) ) import Data.Monoid import Control.Exception as E import Control.Applicative ((<$>), (<*>), pure) import Data.Maybe import Data.List import Sound.Tidal.Pattern import Sound.Tidal.Time (Arc, Time) -- | AST representation of patterns data TPat a = TPat_Atom a | TPat_Density Time (TPat a) | TPat_Slow Time (TPat a) | TPat_Zoom Arc (TPat a) | TPat_DegradeBy Double (TPat a) | TPat_Silence | TPat_Foot | TPat_Elongate Int | TPat_EnumFromTo (TPat a) (TPat a) | TPat_Cat [TPat a] | TPat_TimeCat [TPat a] | TPat_Overlay (TPat a) (TPat a) | TPat_ShiftL Time (TPat a) -- TPat_E Int Int (TPat a) | TPat_pE (TPat Int) (TPat Int) (TPat Integer) (TPat a) deriving (Show) instance Parseable a => Monoid (TPat a) where mempty = TPat_Silence mappend = TPat_Overlay toPat :: Parseable a => TPat a -> Pattern a toPat = \case TPat_Atom x -> atom x TPat_Density t x -> _density t $ toPat x TPat_Slow t x -> _slow t $ toPat x TPat_Zoom arc x -> zoom arc $ toPat x TPat_DegradeBy amt x -> _degradeBy amt $ toPat x TPat_Silence -> silence TPat_Cat xs -> fastcat $ map toPat xs TPat_TimeCat xs -> timeCat $ map (\(n, p) -> (toRational n, toPat p)) $ durations xs TPat_Overlay x0 x1 -> overlay (toPat x0) (toPat x1) TPat_ShiftL t x -> t `rotL` toPat x TPat_pE n k s thing -> unwrap $ eoff <$> toPat n <*> toPat k <*> toPat s <*> pure (toPat thing) TPat_Foot -> error "Can't happen, feet (.'s) only used internally.." TPat_EnumFromTo a b -> unwrap $ fromTo <$> (toPat a) <*> (toPat b) -- TPat_EnumFromThenTo a b c -> unwrap $ fromThenTo <$> (toPat a) <*> (toPat b) <*> (toPat c) durations :: [TPat a] -> [(Int, TPat a)] durations [] = [] durations ((TPat_Elongate n):xs) = (n, TPat_Silence):(durations xs) durations (a:(TPat_Elongate n):xs) = (n+1,a):(durations xs) durations (a:xs) = (1,a):(durations xs) p :: Parseable a => String -> Pattern a p = toPat . parseTPat class Parseable a where parseTPat :: String -> TPat a fromTo :: a -> a -> Pattern a fromThenTo :: a -> a -> a -> Pattern a instance Parseable Double where parseTPat = parseRhythm pDouble fromTo a b = enumFromTo' a b fromThenTo a b c = enumFromThenTo' a b c instance Parseable String where parseTPat = parseRhythm pVocable fromTo a b = listToPat [a,b] fromThenTo a b c = listToPat [a,b,c] instance Parseable Bool where parseTPat = parseRhythm pBool fromTo a b = listToPat [a,b] fromThenTo a b c = listToPat [a,b,c] instance Parseable Int where parseTPat = parseRhythm pIntegral fromTo a b = enumFromTo' a b fromThenTo a b c = enumFromThenTo' a b c instance Parseable Integer where parseTPat s = parseRhythm pIntegral s fromTo a b = enumFromTo' a b fromThenTo a b c = enumFromThenTo' a b c instance Parseable Rational where parseTPat = parseRhythm pRational fromTo a b = enumFromTo' a b fromThenTo a b c = enumFromThenTo' a b c enumFromTo' a b | a > b = listToPat $ reverse $ enumFromTo b a | otherwise = listToPat $ enumFromTo a b enumFromThenTo' a b c | a > c = listToPat $ reverse $ enumFromThenTo c (c + (a-b)) a | otherwise = listToPat $ enumFromThenTo a b c type ColourD = Colour Double instance Parseable ColourD where parseTPat = parseRhythm pColour fromTo a b = listToPat [a,b] fromThenTo a b c = listToPat [a,b,c] instance (Parseable a) => IsString (Pattern a) where fromString = toPat . parseTPat --instance (Parseable a, Pattern p) => IsString (p a) where -- fromString = p :: String -> p a lexer = P.makeTokenParser haskellDef braces, brackets, parens, angles:: Parser a -> Parser a braces = P.braces lexer brackets = P.brackets lexer parens = P.parens lexer angles = P.angles lexer symbol :: String -> Parser String symbol = P.symbol lexer natural, integer :: Parser Integer natural = P.natural lexer integer = P.integer lexer float :: Parser Double float = P.float lexer naturalOrFloat :: Parser (Either Integer Double) naturalOrFloat = P.naturalOrFloat lexer data Sign = Positive | Negative applySign :: Num a => Sign -> a -> a applySign Positive = id applySign Negative = negate sign :: Parser Sign sign = do char '-' return Negative <|> do char '+' return Positive <|> return Positive intOrFloat :: Parser (Either Integer Double) intOrFloat = do s <- sign num <- naturalOrFloat return (case num of Right x -> Right (applySign s x) Left x -> Left (applySign s x) ) r :: Parseable a => String -> Pattern a -> IO (Pattern a) r s orig = do E.handle (\err -> do putStrLn (show (err :: E.SomeException)) return orig ) (return $ p s) parseRhythm :: Parseable a => Parser (TPat a) -> String -> TPat a parseRhythm f input = either (const TPat_Silence) id $ parse (pSequence f') "" input where f' = f <|> do symbol "~" "rest" return TPat_Silence pSequenceN :: Parseable a => Parser (TPat a) -> GenParser Char () (Int, TPat a) pSequenceN f = do spaces -- d <- pDensity ps <- many $ do a <- pPart f do Text.ParserCombinators.Parsec.try $ symbol ".." b <- pPart f return [TPat_EnumFromTo (TPat_Cat a) (TPat_Cat b)] <|> return a <|> do symbol "." return [TPat_Foot] <|> do es <- many1 (symbol "_") return [TPat_Elongate (length es)] let ps' = TPat_Cat $ map elongate $ splitFeet $ concat ps return (length ps, ps') elongate xs | any (isElongate) xs = TPat_TimeCat xs | otherwise = TPat_Cat xs where isElongate (TPat_Elongate _) = True isElongate _ = False {- expandEnum :: Parseable t => Maybe (TPat t) -> [TPat t] -> [TPat t] expandEnum a [] = [a] expandEnum (Just a) (TPat_Enum:b:ps) = (TPat_EnumFromTo a b) : (expandEnum Nothing ps) -- ignore ..s in other places expandEnum a (TPat_Enum:ps) = expandEnum a ps expandEnum (Just a) (b:ps) = a:(expandEnum b (Just c) ps) expandEnum Nothing (c:ps) = expandEnum (Just c) ps -} -- could use splitOn here but `TPat a` isn't a member of `EQ`.. splitFeet :: [TPat t] -> [[TPat t]] splitFeet [] = [] splitFeet ps = foot:(splitFeet ps') where (foot, ps') = takeFoot ps takeFoot [] = ([], []) takeFoot (TPat_Foot:ps) = ([], ps) takeFoot (p:ps) = (\(a,b) -> (p:a,b)) $ takeFoot ps pSequence :: Parseable a => Parser (TPat a) -> GenParser Char () (TPat a) pSequence f = do (_, p) <- pSequenceN f return p pSingle :: Parseable a => Parser (TPat a) -> Parser (TPat a) pSingle f = f >>= pRand >>= pMult pPart :: Parseable a => Parser (TPat a) -> Parser [TPat a] pPart f = do part <- pSingle f <|> pPolyIn f <|> pPolyOut f part <- pE part part <- pRand part spaces parts <- pStretch part <|> pReplicate part spaces return $ parts pPolyIn :: Parseable a => Parser (TPat a) -> Parser (TPat a) pPolyIn f = do ps <- brackets (pSequence f `sepBy` symbol ",") spaces pMult $ mconcat ps pPolyOut :: Parseable a => Parser (TPat a) -> Parser (TPat a) pPolyOut f = do ps <- braces (pSequenceN f `sepBy` symbol ",") spaces base <- do char '%' spaces i <- integer "integer" return $ Just (fromIntegral i) <|> return Nothing pMult $ mconcat $ scale base ps <|> do ps <- angles (pSequenceN f `sepBy` symbol ",") spaces pMult $ mconcat $ scale (Just 1) ps where scale _ [] = [] scale base (ps@((n,_):_)) = map (\(n',p) -> TPat_Density (fromIntegral (fromMaybe n base)/ fromIntegral n') p) ps pString :: Parser (String) pString = do c <- (letter <|> oneOf "0123456789") "charnum" cs <- many (letter <|> oneOf "0123456789:.-_") "string" return (c:cs) pVocable :: Parser (TPat String) pVocable = do v <- pString return $ TPat_Atom v pDouble :: Parser (TPat Double) pDouble = do nf <- intOrFloat "float" let f = either fromIntegral id nf return $ TPat_Atom f pBool :: Parser (TPat Bool) pBool = do oneOf "t1" return $ TPat_Atom True <|> do oneOf "f0" return $ TPat_Atom False parseIntNote :: Integral i => Parser i parseIntNote = do s <- sign i <- choice [integer, parseNote] return $ applySign s $ fromIntegral i parseInt :: Parser Int parseInt = do s <- sign i <- integer return $ applySign s $ fromIntegral i pIntegral :: Parseable a => Integral a => Parser (TPat a) pIntegral = TPat_Atom <$> parseIntNote parseNote :: Integral a => Parser a parseNote = do n <- notenum modifiers <- many noteModifier octave <- option 5 natural let n' = foldr (+) n modifiers return $ fromIntegral $ n' + ((octave-5)*12) where notenum :: Parser Integer notenum = choice [char 'c' >> return 0, char 'd' >> return 2, char 'e' >> return 4, char 'f' >> return 5, char 'g' >> return 7, char 'a' >> return 9, char 'b' >> return 11 ] noteModifier :: Parser Integer noteModifier = choice [char 's' >> return 1, char 'f' >> return (-1), char 'n' >> return 0 ] fromNote :: Integral c => Pattern String -> Pattern c fromNote p = (\s -> either (const 0) id $ parse parseNote "" s) <$> p pColour :: Parser (TPat ColourD) pColour = do name <- many1 letter "colour name" colour <- readColourName name "known colour" return $ TPat_Atom colour pMult :: Parseable a => TPat a -> Parser (TPat a) pMult thing = do char '*' spaces r <- pRatio return $ TPat_Density r thing <|> do char '/' spaces r <- pRatio return $ TPat_Slow r thing <|> return thing pRand :: Parseable a => TPat a -> Parser (TPat a) pRand thing = do char '?' spaces return $ TPat_DegradeBy 0.5 thing <|> return thing pE :: Parseable a => TPat a -> Parser (TPat a) pE thing = do (n,k,s) <- parens (pair) pure $ TPat_pE n k s thing <|> return thing where pair :: Parser (TPat Int, TPat Int, TPat Integer) pair = do a <- pSequence pIntegral spaces symbol "," spaces b <- pSequence pIntegral c <- do symbol "," spaces pSequence pIntegral <|> return (TPat_Atom 0) return (a, b, c) eoff :: Int -> Int -> Integer -> Pattern a -> Pattern a eoff n k s p = ((s%(fromIntegral k)) `rotL`) (e n k p) -- TPat_ShiftL (s%(fromIntegral k)) (TPat_E n k p) pReplicate :: Parseable a => TPat a -> Parser [TPat a] pReplicate thing = do extras <- many $ do char '!' -- if a number is given (without a space)slow 2 $ fast -- replicate that number of times n <- ((read <$> many1 digit) <|> return 2) spaces thing' <- pRand thing -- -1 because we already have parsed the original one return $ replicate (fromIntegral (n-1)) thing' return (thing:concat extras) pStretch :: Parseable a => TPat a -> Parser [TPat a] pStretch thing = do char '@' n <- ((read <$> many1 digit) <|> return 1) return $ map (\x -> TPat_Zoom (x%n,(x+1)%n) thing) [0 .. (n-1)] pRatio :: Parser (Rational) pRatio = do s <- sign n <- natural result <- do char '%' d <- natural return (n%d) <|> do char '.' s <- many1 digit -- A hack, but not sure if doing this -- numerically would be any faster.. return (toRational $ ((read $ show n ++ "." ++ s) :: Double)) <|> return (n%1) return $ applySign s result pRational :: Parser (TPat Rational) pRational = do r <- pRatio return $ TPat_Atom r {- pDensity :: Parser (Rational) pDensity = angles (pRatio "ratio") <|> return (1 % 1) -} tidal-0.9.5/Sound/Tidal/SuperCollider.hs0000644000000000000000000000166713212576414016264 0ustar0000000000000000{-# LANGUAGE NoMonomorphismRestriction #-} module Sound.Tidal.SuperCollider where import Sound.Tidal.Stream import Sound.Tidal.Pattern import Sound.Tidal.Parse import Sound.OSC.FD import Sound.Tidal.OscStream supercollider :: [Param] -> Double -> Shape supercollider ps l = Shape { params = ps, cpsStamp = False, latency = l } scSlang :: String -> OscSlang scSlang n = OscSlang { -- The OSC path path = "/s_new", preamble = [string n, int32 (-1), int32 1, int32 1], namedParams = True, timestamp = BundleStamp } scBackend :: String -> IO (Backend a) scBackend n = do s <- makeConnection "127.0.0.1" 57110 (scSlang n) return $ Backend s (\_ _ _ -> return ()) scStream :: String -> [Param] -> Double -> IO (ParamPattern -> IO (), Shape) scStream n ps l = do let shape = (supercollider ps l) backend <- scBackend n sc <- stream backend shape return (sc, shape) tidal-0.9.5/Sound/Tidal/Stream.hs0000644000000000000000000002533313212576414014737 0ustar0000000000000000{-# LANGUAGE OverloadedStrings, FlexibleInstances, RankNTypes, NoMonomorphismRestriction, DeriveDataTypeable #-} module Sound.Tidal.Stream where import Data.Maybe import Control.Applicative import Control.Concurrent import Control.Concurrent.MVar import Control.Exception as E import Data.Time (getCurrentTime) import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds) import Data.Ratio import Data.Typeable import Sound.Tidal.Pattern import qualified Sound.Tidal.Parse as P import Sound.Tidal.Tempo (Tempo, logicalTime, clocked,clockedTick,cps) import Sound.Tidal.Utils import qualified Sound.Tidal.Time as T import qualified Data.Map.Strict as Map type ToMessageFunc = Shape -> Tempo -> Int -> (Double, Double, ParamMap) -> Maybe (IO ()) data Backend a = Backend { toMessage :: ToMessageFunc, flush :: Shape -> Tempo -> Int -> IO () } data Param = S {name :: String, sDefault :: Maybe String} | F {name :: String, fDefault :: Maybe Double} | I {name :: String, iDefault :: Maybe Int} deriving Typeable instance Eq Param where a == b = name a == name b instance Ord Param where compare a b = compare (name a) (name b) instance Show Param where show p = name p data Shape = Shape {params :: [Param], latency :: Double, cpsStamp :: Bool} data Value = VS { svalue :: String } | VF { fvalue :: Double } | VI { ivalue :: Int } deriving (Eq,Ord,Typeable) instance Show Value where show (VS s) = s show (VF f) = show f show (VI i) = show i class ParamType a where fromV :: Value -> Maybe a toV :: a -> Value instance ParamType String where fromV (VS s) = Just s fromV _ = Nothing toV s = VS s instance ParamType Double where fromV (VF f) = Just f fromV _ = Nothing toV f = VF f instance ParamType Int where fromV (VI i) = Just i fromV _ = Nothing toV i = VI i type ParamMap = Map.Map Param Value type ParamPattern = Pattern ParamMap ticksPerCycle = 8 defaultValue :: Param -> Value defaultValue (S _ (Just x)) = VS x defaultValue (I _ (Just x)) = VI x defaultValue (F _ (Just x)) = VF x hasDefault :: Param -> Bool hasDefault (S _ Nothing) = False hasDefault (I _ Nothing) = False hasDefault (F _ Nothing) = False hasDefault _ = True defaulted :: Shape -> [Param] defaulted = filter hasDefault . params defaultMap :: Shape -> ParamMap defaultMap s = Map.fromList $ map (\x -> (x, defaultValue x)) (defaulted s) required :: Shape -> [Param] required = filter (not . hasDefault) . params hasRequired :: Shape -> ParamMap -> Bool hasRequired s m = isSubset (required s) (Map.keys m) isSubset :: (Eq a) => [a] -> [a] -> Bool isSubset xs ys = all (\x -> elem x ys) xs doAt t action = do _ <- forkIO $ do now <- getCurrentTime let nowf = realToFrac $ utcTimeToPOSIXSeconds now threadDelay $ floor $ (t - nowf) * 1000000 action return () logicalOnset' change tick o offset = logicalNow + (logicalPeriod * o) + offset where tpc = fromIntegral ticksPerCycle cycleD = ((fromIntegral tick) / tpc) :: Double logicalNow = logicalTime change cycleD logicalPeriod = (logicalTime change (cycleD + (1/tpc))) - logicalNow applyShape' :: Shape -> ParamMap -> Maybe ParamMap applyShape' s m | hasRequired s m = Just $ Map.union m (defaultMap s) | otherwise = Nothing start :: Backend a -> Shape -> IO (MVar (ParamPattern)) start backend shape = do patternM <- newMVar silence let ot = (onTick backend shape patternM) :: Tempo -> Int -> IO () forkIO $ clockedTick ticksPerCycle ot return patternM -- variant of start where history of patterns is available state :: Backend a -> Shape -> IO (MVar (ParamPattern, [ParamPattern])) state backend shape = do patternsM <- newMVar (silence, []) let ot = (onTick' backend shape patternsM) :: Tempo -> Int -> IO () forkIO $ clockedTick ticksPerCycle ot return patternsM stream :: Backend a -> Shape -> IO (ParamPattern -> IO ()) stream backend shape = do patternM <- start backend shape return $ \p -> do swapMVar patternM p return () streamcallback :: (ParamPattern -> IO ()) -> Backend a -> Shape -> IO (ParamPattern -> IO ()) streamcallback callback backend shape = do f <- stream backend shape let f' p = do callback p f p return f' onTick :: Backend a -> Shape -> MVar (ParamPattern) -> Tempo -> Int -> IO () onTick backend shape patternM change ticks = do p <- readMVar patternM let ticks' = (fromIntegral ticks) :: Integer a = ticks' % ticksPerCycle b = (ticks' + 1) % ticksPerCycle messages = mapMaybe (toMessage backend shape change ticks) (seqToRelOnsetDeltas (a, b) p) E.catch (sequence_ messages) (\msg -> putStrLn $ "oops " ++ show (msg :: E.SomeException)) flush backend shape change ticks return () -- Variant where mutable variable contains list as history of the patterns onTick' :: Backend a -> Shape -> MVar (ParamPattern, [ParamPattern]) -> Tempo -> Int -> IO () onTick' backend shape patternsM change ticks = do ps <- readMVar patternsM let ticks' = (fromIntegral ticks) :: Integer toM = (toMessage backend) a = ticks' % ticksPerCycle b = (ticks' + 1) % ticksPerCycle messages = mapMaybe (toM shape change ticks) (seqToRelOnsetDeltas (a, b) $ fst ps) E.catch (sequence_ messages) (\msg -> putStrLn $ "oops " ++ show (msg :: E.SomeException)) flush backend shape change ticks return () make :: (a -> Value) -> Shape -> String -> Pattern a -> ParamPattern make toValue s nm p = fmap (\x -> Map.singleton nParam (defaultV x)) p where nParam = param s nm defaultV a = toValue a --defaultV Nothing = defaultValue nParam make' :: ParamType a => (a -> Value) -> Param -> Pattern a -> ParamPattern make' toValue par p = fmap (\x -> Map.singleton par (toValue x)) p makeP :: ParamType a => Param -> Pattern a -> ParamPattern makeP par p = coerce par $ fmap (\x -> Map.singleton par (toV x)) p makeS = make VS makeF :: Shape -> String -> Pattern Double -> ParamPattern makeF = make VF makeI :: Shape -> String -> Pattern Int -> ParamPattern makeI = make VI param :: Shape -> String -> Param param shape n = head $ filter (\x -> name x == n) (params shape) merge :: ParamPattern -> ParamPattern -> ParamPattern merge x y = (flip Map.union) <$> x <*> y infixl 1 |=| (|=|) :: ParamPattern -> ParamPattern -> ParamPattern (|=|) = merge infixl 1 # (#) = (|=|) mergeWith op x y = (Map.unionWithKey op) <$> x <*> y mergeWith :: (Ord k, Applicative f) => (k -> a -> a -> a) -> f (Map.Map k a) -> f (Map.Map k a) -> f (Map.Map k a) mergeNumWith intOp floatOp = mergeWith f where f (F _ _) (VF a) (VF b) = VF $ floatOp a b f (I _ _) (VI a) (VI b) = VI $ intOp a b f _ _ b = b mergePlus = mergeWith f where f (F _ _) (VF a) (VF b) = VF $ a + b f (I _ _) (VI a) (VI b) = VI $ a + b f (S _ _) (VS a) (VS b) = VS $ a ++ b f _ _ b = b infixl 1 |*| (|*|) :: ParamPattern -> ParamPattern -> ParamPattern (|*|) = mergeNumWith (*) (*) infixl 1 |+| (|+|) :: ParamPattern -> ParamPattern -> ParamPattern (|+|) = mergePlus infixl 1 |-| (|-|) :: ParamPattern -> ParamPattern -> ParamPattern (|-|) = mergeNumWith (-) (-) infixl 1 |/| (|/|) :: ParamPattern -> ParamPattern -> ParamPattern (|/|) = mergeNumWith (div) (/) {- | These are shorthand for merging lists of patterns with @#@, @|*|@, @|+|@, or @|/|@. Sometimes this saves a little typing and can improve readability when passing things into other functions. As an example, instead of writing @ d1 $ sometimes ((|*| speed "2") . (|*| cutoff "2") . (|*| shape "1.5")) $ sound "arpy*4" # cutoff "350" # shape "0.3" @ you can write @ d1 $ sometimes (*** [speed "2", cutoff "2", shape "1.5"]) $ sound "arpy*4" ### [cutoff "350", shape "0.3"] @ -} (###) = foldl (#) (***) = foldl (|*|) (+++) = foldl (|+|) (///) = foldl (|/|) setter :: MVar (a, [a]) -> a -> IO () setter ds p = do ps <- takeMVar ds putMVar ds $ (p, p:snd ps) return () {- | Copies values from one parameter to another. Used by @nToOrbit@ in @Sound.Tidal.Dirt@. -} copyParam:: Param -> Param -> ParamPattern -> ParamPattern copyParam fromParam toParam pat = f <$> pat where f m = maybe m (updateValue m) (Map.lookup fromParam m) updateValue m v = Map.union m (Map.fromList [(toParam,v)]) get :: ParamType a => Param -> ParamPattern -> Pattern a get param p = filterJust $ fromV <$> (filterJust $ Map.lookup param <$> p) getI :: Param -> ParamPattern -> Pattern Int getI = get getF :: Param -> ParamPattern -> Pattern Double getF = get getS :: Param -> ParamPattern -> Pattern String getS = get with :: (ParamType a) => Param -> (Pattern a -> Pattern a) -> ParamPattern -> ParamPattern with param f p = p # (makeP param) ((\x -> f (get param x)) p) withI :: Param -> (Pattern Int -> Pattern Int) -> ParamPattern -> ParamPattern withI = with withF :: Param -> (Pattern Double -> Pattern Double) -> ParamPattern -> ParamPattern withF = with withS :: Param -> (Pattern String -> Pattern String) -> ParamPattern -> ParamPattern withS = with follow :: (ParamType a, ParamType b) => Param -> Param -> (Pattern a -> Pattern b) -> ParamPattern -> ParamPattern follow source dest f p = p # (makeP dest $ f (get source p)) -- follow :: ParamType a => Param -> (Pattern a -> ParamPattern) -> ParamPattern -> ParamPattern -- follow source dest p = p # (dest $ get source p) follow' :: ParamType a => Param -> Param -> (Pattern a -> Pattern a) -> ParamPattern -> ParamPattern follow' source dest f p = p # (makeP dest $ f (get source p)) followI :: Param -> Param -> (Pattern Int -> Pattern Int) -> ParamPattern -> ParamPattern followI = follow' followF :: Param -> Param -> (Pattern Double -> Pattern Double) -> ParamPattern -> ParamPattern followF = follow' followS :: Param -> Param -> (Pattern String -> Pattern String) -> ParamPattern -> ParamPattern followS = follow' -- with :: ParamType a => Param -> (Pattern a -> Pattern a) -> ParamPattern -> ParamPattern -- with source f p = p # (makeP source $ f (get source p)) coerce :: Param -> ParamPattern -> ParamPattern coerce par@(S _ _) p = (Map.update f par) <$> p where f (VS s) = Just (VS s) f (VI i) = Just (VS $ show i) f (VF f) = Just (VS $ show f) coerce par@(I _ _) p = (Map.update f par) <$> p where f (VS s) = Just (VI $ read s) f (VI i) = Just (VI i) f (VF f) = Just (VI $ floor f) coerce par@(F _ _) p = (Map.update f par) <$> p where f (VS s) = Just (VF $ read s) f (VI i) = Just (VF $ fromIntegral i) f (VF f) = Just (VF f) tidal-0.9.5/Sound/Tidal/Transition.hs0000644000000000000000000001137113212576414015633 0ustar0000000000000000module Sound.Tidal.Transition where import Sound.Tidal.Stream import Sound.Tidal.Pattern import Sound.Tidal.Time import Sound.Tidal.Params import Sound.Tidal.Utils import Control.Concurrent.MVar import Control.Applicative import Data.Maybe import qualified Data.Map.Strict as Map import Data.Monoid transition :: (IO Time) -> MVar (ParamPattern, [ParamPattern]) -> (Time -> [ParamPattern] -> ParamPattern) -> ParamPattern -> IO () transition getNow mv f p = do now <- getNow ps <- takeMVar mv let p' = f now (p:snd ps) -- don't put the transition in history, only -- the target pattern, or things get overcomplex -- (transitions of transitions) putMVar mv (p', (p:snd ps)) return () -- | Pans the last n versions of the pattern across the field histpan :: Int -> Time -> [ParamPattern] -> ParamPattern histpan _ _ [] = silence histpan 0 _ _ = silence histpan n _ ps = stack $ map (\(i,p) -> p # pan (atom $ (fromIntegral i) / (fromIntegral n'))) (enumerate ps') where ps' = take n ps n' = length ps' -- in case there's fewer patterns than requested {-| A generalization of `wash`. Washes away the current pattern after a certain delay by applying a function to it over time, then switching over to the next pattern to which another function is applied. -} superwash :: (Pattern a -> Pattern a) -> (Pattern a -> Pattern a) -> Time -> Time -> Time -> Time -> [Pattern a] -> Pattern a superwash _ _ _ _ _ _ [] = silence superwash _ _ _ _ _ _ (p:[]) = p superwash fout fin delay durin durout now (p:p':_) = (playWhen (< (now + delay)) p') <> (playWhen (between (now + delay) (now + delay + durin)) $ fout p') <> (playWhen (between (now + delay + durin) (now + delay + durin + durout)) $ fin p) <> (playWhen (>= (now + delay + durin + durout)) $ p) where between lo hi x = (x >= lo) && (x < hi) {-| Wash away the current pattern by applying a function to it over time, then switching over to the next. @ d1 $ sound "feel ! feel:1 feel:2" t1 (wash (chop 8) 4) $ sound "feel*4 [feel:2 sn:2]" @ Note that `chop 8` is applied to `sound "feel ! feel:1 feel:2"` for 4 cycles and then the whole pattern is replaced by `sound "feel*4 [feel:2 sn:2]` -} wash :: (Pattern a -> Pattern a) -> Time -> Time -> [Pattern a] -> Pattern a wash _ _ _ [] = silence wash _ _ _ (p:[]) = p wash f t now (p:p':_) = overlay (playWhen (< (now + t)) $ f p') (playWhen (>= (now + t)) p) -- | Just stop for a bit before playing new pattern wait :: Time -> Time -> [ParamPattern] -> ParamPattern wait _ _ [] = silence wait t now (p:_) = playWhen (>= (nextSam (now+t-1))) p {- | Just as `wait`, `wait'` stops for a bit and then applies the given transition to the playing pattern @ d1 $ sound "bd" t1 (wait' (xfadeIn 8) 4) $ sound "hh*8" @ -} wait' :: (Time -> [ParamPattern] -> ParamPattern) -> Time -> Time -> [ParamPattern] -> ParamPattern wait' _ t _ [] = silence wait' f t now ps@(p:_) = playWhen (>= (nextSam (now+t-1))) (f (now + t) ps) {- | Jumps directly into the given pattern, this is essentially the _no transition_-transition. Variants of `jump` provide more useful capabilities, see `jumpIn` and `jumpMod` -} jump :: Time -> [ParamPattern] -> ParamPattern jump = jumpIn 0 {- | Sharp `jump` transition after the specified number of cycles have passed. @ t1 (jumpIn 2) $ sound "kick(3,8)" @ -} jumpIn :: Int -> Time -> [ParamPattern] -> ParamPattern jumpIn n = superwash id id (fromIntegral n) 0 0 {- | Unlike `jumpIn` the variant `jumpIn'` will only transition at cycle boundary (e.g. when the cycle count is an integer). -} jumpIn' :: Int -> Time -> [ParamPattern] -> ParamPattern jumpIn' n now = superwash id id ((nextSam now) - now + (fromIntegral n)) 0 0 now -- | Sharp `jump` transition at next cycle boundary where cycle mod n == 0 jumpMod :: Int -> Time -> [ParamPattern] -> ParamPattern jumpMod n now = jumpIn ((n-1) - ((floor now) `mod` n)) now -- | Degrade the new pattern over time until it ends in silence mortal :: Time -> Time -> Time -> [ParamPattern] -> ParamPattern mortal _ _ _ [] = silence mortal lifespan release now (p:_) = overlay (playWhen (<(now+lifespan)) p) (playWhen (>= (now+lifespan)) (fadeOut' (now + lifespan) release p)) combineV :: (Value -> Value -> Value) -> ParamMap -> ParamMap -> ParamMap combineV f a b = Map.mapWithKey pairUp a where pairUp k v | Map.notMember k b = v | otherwise = f v (fromJust $ Map.lookup k b) mixNums v (VF a) (VF b) = VF $ (a * v) + (b * (1-v)) mixNums v (VI a) (VI b) = VI $ floor $ (fromIntegral a * v) + (fromIntegral b * (1-v)) mixNums v _ b = b interpolateIn :: Time -> Time -> [ParamPattern] -> ParamPattern interpolateIn _ _ [] = silence interpolateIn _ _ (p:[]) = p interpolateIn t now (p:p':_) = do n <- now `rotR` (_slow t envL) combineV (mixNums n) <$> p <*> p' tidal-0.9.5/Sound/Tidal/Time.hs0000644000000000000000000000766013212576414014405 0ustar0000000000000000{-| Module: Time Description: Defines core data types and functions for handling tidal's concept of time in `Arc`s & `Event`s -} module Sound.Tidal.Time where import Sound.Tidal.Utils import Data.Ratio -- | Time is represented by a rational number. Each natural number -- represents both the start of the next rhythmic cycle, and the end -- of the previous one. Rational numbers are used so that subdivisions -- of each cycle can be accurately represented. type Time = Rational -- | @(s,e) :: Arc@ represents a time interval with a start and end value. -- @ { t : s <= t && t < e } @ type Arc = (Time, Time) -- | An Event is a value that occurs during the period given by the -- first @Arc@. The second one indicates the event's "domain of -- influence". These will often be the same, but many temporal -- transformations, such as rotation and scaling time, may result in -- arcs being split or truncated. In such cases, the first arc is -- preserved, but the second arc reflects the portion of the event -- which is relevant. type Event a = (Arc, Arc, a) -- | The starting point of the current cycle. A cycle occurs from each -- natural number to the next, so this is equivalent to @floor@. sam :: Time -> Time sam = fromIntegral . floor -- | The end point of the current cycle (and starting point of the next cycle) nextSam :: Time -> Time nextSam = (1+) . sam -- | The position of a time value relative to the start of its cycle. cyclePos :: Time -> Time cyclePos t = t - sam t -- | @isIn a t@ is @True@ if @t@ is inside -- the arc represented by @a@. isIn :: Arc -> Time -> Bool isIn (s,e) t = t >= s && t < e -- | Splits the given @Arc@ into a list of @Arc@s, at cycle boundaries. arcCycles :: Arc -> [Arc] arcCycles (s,e) | s >= e = [] | sam s == sam e = [(s,e)] | otherwise = (s, nextSam s) : (arcCycles (nextSam s, e)) -- | Splits the given @Arc@ into a list of @Arc@s, at cycle boundaries, but wrapping the arcs within the same cycle. arcCycles' :: Arc -> [Arc] arcCycles' (s,e) | s >= e = [] | sam s == sam e = [(s,e)] | otherwise = (s, nextSam s) : (arcCycles' ((nextSam s) - 1, e - 1)) -- | @subArc i j@ is the arc that is the intersection of @i@ and @j@. subArc :: Arc -> Arc -> Maybe Arc subArc (s, e) (s',e') | s'' < e'' = Just (s'', e'') | otherwise = Nothing where s'' = max s s' e'' = min e e' -- | Map the given function over both the start and end @Time@ values -- of the given @Arc@. mapArc :: (Time -> Time) -> Arc -> Arc mapArc f (s,e) = (f s, f e) -- | Similar to @mapArc@ but time is relative to the cycle (i.e. the -- sam of the start of the arc) mapCycle :: (Time -> Time) -> Arc -> Arc mapCycle f (s,e) = (sam' + (f $ s - sam'), sam' + (f $ e - sam')) where sam' = sam s -- | Returns the `mirror image' of an @Arc@ around the given point intime, used by @Sound.Tidal.Pattern.rev@. mirrorArc :: Time -> Arc -> Arc mirrorArc mid (s, e) = (mid - (e-mid), mid+(mid-s)) -- | The start time of the given @Event@ eventStart :: Event a -> Time eventStart = fst . snd' -- | The original onset of the given @Event@ eventOnset :: Event a -> Time eventOnset = fst . fst' -- | The original offset of the given @Event@ eventOffset :: Event a -> Time eventOffset = snd . fst' -- | The arc of the given @Event@ eventArc :: Event a -> Arc eventArc = snd' -- | The midpoint of an @Arc@ midPoint :: Arc -> Time midPoint (s,e) = s + ((e - s) / 2) -- | `True` if an `Event`'s first and second `Arc`'s start times match hasOnset :: Event a -> Bool hasOnset ((s,_), (s',_), _) = s == s' -- | `True` if an `Event`'s first and second `Arc`'s end times match hasOffset :: Event a -> Bool hasOffset ((_,e), (_,e'), _) = e == e' -- | `True` if an `Event`'s starts is within given `Arc` onsetIn :: Arc -> Event a -> Bool onsetIn a e = isIn a (eventOnset e) -- | `True` if an `Event`'s ends is within given `Arc` offsetIn :: Arc -> Event a -> Bool offsetIn a e = isIn a (eventOffset e) tidal-0.9.5/Sound/Tidal/Params.hs0000644000000000000000000003710413212576414014726 0ustar0000000000000000module Sound.Tidal.Params where import Sound.Tidal.Stream import Sound.Tidal.Pattern import qualified Data.Map as Map import Sound.Tidal.Utils import Control.Applicative -- | group multiple params into one grp :: [Param] -> Pattern String -> ParamPattern grp [] _ = silence grp params p = (fmap lookupPattern p) where lookupPattern :: String -> ParamMap lookupPattern s = Map.fromList $ map (\(param,s') -> toPV param s') $ zip params $ (split s) split s = wordsBy (==':') s toPV :: Param -> String -> (Param, Value) toPV param@(S _ _) s = (param, (VS s)) toPV param@(F _ _) s = (param, (VF $ read s)) toPV param@(I _ _) s = (param, (VI $ read s)) {- | A pattern of strings representing sounds or synth notes. Internally, `sound` or its shorter alias `s` is a combination of the samplebank name and number when used with samples, or synth name and note number when used with a synthesiser. For example `bd:2` specifies the third sample (not the second as you might expect, because we start counting at zero) in the `bd` sample folder. *Internally, `sound`/`s` is a combination of two parameters, the hidden parameter `s'` which specifies the samplebank or synth, and the `n` parameter which specifies the sample or note number. For example: @ d1 $ sound "bd:2 sn:0" @ is essentially the same as: @ d1 $ s' "bd sn" # n "2 0" @ `n` is therefore useful when you want to pattern the sample or note number separately from the samplebank or synth. For example: @ d1 $ n "0 5 ~ 2" # sound "drum" @ is equivalent to: @ d1 $ sound "drum:0 drum:5 ~ drum:2" @ -} sound :: Pattern String -> ParamPattern sound = grp [s_p, n_p] s = sound pF name defaultV = (make' VF param, param) where param = F name defaultV pI name defaultV = (make' VI param, param) where param = I name defaultV pS name defaultV = (make' VS param, param) where param = S name defaultV -- | a pattern of numbers that speed up (or slow down) samples while they play. (accelerate, accelerate_p) = pF "accelerate" (Just 0) -- | a pattern of numbers to specify the attack time (in seconds) of an envelope applied to each sample. Only takes effect if `release` is also specified. (attack, attack_p) = pF "attack" (Just (-1)) -- | a pattern of numbers from 0 to 1. Sets the center frequency of the band-pass filter. (bandf, bandf_p) = pF "bandf" (Just 0) -- | a pattern of numbers from 0 to 1. Sets the q-factor of the band-pass filter. (bandq, bandq_p) = pF "bandq" (Just 0) {- | a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. Using `begin "-1"` combined with `cut "-1"` means that when the sample cuts itself it will begin playback from where the previous one left off, so it will sound like one seamless sample. This allows you to apply a synth param across a long sample in a way similar to `chop`: @ cps 0.5 d1 $ sound "breaks125*8" # unit "c" # begin "-1" # cut "-1" # coarse "1 2 4 8 16 32 64 128" @ This will play the `breaks125` sample and apply the changing `coarse` parameter over the sample. Compare to: @ d1 $ (chop 8 $ sounds "breaks125") # unit "c" # coarse "1 2 4 8 16 32 64 128" @ which performs a similar effect, but due to differences in implementation sounds different. -} begin_p, channel_p, legato_p, clhatdecay_p, coarse_p, crush_p :: Param begin, legato, clhatdecay, crush :: Pattern Double -> ParamPattern channel, coarse :: Pattern Int -> ParamPattern (begin, begin_p) = pF "begin" (Just 0) -- | choose the physical channel the pattern is sent to, this is super dirt specific (channel, channel_p) = pI "channel" Nothing --legato controls the amount of overlap between two adjacent synth sounds (legato, legato_p) = pF "legato" (Just 1) (clhatdecay, clhatdecay_p) = pF "clhatdecay" (Just 0) -- | fake-resampling, a pattern of numbers for lowering the sample rate, i.e. 1 for original 2 for half, 3 for a third and so on. (coarse, coarse_p) = pI "coarse" (Just 0) -- | bit crushing, a pattern of numbers from 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction). (crush, crush_p) = pF "crush" (Just 0) {- | In the style of classic drum-machines, `cut` will stop a playing sample as soon as another samples with in same cutgroup is to be played. An example would be an open hi-hat followed by a closed one, essentially muting the open. @ d1 $ stack [ sound "bd", sound "~ [~ [ho:2 hc/2]]" # cut "1" ] @ This will mute the open hi-hat every second cycle when the closed one is played. Using `cut` with negative values will only cut the same sample. This is useful to cut very long samples @ d1 $ sound "[bev, [ho:3](3,8)]" # cut "-1" @ Using `cut "0"` is effectively _no_ cutgroup. -} (cut, cut_p) = pI "cut" (Just 0) -- | a pattern of numbers from 0 to 1. Applies the cutoff frequency of the low-pass filter. (cutoff, cutoff_p) = pF "cutoff" (Just 0) (cutoffegint, cutoffegint_p) = pF "cutoffegint" (Just 0) (decay, decay_p) = pF "decay" (Just 0) -- | a pattern of numbers from 0 to 1. Sets the level of the delay signal. (delay, delay_p) = pF "delay" (Just 0) -- | a pattern of numbers from 0 to 1. Sets the amount of delay feedback. (delayfeedback, delayfeedback_p) = pF "delayfeedback" (Just (-1)) -- | a pattern of numbers from 0 to 1. Sets the length of the delay. (delaytime, delaytime_p) = pF "delaytime" (Just (-1)) (detune, detune_p) = pF "detune" (Just 0) -- | when set to `1` will disable all reverb for this pattern. See `room` and `size` for more information about reverb. (dry, dry_p) = pF "dry" (Just 0) {- the same as `begin`, but cuts the end off samples, shortening them; e.g. `0.75` to cut off the last quarter of each sample. -} (end, end_p) = pF "end" (Just 1) -- | a pattern of numbers that specify volume. Values less than 1 make the sound quieter. Values greater than 1 make the sound louder. (gain, gain_p) = pF "gain" (Just 1) (gate, gate_p) = pF "gate" (Just 0) (hatgrain, hatgrain_p) = pF "hatgrain" (Just 0) -- | a pattern of numbers from 0 to 1. Applies the cutoff frequency of the high-pass filter. (hcutoff, hcutoff_p) = pF "hcutoff" (Just 0) -- | a pattern of numbers to specify the hold time (in seconds) of an envelope applied to each sample. Only takes effect if `attack` and `release` are also specified. (hold, hold_p) = pF "hold" (Just 0) -- | a pattern of numbers from 0 to 1. Applies the resonance of the high-pass filter. (hresonance, hresonance_p) = pF "hresonance" (Just 0) (kriole, kriole_p) = pI "kriole" (Just 0) (lagogo, lagogo_p) = pF "lagogo" (Just 0) (lclap, lclap_p) = pF "lclap" (Just 0) (lclaves, lclaves_p) = pF "lclaves" (Just 0) (lclhat, lclhat_p) = pF "lclhat" (Just 0) (lcrash, lcrash_p) = pF "lcrash" (Just 0) (lfo, lfo_p) = pF "lfo" (Just 0) (lfocutoffint, lfocutoffint_p) = pF "lfocutoffint" (Just 0) (lfodelay, lfodelay_p) = pF "lfodelay" (Just 0) (lfoint, lfoint_p) = pF "lfoint" (Just 0) (lfopitchint, lfopitchint_p) = pF "lfopitchint" (Just 0) (lfoshape, lfoshape_p) = pF "lfoshape" (Just 0) (lfosync, lfosync_p) = pF "lfosync" (Just 0) (lhitom, lhitom_p) = pF "lhitom" (Just 0) (lkick, lkick_p) = pF "lkick" (Just 0) (llotom, llotom_p) = pF "llotom" (Just 0) {- | A pattern of numbers. Specifies whether delaytime is calculated relative to cps. When set to 1, delaytime is a direct multiple of a cycle. -} (lock, lock_p) = pF "lock" (Just 0) -- | loops the sample (from `begin` to `end`) the specified number of times. (loop, loop_p) = pF "loop" (Just 1) (lophat, lophat_p) = pF "lophat" (Just 0) (lsnare, lsnare_p) = pF "lsnare" (Just 0) -- | specifies the sample variation to be used (n, n_p) = pI "n" (Just 0) {- | Pushes things forward (or backwards within built-in latency) in time. Allows for nice things like _swing_ feeling: @ d1 $ stack [ sound "bd bd/4", sound "hh(5,8)" ] # nudge "[0 0.04]*4" @ --pitch model -} degree, mtranspose, ctranspose, harmonic, stepsPerOctave, octaveRatio :: Pattern Double -> ParamPattern degree_p, mtranspose_p, ctranspose_p, harmonic_p, stepsPerOctave_p, octaveRatio_p :: Param (degree, degree_p) = pF "degree" Nothing (mtranspose, mtranspose_p) = pF "mtranspose" Nothing (ctranspose, ctranspose_p) = pF "ctranspose" Nothing (harmonic, harmonic_p) = pF "ctranspose" Nothing (stepsPerOctave, stepsPerOctave_p) = pF "stepsPerOctave" Nothing (octaveRatio, octaveRatio_p) = pF "octaveRatio" Nothing --Low values will give a more _human_ feeling, high values might result in quite the contrary. (nudge, nudge_p) = pF "nudge" (Just 0) (octave, octave_p) = pI "octave" (Just 3) (offset, offset_p) = pF "offset" (Just 0) (ophatdecay, ophatdecay_p) = pF "ophatdecay" (Just 0) {- | a pattern of numbers. An `orbit` is a global parameter context for patterns. Patterns with the same orbit will share hardware output bus offset and global effects, e.g. reverb and delay. The maximum number of orbits is specified in the superdirt startup, numbers higher than maximum will wrap around. -} (orbit, orbit_p) = pI "orbit" (Just 0) -- | a pattern of numbers between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel) (pan, pan_p) = pF "pan" (Just 0.5) -- | a pattern of numbers between -inf and inf, which controls how much multichannel output is fanned out (negative is backwards ordering) (panspan, panspan_p) = pF "span" (Just 1.0) -- | a pattern of numbers between 0.0 and 1.0, which controls the multichannel spread range (multichannel only) (pansplay, pansplay_p) = pF "splay" (Just 1.0) -- | a pattern of numbers between 0.0 and inf, which controls how much each channel is distributed over neighbours (multichannel only) (panwidth, panwidth_p) = pF "panwidth" (Just 2.0) -- | a pattern of numbers between -1.0 and 1.0, which controls the relative position of the centre pan in a pair of adjacent speakers (multichannel only) (panorient, panorient_p) = pF "orientation" (Just 0.5) (pitch1, pitch1_p) = pF "pitch1" (Just 0) (pitch2, pitch2_p) = pF "pitch2" (Just 0) (pitch3, pitch3_p) = pF "pitch3" (Just 0) (portamento, portamento_p) = pF "portamento" (Just 0) -- | a pattern of numbers to specify the release time (in seconds) of an envelope applied to each sample. Only takes effect if `attack` is also specified. (release, release_p) = pF "release" (Just (-1)) -- | a pattern of numbers from 0 to 1. Specifies the resonance of the low-pass filter. (resonance, resonance_p) = pF "resonance" (Just 0) -- | a pattern of numbers from 0 to 1. Sets the level of reverb. (room, room_p) = pF "room" Nothing (sagogo, sagogo_p) = pF "sagogo" (Just 0) (sclap, sclap_p) = pF "sclap" (Just 0) (sclaves, sclaves_p) = pF "sclaves" (Just 0) (scrash, scrash_p) = pF "scrash" (Just 0) (semitone, semitone_p) = pF "semitone" (Just 0) -- | wave shaping distortion, a pattern of numbers from 0 for no distortion up to 1 for loads of distortion. (shape, shape_p) = pF "shape" (Just 0) -- | a pattern of numbers from 0 to 1. Sets the perceptual size (reverb time) of the `room` to be used in reverb. (size, size_p) = pF "size" Nothing (slide, slide_p) = pF "slide" (Just 0) -- | a pattern of numbers which changes the speed of sample playback, i.e. a cheap way of changing pitch. Negative values will play the sample backwards! (speed, speed_p) = pF "speed" (Just 1) -- | a pattern of strings. Selects the sample to be played. (s', s_p) = pS "s" Nothing (stutterdepth, stutterdepth_p) = pF "stutterdepth" (Just 0) (stuttertime, stuttertime_p) = pF "stuttertime" (Just 0) (sustain, sustain_p) = pF "sustain" (Just 0) (tomdecay, tomdecay_p) = pF "tomdecay" (Just 0) {- | used in conjunction with `speed`, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`. -} (unit, unit_p) = pS "unit" (Just "rate") (velocity, velocity_p) = pF "velocity" (Just 0.5) (vcfegint, vcfegint_p) = pF "vcfegint" (Just 0) (vcoegint, vcoegint_p) = pF "vcoegint" (Just 0) (voice, voice_p) = pF "voice" (Just 0) -- | formant filter to make things sound like vowels, a pattern of either `a`, `e`, `i`, `o` or `u`. Use a rest (`~`) for no effect. (vowel, vowel_p) = pS "vowel" (Just "") -- MIDI-specific params (dur,dur_p) = pF "dur" (Just 0.05) (modwheel,modwheel_p) = pF "modwheel" (Just 0) (expression,expression_p) = pF "expression" (Just 1) (sustainpedal,sustainpedal_p) = pF "sustainpedal" (Just 0) -- Tremolo Audio DSP effect | params are "tremolorate" and "tremolodepth" tremolorate, tremolodepth :: Pattern Double -> ParamPattern tremolorate_p, tremolodepth_p :: Param (tremolorate,tremolorate_p) = pF "tremolorate" (Just 1) (tremolodepth,tremolodepth_p) = pF "tremolodepth" (Just 0.5) -- Phaser Audio DSP effect | params are "phaserrate" and "phaserdepth" phaserrate, phaserdepth :: Pattern Double -> ParamPattern phaserrate_p, phaserdepth_p :: Param (phaserrate,phaserrate_p) = pF "phaserrate" (Just 1) (phaserdepth,phaserdepth_p) = pF "phaserdepth" (Just 0.5) -- aliases att, chdecay, ctf, ctfg, delayfb, delayt, lbd, lch, lcl, lcp, lcr, lfoc, lfoi , lfop, lht, llt, loh, lsn, ohdecay, phasdp, phasr, pit1, pit2, pit3, por, sag, scl, scp , scr, sld, std, stt, sus, tdecay, tremdp, tremr, vcf, vco, voi :: Pattern Double -> ParamPattern att = attack bpf = bandf bpf_p = bandf_p bpq = bandq bpq_p = bandq_p chdecay = clhatdecay ctf = cutoff ctfg = cutoffegint delayfb = delayfeedback delayt = delaytime det = detune gat = gate hg = hatgrain hpf = hcutoff hpf_p = hcutoff_p hpq = hresonance hpq_p = hresonance_p lag = lagogo lbd = lkick lch = lclhat lcl = lclaves lcp = lclap lcr = lcrash lfoc = lfocutoffint lfoi = lfoint lfop = lfopitchint lht = lhitom llt = llotom loh = lophat lpf = cutoff lpf_p = cutoff_p lpq = resonance lpq_p = resonance_p lsn = lsnare ohdecay = ophatdecay phasdp = phaserdepth phasr = phaserrate pit1 = pitch1 pit2 = pitch2 pit3 = pitch3 por = portamento rel = release sag = sagogo scl = sclaves scp = sclap scr = scrash sz = size sld = slide std = stutterdepth stt = stuttertime sus = sustain tdecay = tomdecay tremdp = tremolodepth tremr = tremolorate vcf = vcfegint vco = vcoegint voi = voice note, midinote :: Pattern Int -> ParamPattern note = n midinote = n . ((subtract 60) <$>) drum :: Pattern String -> ParamPattern drum = midinote . (drumN <$>) drumN :: String -> Int drumN "bd" = 36 drumN "sn" = 38 drumN "lt" = 43 drumN "ht" = 50 drumN "ch" = 42 drumN "oh" = 46 drumN "cp" = 39 drumN "cl" = 75 drumN "ag" = 67 drumN "cr" = 49 drumN _ = 0 tidal-0.9.5/Sound/Tidal/Dirt.hs0000644000000000000000000003303613212576414014405 0ustar0000000000000000{-# LANGUAGE NoMonomorphismRestriction #-} module Sound.Tidal.Dirt where import Sound.OSC.FD (Datum) import qualified Data.Map as Map import Control.Applicative import Control.Concurrent.MVar --import Visual import Data.Colour.SRGB import Data.Colour.Names import Data.Hashable import Data.Bits import Data.Maybe import Data.Fixed import Data.Ratio import Data.List (elemIndex, sort) import Sound.Tidal.Stream import Sound.Tidal.OscStream import Sound.Tidal.Pattern import Sound.Tidal.Parse import Sound.Tidal.Params import Sound.Tidal.Time import Sound.Tidal.Tempo import Sound.Tidal.Transition (transition, wash) import Sound.Tidal.Utils (enumerate, fst') dirt :: Shape dirt = Shape { params = [ s_p, offset_p, begin_p, end_p, speed_p, pan_p, velocity_p, vowel_p, cutoff_p, resonance_p, accelerate_p, shape_p, kriole_p, gain_p, cut_p, delay_p, delaytime_p, delayfeedback_p, crush_p, coarse_p, hcutoff_p, hresonance_p, bandf_p, bandq_p, unit_p, loop_p, n_p, attack_p, hold_p, release_p ], cpsStamp = True, latency = 0.3 } dirtSlang = OscSlang { path = "/play", timestamp = MessageStamp, namedParams = False, preamble = [] } superDirtSlang = dirtSlang { timestamp = BundleStamp, path = "/play2", namedParams = True } superDirtBackend port = do s <- makeConnection "127.0.0.1" port superDirtSlang return $ Backend s (\_ _ _ -> return ()) superDirtState port = do backend <- superDirtBackend port Sound.Tidal.Stream.state backend dirt dirtBackend = do s <- makeConnection "127.0.0.1" 7771 dirtSlang return $ Backend s (\_ _ _ -> return ()) -- dirtstart name = start "127.0.0.1" 7771 dirt dirtStream = do backend <- dirtBackend stream backend dirt dirtState = do backend <- dirtBackend Sound.Tidal.Stream.state backend dirt dirtSetters :: IO Time -> IO (ParamPattern -> IO (), (Time -> [ParamPattern] -> ParamPattern) -> ParamPattern -> IO ()) dirtSetters getNow = do ds <- dirtState return (setter ds, transition getNow ds) superDirtSetters :: IO Time -> IO (ParamPattern -> IO (), (Time -> [ParamPattern] -> ParamPattern) -> ParamPattern -> IO ()) superDirtSetters getNow = do ds <- superDirtState 57120 return (setter ds, transition getNow ds) superDirts :: [Int] -> IO [(ParamPattern -> IO (), (Time -> [ParamPattern] -> ParamPattern) -> ParamPattern -> IO ())] superDirts ports = do (_, getNow) <- cpsUtils states <- mapM (superDirtState) ports return $ map (\state -> (setter state, transition getNow state)) states -- -- disused parameter.. dirtstream _ = dirtStream -- doubledirt = do remote <- stream "178.77.72.138" 7777 dirt -- local <- stream "192.168.0.102" 7771 dirt -- return $ \p -> do remote p -- local p -- return () dirtToColour :: ParamPattern -> Pattern ColourD --dirtToColour p = s -- where s = fmap (\x -> maybe black (datumToColour) (Map.lookup (param dirt "s") x)) p dirtToColour = fmap (stringToColour . show) showToColour :: Show a => a -> ColourD showToColour = stringToColour . show datumToColour :: Value -> ColourD datumToColour = showToColour stringToColour :: String -> ColourD stringToColour s = sRGB (r/256) (g/256) (b/256) where i = (hash s) `mod` 16777216 r = fromIntegral $ (i .&. 0xFF0000) `shiftR` 16; g = fromIntegral $ (i .&. 0x00FF00) `shiftR` 8; b = fromIntegral $ (i .&. 0x0000FF); {- visualcallback :: IO (ParamPattern -> IO ()) visualcallback = do t <- ticker mv <- startVis t let f p = do let p' = dirtToColour p swapMVar mv p' return () return f -} --dirtyvisualstream name = do cb <- visualcallback -- streamcallback cb "127.0.0.1" "127.0.0.1" name "127.0.0.1" 7771 dirt pick :: String -> Int -> String pick name n = name ++ ":" ++ (show n) {- | Striate is a kind of granulator, for example: @ d1 $ striate 3 $ sound "ho ho:2 ho:3 hc" @ This plays the loop the given number of times, but triggering progressive portions of each sample. So in this case it plays the loop three times, the first time playing the first third of each sample, then the second time playing the second third of each sample, etc.. With the highhat samples in the above example it sounds a bit like reverb, but it isn't really. You can also use striate with very long samples, to cut it into short chunks and pattern those chunks. This is where things get towards granular synthesis. The following cuts a sample into 128 parts, plays it over 8 cycles and manipulates those parts by reversing and rotating the loops. @ d1 $ slow 8 $ striate 128 $ sound "bev" @ -} striate :: Pattern Int -> ParamPattern -> ParamPattern striate = temporalParam _striate _striate :: Int -> ParamPattern -> ParamPattern _striate n p = fastcat $ map (\x -> off (fromIntegral x) p) [0 .. n-1] where off i p = p # begin (atom (fromIntegral i / fromIntegral n)) # end (atom (fromIntegral (i+1) / fromIntegral n)) {-| The `striate'` function is a variant of `striate` with an extra parameter, which specifies the length of each part. The `striate'` function still scans across the sample over a single cycle, but if each bit is longer, it creates a sort of stuttering effect. For example the following will cut the bev sample into 32 parts, but each will be 1/16th of a sample long: @ d1 $ slow 32 $ striate' 32 (1/16) $ sound "bev" @ Note that `striate` uses the `begin` and `end` parameters internally. This means that if you're using `striate` (or `striate'`) you probably shouldn't also specify `begin` or `end`. -} striate' :: Pattern Int -> Pattern Double -> ParamPattern -> ParamPattern striate' = temporalParam2 _striate' _striate' :: Int -> Double -> ParamPattern -> ParamPattern _striate' n f p = fastcat $ map (\x -> off (fromIntegral x) p) [0 .. n-1] where off i p = p # begin (atom (slot * i) :: Pattern Double) # end (atom ((slot * i) + f) :: Pattern Double) slot = (1 - f) / (fromIntegral n) {- | like `striate`, but with an offset to the begin and end values -} striateO :: Pattern Int -> Pattern Double -> ParamPattern -> ParamPattern striateO = temporalParam2 _striateO _striateO :: Int -> Double -> ParamPattern -> ParamPattern _striateO n o p = _striate n p |+| begin (atom o :: Pattern Double) |+| end (atom o :: Pattern Double) {- | Just like `striate`, but also loops each sample chunk a number of times specified in the second argument. The primed version is just like `striate'`, where the loop count is the third argument. For example: @ d1 $ striateL' 3 0.125 4 $ sound "feel sn:2" @ Like `striate`, these use the `begin` and `end` parameters internally, as well as the `loop` parameter for these versions. -} striateL :: Pattern Int -> Pattern Int -> ParamPattern -> ParamPattern striateL = temporalParam2 _striateL striateL' :: Pattern Int -> Pattern Double -> Pattern Int -> ParamPattern -> ParamPattern striateL' = temporalParam3 _striateL' _striateL :: Int -> Int -> ParamPattern -> ParamPattern _striateL n l p = _striate n p # loop (atom $ fromIntegral l) _striateL' n f l p = _striate' n f p # loop (atom $ fromIntegral l) metronome = _slow 2 $ sound (p "[odx, [hh]*8]") {-| Also degrades the current pattern and undegrades the next. To change the number of cycles the transition takes, you can use @clutchIn@ like so: @ d1 $ sound "bd(5,8)" t1 (clutchIn 8) $ sound "[hh*4, odx(3,8)]" @ will take 8 cycles for the transition. -} clutchIn :: Time -> Time -> [Pattern a] -> Pattern a clutchIn _ _ [] = silence clutchIn _ _ (p:[]) = p clutchIn t now (p:p':_) = overlay (fadeOut' now t p') (fadeIn' now t p) {-| Degrades the current pattern while undegrading the next. This is like @xfade@ but not by gain of samples but by randomly removing events from the current pattern and slowly adding back in missing events from the next one. @ d1 $ sound "bd(3,8)" t1 clutch $ sound "[hh*4, odx(3,8)]" @ @clutch@ takes two cycles for the transition, essentially this is @clutchIn 2@. -} clutch :: Time -> [Pattern a] -> Pattern a clutch = clutchIn 2 {- | crossfades between old and new pattern over given number of cycles, e.g.: @ d1 $ sound "bd sn" t1 (xfadeIn 16) $ sound "jvbass*3" @ Will fade over 16 cycles from "bd sn" to "jvbass*3" -} xfadeIn :: Time -> Time -> [ParamPattern] -> ParamPattern xfadeIn _ _ [] = silence xfadeIn _ _ (p:[]) = p xfadeIn t now (p:p':_) = overlay (p |*| gain (now `rotR` (_slow t envEqR))) (p' |*| gain (now `rotR` (_slow t (envEq)))) {- | Crossfade between old and new pattern over the next two cycles. @ d1 $ sound "bd sn" t1 xfade $ sound "can*3" @ `xfade` is built with `xfadeIn` in this case taking two cycles for the fade. -} xfade :: Time -> [ParamPattern] -> ParamPattern xfade = xfadeIn 2 {- | Stut applies a type of delay to a pattern. It has three parameters, which could be called depth, feedback and time. Depth is an integer and the others floating point. This adds a bit of echo: @ d1 $ stut 4 0.5 0.2 $ sound "bd sn" @ The above results in 4 echos, each one 50% quieter than the last, with 1/5th of a cycle between them. It is possible to reverse the echo: @ d1 $ stut 4 0.5 (-0.2) $ sound "bd sn" @ -} stut :: Pattern Integer -> Pattern Double -> Pattern Rational -> ParamPattern -> ParamPattern stut = temporalParam3 _stut _stut :: Integer -> Double -> Rational -> ParamPattern -> ParamPattern _stut steps feedback time p = stack (p:(map (\x -> (((x%steps)*time) `rotR` (p |*| gain (pure $ scale (fromIntegral x))))) [1..(steps-1)])) where scale x = ((+feedback) . (*(1-feedback)) . (/(fromIntegral steps)) . ((fromIntegral steps)-)) x {- | Instead of just decreasing volume to produce echoes, @stut'@ allows to apply a function for each step and overlays the result delayed by the given time. @ d1 $ stut' 2 (1%3) (# vowel "{a e i o u}%2") $ sound "bd sn" @ In this case there are two _overlays_ delayed by 1/3 of a cycle, where each has the @vowel@ filter applied. -} stut' :: Pattern Int -> Pattern Time -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a stut' n t f p = unwrap $ (\a b -> _stut' a b f p) <$> n <*> t _stut' :: (Num n, Ord n) => n -> Time -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a _stut' steps steptime f p | steps <= 0 = p | otherwise = overlay (f (steptime `rotR` _stut' (steps-1) steptime f p)) p {- | @durPattern@ takes a pattern and returns the length of events in that pattern as a new pattern. For example the result of `durPattern "[a ~] b"` would be `"[0.25 ~] 0.5"`. -} durPattern :: Pattern a -> Pattern Time durPattern p = Pattern $ \a -> map eventLengthEvent $ arc p a where eventLengthEvent (a1@(s1,e1), a2, x) = (a1, a2, e1-s1) {- | @durPattern'@ is similar to @durPattern@, but does some lookahead to try to find the length of time to the *next* event. For example, the result of `durPattern' "[a ~] b"` would be `"[0.5 ~] 0.5"`. -} durPattern' :: Pattern a -> Pattern Time durPattern' p = Pattern $ \a@(s,e) -> map (eventDurToNext (arc p (s,e+1))) (arc p a) where eventDurToNext evs ev@(a1,a2,x) = (a1, a2, (nextNum (t ev) (mt evs)) - (t ev)) t = fst . fst' mt = (map fst) . (map fst') nextNum a = head . sort . filter (\x -> x >a) {- | @stutx@ is like @stut'@ but will limit the number of repeats using the duration of the original sound. This usually prevents overlapping "stutters" from subsequent sounds. -} stutx :: Pattern Int -> Pattern Time -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a stutx n t f p = stut' (liftA2 min n (fmap floor $ durPattern' p / (t+0.001))) t f p {-| same as `anticipate` though it allows you to specify the number of cycles until dropping to the new pattern, e.g.: @ d1 $ sound "jvbass(3,8)" t1 (anticipateIn 4) $ sound "jvbass(5,8)" @-} anticipateIn :: Time -> Time -> [ParamPattern] -> ParamPattern anticipateIn t now = wash (spread' (_stut 8 0.2) (now `rotR` (_slow t $ (toRational . (1-)) <$> envL))) t now {- | `anticipate` is an increasing comb filter. Build up some tension, culminating in a _drop_ to the new pattern after 8 cycles. -} anticipate :: Time -> [ParamPattern] -> ParamPattern anticipate = anticipateIn 8 {- | Copies the @n@ parameter to the @orbit@ parameter, so different sound variants or notes go to different orbits in SuperDirt. -} nToOrbit = copyParam n_p orbit_p {- | Maps the sample or synth names to different @orbit@s, using indexes from the given list. E.g. @soundToOrbit ["bd", "sn", "cp"] $ sound "bd [cp sn]"@ would cause the bd, sn and cp smamples to be sent to orbit 0, 1, 2 respectively.-} soundToOrbit :: [String] -> ParamPattern -> ParamPattern soundToOrbit sounds p = follow s_p orbit_p ((\s -> fromMaybe 0 $ elemIndex s sounds) <$>) p tidal-0.9.5/Sound/Tidal/Chords.hs0000644000000000000000000000524313212576414014724 0ustar0000000000000000module Sound.Tidal.Chords where import Sound.Tidal.Pattern import Data.Maybe import Control.Applicative major :: Num a => [a] major = [0,4,7] minor :: Num a => [a] minor = [0,3,7] major7 :: Num a => [a] major7 = [0,4,7,11] dom7 :: Num a => [a] dom7 = [0,4,7,10] minor7 :: Num a => [a] minor7 = [0,3,7,10] aug :: Num a => [a] aug = [0,4,8] dim :: Num a => [a] dim = [0,3,6] dim7 :: Num a => [a] dim7 = [0,3,6,9] one :: Num a => [a] one = [0] five :: Num a => [a] five = [0,7] plus :: Num a => [a] plus = [0,4,8] sharp5 :: Num a => [a] sharp5 = [0,4,8] msharp5 :: Num a => [a] msharp5 = [0,3,8] sus2 :: Num a => [a] sus2 = [0,2,7] sus4 :: Num a => [a] sus4 = [0,5,7] six :: Num a => [a] six = [0,4,7,9] m6 :: Num a => [a] m6 = [0,3,7,9] sevenSus2 :: Num a => [a] sevenSus2 = [0,2,7,10] sevenSus4 :: Num a => [a] sevenSus4 = [0,5,7,10] sevenFlat5 :: Num a => [a] sevenFlat5 = [0,4,6,10] m7flat5 :: Num a => [a] m7flat5 = [0,3,6,10] sevenSharp5 :: Num a => [a] sevenSharp5 = [0,4,8,10] m7sharp5 :: Num a => [a] m7sharp5 = [0,3,8,10] nine :: Num a => [a] nine = [0,4,7,10,14] m9 :: Num a => [a] m9 = [0,3,7,10,14] m7sharp9 :: Num a => [a] m7sharp9 = [0,3,7,10,14] maj9 :: Num a => [a] maj9 = [0,4,7,11,14] nineSus4 :: Num a => [a] nineSus4 = [0,5,7,10,14] sixby9 :: Num a => [a] sixby9 = [0,4,7,9,14] m6by9 :: Num a => [a] m6by9 = [0,3,9,7,14] sevenFlat9 :: Num a => [a] sevenFlat9 = [0,4,7,10,13] m7flat9 :: Num a => [a] m7flat9 = [0,3,7,10,13] sevenFlat10 :: Num a => [a] sevenFlat10 = [0,4,7,10,15] nineSharp5 :: Num a => [a] nineSharp5 = [0,1,13] m9sharp5 :: Num a => [a] m9sharp5 = [0,1,14] sevenSharp5flat9 :: Num a => [a] sevenSharp5flat9 = [0,4,8,10,13] m7sharp5flat9 :: Num a => [a] m7sharp5flat9 = [0,3,8,10,13] eleven :: Num a => [a] eleven = [0,4,7,10,14,17] m11 :: Num a => [a] m11 = [0,3,7,10,14,17] maj11 :: Num a => [a] maj11 = [0,4,7,11,14,17] evelenSharp :: Num a => [a] evelenSharp = [0,4,7,10,14,18] m11sharp :: Num a => [a] m11sharp = [0,3,7,10,14,18] thirteen :: Num a => [a] thirteen = [0,4,7,10,14,17,21] m13 :: Num a => [a] m13 = [0,3,7,10,14,17,21] -- | @chordate cs m n@ selects the @n@th "chord" (a chord is a list of Ints) -- from a list of chords @cs@ and transposes it by @m@ chordate :: Num b => [[b]] -> b -> Int -> [b] chordate cs m n = map (+m) $ cs!!n -- | @enchord chords pn pc@ turns every note in the note pattern @pn@ into -- a chord, selecting from the chord lists @chords@ using the index pattern -- @pc@. For example, @Chords.enchord [Chords.major Chords.minor] "c g" "0 1"@ -- will create a pattern of a C-major chord followed by a G-minor chord. enchord :: Num a => [[a]] -> Pattern a -> Pattern Int -> Pattern a enchord chords pn pc = flatpat $ (chordate chords) <$> pn <*> pc tidal-0.9.5/Sound/Tidal/Sieve.hs0000644000000000000000000000735213212576414014560 0ustar0000000000000000{-# LANGUAGE FlexibleInstances #-} {-# OPTIONS_GHC -Wall -fno-warn-orphans -fno-warn-name-shadowing #-} module Sound.Tidal.Sieve where import Control.Applicative import Data.Monoid import Sound.Tidal.Parse import Sound.Tidal.Pattern import Sound.Tidal.Strategies import Sound.Tidal.Time -- The 'Sieve' datatype, which represents a Xenakis sieve. -- (for an overview, see www.mitpressjournals.org/doi/pdf/10.1162/0148926054094396) data Sieve a = Sieve {sieveAt :: Int -> a} instance Functor Sieve where fmap f s = Sieve $ \i -> f (sieveAt s i) instance Applicative Sieve where pure b = Sieve $ \i -> b a <*> b = Sieve $ \i -> (sieveAt a i) (sieveAt b i) -- | The basic notation for and constructor of a boolean 'Sieve' is @m\@\@n@, -- which represents all integers whose modulo with @m@ is equal to @n@ infixl 9 @@ (@@) :: Int -> Int -> Sieve Bool m @@ i = Sieve $ \j -> (j `mod` m) == i -- If Haskell's logic operators had been defined on a type class, we could -- declare Sieve to be an instance, but they haven't so here we are -- | @not'@ gives the complement of a sieve not' :: Applicative f => f Bool -> f Bool not' = fmap not -- | @#||#@ gives the union (logical OR) of two sieves infixl 2 #||# (#||#) :: Applicative f => f Bool -> f Bool -> f Bool (#||#) = liftA2 (||) -- | @#&&#@ gives the intersection (logical AND) of two sieves infixl 3 #&&# (#&&#) :: Applicative f => f Bool -> f Bool -> f Bool (#&&#) = liftA2 (&&) -- | `#^^#` gives the exclusive disjunction (logical XOR) of two sieves infixl 2 #^^# (#^^#) :: Applicative f => f Bool -> f Bool -> f Bool (#^^#) x y = (x #&&# not' y) #||# (y #&&# not' x) -- | @sieveToList n@ returns a list of the values of the sieve for each -- nonnegative integer less than @n@ -- For example: @sieveToList 10 $ 3\@\@1@ returns -- `[False, True, False, False, True, False, False, True, False, False]` sieveToList :: Int -> Sieve a -> [a] sieveToList n s = map (sieveAt s) [0..n-1] -- | @sieveToString n@ represents the sieve as a character string, where -- @-@ represents False and @x@ represents True sieveToString :: Int -> Sieve Bool -> [Char] sieveToString n s = map b2c $ sieveToList n s where b2c b | b == True = 'x' | otherwise = '-' -- | @sieveToInts n@ returns a list of nonnegative integers less than @n@ -- where the sieve is True sieveToInts :: Int -> Sieve Bool -> [Int] sieveToInts n s = map snd $ filter fst $ zip (sieveToList n s) [0..n-1] -- | @sieveToPat n@ returns a pattern where the cycle is divided into @n@ -- beats, and there is an event whenever the matching beat number is in the -- sieve -- For example: @sieveToPat 8 $ 3\@\@1@ returns @"~ x ~ ~ x ~ ~ x"@ sieveToPat :: Int -> Sieve Bool -> Pattern String sieveToPat n s = p $ concatMap b2s $ sieveToList n s where b2s b | b == True = "x " | otherwise = "~ " -- | @stepSieve n str@ works like 'sieveToPat' but uses @str@ in the pattern -- instead of @x@ stepSieve :: Int -> String -> Sieve Bool -> Pattern String stepSieve n str sieve = step str (sieveToString n sieve) -- | @slowstepSieve t@ is shorthand for applying @slow t@ to the result of -- `stepSieve` slowstepSieve :: Pattern Time -> Int -> String -> Sieve Bool -> Pattern String slowstepSieve t n str sieve = slow t $ stepSieve n str sieve -- | @scaleSieve n@ uses 'sieveToInts' to turn a sieve into a list of -- integers, and then uses that with the @toScale@ function to -- turn a pattern of numbers into a pattern of notes in the scale. -- For example: @scaleSieve 8 (3\@\@1) "0 1 2 1"@ first converts the sieve -- to the scale @[1, 4, 7]@ and then uses that with @toScale@ to return the -- pattern @"1 4 7 4"@ scaleSieve :: Int -> Sieve Bool -> Pattern Int -> Pattern Int scaleSieve n sieve = toScale (sieveToInts n sieve) instance Show (Sieve Bool) where show = sieveToString 32 tidal-0.9.5/Sound/Tidal/Bjorklund.hs0000644000000000000000000000200113212576414015421 0ustar0000000000000000module Sound.Tidal.Bjorklund (bjorklund) where -- The below is (c) Rohan Drape, taken from the hmt library and -- distributed here under the terms of the GNU Public Licence. Tidal -- used to just include the library but removed for now due to -- dependency problems.. We could however likely benefit from other -- parts of the library.. type STEP a = ((Int,Int),([[a]],[[a]])) left :: STEP a -> STEP a left ((i,j),(xs,ys)) = let (xs',xs'') = splitAt j xs in ((j,i-j),(zipWith (++) xs' ys,xs'')) right :: STEP a -> STEP a right ((i,j),(xs,ys)) = let (ys',ys'') = splitAt i ys in ((i,j-i),(zipWith (++) xs ys',ys'')) bjorklund' :: STEP a -> STEP a bjorklund' (n,x) = let (i,j) = n in if min i j <= 1 then (n,x) else bjorklund' (if i > j then left (n,x) else right (n,x)) bjorklund :: (Int,Int) -> [Bool] bjorklund (i,j') = let j = j' - i x = replicate i [True] y = replicate j [False] (_,(x',y')) = bjorklund' ((i,j),(x,y)) in concat x' ++ concat y' tidal-0.9.5/Sound/Tidal/Strategies.hs0000644000000000000000000003572613212576414015625 0ustar0000000000000000{-# OPTIONS_GHC -XNoMonomorphismRestriction -XOverloadedStrings #-} module Sound.Tidal.Strategies where import Data.Ratio import Control.Applicative import qualified Data.Map as Map import qualified Data.Char as Char import Data.Fixed import Data.Maybe import Sound.Tidal.Dirt import Sound.Tidal.Pattern import Sound.Tidal.Stream import Sound.Tidal.Time import Sound.Tidal.Utils import Sound.Tidal.Params import Sound.Tidal.Parse import Data.List (transpose) stutter :: Integral i => i -> Time -> Pattern a -> Pattern a stutter n t p = stack $ map (\i -> (t * (fromIntegral i)) `rotR` p) [0 .. (n-1)] echo, triple, quad, double :: Time -> Pattern a -> Pattern a echo = stutter 2 triple = stutter 3 quad = stutter 4 double = echo {- | The `jux` function creates strange stereo effects, by applying a function to a pattern, but only in the right-hand channel. For example, the following reverses the pattern on the righthand side: @ d1 $ slow 32 $ jux (rev) $ striate' 32 (1/16) $ sound "bev" @ When passing pattern transforms to functions like [jux](#jux) and [every](#every), it's possible to chain multiple transforms together with `.`, for example this both reverses and halves the playback speed of the pattern in the righthand channel: @ d1 $ slow 32 $ jux ((# speed "0.5") . rev) $ striate' 32 (1/16) $ sound "bev" @ -} jux = juxBy 1 juxcut f p = stack [p # pan (pure 0) # cut (pure (-1)), f $ p # pan (pure 1) # cut (pure (-2)) ] juxcut' fs p = stack $ map (\n -> ((fs !! n) p |+| cut (pure $ 1-n)) # pan (pure $ fromIntegral n / fromIntegral l)) [0 .. l-1] where l = length fs {- | In addition to `jux`, `jux'` allows using a list of pattern transform. resulting patterns from each transformation will be spread via pan from left to right. For example: @ d1 $ jux' [iter 4, chop 16, id, rev, palindrome] $ sound "bd sn" @ will put `iter 4` of the pattern to the far left and `palindrome` to the far right. In the center the original pattern will play and mid left mid right the chopped and the reversed version will appear. One could also write: @ d1 $ stack [ iter 4 $ sound "bd sn" # pan "0", chop 16 $ sound "bd sn" # pan "0.25", sound "bd sn" # pan "0.5", rev $ sound "bd sn" # pan "0.75", palindrome $ sound "bd sn" # pan "1", ] @ -} jux' fs p = stack $ map (\n -> ((fs !! n) p) # pan (pure $ fromIntegral n / fromIntegral l)) [0 .. l-1] where l = length fs -- | Multichannel variant of `jux`, _not sure what it does_ jux4 f p = stack [p # pan (pure (5/8)), f $ p # pan (pure (1/8))] {- | With `jux`, the original and effected versions of the pattern are panned hard left and right (i.e., panned at 0 and 1). This can be a bit much, especially when listening on headphones. The variant `juxBy` has an additional parameter, which brings the channel closer to the centre. For example: @ d1 $ juxBy 0.5 (density 2) $ sound "bd sn:1" @ In the above, the two versions of the pattern would be panned at 0.25 and 0.75, rather than 0 and 1. -} juxBy n f p = stack [p |+| pan (pure $ 0.5 - (n/2)), f $ p |+| pan (pure $ 0.5 + (n/2))] {- | Smash is a combination of `spread` and `striate` - it cuts the samples into the given number of bits, and then cuts between playing the loop at different speeds according to the values in the list. So this: @ d1 $ smash 3 [2,3,4] $ sound "ho ho:2 ho:3 hc" @ Is a bit like this: @ d1 $ spread (slow) [2,3,4] $ striate 3 $ sound "ho ho:2 ho:3 hc" @ This is quite dancehall: @ d1 $ (spread' slow "1%4 2 1 3" $ spread (striate) [2,3,4,1] $ sound "sn:2 sid:3 cp sid:4") # speed "[1 2 1 1]/2" @ -} smash n xs p = slowcat $ map (\n -> slow n p') xs where p' = striate n p {- | an altenative form to `smash` is `smash'` which will use `chop` instead of `striate`. -} smash' n xs p = slowcat $ map (\n -> slow n p') xs where p' = _chop n p -- samples "jvbass [~ latibro] [jvbass [latibro jvbass]]" ((1%2) `rotL` slow 6 "[1 6 8 7 3]") samples :: Applicative f => f String -> f Int -> f String samples p p' = pick <$> p <*> p' samples' :: Applicative f => f String -> f Int -> f String samples' p p' = (flip pick) <$> p' <*> p {- scrumple :: Time -> Pattern a -> Pattern a -> Pattern a scrumple o p p' = p'' -- overlay p (o `rotR` p'') where p'' = Pattern $ \a -> concatMap (\((s,d), vs) -> map (\x -> ((s,d), snd x ) ) (arc p' (s,s)) ) (arc p a) -} --rev :: Pattern a -> Pattern a --rev p = Pattern $ \a -> concatMap -- (\a' -> mapFsts mirrorArc $ -- (arc p (mirrorArc a'))) -- (arcCycles a) --spreadf :: [Pattern a -> Pattern b] -> Pattern a -> Pattern b spreadf ts p = spread ($) {- | `spin` will "spin" a layer up a pattern the given number of times, with each successive layer offset in time by an additional `1/n` of a cycle, and panned by an additional `1/n`. The result is a pattern that seems to spin around. This function works best on multichannel systems. @ d1 $ slow 3 $ spin 4 $ sound "drum*3 tabla:4 [arpy:2 ~ arpy] [can:2 can:3]" @ -} spin :: Pattern Int -> ParamPattern -> ParamPattern spin = temporalParam _spin _spin :: Int -> ParamPattern -> ParamPattern _spin copies p = stack $ map (\n -> let offset = toInteger n % toInteger copies in offset `rotL` p # pan (pure $ fromRational offset) ) [0 .. (copies - 1)] {-stripe :: Arc -> Pattern a -> Pattern a stripe (stripeS, stripeE) p = slow t $ Pattern $ \a -> concatMap f $ arcCycles a where f a = mapFsts (stretch . stripe') $ arc p (stripe' a) trunc' (s,e) = (min s ((sam s) + t), min e ((sam s) + t)) stretch (s,e) = (sam s + ((s - sam s) / t), sam s + ((e - sam s) / t)) -} sawwave4, sinewave4, rand4 :: Pattern Double sawwave4 = ((*4) <$> sawwave1) sinewave4 = ((*4) <$> sinewave1) rand4 = ((*4) <$> rand) stackwith p ps | null ps = silence | otherwise = stack $ map (\(i, p') -> p' # (((fromIntegral i) % l) `rotL` p)) (zip [0 ..] ps) where l = fromIntegral $ length ps {- cross f p p' = Pattern $ \t -> concat [filter flt $ arc p t, filter (not . flt) $ arc p' t ] ] where flt = f . cyclePos . fst . fst -} {- | `scale` will take a pattern which goes from 0 to 1 (like `sine1`), and scale it to a different range - between the first and second arguments. In the below example, `scale 1 1.5` shifts the range of `sine1` from 0 - 1 to 1 - 1.5. @ d1 $ jux (iter 4) $ sound "arpy arpy:2*2" |+| speed (slow 4 $ scale 1 1.5 sine1) @ -} scale :: (Functor f, Num b) => b -> b -> f b -> f b scale from to p = ((+ from) . (* (to-from))) <$> p {- | `scalex` is an exponential version of `scale`, good for using with frequencies. Do *not* use negative numbers or zero as arguments! -} scalex :: (Functor f, Floating b) => b -> b -> f b -> f b scalex from to p = exp <$> scale (log from) (log to) p {- | `chop` granualizes every sample in place as it is played, turning a pattern of samples into a pattern of sample parts. Use an integer value to specify how many granules each sample is chopped into: @ d1 $ chop 16 $ sound "arpy arp feel*4 arpy*4" @ Different values of `chop` can yield very different results, depending on the samples used: @ d1 $ chop 16 $ sound (samples "arpy*8" (run 16)) d1 $ chop 32 $ sound (samples "arpy*8" (run 16)) d1 $ chop 256 $ sound "bd*4 [sn cp] [hh future]*2 [cp feel]" @ -} chop :: Pattern Int -> ParamPattern -> ParamPattern chop = temporalParam _chop _chop :: Int -> ParamPattern -> ParamPattern _chop n p = Pattern $ \queryA -> concatMap (f queryA) $ arcCycles queryA where f queryA a = concatMap (chopEvent queryA) (arc p a) chopEvent (queryS, queryE) (a,_a',v) = map (newEvent v) $ filter (\(_, (s,e)) -> not $ or [e < queryS, s >= queryE]) (enumerate $ chopArc a n) newEvent :: ParamMap -> (Int, Arc) -> Event ParamMap newEvent v (i, a) = (a,a,Map.insert (param dirt "end") (VF ((fromIntegral $ i+1)/(fromIntegral n))) $ Map.insert (param dirt "begin") (VF ((fromIntegral i)/(fromIntegral n))) v) {- | `gap` is similar to `chop` in that it granualizes every sample in place as it is played, but every other grain is silent. Use an integer value to specify how many granules each sample is chopped into: @ d1 $ gap 8 $ sound "jvbass" d1 $ gap 16 $ sound "[jvbass drum:4]" @-} gap :: Pattern Int -> ParamPattern -> ParamPattern gap = temporalParam _gap _gap :: Int -> ParamPattern -> ParamPattern _gap n p = Pattern $ \queryA -> concatMap (f queryA) $ arcCycles queryA where f queryA a = concatMap (chopEvent queryA) (arc p a) chopEvent (queryS, queryE) (a,_a',v) = map (newEvent v) $ filter (\(_, (s,e)) -> not $ or [e < queryS, s >= queryE]) (enumerate $ everyOther $ chopArc a n) newEvent :: ParamMap -> (Int, Arc) -> Event ParamMap newEvent v (i, a) = (a,a,Map.insert (param dirt "end") (VF ((fromIntegral $ i+1)/(fromIntegral n))) $ Map.insert (param dirt "begin") (VF ((fromIntegral i)/(fromIntegral n))) v) everyOther (x:_:xs) = x:everyOther xs everyOther xs = xs chopArc :: Arc -> Int -> [Arc] chopArc (s, e) n = map (\i -> ((s + (e-s)*(fromIntegral i/fromIntegral n)), s + (e-s)*((fromIntegral $ i+1)/fromIntegral n))) [0 .. n-1] {- normEv :: Event a -> Event a -> Event a normEv ev@(_, (s,e), _) ev'@(_, (s',e'), _) | not on && not off = [] -- shouldn't happen | on && off = splitEv ev' | not on && s' > sam s = [] | not off && e' < nextSam s = [(fst' ev, mapSnd' (mapSnd (min $ nextSam s)) ev, thd' ev)] where on = onsetIn (sam s, nextSam s) ev off = offsetIn (sam s, nextSam s) ev eplitEv -} --mapCycleEvents :: Pattern a -> ([Event a] -> [Event a]) -> Pattern a --mapCycleEvents p f = splitQueries $ Pattern $ \(s,e) -> filter (\ev -> isJust $ subArc (s,e) (eventArc ev)) $ f $ arc p (sam s, nextSam s) --off :: Time -> Pattern a -> Pattern a --off t p = mapCycleEvents p (mapArcs (mapSnd wrappedPlus . mapFst wrappedPlus)) -- where wrapAtCycle f t' = sam t' + cyclePos (f t') -- wrappedPlus = wrapAtCycle (+t) en :: [(Int, Int)] -> Pattern String -> Pattern String en ns p = stack $ map (\(i, (k, n)) -> e k n (samples p (pure i))) $ enumerate ns {- | `weave` applies a function smoothly over an array of different patterns. It uses an `OscPattern` to apply the function at different levels to each pattern, creating a weaving effect. @ d1 $ weave 3 (shape $ sine1) [sound "bd [sn drum:2*2] bd*2 [sn drum:1]", sound "arpy*8 ~"] @ -} weave :: Rational -> ParamPattern -> [ParamPattern] -> ParamPattern weave t p ps = weave' t p (map (\x -> (x #)) ps) {- | `weave'` is similar in that it blends functions at the same time at different amounts over a pattern: @ d1 $ weave' 3 (sound "bd [sn drum:2*2] bd*2 [sn drum:1]") [density 2, (# speed "0.5"), chop 16] @ -} weave' :: Rational -> Pattern a -> [Pattern a -> Pattern a] -> Pattern a weave' t p fs | l == 0 = silence | otherwise = _slow t $ stack $ map (\(i, f) -> (fromIntegral i % l) `rotL` (_density t $ f (_slow t p))) (zip [0 ..] fs) where l = fromIntegral $ length fs {- | (A function that takes two OscPatterns, and blends them together into a new OscPattern. An OscPattern is basically a pattern of messages to a synthesiser.) Shifts between the two given patterns, using distortion. Example: @ d1 $ interlace (sound "bd sn kurt") (every 3 rev $ sound "bd sn:2") @ -} interlace :: ParamPattern -> ParamPattern -> ParamPattern interlace a b = weave 16 (shape $ ((* 0.9) <$> sinewave1)) [a, b] -- | Step sequencing step :: String -> String -> Pattern String step s steps = fastcat $ map f steps where f c | c == 'x' = atom s | c >= '0' && c <= '9' = atom $ s ++ ":" ++ [c] | otherwise = silence steps :: [(String, String)] -> Pattern String steps = stack . map (\(a,b) -> step a b) -- | like `step`, but allows you to specify an array of strings to use for 0,1,2... step' :: [String] -> String -> Pattern String step' ss steps = fastcat $ map f steps where f c | c == 'x' = atom $ ss!!0 | c >= '0' && c <= '9' = atom $ ss!!(Char.digitToInt c) | otherwise = silence off :: Pattern Time -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a off tp f p = unwrap $ (\tv -> _off tv f p) <$> tp _off :: Time -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a _off t f p = superimpose (f . (t `rotR`)) p offadd :: Num a => Pattern Time -> Pattern a -> Pattern a -> Pattern a offadd tp pn p = off tp (+pn) p {- | `up` does a poor man's pitchshift by semitones via `speed`. You can easily produce melodies from a single sample with up: @ d1 # up "0 5 4 12" # sound "arpy" @ This will play the _arpy_ sample four times a cycle in the original pitch, pitched by 5 semitones, by 4 and then by an octave. -} up :: Pattern Double -> ParamPattern up = speed . ((1.059466**) <$>) ghost'' a f p = superimpose (((a*2.5) `rotR`) . f) $ superimpose (((a*1.5) `rotR`) . f) $ p ghost' a p = ghost'' 0.125 ((|*| gain (pure 0.7)) . (|=| end (pure 0.2)) . (|*| speed (pure 1.25))) p ghost p = ghost' 0.125 p slice :: Int -> Int -> ParamPattern -> ParamPattern slice i n p = p # begin (pure $ fromIntegral i / fromIntegral n) # end (pure $ fromIntegral (i+1) / fromIntegral n) randslice :: Int -> ParamPattern -> ParamPattern randslice n p = unwrap $ (\i -> slice i n p) <$> irand n {- | `loopAt` makes a sample fit the given number of cycles. Internally, it works by setting the `unit` parameter to "c", changing the playback speed of the sample with the `speed` parameter, and setting setting the `density` of the pattern to match. @ d1 $ loopAt 4 $ sound "breaks125" d1 $ juxBy 0.6 (|*| speed "2") $ slowspread (loopAt) [4,6,2,3] $ chop 12 $ sound "fm:14" @ -} loopAt :: Pattern Time -> ParamPattern -> ParamPattern loopAt n p = slow n p |*| speed (fromRational <$> (1/n)) # unit (pure "c") {- | tabby - A more literal weaving than the `weave` function, give number of 'threads' per cycle and two patterns, and this function will weave them together using a plain (aka 'tabby') weave, with a simple over/under structure -} tabby n p p' = stack [maskedWarp n p, maskedWeft n p' ] where weft n = concatMap (\x -> [[0..n-1],(reverse [0..n-1])]) [0 .. (n `div` 2) - 1] warp = transpose . weft thread xs n p = _slow (n%1) $ fastcat $ map (\i -> zoom (i%n,(i+1)%n) p) (concat xs) weftP n p = thread (weft n) n p warpP n p = thread (warp n) n p maskedWeft n p = Sound.Tidal.Pattern.mask (every 2 rev $ _density ((n)%2) "~ 1" :: Pattern Int) $ weftP n p maskedWarp n p = mask (every 2 rev $ _density ((n)%2) "1 ~" :: Pattern Int) $ warpP n p hurry :: Pattern Rational -> ParamPattern -> ParamPattern hurry x = (|*| speed (fromRational <$> x)) . fast x tidal-0.9.5/doc/0000755000000000000000000000000013212576414011562 5ustar0000000000000000tidal-0.9.5/doc/tidal.md0000644000000000000000000000011313212576414013174 0ustar0000000000000000Documentation has now moved to the Tidal website: http://tidal.lurk.org/