├── .gitignore
├── README.md
├── css
├── controls.svg
├── mediaelementplayer.min.css
└── style.css
├── data_samples
├── alb_esp1.mid
├── ashover_simple_chords_1.mid
├── bach_chorale.mid
├── koopa_troopa_beach.mid
└── reels_simple_chords_157.mid
├── favicon.ico
├── generated_music
└── mp3
│ ├── 1.mp3
│ ├── 10.mp3
│ ├── 2.mp3
│ ├── 3.mp3
│ ├── 4.mp3
│ ├── 5.mp3
│ ├── 6.mp3
│ ├── 7.mp3
│ ├── 8.mp3
│ ├── 9.mp3
│ └── nottingham_sample.mp3
├── img
├── dual_softmax_fig.png
├── encoding.png
└── overfitting.png
├── index.html
├── install.sh
├── js
├── jquery-1.11.2.min.js
└── mediaelement-and-player.min.js
├── midi_util.py
├── model.py
├── nottingham_util.py
├── requirements.txt
├── rnn.py
├── rnn_sample.py
├── rnn_separate.py
├── rnn_test.py
├── sampling.py
└── util.py
/.gitignore:
--------------------------------------------------------------------------------
1 | data
2 | 2012code
3 | models
4 | python-midi
5 | research
6 | tensorflow
7 |
8 | *.midi
9 | *.pyc
10 | img/.DS_Store
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Overview
2 | ============
3 | A project that trains a LSTM recurrent neural network over a dataset of MIDI files. More information can be found on the [writeup about this project](http://yoavz.com/music_rnn/) or the [final report](http://yoavz.com/music_rnn_paper.pdf) written. *Warning: Some parts of this codebase are unfinished.*
4 |
5 | Dependencies
6 | ============
7 |
8 | * Python 2.7
9 | * Anaconda
10 | * Numpy (http://www.numpy.org/)
11 | * Tensorflow (https://github.com/tensorflow/tensorflow) - 0.8
12 | * Python Midi (https://github.com/vishnubob/python-midi.git)
13 | * Mingus (https://github.com/bspaans/python-mingus)
14 | * Matplotlib (http://matplotlib.org/)
15 |
16 | Basic Usage
17 | ===========
18 |
19 | 1. Run `./install.sh` to create conda env, install dependencies and download data
20 | 2. `source activate music_rnn` to activate the conda environment
21 | 3. Run `python nottingham_util.py` to generate the sequences and chord mapping file to `data/nottingham.pickle`
22 | 4. Run `python rnn.py --run_name YOUR_RUN_NAME_HERE` to start training the model. Use the grid object in `rnn.py` to edit hyperparameter
23 | configurations.
24 | 5. `source deactivate` to deactivate the conda environment
25 |
--------------------------------------------------------------------------------
/css/controls.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/css/mediaelementplayer.min.css:
--------------------------------------------------------------------------------
1 | .mejs-offscreen{clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px);clip-path:polygon(0px 0,0 0,0 0,0 0);position:absolute!important;height:1px;width:1px;overflow:hidden}.mejs-container{position:relative;background:#000;font-family:Helvetica,Arial;text-align:left;vertical-align:top;text-indent:0}.mejs-container:focus{outline:0}.me-plugin{position:absolute}.mejs-embed,.mejs-embed body{width:100%;height:100%;margin:0;padding:0;background:#000;overflow:hidden}.mejs-fullscreen{overflow:hidden!important}.mejs-container-fullscreen{position:fixed;left:0;top:0;right:0;bottom:0;overflow:hidden;z-index:1000}.mejs-container-fullscreen .mejs-mediaelement,.mejs-container-fullscreen video{width:100%;height:100%}.mejs-clear{clear:both}.mejs-background{position:absolute;top:0;left:0}.mejs-mediaelement{position:absolute;top:0;left:0;width:100%;height:100%}.mejs-poster{position:absolute;top:0;left:0;background-size:contain;background-position:50% 50%;background-repeat:no-repeat}:root .mejs-poster img{display:none}.mejs-poster img{border:0;padding:0;border:0}.mejs-overlay{position:absolute;top:0;left:0}.mejs-overlay-play{cursor:pointer}.mejs-overlay-button{position:absolute;top:50%;left:50%;width:100px;height:100px;margin:-50px 0 0 -50px;background:url(bigplay.svg) no-repeat}.no-svg .mejs-overlay-button{background-image:url(bigplay.png)}.mejs-overlay:hover .mejs-overlay-button{background-position:0 -100px}.mejs-overlay-loading{position:absolute;top:50%;left:50%;width:80px;height:80px;margin:-40px 0 0 -40px;background:#333;background:url(background.png);background:rgba(0,0,0,.9);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(50,50,50,.9)),to(rgba(0,0,0,.9)));background:-webkit-linear-gradient(top,rgba(50,50,50,.9),rgba(0,0,0,.9));background:-moz-linear-gradient(top,rgba(50,50,50,.9),rgba(0,0,0,.9));background:-o-linear-gradient(top,rgba(50,50,50,.9),rgba(0,0,0,.9));background:-ms-linear-gradient(top,rgba(50,50,50,.9),rgba(0,0,0,.9));background:linear-gradient(rgba(50,50,50,.9),rgba(0,0,0,.9))}.mejs-overlay-loading span{display:block;width:80px;height:80px;background:transparent url(loading.gif) 50% 50% no-repeat}.mejs-container .mejs-controls{position:absolute;list-style-type:none;margin:0;padding:0;bottom:0;left:0;background:url(background.png);background:rgba(0,0,0,.7);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(50,50,50,.7)),to(rgba(0,0,0,.7)));background:-webkit-linear-gradient(top,rgba(50,50,50,.7),rgba(0,0,0,.7));background:-moz-linear-gradient(top,rgba(50,50,50,.7),rgba(0,0,0,.7));background:-o-linear-gradient(top,rgba(50,50,50,.7),rgba(0,0,0,.7));background:-ms-linear-gradient(top,rgba(50,50,50,.7),rgba(0,0,0,.7));background:linear-gradient(rgba(50,50,50,.7),rgba(0,0,0,.7));height:30px;width:100%}.mejs-container .mejs-controls div{list-style-type:none;background-image:none;display:block;float:left;margin:0;padding:0;width:26px;height:26px;font-size:11px;line-height:11px;font-family:Helvetica,Arial;border:0}.mejs-controls .mejs-button button{cursor:pointer;display:block;font-size:0;line-height:0;text-decoration:none;margin:7px 5px;padding:0;position:absolute;height:16px;width:16px;border:0;background:transparent url(controls.svg) no-repeat}.no-svg .mejs-controls .mejs-button button{background-image:url(controls.png)}.mejs-controls .mejs-button button:focus{outline:dotted 1px #999}.mejs-container .mejs-controls .mejs-time{color:#fff;display:block;height:17px;width:auto;padding:10px 3px 0;overflow:hidden;text-align:center;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}.mejs-container .mejs-controls .mejs-time a{color:#fff;font-size:11px;line-height:12px;display:block;float:left;margin:1px 2px 0 0;width:auto}.mejs-controls .mejs-play button{background-position:0 0}.mejs-controls .mejs-pause button{background-position:0 -16px}.mejs-controls .mejs-stop button{background-position:-112px 0}.mejs-controls div.mejs-time-rail{direction:ltr;width:200px;padding-top:5px}.mejs-controls .mejs-time-rail span,.mejs-controls .mejs-time-rail a{display:block;position:absolute;width:180px;height:10px;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;cursor:pointer}.mejs-controls .mejs-time-rail .mejs-time-total{margin:5px;background:#333;background:rgba(50,50,50,.8);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(30,30,30,.8)),to(rgba(60,60,60,.8)));background:-webkit-linear-gradient(top,rgba(30,30,30,.8),rgba(60,60,60,.8));background:-moz-linear-gradient(top,rgba(30,30,30,.8),rgba(60,60,60,.8));background:-o-linear-gradient(top,rgba(30,30,30,.8),rgba(60,60,60,.8));background:-ms-linear-gradient(top,rgba(30,30,30,.8),rgba(60,60,60,.8));background:linear-gradient(rgba(30,30,30,.8),rgba(60,60,60,.8))}.mejs-controls .mejs-time-rail .mejs-time-buffering{width:100%;background-image:-o-linear-gradient(-45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,.15)),color-stop(0.75,rgba(255,255,255,.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:15px 15px;-moz-background-size:15px 15px;-o-background-size:15px 15px;background-size:15px 15px;-webkit-animation:buffering-stripes 2s linear infinite;-moz-animation:buffering-stripes 2s linear infinite;-ms-animation:buffering-stripes 2s linear infinite;-o-animation:buffering-stripes 2s linear infinite;animation:buffering-stripes 2s linear infinite}@-webkit-keyframes buffering-stripes{from{background-position:0 0}to{background-position:30px 0}}@-moz-keyframes buffering-stripes{from{background-position:0 0}to{background-position:30px 0}}@-ms-keyframes buffering-stripes{from{background-position:0 0}to{background-position:30px 0}}@-o-keyframes buffering-stripes{from{background-position:0 0}to{background-position:30px 0}}@keyframes buffering-stripes{from{background-position:0 0}to{background-position:30px 0}}.mejs-controls .mejs-time-rail .mejs-time-loaded{background:#3caac8;background:rgba(60,170,200,.8);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(44,124,145,.8)),to(rgba(78,183,212,.8)));background:-webkit-linear-gradient(top,rgba(44,124,145,.8),rgba(78,183,212,.8));background:-moz-linear-gradient(top,rgba(44,124,145,.8),rgba(78,183,212,.8));background:-o-linear-gradient(top,rgba(44,124,145,.8),rgba(78,183,212,.8));background:-ms-linear-gradient(top,rgba(44,124,145,.8),rgba(78,183,212,.8));background:linear-gradient(rgba(44,124,145,.8),rgba(78,183,212,.8));width:0}.mejs-controls .mejs-time-rail .mejs-time-current{background:#fff;background:rgba(255,255,255,.8);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.9)),to(rgba(200,200,200,.8)));background:-webkit-linear-gradient(top,rgba(255,255,255,.9),rgba(200,200,200,.8));background:-moz-linear-gradient(top,rgba(255,255,255,.9),rgba(200,200,200,.8));background:-o-linear-gradient(top,rgba(255,255,255,.9),rgba(200,200,200,.8));background:-ms-linear-gradient(top,rgba(255,255,255,.9),rgba(200,200,200,.8));background:linear-gradient(rgba(255,255,255,.9),rgba(200,200,200,.8));width:0}.mejs-controls .mejs-time-rail .mejs-time-handle{display:none;position:absolute;margin:0;width:10px;background:#fff;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;cursor:pointer;border:solid 2px #333;top:-2px;text-align:center}.mejs-controls .mejs-time-rail .mejs-time-float{position:absolute;display:none;background:#eee;width:36px;height:17px;border:solid 1px #333;top:-26px;margin-left:-18px;text-align:center;color:#111}.mejs-controls .mejs-time-rail .mejs-time-float-current{margin:2px;width:30px;display:block;text-align:center;left:0}.mejs-controls .mejs-time-rail .mejs-time-float-corner{position:absolute;display:block;width:0;height:0;line-height:0;border:solid 5px #eee;border-color:#eee transparent transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;top:15px;left:13px}.mejs-long-video .mejs-controls .mejs-time-rail .mejs-time-float{width:48px}.mejs-long-video .mejs-controls .mejs-time-rail .mejs-time-float-current{width:44px}.mejs-long-video .mejs-controls .mejs-time-rail .mejs-time-float-corner{left:18px}.mejs-controls .mejs-fullscreen-button button{background-position:-32px 0}.mejs-controls .mejs-unfullscreen button{background-position:-32px -16px}.mejs-controls .mejs-volume-button{}.mejs-controls .mejs-mute button{background-position:-16px -16px}.mejs-controls .mejs-unmute button{background-position:-16px 0}.mejs-controls .mejs-volume-button{position:relative}.mejs-controls .mejs-volume-button .mejs-volume-slider{display:none;height:115px;width:25px;background:url(background.png);background:rgba(50,50,50,.7);-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;top:-115px;left:0;z-index:1;position:absolute;margin:0}.mejs-controls .mejs-volume-button:hover{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.mejs-controls .mejs-volume-button .mejs-volume-slider .mejs-volume-total{position:absolute;left:11px;top:8px;width:2px;height:100px;background:#ddd;background:rgba(255,255,255,.5);margin:0}.mejs-controls .mejs-volume-button .mejs-volume-slider .mejs-volume-current{position:absolute;left:11px;top:8px;width:2px;height:100px;background:#ddd;background:rgba(255,255,255,.9);margin:0}.mejs-controls .mejs-volume-button .mejs-volume-slider .mejs-volume-handle{position:absolute;left:4px;top:-3px;width:16px;height:6px;background:#ddd;background:rgba(255,255,255,.9);cursor:N-resize;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;margin:0}.mejs-controls a.mejs-horizontal-volume-slider{height:26px;width:56px;position:relative;display:block;float:left;vertical-align:middle}.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-total{position:absolute;left:0;top:11px;width:50px;height:8px;margin:0;padding:0;font-size:1px;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;background:#333;background:rgba(50,50,50,.8);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(30,30,30,.8)),to(rgba(60,60,60,.8)));background:-webkit-linear-gradient(top,rgba(30,30,30,.8),rgba(60,60,60,.8));background:-moz-linear-gradient(top,rgba(30,30,30,.8),rgba(60,60,60,.8));background:-o-linear-gradient(top,rgba(30,30,30,.8),rgba(60,60,60,.8));background:-ms-linear-gradient(top,rgba(30,30,30,.8),rgba(60,60,60,.8));background:linear-gradient(rgba(30,30,30,.8),rgba(60,60,60,.8))}.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-current{position:absolute;left:0;top:11px;width:50px;height:8px;margin:0;padding:0;font-size:1px;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;background:#fff;background:rgba(255,255,255,.8);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.9)),to(rgba(200,200,200,.8)));background:-webkit-linear-gradient(top,rgba(255,255,255,.9),rgba(200,200,200,.8));background:-moz-linear-gradient(top,rgba(255,255,255,.9),rgba(200,200,200,.8));background:-o-linear-gradient(top,rgba(255,255,255,.9),rgba(200,200,200,.8));background:-ms-linear-gradient(top,rgba(255,255,255,.9),rgba(200,200,200,.8));background:linear-gradient(rgba(255,255,255,.9),rgba(200,200,200,.8))}.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-handle{display:none}.mejs-controls .mejs-captions-button{position:relative}.mejs-controls .mejs-captions-button button{background-position:-48px 0}.mejs-controls .mejs-captions-button .mejs-captions-selector{visibility:hidden;position:absolute;bottom:26px;right:-51px;width:85px;height:100px;background:url(background.png);background:rgba(50,50,50,.7);border:solid 1px transparent;padding:10px 10px 0;overflow:hidden;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mejs-controls .mejs-captions-button:hover .mejs-captions-selector{visibility:visible}.mejs-controls .mejs-captions-button .mejs-captions-selector ul{margin:0;padding:0;display:block;list-style-type:none!important;overflow:hidden}.mejs-controls .mejs-captions-button .mejs-captions-selector ul li{margin:0 0 6px;padding:0;list-style-type:none!important;display:block;color:#fff;overflow:hidden}.mejs-controls .mejs-captions-button .mejs-captions-selector ul li input{clear:both;float:left;margin:3px 3px 0 5px}.mejs-controls .mejs-captions-button .mejs-captions-selector ul li label{width:55px;float:left;padding:4px 0 0;line-height:15px;font-family:helvetica,arial;font-size:10px}.mejs-controls .mejs-captions-button .mejs-captions-translations{font-size:10px;margin:0 0 5px}.mejs-chapters{position:absolute;top:0;left:0;-xborder-right:solid 1px #fff;width:10000px;z-index:1}.mejs-chapters .mejs-chapter{position:absolute;float:left;background:#222;background:rgba(0,0,0,.7);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(50,50,50,.7)),to(rgba(0,0,0,.7)));background:-webkit-linear-gradient(top,rgba(50,50,50,.7),rgba(0,0,0,.7));background:-moz-linear-gradient(top,rgba(50,50,50,.7),rgba(0,0,0,.7));background:-o-linear-gradient(top,rgba(50,50,50,.7),rgba(0,0,0,.7));background:-ms-linear-gradient(top,rgba(50,50,50,.7),rgba(0,0,0,.7));background:linear-gradient(rgba(50,50,50,.7),rgba(0,0,0,.7));filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0, startColorstr=#323232, endColorstr=#000000);overflow:hidden;border:0}.mejs-chapters .mejs-chapter .mejs-chapter-block{font-size:11px;color:#fff;padding:5px;display:block;border-right:solid 1px #333;border-bottom:solid 1px #333;cursor:pointer}.mejs-chapters .mejs-chapter .mejs-chapter-block-last{border-right:0}.mejs-chapters .mejs-chapter .mejs-chapter-block:hover{background:#666;background:rgba(102,102,102,.7);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(102,102,102,.7)),to(rgba(50,50,50,.6)));background:-webkit-linear-gradient(top,rgba(102,102,102,.7),rgba(50,50,50,.6));background:-moz-linear-gradient(top,rgba(102,102,102,.7),rgba(50,50,50,.6));background:-o-linear-gradient(top,rgba(102,102,102,.7),rgba(50,50,50,.6));background:-ms-linear-gradient(top,rgba(102,102,102,.7),rgba(50,50,50,.6));background:linear-gradient(rgba(102,102,102,.7),rgba(50,50,50,.6));filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0, startColorstr=#666666, endColorstr=#323232)}.mejs-chapters .mejs-chapter .mejs-chapter-block .ch-title{font-size:12px;font-weight:700;display:block;white-space:nowrap;text-overflow:ellipsis;margin:0 0 3px;line-height:12px}.mejs-chapters .mejs-chapter .mejs-chapter-block .ch-timespan{font-size:12px;line-height:12px;margin:3px 0 4px;display:block;white-space:nowrap;text-overflow:ellipsis}.mejs-captions-layer{position:absolute;bottom:0;left:0;text-align:center;line-height:20px;font-size:16px;color:#fff}.mejs-captions-layer a{color:#fff;text-decoration:underline}.mejs-captions-layer[lang=ar]{font-size:20px;font-weight:400}.mejs-captions-position{position:absolute;width:100%;bottom:15px;left:0}.mejs-captions-position-hover{bottom:35px}.mejs-captions-text{padding:3px 5px;background:url(background.png);background:rgba(20,20,20,.5);white-space:pre-wrap}.me-cannotplay{}.me-cannotplay a{color:#fff;font-weight:700}.me-cannotplay span{padding:15px;display:block}.mejs-controls .mejs-loop-off button{background-position:-64px -16px}.mejs-controls .mejs-loop-on button{background-position:-64px 0}.mejs-controls .mejs-backlight-off button{background-position:-80px -16px}.mejs-controls .mejs-backlight-on button{background-position:-80px 0}.mejs-controls .mejs-picturecontrols-button{background-position:-96px 0}.mejs-contextmenu{position:absolute;width:150px;padding:10px;border-radius:4px;top:0;left:0;background:#fff;border:solid 1px #999;z-index:1001}.mejs-contextmenu .mejs-contextmenu-separator{height:1px;font-size:0;margin:5px 6px;background:#333}.mejs-contextmenu .mejs-contextmenu-item{font-family:Helvetica,Arial;font-size:12px;padding:4px 6px;cursor:pointer;color:#333}.mejs-contextmenu .mejs-contextmenu-item:hover{background:#2C7C91;color:#fff}.mejs-controls .mejs-sourcechooser-button{position:relative}.mejs-controls .mejs-sourcechooser-button button{background-position:-128px 0}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector{visibility:hidden;position:absolute;bottom:26px;right:-10px;width:130px;height:100px;background:url(background.png);background:rgba(50,50,50,.7);border:solid 1px transparent;padding:10px;overflow:hidden;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul{margin:0;padding:0;display:block;list-style-type:none!important;overflow:hidden}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul li{margin:0 0 6px;padding:0;list-style-type:none!important;display:block;color:#fff;overflow:hidden}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul li input{clear:both;float:left;margin:3px 3px 0 5px}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul li label{width:100px;float:left;padding:4px 0 0;line-height:15px;font-family:helvetica,arial;font-size:10px}.mejs-postroll-layer{position:absolute;bottom:0;left:0;width:100%;height:100%;background:url(background.png);background:rgba(50,50,50,.7);z-index:1000;overflow:hidden}.mejs-postroll-layer-content{width:100%;height:100%}.mejs-postroll-close{position:absolute;right:0;top:0;background:url(background.png);background:rgba(50,50,50,.7);color:#fff;padding:4px;z-index:100;cursor:pointer}div.mejs-speed-button{width:46px!important;position:relative}.mejs-controls .mejs-button.mejs-speed-button button{background:transparent;width:36px;font-size:11px;line-height:normal;color:#fff}.mejs-controls .mejs-speed-button .mejs-speed-selector{display:none;position:absolute;top:-100px;left:-10px;width:60px;height:100px;background:url(background.png);background:rgba(50,50,50,.7);border:solid 1px transparent;padding:0;overflow:hidden;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mejs-controls .mejs-speed-button:hover>.mejs-speed-selector{display:block}.mejs-controls .mejs-speed-button .mejs-speed-selector ul li label.mejs-speed-selected{color:rgba(33,248,248,1)}.mejs-controls .mejs-speed-button .mejs-speed-selector ul{margin:0;padding:0;display:block;list-style-type:none!important;overflow:hidden}.mejs-controls .mejs-speed-button .mejs-speed-selector ul li{margin:0 0 6px;padding:0 10px;list-style-type:none!important;display:block;color:#fff;overflow:hidden}.mejs-controls .mejs-speed-button .mejs-speed-selector ul li input{clear:both;float:left;margin:3px 3px 0 5px;display:none}.mejs-controls .mejs-speed-button .mejs-speed-selector ul li label{width:60px;float:left;padding:4px 0 0;line-height:15px;font-family:helvetica,arial;font-size:11.5px;color:#fff;margin-left:5px;cursor:pointer}.mejs-controls .mejs-speed-button .mejs-speed-selector ul li:hover{background-color:#c8c8c8!important;background-color:rgba(255,255,255,.4)!important}.mejs-controls .mejs-button.mejs-jump-forward-button{background:transparent url(jumpforward.png) no-repeat;background-position:3px 3px}.mejs-controls .mejs-button.mejs-jump-forward-button button{background:transparent;font-size:9px;line-height:normal;color:#fff}.mejs-controls .mejs-button.mejs-skip-back-button{background:transparent url(skipback.png) no-repeat;background-position:3px 3px}.mejs-controls .mejs-button.mejs-skip-back-button button{background:transparent;font-size:9px;line-height:normal;color:#fff}
--------------------------------------------------------------------------------
/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: 'Maven Pro', sans-serif;
3 | margin: 20px;
4 | }
5 |
6 | .audio-wrapper {
7 | padding: 10px 0px;
8 | }
9 |
10 | .audio-wrapper p {
11 | font-size: 14px;
12 | text-align: center;
13 | color: gray;
14 | margin-top: 5px;
15 | margin-bottom: 0;
16 | }
17 |
18 | .img-wrapper {
19 | text-align: center;
20 | }
21 |
22 | .img-wrapper > img {
23 | max-width: 560px;
24 | width: 100%;
25 | }
26 |
27 | .img-wrapper > p {
28 | font-size: 14px;
29 | text-align: center;
30 | color: gray;
31 | margin-top: 0;
32 | }
33 |
34 | a {
35 | color:gray;
36 | text-decoration:none;
37 | }
38 |
39 | a:hover {
40 | color:lightgray;
41 | }
42 |
43 | .container {
44 | max-width: 700px;
45 | margin: 0 auto;
46 | }
47 |
48 | h2:after {
49 | margin-top: 5px;
50 | content: ' ';
51 | display: block;
52 | border: 1px solid black;
53 | }
54 |
55 | /* graph titles */
56 | h3 {
57 | text-align: center;
58 | }
59 |
60 | /* LEGEND styling */
61 |
62 | .legend {
63 | overflow: auto;
64 | }
65 |
66 | .legend .legend-title {
67 | text-align: left;
68 | margin-bottom: 8px;
69 | font-weight: bold;
70 | font-size: 90%;
71 | }
72 |
73 | .legend .legend-scale ul {
74 | margin-top: 10px;
75 | margin-bottom: 0px;
76 | padding: 0;
77 | float: left;
78 | list-style: none;
79 | }
80 |
81 | .legend .legend-scale ul li {
82 | display: block;
83 | float: left;
84 | width: 100px;
85 | text-align: center;
86 | font-size: 80%;
87 | list-style: none;
88 | }
89 |
90 | .legend ul.pie-legend li span {
91 | display: inline-block;
92 | height: 15px;
93 | width: 75px;
94 | }
95 |
96 | .legend ul.bar-legend li span {
97 | display: inline-block;
98 | height: 15px;
99 | width: 75px;
100 | }
101 |
--------------------------------------------------------------------------------
/data_samples/alb_esp1.mid:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/data_samples/alb_esp1.mid
--------------------------------------------------------------------------------
/data_samples/ashover_simple_chords_1.mid:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/data_samples/ashover_simple_chords_1.mid
--------------------------------------------------------------------------------
/data_samples/bach_chorale.mid:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/data_samples/bach_chorale.mid
--------------------------------------------------------------------------------
/data_samples/koopa_troopa_beach.mid:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/data_samples/koopa_troopa_beach.mid
--------------------------------------------------------------------------------
/data_samples/reels_simple_chords_157.mid:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/data_samples/reels_simple_chords_157.mid
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/favicon.ico
--------------------------------------------------------------------------------
/generated_music/mp3/1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/generated_music/mp3/1.mp3
--------------------------------------------------------------------------------
/generated_music/mp3/10.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/generated_music/mp3/10.mp3
--------------------------------------------------------------------------------
/generated_music/mp3/2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/generated_music/mp3/2.mp3
--------------------------------------------------------------------------------
/generated_music/mp3/3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/generated_music/mp3/3.mp3
--------------------------------------------------------------------------------
/generated_music/mp3/4.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/generated_music/mp3/4.mp3
--------------------------------------------------------------------------------
/generated_music/mp3/5.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/generated_music/mp3/5.mp3
--------------------------------------------------------------------------------
/generated_music/mp3/6.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/generated_music/mp3/6.mp3
--------------------------------------------------------------------------------
/generated_music/mp3/7.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/generated_music/mp3/7.mp3
--------------------------------------------------------------------------------
/generated_music/mp3/8.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/generated_music/mp3/8.mp3
--------------------------------------------------------------------------------
/generated_music/mp3/9.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/generated_music/mp3/9.mp3
--------------------------------------------------------------------------------
/generated_music/mp3/nottingham_sample.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/generated_music/mp3/nottingham_sample.mp3
--------------------------------------------------------------------------------
/img/dual_softmax_fig.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/img/dual_softmax_fig.png
--------------------------------------------------------------------------------
/img/encoding.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/img/encoding.png
--------------------------------------------------------------------------------
/img/overfitting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yoavz/music_rnn/54f0386664ff87ac1cd6ca09b225f49c6b059b0e/img/overfitting.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Music Language Modeling with RNN's
10 |
11 |
12 |
13 |
14 |
15 |
16 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
Music Language Modeling with Recurrent Neural Networks
35 |
36 |
47 |
48 |
49 |
Introduction
50 |
Neural Networks are all the rage these days, and with good reason. Microsoft Research's winning model on the 2015 ImageNet competition is classifying images with 3.57% error rate (human performance is 5.1%). Google used a variant to crush one of the world's best Go players 4-1. Crazy things are happening in the field, with no sign of slowing down. In this project, I've applied recurrent neural nets to learn a predictive model over symbolic sequences of music.
51 |
Disclaimer: This post assumes a familiarity with machine learning and neural networks. For a good overview of RNN's, I highly recommend reading Andrej Karpathy's excellent blog post here for an in-depth explanation .
52 |
53 |
54 |
55 |
Music Language Modeling
56 |
Music Language Modeling is the problem of modeling symbolic sequences of polyphonic music in a completely general piano roll representation. Piano roll representation is a key distinction here, meaning we're going to use the symbolic note sequences as represented by sheet music, as opposed to more complex, acoustically rich audio signals. MIDI files are perfect for this, as they encode all the note information exactly to how it would be displayed on a piano roll.
57 |
58 |
59 |
60 |
The most straightforward way to learn this way is to discretize a piece of music into uniform time steps. There are 88 possible pitches from A0 to C8 in a MIDI file, so every time step is encoded into an 88-dimensional binary vector as shown above. A value of 1 at index i indicates that pitch i is playing at a given time step. Then we plug this sequence of input vectors into an RNN architecture, where at each step the target is to predict the next time step of the sequence. A trained model outputs the conditional distribution of notes at a time step, given the all the time steps that have occured before it.
61 |
62 |
One problem with this naive formulation is that the amount of potential note configurations is too high ($2^{N}$ for $N$ possible notes) to take the softmax classification approach normally used in image classification and language modeling. Instead, we need to use a sigmoid cross-entropy loss function to predict the probability of whether each note class is active or not separately . However, this approach does not make much sense for the complex joint distribution of notes usually found in a time step. For example, C is much more likely than C# to be playing when E and G are also active, but separate classification targets implicity assumes independence between note probabilities at the same time step. Modeling Temporal Dependencies in High-Dimensional Sequences (Boulanger-Lewandowski, 2012) , perhaps the most succesful research paper on MLM so far, attempts to solve this problem using energy based generative models such as the Restricted Boltzmann Machine (RBM). They propose the combined RNN-RBM architecture, which achieves state-of-the-art performance on several music datasets.
63 |
64 |
65 |
66 |
Model
67 |
For my model, I decided to take the approach of introducing more musical structure into learning. Many musical pieces can be separated into two parts: a melody and harmony. I make the two following assumptions about a piece of music for my model: First, the melody is monophonic (only one note at most playing at every time step). Second, the harmony at each time step can be classified into a chord class. For example, a C, E, G active during a time step would is classified as C Major . These are strong assumptions, but they lead to the nice property of exactly one active melody class and one active harmony class at every time step. This allows us to take the sum of two softmax functions as the loss function for our model.
68 |
69 |
70 |
71 |
My model works in the following way: for every time step I encode the melody note into a one-hot-encoding binary vector. I then use the notes playing in the harmony to infer the chord class, and turn that into a one-hot-encoding binary vector as well. The full input vector is a concatentation of the melody and harmony vectors. This input vector then passes through hidden layer(s) of LSTM cells. The loss function is the sum of two separate softmax loss functions over the respective melody and harmony parts of the output layer.
72 |
73 |
74 | $L(z, m, h) = \alpha \, log \bigg( \frac{ e^{z_m} }{ \sum_{n=0}^{M-1}{ e^{z_n}}} \bigg) + (1 - \alpha) \, log \bigg( \frac{ e^{z_{M+h}} }{ \sum_{n=M}^{M+H}{ e^{z_n}}} \bigg)$
75 |
76 |
77 |
If we have $M$ melody classes and $H$ harmony classes, the function above describes the log-likelihood loss at a time step given the output layer $z \in \mathbb{R}^{M+H}$, a target melody class $m$, and target harmony class $h$. $\alpha$ is what I call the melody coefficient that controls how much the loss function is affected by it's respective melody and harmony loss terms.
78 |
79 |
80 |
81 |
Experiments
82 |
The Nottingham dataset is a collection of 1200 jigs and folk tunes, most of which fit the assumptions specified above: they have a simple monophonic melody on top of recognizable chords. You can download the all the nottingham tunes as MIDI files here . I discretized each of these sequences into time steps of sixteenth notes (1/4 of a quarter note), and used the mingus python package to detect the chord classes in the harmonies. After some filtering out some sequences that didn't fit the assumptions, I ended up with 32 chord classes and 34 possible melody notes (1 class from each of these represented a rest) for a total input dimension of 66 over 997 sequences. The average length of a sequence was 516 (roughly 32 measures in 4/4). Finally, all the sequences were split up into 65% training, 15% validation, and 15% testing.
83 |
84 |
85 |
86 |
An example musical sequence from the Nottingham dataset
87 |
88 |
89 |
I used Google's TensorFlow library to implement my model. The architecture that I found worked best was 2 stacked hidden layers of 200 LSTM units each. I batched sequences by length, and used an unrolling length of 128 (8 measures in 4/4 time signature) for Backpropagation through time (BPTT) . I used RMSProp with a learning rate of 0.005 and decay rate of 0.9 for minibatch gradient descent. When searching over the hyperparameter space, I trained each model for 250 epochs, and saved the model with the lowest validation loss.
90 |
91 |
92 |
93 |
94 |
Training and validation loss plotted over num epochs for a model with 2 stacked layers of 200 LSTM units, with 50% dropout on hidden layers and 80% dropout on input layers. Overfitting issues start showing up after about 20 epochs.
95 |
96 |
97 |
One big issue I ran into when training was extreme overfitting. Adding dropout on the non-recurrent connections helped some, but did not completely eliminate the issue. The best dropout configuration I found and ended up using was 50% on the hidden layers and 80% on the input layers.
98 |
99 |
100 |
101 |
Results
102 |
The best model I found achieved on overall accuracy of 77.84% on the test set. One nice consequence of my model is I can evaluate the melody and harmony accuracies separately, which ended up being 64.15% and 91.57% for the melody and harmony respectively. The higher harmony accuracy makes sense, because most of the pieces in the dataset hold out chords for 8 or 16 time steps (a half or whole note in 4/4 time).
103 |
Alright, enough numbers, let's get to the fun stuff. Once the model is trained, generating music from it is just a matter of sampling a melody and harmony from the probability distribution at each time step and plugging it back into the network. Rinse and repeat. I present to you 8 more pieces generated by my model below. I "primed" each with the starting 16 time steps (1 measure in 4/4 time) from a random test sequence, and then let them do their thing for 2048 time steps.
104 |
105 |
108 |
111 |
114 |
117 |
120 |
123 |
126 |
129 |
Some turned out sounding better than others to my ears, but overall the model clearly does not produce human-level compositions. The lack of long-term structure such as repeated phrases and themes is especially revealing. However, for the most part, the model seems to play a melody in key with the harmony that it chooses. The melody also tends to stay in the same key signature for short-term phrases, and sometimes the harmony accompanies it with short chord progressions in that same key. There does also seem to be small pieces of coherent rhythmic structure, although the "time signature" overall throughout a piece is sporadic.
130 |
131 |
132 |
133 |
Many thanks go out to Fei Sha for providing valuable advice; this work was completed as part of my final project for his research seminar. If you're interested in learning more, the final report contains more about the model and a few more experimental results. The source code is also available on github here if you'd like to use my code to train your own models! (warning: messy code)
.
134 |
135 |
136 |
138 |
139 |
140 |
141 |
144 |
145 |
146 |
--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------
1 | conda create -n music_rnn python=2.7
2 | source activate music_rnn
3 |
4 | pip install -r requirements.txt
5 |
6 | mkdir models
7 |
8 | mkdir data
9 | # http://www-etud.iro.umontreal.ca/~boulanni/icml2012
10 | wget http://www-etud.iro.umontreal.ca/~boulanni/Nottingham.zip -O data/Nottingham.zip
11 | unzip data/Nottingham.zip -d data/
12 |
--------------------------------------------------------------------------------
/js/mediaelement-and-player.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | *
3 | * MediaElement.js
4 | * HTML5
and shim and player
5 | * http://mediaelementjs.com/
6 | *
7 | * Creates a JavaScript object that mimics HTML5 MediaElement API
8 | * for browsers that don't understand HTML5 or can't play the provided codec
9 | * Can play MP4 (H.264), Ogg, WebM, FLV, WMV, WMA, ACC, and MP3
10 | *
11 | * Copyright 2010-2014, John Dyer (http://j.hn)
12 | * License: MIT
13 | *
14 | */
15 | var mejs=mejs||{};mejs.version="2.20.1",mejs.meIndex=0,mejs.plugins={silverlight:[{version:[3,0],types:["video/mp4","video/m4v","video/mov","video/wmv","audio/wma","audio/m4a","audio/mp3","audio/wav","audio/mpeg"]}],flash:[{version:[9,0,124],types:["video/mp4","video/m4v","video/mov","video/flv","video/rtmp","video/x-flv","audio/flv","audio/x-flv","audio/mp3","audio/m4a","audio/mpeg","video/dailymotion","video/x-dailymotion","application/x-mpegURL"]}],youtube:[{version:null,types:["video/youtube","video/x-youtube","audio/youtube","audio/x-youtube"]}],vimeo:[{version:null,types:["video/vimeo","video/x-vimeo"]}]},mejs.Utility={encodeUrl:function(a){return encodeURIComponent(a)},escapeHTML:function(a){return a.toString().split("&").join("&").split("<").join("<").split('"').join(""")},absolutizeUrl:function(a){var b=document.createElement("div");return b.innerHTML='x ',b.firstChild.href},getScriptPath:function(a){for(var b,c,d,e,f,g,h=0,i="",j="",k=document.getElementsByTagName("script"),l=k.length,m=a.length;l>h;h++){for(e=k[h].src,c=e.lastIndexOf("/"),c>-1?(g=e.substring(c+1),f=e.substring(0,c+1)):(g=e,f=""),b=0;m>b;b++)if(j=a[b],d=g.indexOf(j),d>-1){i=f;break}if(""!==i)break}return i},calculateTimeFormat:function(a,b,c){0>a&&(a=0),"undefined"==typeof c&&(c=25);var d=b.timeFormat,e=d[0],f=d[1]==d[0],g=f?2:1,h=":",i=Math.floor(a/3600)%24,j=Math.floor(a/60)%60,k=Math.floor(a%60),l=Math.floor((a%1*c).toFixed(3)),m=[[l,"f"],[k,"s"],[j,"m"],[i,"h"]];d.lengtho;o++)if(-1!==d.indexOf(m[o][1]))n=!0;else if(n){for(var q=!1,r=o;p>r;r++)if(m[r][0]>0){q=!0;break}if(!q)break;f||(d=e+d),d=m[o][1]+h+d,f&&(d=m[o][1]+d),e=m[o][1]}b.currentTimeFormat=d},twoDigitsString:function(a){return 10>a?"0"+a:String(a)},secondsToTimeCode:function(a,b){if(0>a&&(a=0),"object"!=typeof b){var c="m:ss";c=arguments[1]?"hh:mm:ss":c,c=arguments[2]?c+":ff":c,b={currentTimeFormat:c,framesPerSecond:arguments[3]||25}}var d=b.framesPerSecond;"undefined"==typeof d&&(d=25);var c=b.currentTimeFormat,e=Math.floor(a/3600)%24,f=Math.floor(a/60)%60,g=Math.floor(a%60),h=Math.floor((a%1*d).toFixed(3));lis=[[h,"f"],[g,"s"],[f,"m"],[e,"h"]];var j=c;for(i=0,len=lis.length;i0&&(d=Math.pow(60,e)),b+=Number(a[e])*d;return Number(b.toFixed(c))},removeSwf:function(a){var b=document.getElementById(a);b&&/object|embed/i.test(b.nodeName)&&(mejs.MediaFeatures.isIE?(b.style.display="none",function(){4==b.readyState?mejs.Utility.removeObjectInIE(a):setTimeout(arguments.callee,10)}()):b.parentNode.removeChild(b))},removeObjectInIE:function(a){var b=document.getElementById(a);if(b){for(var c in b)"function"==typeof b[c]&&(b[c]=null);b.parentNode.removeChild(b)}}},mejs.PluginDetector={hasPluginVersion:function(a,b){var c=this.plugins[a];return b[1]=b[1]||0,b[2]=b[2]||0,c[0]>b[0]||c[0]==b[0]&&c[1]>b[1]||c[0]==b[0]&&c[1]==b[1]&&c[2]>=b[2]?!0:!1},nav:window.navigator,ua:window.navigator.userAgent.toLowerCase(),plugins:[],addPlugin:function(a,b,c,d,e){this.plugins[a]=this.detectPlugin(b,c,d,e)},detectPlugin:function(a,b,c,d){var e,f,g,h=[0,0,0];if("undefined"!=typeof this.nav.plugins&&"object"==typeof this.nav.plugins[a]){if(e=this.nav.plugins[a].description,e&&("undefined"==typeof this.nav.mimeTypes||!this.nav.mimeTypes[b]||this.nav.mimeTypes[b].enabledPlugin))for(h=e.replace(a,"").replace(/^\s+/,"").replace(/\sr/gi,".").split("."),f=0;f0;)this.removeChild(b[0]);if("string"==typeof a)this.src=a;else{var c,d;for(c=0;c0&&null!==q[0].url&&this.getTypeFromFile(q[0].url).indexOf("audio")>-1&&(r.isVideo=!1),mejs.MediaFeatures.isBustedAndroid&&(a.canPlayType=function(a){return null!==a.match(/video\/(mp4|m4v)/gi)?"maybe":""}),mejs.MediaFeatures.isChromium&&(a.canPlayType=function(a){return null!==a.match(/video\/(webm|ogv|ogg)/gi)?"maybe":""}),c&&("auto"===b.mode||"auto_plugin"===b.mode||"native"===b.mode)&&(!mejs.MediaFeatures.isBustedNativeHTTPS||b.httpsBasicAuthSite!==!0)){for(d||(o=document.createElement(r.isVideo?"video":"audio"),a.parentNode.insertBefore(o,a),a.style.display="none",r.htmlMediaElement=a=o),f=0;f0&&(r.url=q[0].url),r)},formatType:function(a,b){return a&&!b?this.getTypeFromFile(a):b&&~b.indexOf(";")?b.substr(0,b.indexOf(";")):b},getTypeFromFile:function(a){a=a.split("?")[0];var b=a.substring(a.lastIndexOf(".")+1).toLowerCase(),c=/(mp4|m4v|ogg|ogv|m3u8|webm|webmv|flv|wmv|mpeg|mov)/gi.test(b)?"video/":"audio/";return this.getTypeFromExtension(b,c)},getTypeFromExtension:function(a,b){switch(b=b||"",a){case"mp4":case"m4v":case"m4a":case"f4v":case"f4a":return b+"mp4";case"flv":return b+"x-flv";case"webm":case"webma":case"webmv":return b+"webm";case"ogg":case"oga":case"ogv":return b+"ogg";case"m3u8":return"application/x-mpegurl";case"ts":return b+"mp2t";default:return b+a}},createErrorMessage:function(a,b,c){var d=a.htmlMediaElement,e=document.createElement("div"),f=b.customError;e.className="me-cannotplay";try{e.style.width=d.width+"px",e.style.height=d.height+"px"}catch(g){}f||(f='',""!==c&&(f+=' '),f+=""+mejs.i18n.t("Download File")+" "),e.innerHTML=f,d.parentNode.insertBefore(e,d),d.style.display="none",b.error(d)},createPlugin:function(a,b,c,d,e,f){var g,h,i,j=a.htmlMediaElement,k=1,l=1,m="me_"+a.method+"_"+mejs.meIndex++,n=new mejs.PluginMediaElement(m,a.method,a.url),o=document.createElement("div");n.tagName=j.tagName;for(var p=0;p0?b.pluginWidth:b.videoWidth>0?b.videoWidth:null!==j.getAttribute("width")?j.getAttribute("width"):b.defaultVideoWidth,l=b.pluginHeight>0?b.pluginHeight:b.videoHeight>0?b.videoHeight:null!==j.getAttribute("height")?j.getAttribute("height"):b.defaultVideoHeight,k=mejs.Utility.encodeUrl(k),l=mejs.Utility.encodeUrl(l)):b.enablePluginDebug&&(k=320,l=240),n.success=b.success,mejs.MediaPluginBridge.registerPluginElement(m,n,j),o.className="me-plugin",o.id=m+"_container",a.isVideo?j.parentNode.insertBefore(o,j):document.body.insertBefore(o,document.body.childNodes[0]),i=["id="+m,"jsinitfunction=mejs.MediaPluginBridge.initPlugin","jscallbackfunction=mejs.MediaPluginBridge.fireEvent","isvideo="+(a.isVideo?"true":"false"),"autoplay="+(d?"true":"false"),"preload="+e,"width="+k,"startvolume="+b.startVolume,"timerrate="+b.timerRate,"flashstreamer="+b.flashStreamer,"height="+l,"pseudostreamstart="+b.pseudoStreamingStartQueryParam],null!==a.url&&("flash"==a.method?i.push("file="+mejs.Utility.encodeUrl(a.url)):i.push("file="+a.url)),b.enablePluginDebug&&i.push("debug=true"),b.enablePluginSmoothing&&i.push("smoothing=true"),b.enablePseudoStreaming&&i.push("pseudostreaming=true"),f&&i.push("controls=true"),b.pluginVars&&(i=i.concat(b.pluginVars)),a.method){case"silverlight":o.innerHTML=' ';break;case"flash":mejs.MediaFeatures.isIE?(g=document.createElement("div"),o.appendChild(g),g.outerHTML=' '):o.innerHTML=' ';break;case"youtube":var r;-1!=a.url.lastIndexOf("youtu.be")?(r=a.url.substr(a.url.lastIndexOf("/")+1),-1!=r.indexOf("?")&&(r=r.substr(0,r.indexOf("?")))):r=a.url.substr(a.url.lastIndexOf("=")+1),youtubeSettings={container:o,containerId:o.id,pluginMediaElement:n,pluginId:m,videoId:r,height:l,width:k},window.postMessage?mejs.YouTubeApi.enqueueIframe(youtubeSettings):mejs.PluginDetector.hasPluginVersion("flash",[10,0,0])&&mejs.YouTubeApi.createFlash(youtubeSettings,b);break;case"vimeo":var s=m+"_player";if(n.vimeoid=a.url.substr(a.url.lastIndexOf("/")+1),o.innerHTML='',"function"==typeof $f){var t=$f(o.childNodes[0]);t.addEvent("ready",function(){function a(a,b,c,d){var e={type:c,target:b};"timeupdate"==c&&(b.currentTime=e.currentTime=d.seconds,b.duration=e.duration=d.duration),b.dispatchEvent(e)}t.playVideo=function(){t.api("play")},t.stopVideo=function(){t.api("unload")},t.pauseVideo=function(){t.api("pause")},t.seekTo=function(a){t.api("seekTo",a)},t.setVolume=function(a){t.api("setVolume",a)},t.setMuted=function(a){a?(t.lastVolume=t.api("getVolume"),t.api("setVolume",0)):(t.api("setVolume",t.lastVolume),delete t.lastVolume)},t.addEvent("play",function(){a(t,n,"play"),a(t,n,"playing")}),t.addEvent("pause",function(){a(t,n,"pause")}),t.addEvent("finish",function(){a(t,n,"ended")}),t.addEvent("playProgress",function(b){a(t,n,"timeupdate",b)}),n.pluginElement=o,n.pluginApi=t,mejs.MediaPluginBridge.initPlugin(m)})}else console.warn("You need to include froogaloop for vimeo to work")}return j.style.display="none",j.removeAttribute("autoplay"),n},updateNative:function(a,b,c,d){var e,f=a.htmlMediaElement;for(e in mejs.HtmlMediaElement)f[e]=mejs.HtmlMediaElement[e];return b.success(f,f),f}},mejs.YouTubeApi={isIframeStarted:!1,isIframeLoaded:!1,loadIframeApi:function(){if(!this.isIframeStarted){var a=document.createElement("script");a.src="//www.youtube.com/player_api";var b=document.getElementsByTagName("script")[0];b.parentNode.insertBefore(a,b),this.isIframeStarted=!0}},iframeQueue:[],enqueueIframe:function(a){this.isLoaded?this.createIframe(a):(this.loadIframeApi(),this.iframeQueue.push(a))},createIframe:function(a){var b=a.pluginMediaElement,c=new YT.Player(a.containerId,{height:a.height,width:a.width,videoId:a.videoId,playerVars:{controls:0,wmode:"transparent"},events:{onReady:function(){c.setVideoSize=function(a,b){c.setSize(a,b)},a.pluginMediaElement.pluginApi=c,a.pluginMediaElement.pluginElement=document.getElementById(a.containerId),mejs.MediaPluginBridge.initPlugin(a.pluginId),setInterval(function(){mejs.YouTubeApi.createEvent(c,b,"timeupdate")},250)},onStateChange:function(a){mejs.YouTubeApi.handleStateChange(a.data,c,b)}}})},createEvent:function(a,b,c){var d={type:c,target:b};if(a&&a.getDuration){b.currentTime=d.currentTime=a.getCurrentTime(),b.duration=d.duration=a.getDuration(),d.paused=b.paused,d.ended=b.ended,d.muted=a.isMuted(),d.volume=a.getVolume()/100,d.bytesTotal=a.getVideoBytesTotal(),d.bufferedBytes=a.getVideoBytesLoaded();var e=d.bufferedBytes/d.bytesTotal*d.duration;d.target.buffered=d.buffered={start:function(a){return 0},end:function(a){return e},length:1}}b.dispatchEvent(d)},iFrameReady:function(){for(this.isLoaded=!0,this.isIframeLoaded=!0;this.iframeQueue.length>0;){var a=this.iframeQueue.pop();this.createIframe(a)}},flashPlayers:{},createFlash:function(a){this.flashPlayers[a.pluginId]=a;var b,c="//www.youtube.com/apiplayer?enablejsapi=1&playerapiid="+a.pluginId+"&version=3&autoplay=0&controls=0&modestbranding=1&loop=0";mejs.MediaFeatures.isIE?(b=document.createElement("div"),a.container.appendChild(b),b.outerHTML=' '):a.container.innerHTML=' '},flashReady:function(a){var b=this.flashPlayers[a],c=document.getElementById(a),d=b.pluginMediaElement;d.pluginApi=d.pluginElement=c,mejs.MediaPluginBridge.initPlugin(a),c.cueVideoById(b.videoId);var e=b.containerId+"_callback";window[e]=function(a){mejs.YouTubeApi.handleStateChange(a,c,d)},c.addEventListener("onStateChange",e),setInterval(function(){mejs.YouTubeApi.createEvent(c,d,"timeupdate")},250),mejs.YouTubeApi.createEvent(c,d,"canplay")},handleStateChange:function(a,b,c){switch(a){case-1:c.paused=!0,c.ended=!0,mejs.YouTubeApi.createEvent(b,c,"loadedmetadata");break;case 0:c.paused=!1,c.ended=!0,mejs.YouTubeApi.createEvent(b,c,"ended");break;case 1:c.paused=!1,c.ended=!1,mejs.YouTubeApi.createEvent(b,c,"play"),mejs.YouTubeApi.createEvent(b,c,"playing");break;case 2:c.paused=!0,c.ended=!1,mejs.YouTubeApi.createEvent(b,c,"pause");break;case 3:mejs.YouTubeApi.createEvent(b,c,"progress");break;case 5:}}},window.onYouTubePlayerAPIReady=function(){mejs.YouTubeApi.iFrameReady()},window.onYouTubePlayerReady=function(a){mejs.YouTubeApi.flashReady(a)},window.mejs=mejs,window.MediaElement=mejs.MediaElement,function(a,b,c){"use strict";var d={locale:{language:b.i18n&&b.i18n.locale.language||"",strings:b.i18n&&b.i18n.locale.strings||{}},ietf_lang_regex:/^(x\-)?[a-z]{2,}(\-\w{2,})?(\-\w{2,})?$/,methods:{}};d.getLanguage=function(){var a=d.locale.language||window.navigator.userLanguage||window.navigator.language;return d.ietf_lang_regex.exec(a)?a:null},"undefined"!=typeof mejsL10n&&(d.locale.language=mejsL10n.language),d.methods.checkPlain=function(a){var b,c,d={"&":"&",'"':""","<":"<",">":">"};a=String(a);for(b in d)d.hasOwnProperty(b)&&(c=new RegExp(b,"g"),a=a.replace(c,d[b]));return a},d.methods.t=function(a,b){return d.locale.strings&&d.locale.strings[b.context]&&d.locale.strings[b.context][a]&&(a=d.locale.strings[b.context][a]),d.methods.checkPlain(a)},d.t=function(a,b){if("string"==typeof a&&a.length>0){var c=d.getLanguage();return b=b||{context:c},d.methods.t(a,b)}throw{name:"InvalidArgumentException",message:"First argument is either not a string or empty."}},b.i18n=d}(document,mejs),function(a,b){"use strict";"undefined"!=typeof mejsL10n&&(a[mejsL10n.language]=mejsL10n.strings)}(mejs.i18n.locale.strings),/*!
16 | *
17 | * MediaElementPlayer
18 | * http://mediaelementjs.com/
19 | *
20 | * Creates a controller bar for HTML5 add tags
21 | * using jQuery and MediaElement.js (HTML5 Flash/Silverlight wrapper)
22 | *
23 | * Copyright 2010-2013, John Dyer (http://j.hn/)
24 | * License: MIT
25 | *
26 | */
27 | "undefined"!=typeof jQuery?mejs.$=jQuery:"undefined"!=typeof Zepto?(mejs.$=Zepto,Zepto.fn.outerWidth=function(a){var b=$(this).width();return a&&(b+=parseInt($(this).css("margin-right"),10),b+=parseInt($(this).css("margin-left"),10)),b}):"undefined"!=typeof ender&&(mejs.$=ender),function(a){mejs.MepDefaults={poster:"",showPosterWhenEnded:!1,defaultVideoWidth:480,defaultVideoHeight:270,videoWidth:-1,videoHeight:-1,defaultAudioWidth:400,defaultAudioHeight:30,defaultSeekBackwardInterval:function(a){return.05*a.duration},defaultSeekForwardInterval:function(a){return.05*a.duration},setDimensions:!0,audioWidth:-1,audioHeight:-1,startVolume:.8,loop:!1,autoRewind:!0,enableAutosize:!0,timeFormat:"",alwaysShowHours:!1,showTimecodeFrameCount:!1,framesPerSecond:25,autosizeProgress:!0,alwaysShowControls:!1,hideVideoControlsOnLoad:!1,clickToPlayPause:!0,iPadUseNativeControls:!1,iPhoneUseNativeControls:!1,AndroidUseNativeControls:!1,features:["playpause","current","progress","duration","tracks","volume","fullscreen"],isVideo:!0,enableKeyboard:!0,pauseOtherPlayers:!0,keyActions:[{keys:[32,179],action:function(a,b){b.paused||b.ended?b.play():b.pause()}},{keys:[38],action:function(a,b){a.container.find(".mejs-volume-slider").css("display","block"),a.isVideo&&(a.showControls(),a.startControlsTimer());var c=Math.min(b.volume+.1,1);b.setVolume(c)}},{keys:[40],action:function(a,b){a.container.find(".mejs-volume-slider").css("display","block"),a.isVideo&&(a.showControls(),a.startControlsTimer());var c=Math.max(b.volume-.1,0);b.setVolume(c)}},{keys:[37,227],action:function(a,b){if(!isNaN(b.duration)&&b.duration>0){a.isVideo&&(a.showControls(),a.startControlsTimer());var c=Math.max(b.currentTime-a.options.defaultSeekBackwardInterval(b),0);b.setCurrentTime(c)}}},{keys:[39,228],action:function(a,b){if(!isNaN(b.duration)&&b.duration>0){a.isVideo&&(a.showControls(),a.startControlsTimer());var c=Math.min(b.currentTime+a.options.defaultSeekForwardInterval(b),b.duration);b.setCurrentTime(c)}}},{keys:[70],action:function(a,b){"undefined"!=typeof a.enterFullScreen&&(a.isFullScreen?a.exitFullScreen():a.enterFullScreen())}},{keys:[77],action:function(a,b){a.container.find(".mejs-volume-slider").css("display","block"),a.isVideo&&(a.showControls(),a.startControlsTimer()),a.media.muted?a.setMuted(!1):a.setMuted(!0)}}]},mejs.mepIndex=0,mejs.players={},mejs.MediaElementPlayer=function(b,c){if(!(this instanceof mejs.MediaElementPlayer))return new mejs.MediaElementPlayer(b,c);var d=this;return d.$media=d.$node=a(b),d.node=d.media=d.$media[0],d.node?"undefined"!=typeof d.node.player?d.node.player:("undefined"==typeof c&&(c=d.$node.data("mejsoptions")),d.options=a.extend({},mejs.MepDefaults,c),d.options.timeFormat||(d.options.timeFormat="mm:ss",d.options.alwaysShowHours&&(d.options.timeFormat="hh:mm:ss"),d.options.showTimecodeFrameCount&&(d.options.timeFormat+=":ff")),mejs.Utility.calculateTimeFormat(0,d.options,d.options.framesPerSecond||25),d.id="mep_"+mejs.mepIndex++,mejs.players[d.id]=d,d.init(),d):void 0},mejs.MediaElementPlayer.prototype={hasFocus:!1,controlsAreVisible:!0,init:function(){var b=this,c=mejs.MediaFeatures,d=a.extend(!0,{},b.options,{success:function(a,c){b.meReady(a,c)},error:function(a){b.handleError(a)}}),e=b.media.tagName.toLowerCase();if(b.isDynamic="audio"!==e&&"video"!==e,b.isDynamic?b.isVideo=b.options.isVideo:b.isVideo="audio"!==e&&b.options.isVideo,c.isiPad&&b.options.iPadUseNativeControls||c.isiPhone&&b.options.iPhoneUseNativeControls)b.$media.attr("controls","controls"),c.isiPad&&null!==b.media.getAttribute("autoplay")&&b.play();else if(c.isAndroid&&b.options.AndroidUseNativeControls);else{b.$media.removeAttr("controls");var f=b.isVideo?mejs.i18n.t("Video Player"):mejs.i18n.t("Audio Player");a(''+f+" ").insertBefore(b.$media),b.container=a('').addClass(b.$media[0].className).insertBefore(b.$media).focus(function(a){if(!b.controlsAreVisible){b.showControls(!0);var c=b.container.find(".mejs-playpause-button > button");c.focus()}}),b.container.addClass((c.isAndroid?"mejs-android ":"")+(c.isiOS?"mejs-ios ":"")+(c.isiPad?"mejs-ipad ":"")+(c.isiPhone?"mejs-iphone ":"")+(b.isVideo?"mejs-video ":"mejs-audio ")),b.container.find(".mejs-mediaelement").append(b.$media),b.node.player=b,b.controls=b.container.find(".mejs-controls"),b.layers=b.container.find(".mejs-layers");var g=b.isVideo?"video":"audio",h=g.substring(0,1).toUpperCase()+g.substring(1);b.options[g+"Width"]>0||b.options[g+"Width"].toString().indexOf("%")>-1?b.width=b.options[g+"Width"]:""!==b.media.style.width&&null!==b.media.style.width?b.width=b.media.style.width:null!==b.media.getAttribute("width")?b.width=b.$media.attr("width"):b.width=b.options["default"+h+"Width"],b.options[g+"Height"]>0||b.options[g+"Height"].toString().indexOf("%")>-1?b.height=b.options[g+"Height"]:""!==b.media.style.height&&null!==b.media.style.height?b.height=b.media.style.height:null!==b.$media[0].getAttribute("height")?b.height=b.$media.attr("height"):b.height=b.options["default"+h+"Height"],b.setPlayerSize(b.width,b.height),d.pluginWidth=b.width,d.pluginHeight=b.height}mejs.MediaElement(b.$media[0],d),"undefined"!=typeof b.container&&b.controlsAreVisible&&b.container.trigger("controlsshown")},showControls:function(a){var b=this;a="undefined"==typeof a||a,b.controlsAreVisible||(a?(b.controls.removeClass("mejs-offscreen").stop(!0,!0).fadeIn(200,function(){b.controlsAreVisible=!0,b.container.trigger("controlsshown")}),b.container.find(".mejs-control").removeClass("mejs-offscreen").stop(!0,!0).fadeIn(200,function(){b.controlsAreVisible=!0})):(b.controls.removeClass("mejs-offscreen").css("display","block"),b.container.find(".mejs-control").removeClass("mejs-offscreen").css("display","block"),b.controlsAreVisible=!0,b.container.trigger("controlsshown")),b.setControlsSize())},hideControls:function(b){var c=this;b="undefined"==typeof b||b,!c.controlsAreVisible||c.options.alwaysShowControls||c.keyboardAction||(b?(c.controls.stop(!0,!0).fadeOut(200,function(){a(this).addClass("mejs-offscreen").css("display","block"),c.controlsAreVisible=!1,c.container.trigger("controlshidden")}),c.container.find(".mejs-control").stop(!0,!0).fadeOut(200,function(){a(this).addClass("mejs-offscreen").css("display","block")})):(c.controls.addClass("mejs-offscreen").css("display","block"),c.container.find(".mejs-control").addClass("mejs-offscreen").css("display","block"),c.controlsAreVisible=!1,c.container.trigger("controlshidden")))},controlsTimer:null,startControlsTimer:function(a){var b=this;a="undefined"!=typeof a?a:1500,b.killControlsTimer("start"),b.controlsTimer=setTimeout(function(){b.hideControls(),b.killControlsTimer("hide")},a)},killControlsTimer:function(a){var b=this;null!==b.controlsTimer&&(clearTimeout(b.controlsTimer),delete b.controlsTimer,b.controlsTimer=null)},controlsEnabled:!0,disableControls:function(){var a=this;a.killControlsTimer(),a.hideControls(!1),this.controlsEnabled=!1},enableControls:function(){var a=this;a.showControls(!1),a.controlsEnabled=!0},meReady:function(b,c){var d,e,f=this,g=mejs.MediaFeatures,h=c.getAttribute("autoplay"),i=!("undefined"==typeof h||null===h||"false"===h);if(!f.created){if(f.created=!0,f.media=b,f.domNode=c,!(g.isAndroid&&f.options.AndroidUseNativeControls||g.isiPad&&f.options.iPadUseNativeControls||g.isiPhone&&f.options.iPhoneUseNativeControls)){f.buildposter(f,f.controls,f.layers,f.media),f.buildkeyboard(f,f.controls,f.layers,f.media),f.buildoverlays(f,f.controls,f.layers,f.media),f.findTracks();for(d in f.options.features)if(e=f.options.features[d],f["build"+e])try{f["build"+e](f,f.controls,f.layers,f.media)}catch(j){}f.container.trigger("controlsready"),f.setPlayerSize(f.width,f.height),f.setControlsSize(),f.isVideo&&(mejs.MediaFeatures.hasTouch?f.$media.bind("touchstart",function(){f.controlsAreVisible?f.hideControls(!1):f.controlsEnabled&&f.showControls(!1)}):(f.clickToPlayPauseCallback=function(){f.options.clickToPlayPause&&(f.media.paused?f.play():f.pause())},f.media.addEventListener("click",f.clickToPlayPauseCallback,!1),f.container.bind("mouseenter",function(){f.controlsEnabled&&(f.options.alwaysShowControls||(f.killControlsTimer("enter"),f.showControls(),f.startControlsTimer(2500)))}).bind("mousemove",function(){f.controlsEnabled&&(f.controlsAreVisible||f.showControls(),f.options.alwaysShowControls||f.startControlsTimer(2500))}).bind("mouseleave",function(){f.controlsEnabled&&(f.media.paused||f.options.alwaysShowControls||f.startControlsTimer(1e3))})),f.options.hideVideoControlsOnLoad&&f.hideControls(!1),i&&!f.options.alwaysShowControls&&f.hideControls(),f.options.enableAutosize&&f.media.addEventListener("loadedmetadata",function(a){f.options.videoHeight<=0&&null===f.domNode.getAttribute("height")&&!isNaN(a.target.videoHeight)&&(f.setPlayerSize(a.target.videoWidth,a.target.videoHeight),f.setControlsSize(),f.media.setVideoSize(a.target.videoWidth,a.target.videoHeight))},!1)),b.addEventListener("play",function(){var a;for(a in mejs.players){var b=mejs.players[a];b.id==f.id||!f.options.pauseOtherPlayers||b.paused||b.ended||b.pause(),b.hasFocus=!1}f.hasFocus=!0},!1),f.media.addEventListener("ended",function(b){if(f.options.autoRewind)try{f.media.setCurrentTime(0),window.setTimeout(function(){a(f.container).find(".mejs-overlay-loading").parent().hide()},20)}catch(c){}f.media.pause(),f.setProgressRail&&f.setProgressRail(),f.setCurrentRail&&f.setCurrentRail(),f.options.loop?f.play():!f.options.alwaysShowControls&&f.controlsEnabled&&f.showControls()},!1),f.media.addEventListener("loadedmetadata",function(a){f.updateDuration&&f.updateDuration(),f.updateCurrent&&f.updateCurrent(),f.isFullScreen||(f.setPlayerSize(f.width,f.height),f.setControlsSize())},!1);var k=null;f.media.addEventListener("timeupdate",function(){k!==this.duration&&(k=this.duration,mejs.Utility.calculateTimeFormat(k,f.options,f.options.framesPerSecond||25))},!1),f.container.focusout(function(b){if(b.relatedTarget){var c=a(b.relatedTarget);f.keyboardAction&&0===c.parents(".mejs-container").length&&(f.keyboardAction=!1,f.hideControls(!0))}}),setTimeout(function(){f.setPlayerSize(f.width,f.height),f.setControlsSize()},50),f.globalBind("resize",function(){f.isFullScreen||mejs.MediaFeatures.hasTrueNativeFullScreen&&document.webkitIsFullScreen||f.setPlayerSize(f.width,f.height),f.setControlsSize()}),"youtube"==f.media.pluginType&&(g.isiOS||g.isAndroid)&&f.container.find(".mejs-overlay-play").hide()}i&&"native"==b.pluginType&&f.play(),f.options.success&&("string"==typeof f.options.success?window[f.options.success](f.media,f.domNode,f):f.options.success(f.media,f.domNode,f))}},handleError:function(a){var b=this;b.controls&&b.controls.hide(),b.options.error&&b.options.error(a)},setPlayerSize:function(b,c){var d=this;if(!d.options.setDimensions)return!1;if("undefined"!=typeof b&&(d.width=b),"undefined"!=typeof c&&(d.height=c),d.height.toString().indexOf("%")>0||"none"!==d.$node.css("max-width")&&"t.width"!==d.$node.css("max-width")||d.$node[0].currentStyle&&"100%"===d.$node[0].currentStyle.maxWidth){var e=function(){return d.isVideo?d.media.videoWidth&&d.media.videoWidth>0?d.media.videoWidth:null!==d.media.getAttribute("width")?d.media.getAttribute("width"):d.options.defaultVideoWidth:d.options.defaultAudioWidth}(),f=function(){return d.isVideo?d.media.videoHeight&&d.media.videoHeight>0?d.media.videoHeight:null!==d.media.getAttribute("height")?d.media.getAttribute("height"):d.options.defaultVideoHeight:d.options.defaultAudioHeight}(),g=d.container.parent().closest(":visible").width(),h=d.container.parent().closest(":visible").height(),i=d.isVideo||!d.options.autosizeProgress?parseInt(g*f/e,10):f;isNaN(i)&&(i=h),d.container.parent().length>0&&"body"===d.container.parent()[0].tagName.toLowerCase()&&(g=a(window).width(),i=a(window).height()),i&&g&&(d.container.width(g).height(i),d.$media.add(d.container.find(".mejs-shim")).width("100%").height("100%"),d.isVideo&&d.media.setVideoSize&&d.media.setVideoSize(g,i),d.layers.children(".mejs-layer").width("100%").height("100%"))}else d.container.width(d.width).height(d.height),d.layers.children(".mejs-layer").width(d.width).height(d.height)},setControlsSize:function(){var b=this,c=0,d=0,e=b.controls.find(".mejs-time-rail"),f=b.controls.find(".mejs-time-total"),g=e.siblings(),h=g.last(),i=null;if(b.container.is(":visible")&&e.length&&e.is(":visible")){b.options&&!b.options.autosizeProgress&&(d=parseInt(e.css("width"),10)),0!==d&&d||(g.each(function(){var b=a(this);"absolute"!=b.css("position")&&b.is(":visible")&&(c+=a(this).outerWidth(!0))}),d=b.controls.width()-c-(e.outerWidth(!0)-e.width()));do e.width(d),f.width(d-(f.outerWidth(!0)-f.width())),"absolute"!=h.css("position")&&(i=h.length?h.position():null,d--);while(null!==i&&i.top>0&&d>0);b.container.trigger("controlsresize")}},buildposter:function(b,c,d,e){var f=this,g=a('
').appendTo(d),h=b.$media.attr("poster");""!==b.options.poster&&(h=b.options.poster),h?f.setPoster(h):g.hide(),e.addEventListener("play",function(){g.hide()},!1),b.options.showPosterWhenEnded&&b.options.autoRewind&&e.addEventListener("ended",function(){g.show()},!1)},setPoster:function(b){var c=this,d=c.container.find(".mejs-poster"),e=d.find("img");0===e.length&&(e=a(' ').appendTo(d)),e.attr("src",b),d.css({"background-image":"url("+b+")"})},buildoverlays:function(b,c,d,e){var f=this;if(b.isVideo){var g=a('').hide().appendTo(d),h=a('').hide().appendTo(d),i=a('').appendTo(d).bind("click",function(){f.options.clickToPlayPause&&e.paused&&e.play()});e.addEventListener("play",function(){i.hide(),g.hide(),c.find(".mejs-time-buffering").hide(),h.hide()},!1),e.addEventListener("playing",function(){i.hide(),g.hide(),c.find(".mejs-time-buffering").hide(),h.hide()},!1),e.addEventListener("seeking",function(){g.show(),c.find(".mejs-time-buffering").show()},!1),e.addEventListener("seeked",function(){g.hide(),c.find(".mejs-time-buffering").hide()},!1),e.addEventListener("pause",function(){mejs.MediaFeatures.isiPhone||i.show()},!1),e.addEventListener("waiting",function(){g.show(),c.find(".mejs-time-buffering").show()},!1),e.addEventListener("loadeddata",function(){g.show(),c.find(".mejs-time-buffering").show(),mejs.MediaFeatures.isAndroid&&(e.canplayTimeout=window.setTimeout(function(){if(document.createEvent){var a=document.createEvent("HTMLEvents");return a.initEvent("canplay",!0,!0),e.dispatchEvent(a)}},300))},!1),e.addEventListener("canplay",function(){g.hide(),c.find(".mejs-time-buffering").hide(),clearTimeout(e.canplayTimeout)},!1),e.addEventListener("error",function(a){f.handleError(a),g.hide(),i.hide(),h.show(),h.find(".mejs-overlay-error").html("Error loading this resource")},!1),e.addEventListener("keydown",function(a){f.onkeydown(b,e,a)},!1)}},buildkeyboard:function(b,c,d,e){var f=this;f.container.keydown(function(){f.keyboardAction=!0}),f.globalBind("keydown",function(c){return b.hasFocus=0!==a(c.target).closest(".mejs-container").length,f.onkeydown(b,e,c)}),f.globalBind("click",function(c){b.hasFocus=0!==a(c.target).closest(".mejs-container").length})},onkeydown:function(a,b,c){if(a.hasFocus&&a.options.enableKeyboard)for(var d=0,e=a.options.keyActions.length;e>d;d++)for(var f=a.options.keyActions[d],g=0,h=f.keys.length;h>g;g++)if(c.keyCode==f.keys[g])return"function"==typeof c.preventDefault&&c.preventDefault(),f.action(a,b,c.keyCode),!1;return!0},findTracks:function(){var b=this,c=b.$media.find("track");b.tracks=[],c.each(function(c,d){d=a(d),b.tracks.push({srclang:d.attr("srclang")?d.attr("srclang").toLowerCase():"",src:d.attr("src"),kind:d.attr("kind"),label:d.attr("label")||"",entries:[],isLoaded:!1})})},changeSkin:function(a){this.container[0].className="mejs-container "+a,this.setPlayerSize(this.width,this.height),this.setControlsSize()},play:function(){this.load(),this.media.play()},pause:function(){try{this.media.pause()}catch(a){}},load:function(){this.isLoaded||this.media.load(),this.isLoaded=!0},setMuted:function(a){this.media.setMuted(a)},setCurrentTime:function(a){this.media.setCurrentTime(a)},getCurrentTime:function(){return this.media.currentTime},setVolume:function(a){this.media.setVolume(a)},getVolume:function(){return this.media.volume},setSrc:function(a){this.media.setSrc(a)},remove:function(){var a,b,c=this;c.container.prev(".mejs-offscreen").remove();for(a in c.options.features)if(b=c.options.features[a],c["clean"+b])try{c["clean"+b](c)}catch(d){}c.isDynamic?c.$node.insertBefore(c.container):(c.$media.prop("controls",!0),c.$node.clone().insertBefore(c.container).show(),c.$node.remove()),"native"!==c.media.pluginType&&c.media.remove(),delete mejs.players[c.id],"object"==typeof c.container&&c.container.remove(),c.globalUnbind(),delete c.node.player},rebuildtracks:function(){var a=this;a.findTracks(),a.buildtracks(a,a.controls,a.layers,a.media)},resetSize:function(){var a=this;setTimeout(function(){a.setPlayerSize(a.width,a.height),a.setControlsSize()},50)}},function(){function b(b,d){var e={d:[],w:[]};return a.each((b||"").split(" "),function(a,b){var f=b+"."+d;0===f.indexOf(".")?(e.d.push(f),e.w.push(f)):e[c.test(b)?"w":"d"].push(f)}),e.d=e.d.join(" "),e.w=e.w.join(" "),e}var c=/^((after|before)print|(before)?unload|hashchange|message|o(ff|n)line|page(hide|show)|popstate|resize|storage)\b/;mejs.MediaElementPlayer.prototype.globalBind=function(c,d,e){var f=this,g=f.node?f.node.ownerDocument:document;c=b(c,f.id),c.d&&a(g).bind(c.d,d,e),c.w&&a(window).bind(c.w,d,e)},mejs.MediaElementPlayer.prototype.globalUnbind=function(c,d){var e=this,f=e.node?e.node.ownerDocument:document;c=b(c,e.id),c.d&&a(f).unbind(c.d,d),c.w&&a(window).unbind(c.w,d)}}(),"undefined"!=typeof a&&(a.fn.mediaelementplayer=function(b){return b===!1?this.each(function(){var b=a(this).data("mediaelementplayer");b&&b.remove(),a(this).removeData("mediaelementplayer")}):this.each(function(){a(this).data("mediaelementplayer",new mejs.MediaElementPlayer(this,b))}),this},a(document).ready(function(){a(".mejs-player").mediaelementplayer()})),window.MediaElementPlayer=mejs.MediaElementPlayer}(mejs.$),function(a){a.extend(mejs.MepDefaults,{playText:mejs.i18n.t("Play"),pauseText:mejs.i18n.t("Pause")}),a.extend(MediaElementPlayer.prototype,{buildplaypause:function(b,c,d,e){function f(a){"play"===a?(i.removeClass("mejs-play").addClass("mejs-pause"),j.attr({title:h.pauseText,"aria-label":h.pauseText})):(i.removeClass("mejs-pause").addClass("mejs-play"),j.attr({title:h.playText,"aria-label":h.playText}))}var g=this,h=g.options,i=a('
').appendTo(c).click(function(a){return a.preventDefault(),e.paused?e.play():e.pause(),!1}),j=i.find("button");f("pse"),e.addEventListener("play",function(){f("play")},!1),e.addEventListener("playing",function(){f("play")},!1),e.addEventListener("pause",function(){f("pse")},!1),e.addEventListener("paused",function(){f("pse")},!1)}})}(mejs.$),function(a){a.extend(mejs.MepDefaults,{stopText:"Stop"}),a.extend(MediaElementPlayer.prototype,{buildstop:function(b,c,d,e){var f=this;a('
').appendTo(c).click(function(){e.paused||e.pause(),e.currentTime>0&&(e.setCurrentTime(0),e.pause(),c.find(".mejs-time-current").width("0px"),c.find(".mejs-time-handle").css("left","0px"),c.find(".mejs-time-float-current").html(mejs.Utility.secondsToTimeCode(0,b.options)),c.find(".mejs-currenttime").html(mejs.Utility.secondsToTimeCode(0,b.options)),d.find(".mejs-poster").show())})}})}(mejs.$),function(a){a.extend(mejs.MepDefaults,{progessHelpText:mejs.i18n.t("Use Left/Right Arrow keys to advance one second, Up/Down arrows to advance ten seconds.")}),a.extend(MediaElementPlayer.prototype,{buildprogress:function(b,c,d,e){a('00:00
').appendTo(c),c.find(".mejs-time-buffering").hide();var f=this,g=c.find(".mejs-time-total"),h=c.find(".mejs-time-loaded"),i=c.find(".mejs-time-current"),j=c.find(".mejs-time-handle"),k=c.find(".mejs-time-float"),l=c.find(".mejs-time-float-current"),m=c.find(".mejs-time-slider"),n=function(a){var c,d=g.offset(),f=g.width(),h=0,i=0,j=0;c=a.originalEvent&&a.originalEvent.changedTouches?a.originalEvent.changedTouches[0].pageX:a.changedTouches?a.changedTouches[0].pageX:a.pageX,e.duration&&(cf+d.left&&(c=f+d.left),j=c-d.left,h=j/f,i=.02>=h?0:h*e.duration,o&&i!==e.currentTime&&e.setCurrentTime(i),mejs.MediaFeatures.hasTouch||(k.css("left",j),l.html(mejs.Utility.secondsToTimeCode(i,b.options)),k.show()))},o=!1,p=!1,q=0,r=!1,s=b.options.autoRewind,t=function(a){var c=e.currentTime,d=mejs.i18n.t("Time Slider"),f=mejs.Utility.secondsToTimeCode(c,b.options),g=e.duration;m.attr({"aria-label":d,"aria-valuemin":0,"aria-valuemax":g,"aria-valuenow":c,"aria-valuetext":f,role:"slider",tabindex:0})},u=function(){var a=new Date;a-q>=1e3&&e.play()};m.bind("focus",function(a){b.options.autoRewind=!1}),m.bind("blur",function(a){b.options.autoRewind=s}),m.bind("keydown",function(a){new Date-q>=1e3&&(r=e.paused);var b=a.keyCode,c=e.duration,d=e.currentTime;switch(b){case 37:d-=1;break;case 39:d+=1;break;case 38:d+=Math.floor(.1*c);break;case 40:d-=Math.floor(.1*c);break;case 36:d=0;break;case 35:d=c;break;case 10:return void(e.paused?e.play():e.pause());case 13:return void(e.paused?e.play():e.pause());default:return}return d=0>d?0:d>=c?c:Math.floor(d),q=new Date,r||e.pause(),d0&&c.buffered.end&&c.duration?d=c.buffered.end(c.buffered.length-1)/c.duration:c&&void 0!==c.bytesTotal&&c.bytesTotal>0&&void 0!==c.bufferedBytes?d=c.bufferedBytes/c.bytesTotal:a&&a.lengthComputable&&0!==a.total&&(d=a.loaded/a.total),null!==d&&(d=Math.min(1,Math.max(0,d)),b.loaded&&b.total&&b.loaded.width(b.total.width()*d))},setCurrentRail:function(){var a=this;if(void 0!==a.media.currentTime&&a.media.duration&&a.total&&a.handle){var b=Math.round(a.total.width()*a.media.currentTime/a.media.duration),c=b-Math.round(a.handle.outerWidth(!0)/2);a.current.width(b),a.handle.css("left",c)}}})}(mejs.$),function(a){a.extend(mejs.MepDefaults,{duration:-1,timeAndDurationSeparator:" | "}),a.extend(MediaElementPlayer.prototype,{buildcurrent:function(b,c,d,e){var f=this;a(''+mejs.Utility.secondsToTimeCode(0,b.options)+"
").appendTo(c),f.currenttime=f.controls.find(".mejs-currenttime"),e.addEventListener("timeupdate",function(){b.updateCurrent()},!1)},buildduration:function(b,c,d,e){var f=this;c.children().last().find(".mejs-currenttime").length>0?a(f.options.timeAndDurationSeparator+''+mejs.Utility.secondsToTimeCode(f.options.duration,f.options)+" ").appendTo(c.find(".mejs-time")):(c.find(".mejs-currenttime").parent().addClass("mejs-currenttime-container"),a(''+mejs.Utility.secondsToTimeCode(f.options.duration,f.options)+"
").appendTo(c)),f.durationD=f.controls.find(".mejs-duration"),e.addEventListener("timeupdate",function(){b.updateDuration()},!1)},updateCurrent:function(){var a=this,b=a.media.currentTime;isNaN(b)&&(b=0),a.currenttime&&a.currenttime.html(mejs.Utility.secondsToTimeCode(b,a.options))},updateDuration:function(){var a=this,b=a.media.duration;a.options.duration>0&&(b=a.options.duration),isNaN(b)&&(b=0),a.container.toggleClass("mejs-long-video",b>3600),a.durationD&&b>0&&a.durationD.html(mejs.Utility.secondsToTimeCode(b,a.options))}})}(mejs.$),function(a){a.extend(mejs.MepDefaults,{muteText:mejs.i18n.t("Mute Toggle"),allyVolumeControlText:mejs.i18n.t("Use Up/Down Arrow keys to increase or decrease volume."),hideVolumeOnTouchDevices:!0,audioVolume:"horizontal",videoVolume:"vertical"}),a.extend(MediaElementPlayer.prototype,{buildvolume:function(b,c,d,e){if(!mejs.MediaFeatures.isAndroid&&!mejs.MediaFeatures.isiOS||!this.options.hideVolumeOnTouchDevices){var f=this,g=f.isVideo?f.options.videoVolume:f.options.audioVolume,h="horizontal"==g?a('
'+f.options.allyVolumeControlText+'
').appendTo(c):a('').appendTo(c),i=f.container.find(".mejs-volume-slider, .mejs-horizontal-volume-slider"),j=f.container.find(".mejs-volume-total, .mejs-horizontal-volume-total"),k=f.container.find(".mejs-volume-current, .mejs-horizontal-volume-current"),l=f.container.find(".mejs-volume-handle, .mejs-horizontal-volume-handle"),m=function(a,b){if(!i.is(":visible")&&"undefined"==typeof b)return i.show(),m(a,!0),void i.hide();a=Math.max(0,a),a=Math.min(a,1),0===a?(h.removeClass("mejs-mute").addClass("mejs-unmute"),h.children("button").attr("title",mejs.i18n.t("Unmute")).attr("aria-label",mejs.i18n.t("Unmute"))):(h.removeClass("mejs-unmute").addClass("mejs-mute"),h.children("button").attr("title",mejs.i18n.t("Mute")).attr("aria-label",mejs.i18n.t("Mute")));var c=j.position();if("vertical"==g){var d=j.height(),e=d-d*a;l.css("top",Math.round(c.top+e-l.height()/2)),k.height(d-e),k.css("top",c.top+e)}else{var f=j.width(),n=f*a;l.css("left",Math.round(c.left+n-l.width()/2)),k.width(Math.round(n))}},n=function(a){var b=null,c=j.offset();if("vertical"===g){var d=j.height(),f=a.pageY-c.top;if(b=(d-f)/d,0===c.top||0===c.left)return}else{var h=j.width(),i=a.pageX-c.left;b=i/h}b=Math.max(0,b),b=Math.min(b,1),m(b),0===b?e.setMuted(!0):e.setMuted(!1),e.setVolume(b)},o=!1,p=!1;h.hover(function(){i.show(),p=!0},function(){p=!1,o||"vertical"!=g||i.hide()});var q=function(a){var b=Math.floor(100*e.volume);i.attr({"aria-label":mejs.i18n.t("volumeSlider"),"aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":b,"aria-valuetext":b+"%",role:"slider",tabindex:0})};i.bind("mouseover",function(){p=!0}).bind("mousedown",function(a){return n(a),f.globalBind("mousemove.vol",function(a){n(a)}),f.globalBind("mouseup.vol",function(){o=!1,f.globalUnbind(".vol"),p||"vertical"!=g||i.hide()}),o=!0,!1}).bind("keydown",function(a){var b=a.keyCode,c=e.volume;switch(b){case 38:c+=.1;break;case 40:c-=.1;break;default:return!0}return o=!1,m(c),e.setVolume(c),!1}),h.find("button").click(function(){e.setMuted(!e.muted)}),h.find("button").bind("focus",function(){i.show()}),e.addEventListener("volumechange",function(a){o||(e.muted?(m(0),h.removeClass("mejs-mute").addClass("mejs-unmute")):(m(e.volume),h.removeClass("mejs-unmute").addClass("mejs-mute"))),q(a)},!1),0===b.options.startVolume&&e.setMuted(!0),"native"===e.pluginType&&e.setVolume(b.options.startVolume),f.container.on("controlsresize",function(){m(e.volume)})}}})}(mejs.$),function(a){a.extend(mejs.MepDefaults,{usePluginFullScreen:!0,newWindowCallback:function(){return""},fullscreenText:mejs.i18n.t("Fullscreen")}),a.extend(MediaElementPlayer.prototype,{isFullScreen:!1,isNativeFullScreen:!1,isInIframe:!1,fullscreenMode:"",buildfullscreen:function(b,c,d,e){if(b.isVideo){b.isInIframe=window.location!=window.parent.location,e.addEventListener("play",function(){b.detectFullscreenMode()});var f=this,g=null,h=a('
').appendTo(c).on("click",function(){var a=mejs.MediaFeatures.hasTrueNativeFullScreen&&mejs.MediaFeatures.isFullScreen()||b.isFullScreen;a?b.exitFullScreen():b.enterFullScreen()}).on("mouseover",function(){if("plugin-hover"==f.fullscreenMode){null!==g&&(clearTimeout(g),delete g);var a=h.offset(),c=b.container.offset();e.positionFullscreenButton(a.left-c.left,a.top-c.top,!0)}}).on("mouseout",function(){"plugin-hover"==f.fullscreenMode&&(null!==g&&(clearTimeout(g),delete g),g=setTimeout(function(){e.hideFullscreenButton()},1500))});if(b.fullscreenBtn=h,f.globalBind("keydown",function(a){27==a.keyCode&&(mejs.MediaFeatures.hasTrueNativeFullScreen&&mejs.MediaFeatures.isFullScreen()||f.isFullScreen)&&b.exitFullScreen()}),f.normalHeight=0,f.normalWidth=0,mejs.MediaFeatures.hasTrueNativeFullScreen){var i=function(a){b.isFullScreen&&(mejs.MediaFeatures.isFullScreen()?(b.isNativeFullScreen=!0,b.setControlsSize()):(b.isNativeFullScreen=!1,b.exitFullScreen()))};b.globalBind(mejs.MediaFeatures.fullScreenEventName,i)}}},detectFullscreenMode:function(){var a=this,b="",c=mejs.MediaFeatures;return c.hasTrueNativeFullScreen&&"native"===a.media.pluginType?b="native-native":c.hasTrueNativeFullScreen&&"native"!==a.media.pluginType&&!c.hasFirefoxPluginMovingProblem?b="plugin-native":a.usePluginFullScreen?mejs.MediaFeatures.supportsPointerEvents?(b="plugin-click",a.createPluginClickThrough()):b="plugin-hover":b="fullwindow",a.fullscreenMode=b,b},isPluginClickThroughCreated:!1,createPluginClickThrough:function(){var b=this;if(!b.isPluginClickThroughCreated){var c,d,e=!1,f=function(){if(e){for(var a in g)g[a].hide();b.fullscreenBtn.css("pointer-events",""),b.controls.css("pointer-events",""),b.media.removeEventListener("click",b.clickToPlayPauseCallback),e=!1}},g={},h=["top","left","right","bottom"],i=function(){var a=fullscreenBtn.offset().left-b.container.offset().left,d=fullscreenBtn.offset().top-b.container.offset().top,e=fullscreenBtn.outerWidth(!0),f=fullscreenBtn.outerHeight(!0),h=b.container.width(),i=b.container.height();
28 | for(c in g)g[c].css({position:"absolute",top:0,left:0});g.top.width(h).height(d),g.left.width(a).height(f).css({top:d}),g.right.width(h-a-e).height(f).css({top:d,left:a+e}),g.bottom.width(h).height(i-f-d).css({top:d+f})};for(b.globalBind("resize",function(){i()}),c=0,d=h.length;d>c;c++)g[h[c]]=a('
').appendTo(b.container).mouseover(f).hide();fullscreenBtn.on("mouseover",function(){if(!b.isFullScreen){var a=fullscreenBtn.offset(),d=player.container.offset();media.positionFullscreenButton(a.left-d.left,a.top-d.top,!1),b.fullscreenBtn.css("pointer-events","none"),b.controls.css("pointer-events","none"),b.media.addEventListener("click",b.clickToPlayPauseCallback);for(c in g)g[c].show();i(),e=!0}}),media.addEventListener("fullscreenchange",function(a){b.isFullScreen=!b.isFullScreen,b.isFullScreen?b.media.removeEventListener("click",b.clickToPlayPauseCallback):b.media.addEventListener("click",b.clickToPlayPauseCallback),f()}),b.globalBind("mousemove",function(a){if(e){var c=fullscreenBtn.offset();(a.pageYc.top+fullscreenBtn.outerHeight(!0)||a.pageXc.left+fullscreenBtn.outerWidth(!0))&&(fullscreenBtn.css("pointer-events",""),b.controls.css("pointer-events",""),e=!1)}}),b.isPluginClickThroughCreated=!0}},cleanfullscreen:function(a){a.exitFullScreen()},containerSizeTimeout:null,enterFullScreen:function(){var b=this;return mejs.MediaFeatures.hasiOSFullScreen?void b.media.webkitEnterFullscreen():(a(document.documentElement).addClass("mejs-fullscreen"),b.normalHeight=b.container.height(),b.normalWidth=b.container.width(),"native-native"===b.fullscreenMode||"plugin-native"===b.fullscreenMode?(mejs.MediaFeatures.requestFullScreen(b.container[0]),b.isInIframe&&setTimeout(function c(){if(b.isNativeFullScreen){var d=window.devicePixelRatio||1,e=.002,f=d*a(window).width(),g=screen.width,h=d*f;Math.abs(g-f)>Math.abs(g-h)&&(f=h);var i=Math.abs(g-f),j=g*e;i>j?b.exitFullScreen():setTimeout(c,500)}},1e3)):"fullwindow"==b.fullscreeMode,b.container.addClass("mejs-container-fullscreen").width("100%").height("100%"),b.containerSizeTimeout=setTimeout(function(){b.container.css({width:"100%",height:"100%"}),b.setControlsSize()},500),"native"===b.media.pluginType?b.$media.width("100%").height("100%"):(b.container.find(".mejs-shim").width("100%").height("100%"),setTimeout(function(){var c=a(window),d=c.width(),e=c.height();b.media.setVideoSize(d,e)},500)),b.layers.children("div").width("100%").height("100%"),b.fullscreenBtn&&b.fullscreenBtn.removeClass("mejs-fullscreen").addClass("mejs-unfullscreen"),b.setControlsSize(),b.isFullScreen=!0,b.container.find(".mejs-captions-text").css("font-size",screen.width/b.width*1*100+"%"),b.container.find(".mejs-captions-position").css("bottom","45px"),void b.container.trigger("enteredfullscreen"))},exitFullScreen:function(){var b=this;clearTimeout(b.containerSizeTimeout),mejs.MediaFeatures.hasTrueNativeFullScreen&&(mejs.MediaFeatures.isFullScreen()||b.isFullScreen)&&mejs.MediaFeatures.cancelFullScreen(),a(document.documentElement).removeClass("mejs-fullscreen"),b.container.removeClass("mejs-container-fullscreen").width(b.normalWidth).height(b.normalHeight),"native"===b.media.pluginType?b.$media.width(b.normalWidth).height(b.normalHeight):(b.container.find(".mejs-shim").width(b.normalWidth).height(b.normalHeight),b.media.setVideoSize(b.normalWidth,b.normalHeight)),b.layers.children("div").width(b.normalWidth).height(b.normalHeight),b.fullscreenBtn.removeClass("mejs-unfullscreen").addClass("mejs-fullscreen"),b.setControlsSize(),b.isFullScreen=!1,b.container.find(".mejs-captions-text").css("font-size",""),b.container.find(".mejs-captions-position").css("bottom",""),b.container.trigger("exitedfullscreen")}})}(mejs.$),function(a){a.extend(mejs.MepDefaults,{speeds:["2.00","1.50","1.25","1.00","0.75"],defaultSpeed:"1.00",speedChar:"x"}),a.extend(MediaElementPlayer.prototype,{buildspeed:function(b,c,d,e){var f=this;if("native"==f.media.pluginType){for(var g=null,h=null,i=null,j=null,k=[],l=!1,m=0,n=f.options.speeds.length;n>m;m++){var o=f.options.speeds[m];"string"==typeof o?(k.push({name:o+f.options.speedChar,value:o}),o===f.options.defaultSpeed&&(l=!0)):(k.push(o),o.value===f.options.defaultSpeed&&(l=!0))}l||k.push({name:f.options.defaultSpeed+f.options.speedChar,value:f.options.defaultSpeed}),k.sort(function(a,b){return parseFloat(b.value)-parseFloat(a.value)});var p=function(a){for(m=0,n=k.length;n>m;m++)if(k[m].value===a)return k[m].name},q='",g=a(q).appendTo(c),h=g.find(".mejs-speed-selector"),i=f.options.defaultSpeed,e.addEventListener("loadedmetadata",function(a){i&&(e.playbackRate=parseFloat(i))},!0),h.on("click",'input[type="radio"]',function(){var b=a(this).attr("value");i=b,e.playbackRate=parseFloat(b),g.find("button").html(p(b)),g.find(".mejs-speed-selected").removeClass("mejs-speed-selected"),g.find('input[type="radio"]:checked').next().addClass("mejs-speed-selected")}),g.one("mouseenter focusin",function(){h.height(g.find(".mejs-speed-selector ul").outerHeight(!0)+g.find(".mejs-speed-translations").outerHeight(!0)).css("top",-1*h.height()+"px")})}}})}(mejs.$),function(a){a.extend(mejs.MepDefaults,{startLanguage:"",tracksText:mejs.i18n.t("Captions/Subtitles"),tracksAriaLive:!1,hideCaptionsButtonWhenEmpty:!0,toggleCaptionsButtonWhenOnlyOne:!1,slidesSelector:""}),a.extend(MediaElementPlayer.prototype,{hasChapters:!1,cleartracks:function(a,b,c,d){a&&(a.captions&&a.captions.remove(),a.chapters&&a.chapters.remove(),a.captionsText&&a.captionsText.remove(),a.captionsButton&&a.captionsButton.remove())},buildtracks:function(b,c,d,e){if(0!==b.tracks.length){var f,g=this,h=g.options.tracksAriaLive?'role="log" aria-live="assertive" aria-atomic="false"':"";if(g.domNode.textTracks)for(f=g.domNode.textTracks.length-1;f>=0;f--)g.domNode.textTracks[f].mode="hidden";g.cleartracks(b,c,d,e),b.chapters=a('
').prependTo(d).hide(),b.captions=a('').prependTo(d).hide(),b.captionsText=b.captions.find(".mejs-captions-text"),b.captionsButton=a('").appendTo(c);var i=0;for(f=0;f0&&c.displayChapters(d)},!1),"slides"==d.kind&&c.setupSlides(d)},error:function(){c.removeTrackButton(d.srclang),c.loadNextTrack()}})},enableTrackButton:function(b,c){var d=this;""===c&&(c=mejs.language.codes[b]||b),d.captionsButton.find("input[value="+b+"]").prop("disabled",!1).siblings("label").html(c),d.options.startLanguage==b&&a("#"+d.id+"_captions_"+b).prop("checked",!0).trigger("click"),d.adjustLanguageBox()},removeTrackButton:function(a){var b=this;b.captionsButton.find("input[value="+a+"]").closest("li").remove(),b.adjustLanguageBox()},addTrackButton:function(b,c){var d=this;""===c&&(c=mejs.language.codes[b]||b),d.captionsButton.find("ul").append(a(''+c+" (loading) ")),d.adjustLanguageBox(),d.container.find(".mejs-captions-translations option[value="+b+"]").remove()},adjustLanguageBox:function(){var a=this;a.captionsButton.find(".mejs-captions-selector").height(a.captionsButton.find(".mejs-captions-selector ul").outerHeight(!0)+a.captionsButton.find(".mejs-captions-translations").outerHeight(!0))},checkForTracks:function(){var a=this,b=!1;if(a.options.hideCaptionsButtonWhenEmpty){for(i=0;i=c.entries.times[a].start&&b.media.currentTime<=c.entries.times[a].stop)return b.captionsText.html(c.entries.text[a]).attr("class","mejs-captions-text "+(c.entries.times[a].identifier||"")),void b.captions.show().height(0);b.captions.hide()}else b.captions.hide()}},setupSlides:function(a){var b=this;b.slides=a,b.slides.entries.imgs=[b.slides.entries.text.length],b.showSlide(0)},showSlide:function(b){if("undefined"!=typeof this.tracks&&"undefined"!=typeof this.slidesContainer){var c=this,d=c.slides.entries.text[b],e=c.slides.entries.imgs[b];"undefined"==typeof e||"undefined"==typeof e.fadeIn?c.slides.entries.imgs[b]=e=a(' ').on("load",function(){e.appendTo(c.slidesContainer).hide().fadeIn().siblings(":visible").fadeOut()}):e.is(":visible")||e.is(":animated")||e.fadeIn().siblings(":visible").fadeOut()}},displaySlides:function(){if("undefined"!=typeof this.slides){var a,b=this,c=b.slides;for(a=0;a=c.entries.times[a].start&&b.media.currentTime<=c.entries.times[a].stop)return void b.showSlide(a)}},displayChapters:function(){var a,b=this;for(a=0;a100||c==b.entries.times.length-1&&100>f+g)&&(f=100-g),e.chapters.append(a(''+b.entries.text[c]+' '+mejs.Utility.secondsToTimeCode(b.entries.times[c].start,e.options)+"–"+mejs.Utility.secondsToTimeCode(b.entries.times[c].stop,e.options)+"
")),g+=f;e.chapters.find("div.mejs-chapter").click(function(){e.media.setCurrentTime(parseFloat(a(this).attr("rel"))),e.media.paused&&e.media.play()}),e.chapters.show()}}),mejs.language={codes:{af:"Afrikaans",sq:"Albanian",ar:"Arabic",be:"Belarusian",bg:"Bulgarian",ca:"Catalan",zh:"Chinese","zh-cn":"Chinese Simplified","zh-tw":"Chinese Traditional",hr:"Croatian",cs:"Czech",da:"Danish",nl:"Dutch",en:"English",et:"Estonian",fl:"Filipino",fi:"Finnish",fr:"French",gl:"Galician",de:"German",el:"Greek",ht:"Haitian Creole",iw:"Hebrew",hi:"Hindi",hu:"Hungarian",is:"Icelandic",id:"Indonesian",ga:"Irish",it:"Italian",ja:"Japanese",ko:"Korean",lv:"Latvian",lt:"Lithuanian",mk:"Macedonian",ms:"Malay",mt:"Maltese",no:"Norwegian",fa:"Persian",pl:"Polish",pt:"Portuguese",ro:"Romanian",ru:"Russian",sr:"Serbian",sk:"Slovak",sl:"Slovenian",es:"Spanish",sw:"Swahili",sv:"Swedish",tl:"Tagalog",th:"Thai",tr:"Turkish",uk:"Ukrainian",vi:"Vietnamese",cy:"Welsh",yi:"Yiddish"}},mejs.TrackFormatParser={webvtt:{pattern_timecode:/^((?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}([,.][0-9]{1,3})?) --\> ((?:[0-9]{1,2}:)?[0-9]{2}:[0-9]{2}([,.][0-9]{3})?)(.*)$/,parse:function(b){for(var c,d,e,f=0,g=mejs.TrackFormatParser.split2(b,/\r?\n/),h={text:[],times:[]};f=0&&""!==g[f-1]&&(e=g[f-1]),f++,d=g[f],f++;""!==g[f]&&f$1"),h.text.push(d),h.times.push({identifier:e,start:0===mejs.Utility.convertSMPTEtoSeconds(c[1])?.2:mejs.Utility.convertSMPTEtoSeconds(c[1]),stop:mejs.Utility.convertSMPTEtoSeconds(c[3]),settings:c[5]})}e=""}return h}},dfxp:{parse:function(b){b=a(b).filter("tt");var c,d,e=0,f=b.children("div").eq(0),g=f.find("p"),h=b.find("#"+f.attr("style")),i={text:[],times:[]};if(h.length){var j=h.removeAttr("id").get(0).attributes;if(j.length)for(c={},e=0;e$1"),i.text.push(d),0===i.times.start&&(i.times.start=2)}return i}},split2:function(a,b){return a.split(b)}},3!="x\n\ny".split(/\n/gi).length&&(mejs.TrackFormatParser.split2=function(a,b){var c,d=[],e="";for(c=0;c ').appendTo(a("body")).hide(),b.container.bind("contextmenu",function(a){return b.isContextMenuEnabled?(a.preventDefault(),b.renderContextMenu(a.clientX-1,a.clientY-1),!1):void 0}),b.container.bind("click",function(){b.contextMenu.hide()}),b.contextMenu.bind("mouseleave",function(){b.startContextMenuTimer()})},cleancontextmenu:function(a){a.contextMenu.remove()},isContextMenuEnabled:!0,enableContextMenu:function(){this.isContextMenuEnabled=!0},disableContextMenu:function(){this.isContextMenuEnabled=!1},contextMenuTimeout:null,startContextMenuTimer:function(){var a=this;a.killContextMenuTimer(),a.contextMenuTimer=setTimeout(function(){a.hideContextMenu(),a.killContextMenuTimer()},750)},killContextMenuTimer:function(){var a=this.contextMenuTimer;null!=a&&(clearTimeout(a),delete a,a=null)},hideContextMenu:function(){this.contextMenu.hide()},renderContextMenu:function(b,c){for(var d=this,e="",f=d.options.contextMenuItems,g=0,h=f.length;h>g;g++)if(f[g].isSeparator)e+='';else{var i=f[g].render(d);null!=i&&(e+=''+i+"
")}d.contextMenu.empty().append(a(e)).css({top:c,left:b}).show(),d.contextMenu.find(".mejs-contextmenu-item").each(function(){var b=a(this),c=parseInt(b.data("itemindex"),10),e=d.options.contextMenuItems[c];"undefined"!=typeof e.show&&e.show(b,d),b.click(function(){"undefined"!=typeof e.click&&e.click(d),d.contextMenu.hide()})}),setTimeout(function(){d.killControlsTimer("rev3")},100)}})}(mejs.$),function(a){a.extend(mejs.MepDefaults,{skipBackInterval:30,skipBackText:mejs.i18n.t("Skip back %1 seconds")}),a.extend(MediaElementPlayer.prototype,{buildskipback:function(b,c,d,e){var f=this,g=f.options.skipBackText.replace("%1",f.options.skipBackInterval);a(''+f.options.skipBackInterval+"
").appendTo(c).click(function(){e.setCurrentTime(Math.max(e.currentTime-f.options.skipBackInterval,0)),a(this).find("button").blur()})}})}(mejs.$),function(a){a.extend(mejs.MepDefaults,{postrollCloseText:mejs.i18n.t("Close")}),a.extend(MediaElementPlayer.prototype,{buildpostroll:function(b,c,d,e){var f=this,g=f.container.find('link[rel="postroll"]').attr("href");"undefined"!=typeof g&&(b.postroll=a('').prependTo(d).hide(),f.media.addEventListener("ended",function(c){a.ajax({dataType:"html",url:g,success:function(a,b){d.find(".mejs-postroll-layer-content").html(a)}}),b.postroll.show()},!1))}})}(mejs.$);
--------------------------------------------------------------------------------
/midi_util.py:
--------------------------------------------------------------------------------
1 | import sys, os
2 | from collections import defaultdict
3 | import numpy as np
4 | import midi
5 |
6 | RANGE = 128
7 |
8 | def round_tick(tick, time_step):
9 | return int(round(tick/float(time_step)) * time_step)
10 |
11 | def ingest_notes(track, verbose=False):
12 |
13 | notes = { n: [] for n in range(RANGE) }
14 | current_tick = 0
15 |
16 | for msg in track:
17 | # ignore all end of track events
18 | if isinstance(msg, midi.EndOfTrackEvent):
19 | continue
20 |
21 | if msg.tick > 0:
22 | current_tick += msg.tick
23 |
24 | # velocity of 0 is equivalent to note off, so treat as such
25 | if isinstance(msg, midi.NoteOnEvent) and msg.get_velocity() != 0:
26 | if len(notes[msg.get_pitch()]) > 0 and \
27 | len(notes[msg.get_pitch()][-1]) != 2:
28 | if verbose:
29 | print "Warning: double NoteOn encountered, deleting the first"
30 | print msg
31 | else:
32 | notes[msg.get_pitch()] += [[current_tick]]
33 | elif isinstance(msg, midi.NoteOffEvent) or \
34 | (isinstance(msg, midi.NoteOnEvent) and msg.get_velocity() == 0):
35 | # sanity check: no notes end without being started
36 | if len(notes[msg.get_pitch()][-1]) != 1:
37 | if verbose:
38 | print "Warning: skipping NoteOff Event with no corresponding NoteOn"
39 | print msg
40 | else:
41 | notes[msg.get_pitch()][-1] += [current_tick]
42 |
43 | return notes, current_tick
44 |
45 | def round_notes(notes, track_ticks, time_step, R=None, O=None):
46 | if not R:
47 | R = RANGE
48 | if not O:
49 | O = 0
50 |
51 | sequence = np.zeros((track_ticks/time_step, R))
52 | disputed = { t: defaultdict(int) for t in range(track_ticks/time_step) }
53 | for note in notes:
54 | for (start, end) in notes[note]:
55 | start_t = round_tick(start, time_step) / time_step
56 | end_t = round_tick(end, time_step) / time_step
57 | # normal case where note is long enough
58 | if end - start > time_step/2 and start_t != end_t:
59 | sequence[start_t:end_t, note - O] = 1
60 | # cases where note is within bounds of time step
61 | elif start > start_t * time_step:
62 | disputed[start_t][note] += (end - start)
63 | elif end <= end_t * time_step:
64 | disputed[end_t-1][note] += (end - start)
65 | # case where a note is on the border
66 | else:
67 | before_border = start_t * time_step - start
68 | if before_border > 0:
69 | disputed[start_t-1][note] += before_border
70 | after_border = end - start_t * time_step
71 | if after_border > 0 and end < track_ticks:
72 | disputed[start_t][note] += after_border
73 |
74 | # solve disputed
75 | for seq_idx in range(sequence.shape[0]):
76 | if np.count_nonzero(sequence[seq_idx, :]) == 0 and len(disputed[seq_idx]) > 0:
77 | # print seq_idx, disputed[seq_idx]
78 | sorted_notes = sorted(disputed[seq_idx].items(),
79 | key=lambda x: x[1])
80 | max_val = max(x[1] for x in sorted_notes)
81 | top_notes = filter(lambda x: x[1] >= max_val, sorted_notes)
82 | for note, _ in top_notes:
83 | sequence[seq_idx, note - O] = 1
84 |
85 | return sequence
86 |
87 | def parse_midi_to_sequence(input_filename, time_step, verbose=False):
88 | sequence = []
89 | pattern = midi.read_midifile(input_filename)
90 |
91 | if len(pattern) < 1:
92 | raise Exception("No pattern found in midi file")
93 |
94 | if verbose:
95 | print "Track resolution: {}".format(pattern.resolution)
96 | print "Number of tracks: {}".format(len(pattern))
97 | print "Time step: {}".format(time_step)
98 |
99 | # Track ingestion stage
100 | notes = { n: [] for n in range(RANGE) }
101 | track_ticks = 0
102 | for track in pattern:
103 | current_tick = 0
104 | for msg in track:
105 | # ignore all end of track events
106 | if isinstance(msg, midi.EndOfTrackEvent):
107 | continue
108 |
109 | if msg.tick > 0:
110 | current_tick += msg.tick
111 |
112 | # velocity of 0 is equivalent to note off, so treat as such
113 | if isinstance(msg, midi.NoteOnEvent) and msg.get_velocity() != 0:
114 | if len(notes[msg.get_pitch()]) > 0 and \
115 | len(notes[msg.get_pitch()][-1]) != 2:
116 | if verbose:
117 | print "Warning: double NoteOn encountered, deleting the first"
118 | print msg
119 | else:
120 | notes[msg.get_pitch()] += [[current_tick]]
121 | elif isinstance(msg, midi.NoteOffEvent) or \
122 | (isinstance(msg, midi.NoteOnEvent) and msg.get_velocity() == 0):
123 | # sanity check: no notes end without being started
124 | if len(notes[msg.get_pitch()][-1]) != 1:
125 | if verbose:
126 | print "Warning: skipping NoteOff Event with no corresponding NoteOn"
127 | print msg
128 | else:
129 | notes[msg.get_pitch()][-1] += [current_tick]
130 |
131 | track_ticks = max(current_tick, track_ticks)
132 |
133 | track_ticks = round_tick(track_ticks, time_step)
134 | if verbose:
135 | print "Track ticks (rounded): {} ({} time steps)".format(track_ticks, track_ticks/time_step)
136 |
137 | sequence = round_notes(notes, track_ticks, time_step)
138 |
139 | return sequence
140 |
141 | class MidiWriter(object):
142 |
143 | def __init__(self, verbose=False):
144 | self.verbose = verbose
145 | self.note_range = RANGE
146 |
147 | def note_off(self, val, tick):
148 | self.track.append(midi.NoteOffEvent(tick=tick, pitch=val))
149 | return 0
150 |
151 | def note_on(self, val, tick):
152 | self.track.append(midi.NoteOnEvent(tick=tick, pitch=val, velocity=70))
153 | return 0
154 |
155 | def dump_sequence_to_midi(self, sequence, output_filename, time_step,
156 | resolution, metronome=24):
157 | if self.verbose:
158 | print "Dumping sequence to MIDI file: {}".format(output_filename)
159 | print "Resolution: {}".format(resolution)
160 | print "Time Step: {}".format(time_step)
161 |
162 | pattern = midi.Pattern(resolution=resolution)
163 | self.track = midi.Track()
164 |
165 | # metadata track
166 | meta_track = midi.Track()
167 | time_sig = midi.TimeSignatureEvent()
168 | time_sig.set_numerator(4)
169 | time_sig.set_denominator(4)
170 | time_sig.set_metronome(metronome)
171 | time_sig.set_thirtyseconds(8)
172 | meta_track.append(time_sig)
173 | pattern.append(meta_track)
174 |
175 | # reshape to (SEQ_LENGTH X NUM_DIMS)
176 | sequence = np.reshape(sequence, [-1, self.note_range])
177 |
178 | time_steps = sequence.shape[0]
179 | if self.verbose:
180 | print "Total number of time steps: {}".format(time_steps)
181 |
182 | tick = time_step
183 | self.notes_on = { n: False for n in range(self.note_range) }
184 | # for seq_idx in range(188, 220):
185 | for seq_idx in range(time_steps):
186 | notes = np.nonzero(sequence[seq_idx, :])[0].tolist()
187 |
188 | # this tick will only be assigned to first NoteOn/NoteOff in
189 | # this time_step
190 |
191 | # NoteOffEvents come first so they'll have the tick value
192 | # go through all notes that are currently on and see if any
193 | # turned off
194 | for n in self.notes_on:
195 | if self.notes_on[n] and n not in notes:
196 | tick = self.note_off(n, tick)
197 | self.notes_on[n] = False
198 |
199 | # Turn on any notes that weren't previously on
200 | for note in notes:
201 | if not self.notes_on[note]:
202 | tick = self.note_on(note, tick)
203 | self.notes_on[note] = True
204 |
205 | tick += time_step
206 |
207 | # flush out notes
208 | for n in self.notes_on:
209 | if self.notes_on[n]:
210 | self.note_off(n, tick)
211 | tick = 0
212 | self.notes_on[n] = False
213 |
214 | pattern.append(self.track)
215 | midi.write_midifile(output_filename, pattern)
216 |
217 | if __name__ == '__main__':
218 | pass
219 |
--------------------------------------------------------------------------------
/model.py:
--------------------------------------------------------------------------------
1 | import os
2 | import logging
3 | import numpy as np
4 | import tensorflow as tf
5 | from tensorflow.models.rnn import rnn_cell
6 | from tensorflow.models.rnn import rnn, seq2seq
7 |
8 | import nottingham_util
9 |
10 | class Model(object):
11 | """
12 | Cross-Entropy Naive Formulation
13 | A single time step may have multiple notes active, so a sigmoid cross entropy loss
14 | is used to match targets.
15 |
16 | seq_input: a [ T x B x D ] matrix, where T is the time steps in the batch, B is the
17 | batch size, and D is the amount of dimensions
18 | """
19 |
20 | def __init__(self, config, training=False):
21 | self.config = config
22 | self.time_batch_len = time_batch_len = config.time_batch_len
23 | self.input_dim = input_dim = config.input_dim
24 | hidden_size = config.hidden_size
25 | num_layers = config.num_layers
26 | dropout_prob = config.dropout_prob
27 | input_dropout_prob = config.input_dropout_prob
28 | cell_type = config.cell_type
29 |
30 | self.seq_input = \
31 | tf.placeholder(tf.float32, shape=[self.time_batch_len, None, input_dim])
32 |
33 | if (dropout_prob <= 0.0 or dropout_prob > 1.0):
34 | raise Exception("Invalid dropout probability: {}".format(dropout_prob))
35 |
36 | if (input_dropout_prob <= 0.0 or input_dropout_prob > 1.0):
37 | raise Exception("Invalid input dropout probability: {}".format(input_dropout_prob))
38 |
39 | # setup variables
40 | with tf.variable_scope("rnnlstm"):
41 | output_W = tf.get_variable("output_w", [hidden_size, input_dim])
42 | output_b = tf.get_variable("output_b", [input_dim])
43 | self.lr = tf.constant(config.learning_rate, name="learning_rate")
44 | self.lr_decay = tf.constant(config.learning_rate_decay, name="learning_rate_decay")
45 |
46 | def create_cell(input_size):
47 | if cell_type == "vanilla":
48 | cell_class = rnn_cell.BasicRNNCell
49 | elif cell_type == "gru":
50 | cell_class = rnn_cell.BasicGRUCell
51 | elif cell_type == "lstm":
52 | cell_class = rnn_cell.BasicLSTMCell
53 | else:
54 | raise Exception("Invalid cell type: {}".format(cell_type))
55 |
56 | cell = cell_class(hidden_size, input_size = input_size)
57 | if training:
58 | return rnn_cell.DropoutWrapper(cell, output_keep_prob = dropout_prob)
59 | else:
60 | return cell
61 |
62 | if training:
63 | self.seq_input_dropout = tf.nn.dropout(self.seq_input, keep_prob = input_dropout_prob)
64 | else:
65 | self.seq_input_dropout = self.seq_input
66 |
67 | self.cell = rnn_cell.MultiRNNCell(
68 | [create_cell(input_dim)] + [create_cell(hidden_size) for i in range(1, num_layers)])
69 |
70 | batch_size = tf.shape(self.seq_input_dropout)[0]
71 | self.initial_state = self.cell.zero_state(batch_size, tf.float32)
72 | inputs_list = tf.unpack(self.seq_input_dropout)
73 |
74 | # rnn outputs a list of [batch_size x H] outputs
75 | outputs_list, self.final_state = rnn.rnn(self.cell, inputs_list,
76 | initial_state=self.initial_state)
77 |
78 | outputs = tf.pack(outputs_list)
79 | outputs_concat = tf.reshape(outputs, [-1, hidden_size])
80 | logits_concat = tf.matmul(outputs_concat, output_W) + output_b
81 | logits = tf.reshape(logits_concat, [self.time_batch_len, -1, input_dim])
82 |
83 | # probabilities of each note
84 | self.probs = self.calculate_probs(logits)
85 | self.loss = self.init_loss(logits, logits_concat)
86 | self.train_step = tf.train.RMSPropOptimizer(self.lr, decay = self.lr_decay) \
87 | .minimize(self.loss)
88 |
89 | def init_loss(self, outputs, _):
90 | self.seq_targets = \
91 | tf.placeholder(tf.float32, [self.time_batch_len, None, self.input_dim])
92 |
93 | batch_size = tf.shape(self.seq_input_dropout)
94 | cross_ent = tf.nn.sigmoid_cross_entropy_with_logits(outputs, self.seq_targets)
95 | return tf.reduce_sum(cross_ent) / self.time_batch_len / tf.to_float(batch_size)
96 |
97 | def calculate_probs(self, logits):
98 | return tf.sigmoid(logits)
99 |
100 | def get_cell_zero_state(self, session, batch_size):
101 | return self.cell.zero_state(batch_size, tf.float32).eval(session=session)
102 |
103 | class NottinghamModel(Model):
104 | """
105 | Dual softmax formulation
106 |
107 | A single time step should be a concatenation of two one-hot-encoding binary vectors.
108 | Loss function is a sum of two softmax loss functions over [:r] and [r:] respectively,
109 | where r is the number of melody classes
110 | """
111 |
112 | def init_loss(self, outputs, outputs_concat):
113 | self.seq_targets = \
114 | tf.placeholder(tf.int64, [self.time_batch_len, None, 2])
115 | batch_size = tf.shape(self.seq_targets)[1]
116 |
117 | with tf.variable_scope("rnnlstm"):
118 | self.melody_coeff = tf.constant(self.config.melody_coeff)
119 |
120 | r = nottingham_util.NOTTINGHAM_MELODY_RANGE
121 | targets_concat = tf.reshape(self.seq_targets, [-1, 2])
122 |
123 | melody_loss = tf.nn.sparse_softmax_cross_entropy_with_logits( \
124 | outputs_concat[:, :r], \
125 | targets_concat[:, 0])
126 | harmony_loss = tf.nn.sparse_softmax_cross_entropy_with_logits( \
127 | outputs_concat[:, r:], \
128 | targets_concat[:, 1])
129 | losses = tf.add(self.melody_coeff * melody_loss, (1 - self.melody_coeff) * harmony_loss)
130 | return tf.reduce_sum(losses) / self.time_batch_len / tf.to_float(batch_size)
131 |
132 | def calculate_probs(self, logits):
133 | steps = []
134 | for t in range(self.time_batch_len):
135 | melody_softmax = tf.nn.softmax(logits[t, :, :nottingham_util.NOTTINGHAM_MELODY_RANGE])
136 | harmony_softmax = tf.nn.softmax(logits[t, :, nottingham_util.NOTTINGHAM_MELODY_RANGE:])
137 | steps.append(tf.concat(1, [melody_softmax, harmony_softmax]))
138 | return tf.pack(steps)
139 |
140 | def assign_melody_coeff(self, session, melody_coeff):
141 | if melody_coeff < 0.0 or melody_coeff > 1.0:
142 | raise Exception("Invalid melody coeffecient")
143 |
144 | session.run(tf.assign(self.melody_coeff, melody_coeff))
145 |
146 | class NottinghamSeparate(Model):
147 | """
148 | Single softmax formulation
149 |
150 | Regular single classification formulation, used to train baseline models
151 | where the melody and harmony are trained separately
152 | """
153 |
154 | def init_loss(self, outputs, outputs_concat):
155 | self.seq_targets = \
156 | tf.placeholder(tf.int64, [self.time_batch_len, None])
157 | batch_size = tf.shape(self.seq_targets)[1]
158 |
159 | with tf.variable_scope("rnnlstm"):
160 | self.melody_coeff = tf.constant(self.config.melody_coeff)
161 |
162 | targets_concat = tf.reshape(self.seq_targets, [-1])
163 | losses = tf.nn.sparse_softmax_cross_entropy_with_logits( \
164 | outputs_concat, targets_concat)
165 |
166 | return tf.reduce_sum(losses) / self.time_batch_len / tf.to_float(batch_size)
167 |
168 | def calculate_probs(self, logits):
169 | steps = []
170 | for t in range(self.time_batch_len):
171 | softmax = tf.nn.softmax(logits[t, :, :])
172 | steps.append(softmax)
173 | return tf.pack(steps)
174 |
--------------------------------------------------------------------------------
/nottingham_util.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import os
3 | import midi
4 | import cPickle
5 | from pprint import pprint
6 |
7 | import midi_util
8 | import mingus
9 | import mingus.core.chords
10 | import sampling
11 |
12 | PICKLE_LOC = 'data/nottingham.pickle'
13 | NOTTINGHAM_MELODY_MAX = 88
14 | NOTTINGHAM_MELODY_MIN = 55
15 | # add one to the range for silence in melody
16 | NOTTINGHAM_MELODY_RANGE = NOTTINGHAM_MELODY_MAX - NOTTINGHAM_MELODY_MIN + 1 + 1
17 | CHORD_BASE = 48
18 | CHORD_BLACKLIST = ['major third', 'minor third', 'perfect fifth']
19 | NO_CHORD = 'NONE'
20 | SHARPS_TO_FLATS = {
21 | "A#": "Bb",
22 | "B#": "C",
23 | "C#": "Db",
24 | "D#": "Eb",
25 | "E#": "F",
26 | "F#": "Gb",
27 | "G#": "Ab",
28 | }
29 |
30 | def resolve_chord(chord):
31 | """
32 | Resolves rare chords to their closest common chord, to limit the total
33 | amount of chord classes.
34 | """
35 | if chord in CHORD_BLACKLIST:
36 | return None
37 | # take the first of dual chords
38 | if "|" in chord:
39 | chord = chord.split("|")[0]
40 | # remove 7ths, 11ths, 9s, 6th,
41 | if chord.endswith("11"):
42 | chord = chord[:-2]
43 | if chord.endswith("7") or chord.endswith("9") or chord.endswith("6"):
44 | chord = chord[:-1]
45 | # replace 'dim' with minor
46 | if chord.endswith("dim"):
47 | chord = chord[:-3] + "m"
48 | return chord
49 |
50 | def prepare_nottingham_pickle(time_step, chord_cutoff=64, filename=PICKLE_LOC, verbose=False):
51 | """
52 | time_step: the time step to discretize all notes into
53 | chord_cutoff: if chords are seen less than this cutoff, they are ignored and marked as
54 | as rests in the resulting dataset
55 | filename: the location where the pickle will be saved to
56 | """
57 |
58 | data = {}
59 | store = {}
60 | chords = {}
61 | max_seq = 0
62 | seq_lens = []
63 |
64 | for d in ["train", "test", "valid"]:
65 | print "Parsing {}...".format(d)
66 | parsed = parse_nottingham_directory("data/Nottingham/{}".format(d), time_step, verbose=False)
67 | metadata = [s[0] for s in parsed]
68 | seqs = [s[1] for s in parsed]
69 | data[d] = seqs
70 | data[d + '_metadata'] = metadata
71 | lens = [len(s[1]) for s in seqs]
72 | seq_lens += lens
73 | max_seq = max(max_seq, max(lens))
74 |
75 | for _, harmony in seqs:
76 | for h in harmony:
77 | if h not in chords:
78 | chords[h] = 1
79 | else:
80 | chords[h] += 1
81 |
82 | avg_seq = float(sum(seq_lens)) / len(seq_lens)
83 |
84 | chords = { c: i for c, i in chords.iteritems() if chords[c] >= chord_cutoff }
85 | chord_mapping = { c: i for i, c in enumerate(chords.keys()) }
86 | num_chords = len(chord_mapping)
87 | store['chord_to_idx'] = chord_mapping
88 | if verbose:
89 | pprint(chords)
90 | print "Number of chords: {}".format(num_chords)
91 | print "Max Sequence length: {}".format(max_seq)
92 | print "Avg Sequence length: {}".format(avg_seq)
93 | print "Num Sequences: {}".format(len(seq_lens))
94 |
95 | def combine(melody, harmony):
96 | full = np.zeros((melody.shape[0], NOTTINGHAM_MELODY_RANGE + num_chords))
97 |
98 | assert melody.shape[0] == len(harmony)
99 |
100 | # for all melody sequences that don't have any notes, add the empty melody marker (last one)
101 | for i in range(melody.shape[0]):
102 | if np.count_nonzero(melody[i, :]) == 0:
103 | melody[i, NOTTINGHAM_MELODY_RANGE-1] = 1
104 |
105 | # all melody encodings should now have exactly one 1
106 | for i in range(melody.shape[0]):
107 | assert np.count_nonzero(melody[i, :]) == 1
108 |
109 | # add all the melodies
110 | full[:, :melody.shape[1]] += melody
111 |
112 | harmony_idxs = [ chord_mapping[h] if h in chord_mapping else chord_mapping[NO_CHORD] \
113 | for h in harmony ]
114 | harmony_idxs = [ NOTTINGHAM_MELODY_RANGE + h for h in harmony_idxs ]
115 | full[np.arange(len(harmony)), harmony_idxs] = 1
116 |
117 | # all full encodings should have exactly two 1's
118 | for i in range(full.shape[0]):
119 | assert np.count_nonzero(full[i, :]) == 2
120 |
121 | return full
122 |
123 | for d in ["train", "test", "valid"]:
124 | print "Combining {}".format(d)
125 | store[d] = [ combine(m, h) for m, h in data[d] ]
126 | store[d + '_metadata'] = data[d + '_metadata']
127 |
128 | with open(filename, 'w') as f:
129 | cPickle.dump(store, f, protocol=-1)
130 |
131 | return True
132 |
133 | def parse_nottingham_directory(input_dir, time_step, verbose=False):
134 | """
135 | input_dir: a directory containing MIDI files
136 |
137 | returns a list of [T x D] matrices, where each matrix represents a
138 | a sequence with T time steps over D dimensions
139 | """
140 |
141 | files = [ os.path.join(input_dir, f) for f in os.listdir(input_dir)
142 | if os.path.isfile(os.path.join(input_dir, f)) ]
143 | sequences = [ \
144 | parse_nottingham_to_sequence(f, time_step=time_step, verbose=verbose) \
145 | for f in files ]
146 |
147 | if verbose:
148 | print "Total sequences: {}".format(len(sequences))
149 |
150 | # filter out the non 2-track MIDI's
151 | sequences = filter(lambda x: x[1] != None, sequences)
152 |
153 | if verbose:
154 | print "Total sequences left: {}".format(len(sequences))
155 |
156 | return sequences
157 |
158 | def parse_nottingham_to_sequence(input_filename, time_step, verbose=False):
159 | """
160 | input_filename: a MIDI filename
161 |
162 | returns a [T x D] matrix representing a sequence with T time steps over
163 | D dimensions
164 | """
165 | sequence = []
166 | pattern = midi.read_midifile(input_filename)
167 |
168 | metadata = {
169 | "path": input_filename,
170 | "name": input_filename.split("/")[-1].split(".")[0]
171 | }
172 |
173 | # Most nottingham midi's have 3 tracks. metadata info, melody, harmony
174 | # throw away any tracks that don't fit this
175 | if len(pattern) != 3:
176 | if verbose:
177 | "Skipping track with {} tracks".format(len(pattern))
178 | return (metadata, None)
179 |
180 | # ticks_per_quarter = -1
181 | for msg in pattern[0]:
182 | if isinstance(msg, midi.TimeSignatureEvent):
183 | metadata["ticks_per_quarter"] = msg.get_metronome()
184 | ticks_per_quarter = msg.get_metronome()
185 |
186 | if verbose:
187 | print "{}".format(input_filename)
188 | print "Track resolution: {}".format(pattern.resolution)
189 | print "Number of tracks: {}".format(len(pattern))
190 | print "Time step: {}".format(time_step)
191 | print "Ticks per quarter: {}".format(ticks_per_quarter)
192 |
193 | # Track ingestion stage
194 | track_ticks = 0
195 |
196 | melody_notes, melody_ticks = midi_util.ingest_notes(pattern[1])
197 | harmony_notes, harmony_ticks = midi_util.ingest_notes(pattern[2])
198 |
199 | track_ticks = midi_util.round_tick(max(melody_ticks, harmony_ticks), time_step)
200 | if verbose:
201 | print "Track ticks (rounded): {} ({} time steps)".format(track_ticks, track_ticks/time_step)
202 |
203 | melody_sequence = midi_util.round_notes(melody_notes, track_ticks, time_step,
204 | R=NOTTINGHAM_MELODY_RANGE, O=NOTTINGHAM_MELODY_MIN)
205 |
206 | for i in range(melody_sequence.shape[0]):
207 | if np.count_nonzero(melody_sequence[i, :]) > 1:
208 | if verbose:
209 | print "Double note found: {}: {} ({})".format(i, np.nonzero(melody_sequence[i, :]), input_filename)
210 | return (metadata, None)
211 |
212 | harmony_sequence = midi_util.round_notes(harmony_notes, track_ticks, time_step)
213 |
214 | harmonies = []
215 | for i in range(harmony_sequence.shape[0]):
216 | notes = np.where(harmony_sequence[i] == 1)[0]
217 | if len(notes) > 0:
218 | notes_shift = [ mingus.core.notes.int_to_note(h%12) for h in notes]
219 | chord = mingus.core.chords.determine(notes_shift, shorthand=True)
220 | if len(chord) == 0:
221 | # try flat combinations
222 | notes_shift = [ SHARPS_TO_FLATS[n] if n in SHARPS_TO_FLATS else n for n in notes_shift]
223 | chord = mingus.core.chords.determine(notes_shift, shorthand=True)
224 | if len(chord) == 0:
225 | if verbose:
226 | print "Could not determine chord: {} ({}, {}), defaulting to last steps chord" \
227 | .format(notes_shift, input_filename, i)
228 | if len(harmonies) > 0:
229 | harmonies.append(harmonies[-1])
230 | else:
231 | harmonies.append(NO_CHORD)
232 | else:
233 | resolved = resolve_chord(chord[0])
234 | if resolved:
235 | harmonies.append(resolved)
236 | else:
237 | harmonies.append(NO_CHORD)
238 | else:
239 | harmonies.append(NO_CHORD)
240 |
241 | return (metadata, (melody_sequence, harmonies))
242 |
243 | class NottinghamMidiWriter(midi_util.MidiWriter):
244 |
245 | def __init__(self, chord_to_idx, verbose=False):
246 | super(NottinghamMidiWriter, self).__init__(verbose)
247 | self.idx_to_chord = { i: c for c, i in chord_to_idx.items() }
248 | self.note_range = NOTTINGHAM_MELODY_RANGE + len(self.idx_to_chord)
249 |
250 | def dereference_chord(self, idx):
251 | if idx not in self.idx_to_chord:
252 | raise Exception("No chord index found: {}".format(idx))
253 | shorthand = self.idx_to_chord[idx]
254 | if shorthand == NO_CHORD:
255 | return []
256 | chord = mingus.core.chords.from_shorthand(shorthand)
257 | return [ CHORD_BASE + mingus.core.notes.note_to_int(n) for n in chord ]
258 |
259 | def note_on(self, val, tick):
260 | if val >= NOTTINGHAM_MELODY_RANGE:
261 | notes = self.dereference_chord(val - NOTTINGHAM_MELODY_RANGE)
262 | else:
263 | # if note is the top of the range, then it stands for gap in melody
264 | if val == NOTTINGHAM_MELODY_RANGE - 1:
265 | notes = []
266 | else:
267 | notes = [NOTTINGHAM_MELODY_MIN + val]
268 |
269 | # print 'turning on {}'.format(notes)
270 | for note in notes:
271 | self.track.append(midi.NoteOnEvent(tick=tick, pitch=note, velocity=70))
272 | tick = 0 # notes that come right after each other should have zero tick
273 |
274 | return tick
275 |
276 | def note_off(self, val, tick):
277 | if val >= NOTTINGHAM_MELODY_RANGE:
278 | notes = self.dereference_chord(val - NOTTINGHAM_MELODY_RANGE)
279 | else:
280 | notes = [NOTTINGHAM_MELODY_MIN + val]
281 |
282 | # print 'turning off {}'.format(notes)
283 | for note in notes:
284 | self.track.append(midi.NoteOffEvent(tick=tick, pitch=note))
285 | tick = 0
286 |
287 | return tick
288 |
289 | class NottinghamSampler(object):
290 |
291 | def __init__(self, chord_to_idx, method = 'sample', harmony_repeat_max = 16, melody_repeat_max = 16, verbose=False):
292 | self.verbose = verbose
293 | self.idx_to_chord = { i: c for c, i in chord_to_idx.items() }
294 | self.method = method
295 |
296 | self.hlast = 0
297 | self.hcount = 0
298 | self.hrepeat = harmony_repeat_max
299 |
300 | self.mlast = 0
301 | self.mcount = 0
302 | self.mrepeat = melody_repeat_max
303 |
304 | def visualize_probs(self, probs):
305 | if not self.verbose:
306 | return
307 |
308 | melodies = sorted(list(enumerate(probs[:NOTTINGHAM_MELODY_RANGE])),
309 | key=lambda x: x[1], reverse=True)[:4]
310 | harmonies = sorted(list(enumerate(probs[NOTTINGHAM_MELODY_RANGE:])),
311 | key=lambda x: x[1], reverse=True)[:4]
312 | harmonies = [(self.idx_to_chord[i], j) for i, j in harmonies]
313 | print 'Top Melody Notes: '
314 | pprint(melodies)
315 | print 'Top Harmony Notes: '
316 | pprint(harmonies)
317 |
318 | def sample_notes_static(self, probs):
319 | top_m = probs[:NOTTINGHAM_MELODY_RANGE].argsort()
320 | if top_m[-1] == self.mlast and self.mcount >= self.mrepeat:
321 | top_m = top_m[:-1]
322 | self.mcount = 0
323 | elif top_m[-1] == self.mlast:
324 | self.mcount += 1
325 | else:
326 | self.mcount = 0
327 | self.mlast = top_m[-1]
328 | top_melody = top_m[-1]
329 |
330 | top_h = probs[NOTTINGHAM_MELODY_RANGE:].argsort()
331 | if top_h[-1] == self.hlast and self.hcount >= self.hrepeat:
332 | top_h = top_h[:-1]
333 | self.hcount = 0
334 | elif top_h[-1] == self.hlast:
335 | self.hcount += 1
336 | else:
337 | self.hcount = 0
338 | self.hlast = top_h[-1]
339 | top_chord = top_h[-1] + NOTTINGHAM_MELODY_RANGE
340 |
341 | chord = np.zeros([len(probs)], dtype=np.int32)
342 | chord[top_melody] = 1.0
343 | chord[top_chord] = 1.0
344 | return chord
345 |
346 | def sample_notes_dist(self, probs):
347 | idxed = [(i, p) for i, p in enumerate(probs)]
348 |
349 | notes = [n[0] for n in idxed]
350 | ps = np.array([n[1] for n in idxed])
351 | r = NOTTINGHAM_MELODY_RANGE
352 |
353 | assert np.allclose(np.sum(ps[:r]), 1.0)
354 | assert np.allclose(np.sum(ps[r:]), 1.0)
355 |
356 | # renormalize so numpy doesn't complain
357 | ps[:r] = ps[:r] / ps[:r].sum()
358 | ps[r:] = ps[r:] / ps[r:].sum()
359 |
360 | melody = np.random.choice(notes[:r], p=ps[:r])
361 | harmony = np.random.choice(notes[r:], p=ps[r:])
362 |
363 | chord = np.zeros([len(probs)], dtype=np.int32)
364 | chord[melody] = 1.0
365 | chord[harmony] = 1.0
366 | return chord
367 |
368 |
369 | def sample_notes(self, probs):
370 | self.visualize_probs(probs)
371 | if self.method == 'static':
372 | return self.sample_notes_static(probs)
373 | elif self.method == 'sample':
374 | return self.sample_notes_dist(probs)
375 |
376 | def accuracy(batch_probs, data, num_samples=1):
377 | """
378 | Batch Probs: { num_time_steps: [ time_step_1, time_step_2, ... ] }
379 | Data: [
380 | [ [ data ], [ target ] ], # batch with one time step
381 | [ [ data1, data2 ], [ target1, target2 ] ], # batch with two time steps
382 | ...
383 | ]
384 | """
385 |
386 | def calc_accuracy():
387 | total = 0
388 | melody_correct, harmony_correct = 0, 0
389 | melody_incorrect, harmony_incorrect = 0, 0
390 | for _, batch_targets in data:
391 | num_time_steps = len(batch_targets)
392 | for ts_targets, ts_probs in zip(batch_targets, batch_probs[num_time_steps]):
393 |
394 | assert ts_targets.shape == ts_targets.shape
395 |
396 | for seq_idx in range(ts_targets.shape[1]):
397 | for step_idx in range(ts_targets.shape[0]):
398 | idxed = [(n, p) for n, p in \
399 | enumerate(ts_probs[step_idx, seq_idx, :])]
400 | notes = [n[0] for n in idxed]
401 | ps = np.array([n[1] for n in idxed])
402 | r = NOTTINGHAM_MELODY_RANGE
403 |
404 | assert np.allclose(np.sum(ps[:r]), 1.0)
405 | assert np.allclose(np.sum(ps[r:]), 1.0)
406 |
407 | # renormalize so numpy doesn't complain
408 | ps[:r] = ps[:r] / ps[:r].sum()
409 | ps[r:] = ps[r:] / ps[r:].sum()
410 |
411 | melody = np.random.choice(notes[:r], p=ps[:r])
412 | harmony = np.random.choice(notes[r:], p=ps[r:])
413 |
414 | melody_target = ts_targets[step_idx, seq_idx, 0]
415 | if melody_target == melody:
416 | melody_correct += 1
417 | else:
418 | melody_incorrect += 1
419 |
420 | harmony_target = ts_targets[step_idx, seq_idx, 1] + r
421 | if harmony_target == harmony:
422 | harmony_correct += 1
423 | else:
424 | harmony_incorrect += 1
425 |
426 | return (melody_correct, melody_incorrect, harmony_correct, harmony_incorrect)
427 |
428 | maccs, haccs, taccs = [], [], []
429 | for i in range(num_samples):
430 | print "Sample {}".format(i)
431 | m, mi, h, hi = calc_accuracy()
432 | maccs.append( float(m) / float(m + mi))
433 | haccs.append( float(h) / float(h + hi))
434 | taccs.append( float(m + h) / float(m + h + mi + hi) )
435 |
436 | print "Melody Precision/Recall: {}".format(sum(maccs)/len(maccs))
437 | print "Harmony Precision/Recall: {}".format(sum(haccs)/len(haccs))
438 | print "Total Precision/Recall: {}".format(sum(taccs)/len(taccs))
439 |
440 | def seperate_accuracy(batch_probs, data, num_samples=1):
441 |
442 | def calc_accuracy():
443 | total = 0
444 | total_correct, total_incorrect = 0, 0
445 | for _, batch_targets in data:
446 | num_time_steps = len(batch_targets)
447 | for ts_targets, ts_probs in zip(batch_targets, batch_probs[num_time_steps]):
448 |
449 | assert ts_targets.shape == ts_targets.shape
450 |
451 | for seq_idx in range(ts_targets.shape[1]):
452 | for step_idx in range(ts_targets.shape[0]):
453 |
454 | idxed = [(n, p) for n, p in \
455 | enumerate(ts_probs[step_idx, seq_idx, :])]
456 | notes = [n[0] for n in idxed]
457 | ps = np.array([n[1] for n in idxed])
458 | r = NOTTINGHAM_MELODY_RANGE
459 |
460 | assert np.allclose(np.sum(ps), 1.0)
461 | ps = ps / ps.sum()
462 | note = np.random.choice(notes, p=ps)
463 |
464 | target = ts_targets[step_idx, seq_idx]
465 | if target == note:
466 | total_correct += 1
467 | else:
468 | total_incorrect += 1
469 |
470 | return (total_correct, total_incorrect)
471 |
472 | taccs = []
473 | for i in range(num_samples):
474 | print "Sample {}".format(i)
475 | c, ic = calc_accuracy()
476 | taccs.append( float(c) / float(c + ic))
477 |
478 | print "Precision/Recall: {}".format(sum(taccs)/len(taccs))
479 |
480 | def i_vi_iv_v(chord_to_idx, repeats, input_dim):
481 | r = NOTTINGHAM_MELODY_RANGE
482 |
483 | i = np.zeros(input_dim)
484 | i[r + chord_to_idx['CM']] = 1
485 |
486 | vi = np.zeros(input_dim)
487 | vi[r + chord_to_idx['Am']] = 1
488 |
489 | iv = np.zeros(input_dim)
490 | iv[r + chord_to_idx['FM']] = 1
491 |
492 | v = np.zeros(input_dim)
493 | v[r + chord_to_idx['GM']] = 1
494 |
495 | full_seq = [i] * 16 + [vi] * 16 + [iv] * 16 + [v] * 16
496 | full_seq = full_seq * repeats
497 |
498 | return full_seq
499 |
500 | if __name__ == '__main__':
501 |
502 | resolution = 480
503 | time_step = 120
504 |
505 | assert resolve_chord("GM7") == "GM"
506 | assert resolve_chord("G#dim|AM7") == "G#m"
507 | assert resolve_chord("Dm9") == "Dm"
508 | assert resolve_chord("AM11") == "AM"
509 |
510 | prepare_nottingham_pickle(time_step, verbose=True)
511 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | matplotlib
2 | mingus
3 | numpy
4 | git+https://github.com/vishnubob/python-midi#egg=midi
5 | # Linux, Python 2.7, GPU
6 | https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow-0.8.0-cp27-none-linux_x86_64.whl
7 |
--------------------------------------------------------------------------------
/rnn.py:
--------------------------------------------------------------------------------
1 | import os, sys
2 | import argparse
3 | import time
4 | import itertools
5 | import cPickle
6 | import logging
7 | import random
8 | import string
9 |
10 | import numpy as np
11 | import tensorflow as tf
12 | import matplotlib.pyplot as plt
13 |
14 | import nottingham_util
15 | import util
16 | from model import Model, NottinghamModel
17 |
18 | def get_config_name(config):
19 | def replace_dot(s): return s.replace(".", "p")
20 | return "nl_" + str(config.num_layers) + "_hs_" + str(config.hidden_size) + \
21 | replace_dot("_mc_{}".format(config.melody_coeff)) + \
22 | replace_dot("_dp_{}".format(config.dropout_prob)) + \
23 | replace_dot("_idp_{}".format(config.input_dropout_prob)) + \
24 | replace_dot("_tb_{}".format(config.time_batch_len))
25 |
26 | class DefaultConfig(object):
27 | # model parameters
28 | num_layers = 2
29 | hidden_size = 200
30 | melody_coeff = 0.5
31 | dropout_prob = 0.5
32 | input_dropout_prob = 0.8
33 | cell_type = 'lstm'
34 |
35 | # learning parameters
36 | max_time_batches = 9
37 | time_batch_len = 128
38 | learning_rate = 5e-3
39 | learning_rate_decay = 0.9
40 | num_epochs = 250
41 |
42 | # metadata
43 | dataset = 'softmax'
44 | model_file = ''
45 |
46 | def __repr__(self):
47 | return """Num Layers: {}, Hidden Size: {}, Melody Coeff: {}, Dropout Prob: {}, Input Dropout Prob: {}, Cell Type: {}, Time Batch Len: {}, Learning Rate: {}, Decay: {}""".format(self.num_layers, self.hidden_size, self.melody_coeff, self.dropout_prob, self.input_dropout_prob, self.cell_type, self.time_batch_len, self.learning_rate, self.learning_rate_decay)
48 |
49 | if __name__ == '__main__':
50 | np.random.seed()
51 |
52 | parser = argparse.ArgumentParser(description='Script to train and save a model.')
53 | parser.add_argument('--dataset', type=str, default='softmax',
54 | # choices = ['bach', 'nottingham', 'softmax'],
55 | choices = ['softmax'])
56 | parser.add_argument('--model_dir', type=str, default='models')
57 | parser.add_argument('--run_name', type=str, default=time.strftime("%m%d_%H%M"))
58 |
59 | args = parser.parse_args()
60 |
61 | if args.dataset == 'softmax':
62 | resolution = 480
63 | time_step = 120
64 | model_class = NottinghamModel
65 | with open(nottingham_util.PICKLE_LOC, 'r') as f:
66 | pickle = cPickle.load(f)
67 | chord_to_idx = pickle['chord_to_idx']
68 |
69 | input_dim = pickle["train"][0].shape[1]
70 | print 'Finished loading data, input dim: {}'.format(input_dim)
71 | else:
72 | raise Exception("Other datasets not yet implemented")
73 |
74 | initializer = tf.random_uniform_initializer(-0.1, 0.1)
75 |
76 | best_config = None
77 | best_valid_loss = None
78 |
79 | # set up run dir
80 | run_folder = os.path.join(args.model_dir, args.run_name)
81 | if os.path.exists(run_folder):
82 | raise Exception("Run name {} already exists, choose a different one", format(run_folder))
83 | os.makedirs(run_folder)
84 |
85 | logger = logging.getLogger(__name__)
86 | logger.setLevel(logging.INFO)
87 | logger.addHandler(logging.StreamHandler())
88 | logger.addHandler(logging.FileHandler(os.path.join(run_folder, "training.log")))
89 |
90 | grid = {
91 | "dropout_prob": [0.5],
92 | "input_dropout_prob": [0.8],
93 | "melody_coeff": [0.5],
94 | "num_layers": [2],
95 | "hidden_size": [200],
96 | "num_epochs": [250],
97 | "learning_rate": [5e-3],
98 | "learning_rate_decay": [0.9],
99 | "time_batch_len": [128],
100 | }
101 |
102 | # Generate product of hyperparams
103 | runs = list(list(itertools.izip(grid, x)) for x in itertools.product(*grid.itervalues()))
104 | logger.info("{} runs detected".format(len(runs)))
105 |
106 | for combination in runs:
107 |
108 | config = DefaultConfig()
109 | config.dataset = args.dataset
110 | config.model_name = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(12)) + '.model'
111 | for attr, value in combination:
112 | setattr(config, attr, value)
113 |
114 | if config.dataset == 'softmax':
115 | data = util.load_data('', time_step, config.time_batch_len, config.max_time_batches, nottingham=pickle)
116 | config.input_dim = data["input_dim"]
117 | else:
118 | raise Exception("Other datasets not yet implemented")
119 |
120 | logger.info(config)
121 | config_file_path = os.path.join(run_folder, get_config_name(config) + '.config')
122 | with open(config_file_path, 'w') as f:
123 | cPickle.dump(config, f)
124 |
125 | with tf.Graph().as_default(), tf.Session() as session:
126 | with tf.variable_scope("model", reuse=None):
127 | train_model = model_class(config, training=True)
128 | with tf.variable_scope("model", reuse=True):
129 | valid_model = model_class(config, training=False)
130 |
131 | saver = tf.train.Saver(tf.all_variables(), max_to_keep=40)
132 | tf.initialize_all_variables().run()
133 |
134 | # training
135 | early_stop_best_loss = None
136 | start_saving = False
137 | saved_flag = False
138 | train_losses, valid_losses = [], []
139 | start_time = time.time()
140 | for i in range(config.num_epochs):
141 | loss = util.run_epoch(session, train_model,
142 | data["train"]["data"], training=True, testing=False)
143 | train_losses.append((i, loss))
144 | if i == 0:
145 | continue
146 |
147 | logger.info('Epoch: {}, Train Loss: {}, Time Per Epoch: {}'.format(\
148 | i, loss, (time.time() - start_time)/i))
149 | valid_loss = util.run_epoch(session, valid_model, data["valid"]["data"], training=False, testing=False)
150 | valid_losses.append((i, valid_loss))
151 | logger.info('Valid Loss: {}'.format(valid_loss))
152 |
153 | if early_stop_best_loss == None:
154 | early_stop_best_loss = valid_loss
155 | elif valid_loss < early_stop_best_loss:
156 | early_stop_best_loss = valid_loss
157 | if start_saving:
158 | logger.info('Best loss so far encountered, saving model.')
159 | saver.save(session, os.path.join(run_folder, config.model_name))
160 | saved_flag = True
161 | elif not start_saving:
162 | start_saving = True
163 | logger.info('Valid loss increased for the first time, will start saving models')
164 | saver.save(session, os.path.join(run_folder, config.model_name))
165 | saved_flag = True
166 |
167 | if not saved_flag:
168 | saver.save(session, os.path.join(run_folder, config.model_name))
169 |
170 | # set loss axis max to 20
171 | axes = plt.gca()
172 | if config.dataset == 'softmax':
173 | axes.set_ylim([0, 2])
174 | else:
175 | axes.set_ylim([0, 100])
176 | plt.plot([t[0] for t in train_losses], [t[1] for t in train_losses])
177 | plt.plot([t[0] for t in valid_losses], [t[1] for t in valid_losses])
178 | plt.legend(['Train Loss', 'Validation Loss'])
179 | chart_file_path = os.path.join(run_folder, get_config_name(config) + '.png')
180 | plt.savefig(chart_file_path)
181 | plt.clf()
182 |
183 | logger.info("Config {}, Loss: {}".format(config, early_stop_best_loss))
184 | if best_valid_loss == None or early_stop_best_loss < best_valid_loss:
185 | logger.info("Found best new model!")
186 | best_valid_loss = early_stop_best_loss
187 | best_config = config
188 |
189 | logger.info("Best Config: {}, Loss: {}".format(best_config, best_valid_loss))
190 |
--------------------------------------------------------------------------------
/rnn_sample.py:
--------------------------------------------------------------------------------
1 | import os, sys
2 | import argparse
3 | import time
4 | import itertools
5 | import cPickle
6 |
7 | import numpy as np
8 | import tensorflow as tf
9 |
10 | import util
11 | import nottingham_util
12 | from model import Model, NottinghamModel
13 | from rnn import DefaultConfig
14 |
15 | if __name__ == '__main__':
16 | np.random.seed()
17 |
18 | parser = argparse.ArgumentParser(description='Script to generated a MIDI file sample from a trained model.')
19 | parser.add_argument('--config_file', type=str, required=True)
20 | parser.add_argument('--sample_melody', action='store_true', default=False)
21 | parser.add_argument('--sample_harmony', action='store_true', default=False)
22 | parser.add_argument('--sample_seq', type=str, default='random',
23 | choices = ['random', 'chords'])
24 | parser.add_argument('--conditioning', type=int, default=-1)
25 | parser.add_argument('--sample_length', type=int, default=512)
26 |
27 | args = parser.parse_args()
28 |
29 | with open(args.config_file, 'r') as f:
30 | config = cPickle.load(f)
31 |
32 | if config.dataset == 'softmax':
33 | config.time_batch_len = 1
34 | config.max_time_batches = -1
35 | model_class = NottinghamModel
36 | with open(nottingham_util.PICKLE_LOC, 'r') as f:
37 | pickle = cPickle.load(f)
38 | chord_to_idx = pickle['chord_to_idx']
39 |
40 | time_step = 120
41 | resolution = 480
42 |
43 | # use time batch len of 1 so that every target is covered
44 | test_data = util.batch_data(pickle['test'], time_batch_len = 1,
45 | max_time_batches = -1, softmax = True)
46 | else:
47 | raise Exception("Other datasets not yet implemented")
48 |
49 | print config
50 |
51 | with tf.Graph().as_default(), tf.Session() as session:
52 | with tf.variable_scope("model", reuse=None):
53 | sampling_model = model_class(config)
54 |
55 | saver = tf.train.Saver(tf.all_variables())
56 | model_path = os.path.join(os.path.dirname(args.config_file),
57 | config.model_name)
58 | saver.restore(session, model_path)
59 |
60 | state = sampling_model.get_cell_zero_state(session, 1)
61 | if args.sample_seq == 'chords':
62 | # 16 - one measure, 64 - chord progression
63 | repeats = args.sample_length / 64
64 | sample_seq = nottingham_util.i_vi_iv_v(chord_to_idx, repeats, config.input_dim)
65 | print 'Sampling melody using a I, VI, IV, V progression'
66 |
67 | elif args.sample_seq == 'random':
68 | sample_index = np.random.choice(np.arange(len(pickle['test'])))
69 | sample_seq = [ pickle['test'][sample_index][i, :]
70 | for i in range(pickle['test'][sample_index].shape[0]) ]
71 |
72 | chord = sample_seq[0]
73 | seq = [chord]
74 |
75 | if args.conditioning > 0:
76 | for i in range(1, args.conditioning):
77 | seq_input = np.reshape(chord, [1, 1, config.input_dim])
78 | feed = {
79 | sampling_model.seq_input: seq_input,
80 | sampling_model.initial_state: state,
81 | }
82 | state = session.run(sampling_model.final_state, feed_dict=feed)
83 | chord = sample_seq[i]
84 | seq.append(chord)
85 |
86 | if config.dataset == 'softmax':
87 | writer = nottingham_util.NottinghamMidiWriter(chord_to_idx, verbose=False)
88 | sampler = nottingham_util.NottinghamSampler(chord_to_idx, verbose=False)
89 | else:
90 | # writer = midi_util.MidiWriter()
91 | # sampler = sampling.Sampler(verbose=False)
92 | raise Exception("Other datasets not yet implemented")
93 |
94 | for i in range(max(args.sample_length - len(seq), 0)):
95 | seq_input = np.reshape(chord, [1, 1, config.input_dim])
96 | feed = {
97 | sampling_model.seq_input: seq_input,
98 | sampling_model.initial_state: state,
99 | }
100 | [probs, state] = session.run(
101 | [sampling_model.probs, sampling_model.final_state],
102 | feed_dict=feed)
103 | probs = np.reshape(probs, [config.input_dim])
104 | chord = sampler.sample_notes(probs)
105 |
106 | if config.dataset == 'softmax':
107 | r = nottingham_util.NOTTINGHAM_MELODY_RANGE
108 | if args.sample_melody:
109 | chord[r:] = 0
110 | chord[r:] = sample_seq[i][r:]
111 | elif args.sample_harmony:
112 | chord[:r] = 0
113 | chord[:r] = sample_seq[i][:r]
114 |
115 | seq.append(chord)
116 |
117 | writer.dump_sequence_to_midi(seq, "best.midi",
118 | time_step=time_step, resolution=resolution)
119 |
--------------------------------------------------------------------------------
/rnn_separate.py:
--------------------------------------------------------------------------------
1 | import os, sys
2 | import argparse
3 | import time
4 | import itertools
5 | import cPickle
6 | import logging
7 | import random
8 | import string
9 | import pprint
10 |
11 | import numpy as np
12 | import tensorflow as tf
13 | import matplotlib.pyplot as plt
14 |
15 | import midi_util
16 | import nottingham_util
17 | import sampling
18 | import util
19 | from rnn import get_config_name, DefaultConfig
20 | from model import Model, NottinghamSeparate
21 |
22 | if __name__ == '__main__':
23 | np.random.seed()
24 |
25 | parser = argparse.ArgumentParser(description='Music RNN')
26 | parser.add_argument('--choice', type=str, default='melody',
27 | choices = ['melody', 'harmony'])
28 | parser.add_argument('--dataset', type=str, default='softmax',
29 | choices = ['bach', 'nottingham', 'softmax'])
30 | parser.add_argument('--model_dir', type=str, default='models')
31 | parser.add_argument('--run_name', type=str, default=time.strftime("%m%d_%H%M"))
32 |
33 | args = parser.parse_args()
34 |
35 | if args.dataset == 'softmax':
36 | resolution = 480
37 | time_step = 120
38 | model_class = NottinghamSeparate
39 | with open(nottingham_util.PICKLE_LOC, 'r') as f:
40 | pickle = cPickle.load(f)
41 | chord_to_idx = pickle['chord_to_idx']
42 |
43 | input_dim = pickle["train"][0].shape[1]
44 | print 'Finished loading data, input dim: {}'.format(input_dim)
45 | else:
46 | raise Exception("Other datasets not yet implemented")
47 |
48 |
49 | initializer = tf.random_uniform_initializer(-0.1, 0.1)
50 |
51 | best_config = None
52 | best_valid_loss = None
53 |
54 | # set up run dir
55 | run_folder = os.path.join(args.model_dir, args.run_name)
56 | if os.path.exists(run_folder):
57 | raise Exception("Run name {} already exists, choose a different one", format(run_folder))
58 | os.makedirs(run_folder)
59 |
60 | logger = logging.getLogger(__name__)
61 | logger.setLevel(logging.INFO)
62 | logger.addHandler(logging.StreamHandler())
63 | logger.addHandler(logging.FileHandler(os.path.join(run_folder, "training.log")))
64 |
65 | # grid
66 | grid = {
67 | "dropout_prob": [0.65],
68 | "input_dropout_prob": [0.9],
69 | "num_layers": [1],
70 | "hidden_size": [100]
71 | }
72 |
73 | # Generate product of hyperparams
74 | runs = list(list(itertools.izip(grid, x)) for x in itertools.product(*grid.itervalues()))
75 | logger.info("{} runs detected".format(len(runs)))
76 |
77 | for combination in runs:
78 |
79 | config = DefaultConfig()
80 | config.dataset = args.dataset
81 | config.model_name = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(12)) + '.model'
82 | for attr, value in combination:
83 | setattr(config, attr, value)
84 |
85 | if config.dataset == 'softmax':
86 | data = util.load_data('', time_step, config.time_batch_len, config.max_time_batches, nottingham=pickle)
87 | config.input_dim = data["input_dim"]
88 | else:
89 | raise Exception("Other datasets not yet implemented")
90 |
91 | # cut away unnecessary parts
92 | r = nottingham_util.NOTTINGHAM_MELODY_RANGE
93 | if args.choice == 'melody':
94 | print "Using only melody"
95 | for d in ['train', 'test', 'valid']:
96 | new_data = []
97 | for batch_data, batch_targets in data[d]["data"]:
98 | new_data.append(([tb[:, :, :r] for tb in batch_data],
99 | [tb[:, :, 0] for tb in batch_targets]))
100 | data[d]["data"] = new_data
101 | else:
102 | print "Using only harmony"
103 | for d in ['train', 'test', 'valid']:
104 | new_data = []
105 | for batch_data, batch_targets in data[d]["data"]:
106 | new_data.append(([tb[:, :, r:] for tb in batch_data],
107 | [tb[:, :, 1] for tb in batch_targets]))
108 | data[d]["data"] = new_data
109 |
110 | input_dim = data["input_dim"] = data["train"]["data"][0][0][0].shape[2]
111 | config.input_dim = input_dim
112 | print "New input dim: {}".format(input_dim)
113 |
114 | logger.info(config)
115 | config_file_path = os.path.join(run_folder, get_config_name(config) + '.config')
116 | with open(config_file_path, 'w') as f:
117 | cPickle.dump(config, f)
118 |
119 | with tf.Graph().as_default(), tf.Session() as session:
120 | with tf.variable_scope("model", reuse=None):
121 | train_model = model_class(config, training=True)
122 | with tf.variable_scope("model", reuse=True):
123 | valid_model = model_class(config, training=False)
124 |
125 | saver = tf.train.Saver(tf.all_variables())
126 | tf.initialize_all_variables().run()
127 |
128 | # training
129 | early_stop_best_loss = None
130 | start_saving = False
131 | saved_flag = False
132 | train_losses, valid_losses = [], []
133 | start_time = time.time()
134 | for i in range(config.num_epochs):
135 | loss = util.run_epoch(session, train_model, data["train"]["data"], training=True, testing=False)
136 | train_losses.append((i, loss))
137 | if i == 0:
138 | continue
139 |
140 | valid_loss = util.run_epoch(session, valid_model, data["valid"]["data"], training=False, testing=False)
141 | valid_losses.append((i, valid_loss))
142 |
143 | logger.info('Epoch: {}, Train Loss: {}, Valid Loss: {}, Time Per Epoch: {}'.format(\
144 | i, loss, valid_loss, (time.time() - start_time)/i))
145 |
146 | # if it's best validation loss so far, save it
147 | if early_stop_best_loss == None:
148 | early_stop_best_loss = valid_loss
149 | elif valid_loss < early_stop_best_loss:
150 | early_stop_best_loss = valid_loss
151 | if start_saving:
152 | logger.info('Best loss so far encountered, saving model.')
153 | saver.save(session, os.path.join(run_folder, config.model_name))
154 | saved_flag = True
155 | elif not start_saving:
156 | start_saving = True
157 | logger.info('Valid loss increased for the first time, will start saving models')
158 | saver.save(session, os.path.join(run_folder, config.model_name))
159 | saved_flag = True
160 |
161 | if not saved_flag:
162 | saver.save(session, os.path.join(run_folder, config.model_name))
163 |
164 | # set loss axis max to 20
165 | axes = plt.gca()
166 | if config.dataset == 'softmax':
167 | axes.set_ylim([0, 2])
168 | else:
169 | axes.set_ylim([0, 100])
170 | plt.plot([t[0] for t in train_losses], [t[1] for t in train_losses])
171 | plt.plot([t[0] for t in valid_losses], [t[1] for t in valid_losses])
172 | plt.legend(['Train Loss', 'Validation Loss'])
173 | chart_file_path = os.path.join(run_folder, get_config_name(config) + '.png')
174 | plt.savefig(chart_file_path)
175 | plt.clf()
176 |
177 | logger.info("Config {}, Loss: {}".format(config, early_stop_best_loss))
178 | if best_valid_loss == None or early_stop_best_loss < best_valid_loss:
179 | logger.info("Found best new model!")
180 | best_valid_loss = early_stop_best_loss
181 | best_config = config
182 |
183 | logger.info("Best Config: {}, Loss: {}".format(best_config, best_valid_loss))
184 |
--------------------------------------------------------------------------------
/rnn_test.py:
--------------------------------------------------------------------------------
1 | import os, sys
2 | import argparse
3 | import cPickle
4 |
5 | import numpy as np
6 | import tensorflow as tf
7 |
8 | import util
9 | import nottingham_util
10 | from model import Model, NottinghamModel, NottinghamSeparate
11 | from rnn import DefaultConfig
12 |
13 | if __name__ == '__main__':
14 | np.random.seed()
15 |
16 | parser = argparse.ArgumentParser(description='Script to test a models performance against the test set')
17 | parser.add_argument('--config_file', type=str, required=True)
18 | parser.add_argument('--num_samples', type=int, default=1)
19 | parser.add_argument('--seperate', action='store_true', default=False)
20 | parser.add_argument('--choice', type=str, default='melody',
21 | choices = ['melody', 'harmony'])
22 | args = parser.parse_args()
23 |
24 | with open(args.config_file, 'r') as f:
25 | config = cPickle.load(f)
26 |
27 | if config.dataset == 'softmax':
28 | config.time_batch_len = 1
29 | config.max_time_batches = -1
30 | with open(nottingham_util.PICKLE_LOC, 'r') as f:
31 | pickle = cPickle.load(f)
32 | if args.seperate:
33 | model_class = NottinghamSeparate
34 | test_data = util.batch_data(pickle['test'], time_batch_len = 1,
35 | max_time_batches = -1, softmax = True)
36 | r = nottingham_util.NOTTINGHAM_MELODY_RANGE
37 | if args.choice == 'melody':
38 | print "Using only melody"
39 | new_data = []
40 | for batch_data, batch_targets in test_data:
41 | new_data.append(([tb[:, :, :r] for tb in batch_data],
42 | [tb[:, :, 0] for tb in batch_targets]))
43 | test_data = new_data
44 | else:
45 | print "Using only harmony"
46 | new_data = []
47 | for batch_data, batch_targets in test_data:
48 | new_data.append(([tb[:, :, r:] for tb in batch_data],
49 | [tb[:, :, 1] for tb in batch_targets]))
50 | test_data = new_data
51 | else:
52 | model_class = NottinghamModel
53 | # use time batch len of 1 so that every target is covered
54 | test_data = util.batch_data(pickle['test'], time_batch_len = 1,
55 | max_time_batches = -1, softmax = True)
56 | else:
57 | raise Exception("Other datasets not yet implemented")
58 |
59 | print config
60 |
61 | with tf.Graph().as_default(), tf.Session() as session:
62 | with tf.variable_scope("model", reuse=None):
63 | test_model = model_class(config, training=False)
64 |
65 | saver = tf.train.Saver(tf.all_variables())
66 | model_path = os.path.join(os.path.dirname(args.config_file),
67 | config.model_name)
68 | saver.restore(session, model_path)
69 |
70 | test_loss, test_probs = util.run_epoch(session, test_model, test_data,
71 | training=False, testing=True)
72 | print 'Testing Loss: {}'.format(test_loss)
73 |
74 | if config.dataset == 'softmax':
75 | if args.seperate:
76 | nottingham_util.seperate_accuracy(test_probs, test_data, num_samples=args.num_samples)
77 | else:
78 | nottingham_util.accuracy(test_probs, test_data, num_samples=args.num_samples)
79 |
80 | else:
81 | util.accuracy(test_probs, test_data, num_samples=50)
82 |
83 | sys.exit(1)
84 |
--------------------------------------------------------------------------------
/sampling.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from pprint import pprint
3 |
4 | import midi_util
5 |
6 |
7 | class Sampler(object):
8 |
9 | def __init__(self, min_prob=0.5, num_notes = 4, method = 'sample', verbose=False):
10 | self.min_prob = min_prob
11 | self.num_notes = num_notes
12 | self.method = method
13 | self.verbose = verbose
14 |
15 | def visualize_probs(self, probs):
16 | if not self.verbose:
17 | return
18 | print 'Highest four probs: '
19 | pprint(sorted(list(enumerate(probs)), key=lambda x: x[1],
20 | reverse=True)[:4])
21 |
22 | def sample_notes_prob(self, probs, max_notes=-1):
23 | """ Samples all notes that are over a certain probability"""
24 | self.visualize_probs(probs)
25 | top_idxs = list()
26 | for idx in probs.argsort()[::-1]:
27 | if max_notes > 0 and len(top_idxs) >= max_notes:
28 | break
29 | if probs[idx] < self.min_prob:
30 | break
31 | top_idxs.append(idx)
32 | chord = np.zeros([len(probs)], dtype=np.int32)
33 | chord[top_idxs] = 1.0
34 | return chord
35 |
36 | def sample_notes_static(self, probs):
37 | top_idxs = probs.argsort()[-self.num_notes:][::-1]
38 | chord = np.zeros([len(probs)], dtype=np.int32)
39 | chord[top_idxs] = 1.0
40 | return chord
41 |
42 | def sample_notes_bernoulli(self, probs):
43 | chord = np.zeros([len(probs)], dtype=np.int32)
44 | for note, prob in enumerate(probs):
45 | if np.random.binomial(1, prob) > 0:
46 | chord[note] = 1
47 | return chord
48 |
49 | def sample_notes(self, probs):
50 | """ Samples a static amount of notes from probabilities by highest prob """
51 | self.visualize_probs(probs)
52 | if self.method == 'sample':
53 | return self.sample_notes_bernoulli(probs)
54 | elif self.method == 'static':
55 | return self.sample_notes_static(probs)
56 | elif self.method == 'min_prob':
57 | return self.sample_notes_prob(probs)
58 | else:
59 | raise Exception("Unrecognized method: {}".format(self.method))
60 |
--------------------------------------------------------------------------------
/util.py:
--------------------------------------------------------------------------------
1 | import os
2 | import math
3 | import cPickle
4 | from collections import defaultdict
5 | from random import shuffle
6 |
7 | import numpy as np
8 | import tensorflow as tf
9 |
10 | import midi_util
11 | import nottingham_util
12 |
13 | def parse_midi_directory(input_dir, time_step):
14 | """
15 | input_dir: data directory full of midi files
16 | time_step: the number of ticks to use as a time step for discretization
17 |
18 | Returns a list of [T x D] matrices, where T is the amount of time steps
19 | and D is the range of notes.
20 | """
21 | files = [ os.path.join(input_dir, f) for f in os.listdir(input_dir)
22 | if os.path.isfile(os.path.join(input_dir, f)) ]
23 | sequences = [ \
24 | (f, midi_util.parse_midi_to_sequence(f, time_step=time_step)) \
25 | for f in files ]
26 |
27 | return sequences
28 |
29 | def batch_data(sequences, time_batch_len=128, max_time_batches=10,
30 | softmax=False, verbose=False):
31 | """
32 | sequences: a list of [T x D] matrices, each matrix representing a sequencey
33 | time_batch_len: the unrolling length that will be used by BPTT.
34 | max_time_batches: the max amount of time batches to consider. Any sequences
35 | longert than max_time_batches * time_batch_len will be ignored
36 | Can be set to -1 to all time batches needed.
37 | softmax: Flag should be set to true if using the dual-softmax formualtion
38 |
39 | returns [
40 | [ [ data ], [ target ] ], # batch with one time step
41 | [ [ data1, data2 ], [ target1, target2 ] ], # batch with two time steps
42 | ...
43 | ]
44 | """
45 |
46 | assert time_batch_len > 0
47 |
48 | dims = sequences[0].shape[1]
49 | sequence_lens = [s.shape[0] for s in sequences]
50 |
51 | if verbose:
52 | avg_seq_len = sum(sequence_lens) / len(sequences)
53 | print "Average Sequence Length: {}".format(avg_seq_len)
54 | print "Max Sequence Length: {}".format(time_batch_len)
55 | print "Number of sequences: {}".format(len(sequences))
56 |
57 | batches = defaultdict(list)
58 | for sequence in sequences:
59 | # -1 because we can't predict the first step
60 | num_time_steps = ((sequence.shape[0]-1) // time_batch_len)
61 | if num_time_steps < 1:
62 | continue
63 | if max_time_batches > 0 and num_time_steps > max_time_batches:
64 | continue
65 | batches[num_time_steps].append(sequence)
66 |
67 | if verbose:
68 | print "Batch distribution:"
69 | print [(k, len(v)) for (k, v) in batches.iteritems()]
70 |
71 | def arrange_batch(sequences, num_time_steps):
72 | sequences = [s[:(num_time_steps*time_batch_len)+1, :] for s in sequences]
73 | stacked = np.dstack(sequences)
74 | # swap axes so that shape is (SEQ_LENGTH X BATCH_SIZE X INPUT_DIM)
75 | data = np.swapaxes(stacked, 1, 2)
76 | targets = np.roll(data, -1, axis=0)
77 | # cutoff final time step
78 | data = data[:-1, :, :]
79 | targets = targets[:-1, :, :]
80 | assert data.shape == targets.shape
81 |
82 | if softmax:
83 | r = nottingham_util.NOTTINGHAM_MELODY_RANGE
84 | labels = np.ones((targets.shape[0], targets.shape[1], 2), dtype=np.int32)
85 | assert np.all(np.sum(targets[:, :, :r], axis=2) == 1)
86 | assert np.all(np.sum(targets[:, :, r:], axis=2) == 1)
87 | labels[:, :, 0] = np.argmax(targets[:, :, :r], axis=2)
88 | labels[:, :, 1] = np.argmax(targets[:, :, r:], axis=2)
89 | targets = labels
90 | assert targets.shape[:2] == data.shape[:2]
91 |
92 | assert data.shape[0] == num_time_steps * time_batch_len
93 |
94 | # split them up into time batches
95 | tb_data = np.split(data, num_time_steps, axis=0)
96 | tb_targets = np.split(targets, num_time_steps, axis=0)
97 |
98 | assert len(tb_data) == len(tb_targets) == num_time_steps
99 | for i in range(len(tb_data)):
100 | assert tb_data[i].shape[0] == time_batch_len
101 | assert tb_targets[i].shape[0] == time_batch_len
102 | if softmax:
103 | assert np.all(np.sum(tb_data[i], axis=2) == 2)
104 |
105 | return (tb_data, tb_targets)
106 |
107 | return [ arrange_batch(b, n) for n, b in batches.iteritems() ]
108 |
109 | def load_data(data_dir, time_step, time_batch_len, max_time_batches, nottingham=None):
110 | """
111 | nottingham: The sequences object as created in prepare_nottingham_pickle
112 | (see nottingham_util for more). If None, parse all the MIDI
113 | files from data_dir
114 | time_step: the time_step used to parse midi files (only used if data_dir
115 | is provided)
116 | time_batch_len and max_time_batches: see batch_data()
117 |
118 | returns {
119 | "train": {
120 | "data": [ batch_data() ],
121 | "metadata: { ... }
122 | },
123 | "valid": { ... }
124 | "test": { ... }
125 | }
126 | """
127 |
128 | data = {}
129 | for dataset in ['train', 'test', 'valid']:
130 |
131 | # For testing, use ALL the sequences
132 | if dataset == 'test':
133 | max_time_batches = -1
134 |
135 | # Softmax formualation preparsed into sequences
136 | if nottingham:
137 | sequences = nottingham[dataset]
138 | metadata = nottingham[dataset + '_metadata']
139 | # Cross-entropy formulation needs to be parsed
140 | else:
141 | sf = parse_midi_directory(os.path.join(data_dir, dataset), time_step)
142 | sequences = [s[1] for s in sf]
143 | files = [s[0] for s in sf]
144 | metadata = [{
145 | 'path': f,
146 | 'name': f.split("/")[-1].split(".")[0]
147 | } for f in files]
148 |
149 | dataset_data = batch_data(sequences, time_batch_len, max_time_batches, softmax = True if nottingham else False)
150 |
151 | data[dataset] = {
152 | "data": dataset_data,
153 | "metadata": metadata,
154 | }
155 |
156 | data["input_dim"] = dataset_data[0][0][0].shape[2]
157 |
158 | return data
159 |
160 |
161 | def run_epoch(session, model, batches, training=False, testing=False):
162 | """
163 | session: Tensorflow session object
164 | model: model object (see model.py)
165 | batches: data object loaded from util_data()
166 |
167 | training: A backpropagation iteration will be performed on the dataset
168 | if this flag is active
169 |
170 | returns average loss per time step over all batches.
171 | if testing flag is active: returns [ loss, probs ] where is the probability
172 | values for each note
173 | """
174 |
175 | # shuffle batches
176 | shuffle(batches)
177 |
178 | target_tensors = [model.loss, model.final_state]
179 | if testing:
180 | target_tensors.append(model.probs)
181 | batch_probs = defaultdict(list)
182 | if training:
183 | target_tensors.append(model.train_step)
184 |
185 | losses = []
186 | for data, targets in batches:
187 | # save state over unrolling time steps
188 | batch_size = data[0].shape[1]
189 | num_time_steps = len(data)
190 | state = model.get_cell_zero_state(session, batch_size)
191 | probs = list()
192 |
193 | for tb_data, tb_targets in zip(data, targets):
194 | if testing:
195 | tbd = tb_data
196 | tbt = tb_targets
197 | else:
198 | # shuffle all the batches of input, state, and target
199 | batches = tb_data.shape[1]
200 | permutations = np.random.permutation(batches)
201 | tbd = np.zeros_like(tb_data)
202 | tbd[:, np.arange(batches), :] = tb_data[:, permutations, :]
203 | tbt = np.zeros_like(tb_targets)
204 | tbt[:, np.arange(batches), :] = tb_targets[:, permutations, :]
205 | state[np.arange(batches)] = state[permutations]
206 |
207 | feed_dict = {
208 | model.initial_state: state,
209 | model.seq_input: tbd,
210 | model.seq_targets: tbt,
211 | }
212 | results = session.run(target_tensors, feed_dict=feed_dict)
213 |
214 | losses.append(results[0])
215 | state = results[1]
216 | if testing:
217 | batch_probs[num_time_steps].append(results[2])
218 |
219 | loss = sum(losses) / len(losses)
220 |
221 | if testing:
222 | return [loss, batch_probs]
223 | else:
224 | return loss
225 |
226 | def accuracy(batch_probs, data, num_samples=20):
227 | """
228 | batch_probs: probs object returned from run_epoch
229 | data: data object passed into run_epoch
230 | num_samples: the number of times to sample each note (an average over all
231 | these samples will be used)
232 |
233 | returns the accuracy metric according to
234 | http://ismir2009.ismir.net/proceedings/PS2-21.pdf
235 | """
236 |
237 | false_positives, false_negatives, true_positives = 0, 0, 0
238 | for _, batch_targets in data:
239 | num_time_steps = len(batch_data)
240 | for ts_targets, ts_probs in zip(batch_targets, batch_probs[num_time_steps]):
241 |
242 | assert ts_targets.shape == ts_targets.shape
243 |
244 | for seq_idx in range(ts_targets.shape[1]):
245 | for step_idx in range(ts_targets.shape[0]):
246 | for note_idx, prob in enumerate(ts_probs[step_idx, seq_idx, :]):
247 | num_occurrences = np.random.binomial(num_samples, prob)
248 | if ts_targets[step_idx, seq_idx, note_idx] == 0.0:
249 | false_positives += num_occurrences
250 | else:
251 | false_negatives += (num_samples - num_occurrences)
252 | true_positives += num_occurrences
253 |
254 | accuracy = (float(true_positives) / float(true_positives + false_positives + false_negatives))
255 |
256 | print "Precision: {}".format(float(true_positives) / (float(true_positives + false_positives)))
257 | print "Recall: {}".format(float(true_positives) / (float(true_positives + false_negatives)))
258 | print "Accuracy: {}".format(accuracy)
259 |
--------------------------------------------------------------------------------