├── configs ├── sshconfig ├── tidebox-service.yml ├── tidebox.yml ├── screenrc ├── emacsrc ├── tidebox.ini └── ffserver.conf ├── demo.gif ├── tidal ├── hello.tidal └── init.tidal ├── Dockerfile └── README.md /configs/sshconfig: -------------------------------------------------------------------------------- 1 | Host github.com 2 | IdentityFile ~/.ssh/id_rsa 3 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DoubleDensity/tidebox/HEAD/demo.gif -------------------------------------------------------------------------------- /tidal/hello.tidal: -------------------------------------------------------------------------------- 1 | -- default test rhythm to verify stream connectivity; should start within 10 seconds 2 | d1 $ stack [sound "bd sn:2" , sound "casio:2 casio:1 house:2*1"] 3 | 4 | hush 5 | -------------------------------------------------------------------------------- /tidal/init.tidal: -------------------------------------------------------------------------------- 1 | hush 2 | 3 | d1 $ silence 4 | 5 | d2 $ silence 6 | 7 | d3 $ silence 8 | 9 | d4 $ silence 10 | 11 | d5 $ silence 12 | 13 | d6 $ silence 14 | 15 | d7 $ silence 16 | 17 | d8 $ silence 18 | 19 | d9 $ silence 20 | -------------------------------------------------------------------------------- /configs/tidebox-service.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: tideboxsvcs 5 | spec: 6 | type: LoadBalancer 7 | ports: 8 | - 9 | name: sshd 10 | port: 22 11 | targetPort: 22 12 | - 13 | name: ffserver 14 | port: 8090 15 | targetPort: 8090 16 | selector: 17 | app: tidebox 18 | -------------------------------------------------------------------------------- /configs/tidebox.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: tidebox 5 | labels: 6 | app: tidebox 7 | spec: 8 | containers: 9 | - name: tidebox 10 | image: quay.io/doubledensity/tidebox:0.2 11 | ports: 12 | - containerPort: 8090 13 | name: ffserver 14 | - containerPort: 22 15 | name: sshd 16 | imagePullSecrets: 17 | - name: myregistrykey 18 | -------------------------------------------------------------------------------- /configs/screenrc: -------------------------------------------------------------------------------- 1 | defshell -zsh 2 | 3 | screen -t tidal 4 | select 0 5 | stuff "emacs^M" 6 | screen -t jackd 7 | select 1 8 | stuff "tail -f ~/jackd.log^M" 9 | screen -t dirt 10 | select 2 11 | stuff "tail -f ~/dirt.log^M" 12 | screen -t ffserver 13 | select 3 14 | stuff "tail -f ~/ffserver.log^M" 15 | screen -t ffmpeg 16 | select 4 17 | stuff "tail -f ~/ffmpeg.log^M" 18 | screen -t files 19 | select 5 20 | stuff "cd /work/scratchpool^M" 21 | select 0 22 | 23 | altscreen off 24 | term screen-256color 25 | bind ',' prev 26 | bind '.' next -------------------------------------------------------------------------------- /configs/emacsrc: -------------------------------------------------------------------------------- 1 | (require 'package) 2 | (custom-set-variables 3 | ;; custom-set-variables was added by Custom. 4 | ;; If you edit it by hand, you could mess it up, so be careful. 5 | ;; Your init file should contain only one such instance. 6 | ;; If there is more than one, they won't work right. 7 | '(package-archives 8 | (quote 9 | (("gnu" . "http://elpa.gnu.org/packages/") 10 | ("melpa-stable" . "http://stable.melpa.org/packages/"))))) 11 | 12 | ;; Hide splash-screen and startup-message 13 | (setq inhibit-splash-screen t) 14 | (setq inhibit-startup-message t) 15 | 16 | (add-hook 'isearch-update-post-hook 'redraw-display) 17 | 18 | (add-to-list 'load-path "/repos/tidal") 19 | (add-to-list 'load-path "/work") 20 | (require 'haskell-mode) 21 | (require 'tidal) 22 | 23 | (add-hook 'haskell-mode-hook 'turn-on-haskell-indentation) 24 | 25 | (find-file "~/init.tidal") 26 | 27 | (fset 'stidal 28 | "\C-c\C-s") 29 | 30 | (fset 'splay 31 | "\C-c\C-c") 32 | 33 | (execute-kbd-macro (symbol-function 'stidal)) 34 | 35 | (find-file "~/hello.tidal") 36 | 37 | (delete-other-windows) 38 | 39 | (defun sseek () 40 | (interactive) 41 | (with-no-warnings 42 | (goto-line 2))) 43 | 44 | (sseek) 45 | 46 | (sleep-for 5) 47 | (execute-kbd-macro (symbol-function 'splay)) 48 | -------------------------------------------------------------------------------- /configs/tidebox.ini: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | logfile = /tmp/supervisord.log 4 | 5 | [program:sshd] 6 | command=/usr/sbin/sshd -D 7 | 8 | [program:jackd] 9 | command=/usr/bin/jackd -d dummy 10 | user=tidal 11 | priority=10 12 | startsecs=10 13 | environment=HOME="/home/tidal",USER="tidal" 14 | autostart=true 15 | autorestart=true 16 | redirect_stderr=true 17 | stdout_logfile=/home/tidal/jackd.log 18 | stderr_logfile=/home/tidal/jackd.error_log 19 | 20 | [program:dirt] 21 | command=/repos/Dirt/dirt 22 | user=tidal 23 | priority=30 24 | startsecs=10 25 | environment=HOME="/repos/Dirt",USER="tidal" 26 | directory=/repos/Dirt 27 | autostart=true 28 | autorestart=true 29 | redirect_stderr=true 30 | stdout_logfile=/home/tidal/dirt.log 31 | stderr_logfile=/home/tidal/dirt.error_log 32 | 33 | [program:ffserver] 34 | command=/usr/bin/ffserver -f /home/tidal/ffserver.conf 35 | user=tidal 36 | priority=40 37 | startsecs=10 38 | autostart=true 39 | autorestart=true 40 | redirect_stderr=false 41 | stdout_logfile=/home/tidal/ffserver.log 42 | stderr_logfile=/home/tidal/ffserver.error_log 43 | 44 | [program:ffmpeg] 45 | command=/usr/bin/ffmpeg -f jack -i ffmpeg -ac 2 http://localhost:8090/feed1.ffm 46 | user=tidal 47 | priority=50 48 | startsecs=10 49 | autostart=true 50 | autorestart=true 51 | redirect_stderr=false 52 | stdout_logfile=/home/tidal/ffmpeg.log 53 | stderr_logfile=/home/tidal/ffmpeg.error_log 54 | 55 | [program:jackd_connect1] 56 | command=/usr/bin/jack_connect dirt:output_0 ffmpeg:input_1 57 | user=tidal 58 | priority=500 59 | startsecs=5 60 | autostart=true 61 | autorestart=false 62 | 63 | [program:jackd_connect2] 64 | command=/usr/bin/jack_connect dirt:output_1 ffmpeg:input_2 65 | user=tidal 66 | priority=500 67 | startsecs=5 68 | autostart=true 69 | autorestart=false -------------------------------------------------------------------------------- /configs/ffserver.conf: -------------------------------------------------------------------------------- 1 | # Port on which the server is listening. You must select a different 2 | # port from your standard HTTP web server if it is running on the same 3 | # computer. 4 | Port 8090 5 | 6 | # Address on which the server is bound. Only useful if you have 7 | # several network interfaces. 8 | BindAddress 0.0.0.0 9 | 10 | # Number of simultaneous HTTP connections that can be handled. It has 11 | # to be defined *before* the MaxClients parameter, since it defines the 12 | # MaxClients maximum limit. 13 | MaxHTTPConnections 2000 14 | 15 | # Number of simultaneous requests that can be handled. Since FFServer 16 | # is very fast, it is more likely that you will want to leave this high 17 | # and use MaxBandwidth, below. 18 | MaxClients 1000 19 | 20 | # This the maximum amount of kbit/sec that you are prepared to 21 | # consume when streaming to clients. 22 | MaxBandwidth 1000 23 | 24 | # Access log file (uses standard Apache log file format) 25 | # '-' is the standard output. 26 | CustomLog - 27 | 28 | # Suppress that if you want to launch ffserver as a daemon. 29 | NoDaemon 30 | 31 | 32 | ################################################################## 33 | # Definition of the live feeds. Each live feed contains one video 34 | # and/or audio sequence coming from an ffmpeg encoder or another 35 | # ffserver. This sequence may be encoded simultaneously with several 36 | # codecs at several resolutions. 37 | 38 | 39 | 40 | # You must use 'ffmpeg' to send a live feed to ffserver. In this 41 | # example, you can type: 42 | # 43 | # ffmpeg http://localhost:8090/feed1.ffm 44 | 45 | # ffserver can also do time shifting. It means that it can stream any 46 | # previously recorded live stream. The request should contain: 47 | # "http://xxxx?date=[YYYY-MM-DDT][[HH:]MM:]SS[.m...]".You must specify 48 | # a path where the feed is stored on disk. You also specify the 49 | # maximum size of the feed, where zero means unlimited. Default: 50 | # File=/tmp/feed_name.ffm FileMaxSize=5M 51 | File /tmp/feed1.ffm 52 | FileMaxSize 200K 53 | 54 | # You could specify 55 | # ReadOnlyFile /saved/specialvideo.ffm 56 | # This marks the file as readonly and it will not be deleted or updated. 57 | 58 | # Specify launch in order to start ffmpeg automatically. 59 | # First ffmpeg must be defined with an appropriate path if needed, 60 | # after that options can follow, but avoid adding the http:// field 61 | #Launch ffmpeg 62 | 63 | # Only allow connections from localhost to the feed. 64 | #ACL allow 127.0.0.1 65 | 66 | 67 | 68 | 69 | ################################################################## 70 | # Now you can define each stream which will be generated from the 71 | # original audio and video stream. Each format has a filename (here 72 | # 'test1.mpg'). FFServer will send this stream when answering a 73 | # request containing this filename. 74 | 75 | # MP3 audio 76 | 77 | Feed feed1.ffm 78 | Format mp2 79 | AudioCodec libmp3lame 80 | AudioBitRate 320 81 | AudioChannels 2 82 | AudioSampleRate 44100 83 | NoVideo 84 | 85 | 86 | 87 | # Ogg Vorbis audio 88 | # 89 | #Feed feed1.ffm 90 | #Format ogg 91 | #AudioCodec libvorbis 92 | #Title "Stream title" 93 | #AudioBitRate 64 94 | #AudioChannels 2 95 | #AudioSampleRate 44100 96 | #NoVideo 97 | # 98 | 99 | ################################################################## 100 | # Special streams 101 | 102 | # Server status 103 | 104 | 105 | Format status 106 | 107 | # Only allow local people to get the status 108 | ACL allow localhost 109 | ACL allow 192.168.1.0 192.168.1.255 110 | 111 | #FaviconURL http://pond1.gladstonefamily.net:8080/favicon.ico 112 | 113 | 114 | # Redirect index.html to the appropriate site 115 | 116 | URL http://www.ffmpeg.org/ 117 | 118 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM fedora 2 | 3 | MAINTAINER Buttetsu Batou 4 | 5 | # Install dependencies and audio tools 6 | 7 | RUN dnf groupinstall -y "C Development Tools and Libraries" 8 | RUN dnf install -y git zsh wget man sudo 9 | RUN dnf install -y libsndfile-devel libsamplerate-devel liblo-devel jack-audio-connection-kit-devel jack-audio-connection-kit-example-clients alsa-lib-devel xz htop grep procps-ng yasm screen supervisor openssh-server 10 | RUN dnf install -y cabal-install ghc-Cabal-devel 11 | 12 | # Install editor 13 | RUN dnf -y install emacs-nox emacs-haskell-mode 14 | 15 | # Build Dirt synth 16 | WORKDIR /repos 17 | RUN git clone --recursive https://github.com/tidalcycles/Dirt.git 18 | WORKDIR Dirt 19 | RUN make 20 | 21 | # Build & Install libmp3lame 22 | WORKDIR /repos 23 | RUN git clone https://github.com/rbrito/lame.git 24 | WORKDIR lame 25 | RUN ./configure --prefix=/usr 26 | RUN make install 27 | WORKDIR /repos 28 | RUN rm -fr lame 29 | 30 | # Build & Install ffmpeg, ffserver 31 | WORKDIR /repos 32 | RUN git clone git://source.ffmpeg.org/ffmpeg.git ffmpeg 33 | WORKDIR ffmpeg 34 | RUN ./configure --enable-indev=jack --enable-libmp3lame --enable-nonfree --prefix=/usr 35 | RUN make install 36 | WORKDIR /repos 37 | RUN rm -fr ffmpeg 38 | 39 | # Install Tidebox supervisord config 40 | COPY configs/tidebox.ini /etc/supervisord.d/tidebox.ini 41 | 42 | # Initialize and configure sshd 43 | RUN ssh-keygen -b 1024 -t rsa -f /etc/ssh/ssh_host_key 44 | RUN ssh-keygen -b 1024 -t rsa -f /etc/ssh/ssh_host_rsa_key 45 | RUN ssh-keygen -b 1024 -t dsa -f /etc/ssh/ssh_host_dsa_key 46 | RUN sed -i 's/UsePAM\syes/UsePAM no/' /etc/ssh/sshd_config 47 | 48 | # Expose sshd service 49 | EXPOSE 22 50 | 51 | # Expose ffserver streaming service 52 | EXPOSE 8090 53 | 54 | # Pull Tidal Emacs binding 55 | RUN mkdir /repos/tidal 56 | WORKDIR /repos 57 | WORKDIR tidal 58 | RUN wget https://raw.github.com/yaxu/Tidal/master/tidal.el 59 | 60 | # Create and configure Tidal user 61 | RUN useradd tidal -s /bin/zsh 62 | RUN echo 'tidal:livecoding' | chpasswd 63 | RUN echo "tidal ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers 64 | 65 | USER tidal 66 | 67 | ENV HOME /home/tidal 68 | WORKDIR /home/tidal 69 | 70 | RUN ln -s /repos /home/tidal/repos 71 | RUN ln -s /work /home/tidal/work 72 | 73 | # Install Tidal 74 | RUN cabal update 75 | RUN cabal install tidal 76 | 77 | # Install Oh-My-Zsh 78 | RUN sh -c "$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" 79 | 80 | # Disable Zsh automatic window titling 81 | RUN sed -i 's/# DISABLE_AUTO_TITLE="true"/DISABLE_AUTO_TITLE="true"/g' /home/tidal/.zshrc 82 | 83 | # Install default configurations 84 | COPY configs/emacsrc /home/tidal/.emacs 85 | COPY configs/screenrc /home/tidal/.screenrc 86 | COPY configs/ffserver.conf /home/tidal/ffserver.conf 87 | 88 | # Install default Tidal files 89 | COPY tidal/init.tidal /home/tidal/init.tidal 90 | COPY tidal/hello.tidal /home/tidal/hello.tidal 91 | 92 | # Prepare scratch workspace for version control 93 | RUN sudo mkdir /work 94 | RUN sudo chown -R tidal:tidal /work 95 | WORKDIR /work 96 | RUN mkdir /home/tidal/.ssh 97 | ADD https://raw.githubusercontent.com/DoubleDensity/scratchpool/master/id_rsa-scratchpool /home/tidal/.ssh/id_rsa 98 | RUN sudo chmod 600 /home/tidal/.ssh/id_rsa 99 | RUN sudo chown tidal.tidal /home/tidal/.ssh/id_rsa 100 | COPY configs/sshconfig /home/tidal/.ssh/config 101 | RUN sudo chmod 600 /home/tidal/.ssh/config 102 | RUN sudo chown tidal.tidal /home/tidal/.ssh/config 103 | RUN ssh-keyscan -H github.com >> ~/.ssh/known_hosts 104 | RUN git clone git@github.com:DoubleDensity/scratchpool.git 105 | WORKDIR /work/scratchpool 106 | RUN git config user.name "Tidebox User" 107 | RUN git config user.email "tidal@jankycloud.com" 108 | 109 | # Set Tidal shell to Screen 110 | USER root 111 | RUN echo "/usr/bin/screen" >> /etc/shells 112 | RUN usermod -s /usr/bin/screen tidal 113 | RUN chown -R tidal.tidal /home/tidal/*.tidal 114 | 115 | CMD ["/usr/bin/supervisord"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tidebox 2 | 3 | [![Docker Repository on Quay](https://quay.io/repository/doubledensity/tidebox/status "Docker Repository on Quay")](https://quay.io/repository/doubledensity/tidebox) 4 | 5 | > A complete Tidal musical live coding and audio streaming environment inside Docker 6 | 7 | ## What is Tidal? 8 | 9 | According to the official [homepage](http://tidal.lurk.org): 10 | 11 | "Tidal is a language for live coding pattern(s). It provides a way to express music with very flexible timing, providing a little language for describing patterns as discrete sequences (which can be polyphonic and polymetric), some generators of continuous patterns (e.g. sinewaves, sawtooths) and a wide range of pattern transformations." 12 | 13 | Said differently, Tidal lets you rock out and explore sound and rhythm in real time from the context of a text editor. 14 | 15 | If you are interested in more detail about the theory and execution of Tidal I highly suggest you read [this paper](https://raw.githubusercontent.com/yaxu/Tidal/master/doc/farm/farm.pdf) from its creator [Alex McLean](https://twitter.com/yaxu). 16 | 17 | For an example of the types of patterns and techniques which are possible in Tidal, see the album [Expedition](https://kindohm.bandcamp.com/album/expedition) from [Kindohm](https://twitter.com/kindohm). 18 | 19 | ## Why Tidebox? 20 | 21 | The goal of Tidebox is to allow anyone to run Tidal immediately on any available compute node and stream the output to any target. No package installation or configuration need. No sound hardware or elevated permissions are required on the Docker host. 22 | This allows for the use of very-low end performance hardware to control and compose the session while harnessing greater resources on remote hosts, clusters and public cloud infrastructure. 23 | 24 | Many thanks to Tidal and the entire live coding community for making such fun and exciting software! 25 | 26 | ## Getting started 27 | 28 | First you will want to start the container: 29 | 30 | ```bash 31 | docker run -d quay.io/doubledensity/tidebox:0.2 32 | ``` 33 | 34 | Tidebox has been re-written for 0.2 so that it can now be run 'headless' as a detached Docker container and no longer requires an interactive terminal to run. 35 | 36 | * After launching the container you can ssh in to it using the user name `tidal` and the password `livecoding` 37 | 38 | The Tidal session will begin automatically upon login. You can detach at any time from your session by using `CTRL-A-D`; Tidal will keep playing, and you can re-attach at any time by ssh'ing back in. 39 | 40 | The FFserver process providing the audio stream is exposed on port 8090 and sshd is on port 22. 41 | 42 | ![Tidebox demo](demo.gif) 43 | 44 | You can connect to the stream with any media player which supports streaming mp3 such as VLC, iTunes, MPlayer, mpg123, etc. 45 | 46 | Here is an example of connecting to the stream with MPlayer: 47 | 48 | ```bash 49 | mplayer http://172.17.0.2:8090/stream.mp3 50 | ``` 51 | * You may need to find the IP address of your Tidebox container using the Docker `inspect` command if you don't use the `--net=host` option 52 | 53 | By default Tidebox will start to play a test Tidal audio sequence automatically when you first login to the container so you can verify audio connectivity. 54 | 55 | ## Controls 56 | 57 | The various components of Tidebox are running in separate windows within the GNU Screen terminal window manager, and should initialize automatically. 58 | 59 | The Emacs / Haskell Tidal environment is the default window `0`, but you can find the logs for the additional components JACK, Dirt, ffmpeg, and ffserver on windows `1-4`. 60 | 61 | Window `5` is currently set aside as an interactive shell for working with and managing Tidal files or anything else you may wish to pull down or manipulate for use in your session. 62 | 63 | You can switch between the Screen windows using `CTRL-,` and `CTRL-.` forward and backward, or `CTRL-A` followed by the default window numbers 1-5. 64 | 65 | ## References 66 | 67 | - [Tidal](http://tidal.lurk.org) 68 | - [Dirt synth](https://github.com/tidalcycles/Dirt) 69 | - [JACK Audio Connection Kit](http://www.jackaudio.org/) 70 | - [FFmpeg](https://www.ffmpeg.org/) 71 | - [GNU Emacs](https://www.gnu.org/software/emacs/) 72 | - [GNU Screen](https://www.gnu.org/software/screen/) 73 | - [TOPLAP The Home of Live Coding](http://toplap.org/) 74 | - [Supervisor](http://supervisor.org) --------------------------------------------------------------------------------