<commit_before>{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Formatting rhythms for real-time stretch\n",
    "\n",
    "The goal of this notebook is to come up with an algorithm to re-format original and target rhythms such that real-time time-stretching (or even real-time stuttering) is possible."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import librosa\n",
    "import numpy as np\n",
    "\n",
    "import pardir; pardir.pardir() # Allow imports from parent directory\n",
    "import bjorklund\n",
    "import fibonaccistretch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "librosa.effects.time_stretch??\n",
    "librosa.core.phase_vocoder??\n",
    "fibonaccistretch.euclidean_stretch??"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Original rhythm: [1, 0, 0, 1, 0, 0, 1, 0]    Target rhythm: [1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1]'"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Generate rhythms based on parameters\n",
    "def generate_original_and_target_rhythms(num_pulses, original_length, target_length):\n",
    "    original_rhythm = bjorklund.bjorklund(pulses=num_pulses, steps=original_length)\n",
    "    target_rhythm = bjorklund.bjorklund(pulses=len(original_rhythm), steps=target_length)\n",
    "    return (original_rhythm, target_rhythm)\n",
    "\n",
    "original_rhythm, target_rhythm = generate_original_and_target_rhythms(3, 8, 13)\n",
    "\n",
    "\"Original rhythm: {}    Target rhythm: {}\".format(original_rhythm, target_rhythm)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "104"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lcm = (8*13) / fibonaccistretch.euclid(8, 13)\n",
    "lcm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "104"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "8*13"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## \"Equalize\": Make rhythms the same length\n",
    "\n",
    "Use LCM to \"equalize\" rhythms so that they're of equal length.\n",
    "\n",
    "e.g.\n",
    "\n",
    "`a = [1,0,0,1]`\n",
    "\n",
    "\n",
    "\n",
    "`b = [1,1,0]`\n",
    "\n",
    "become\n",
    "\n",
    "`equalized_a = [1,-,-,0,-,-,0,-,-,1,-,-]`\n",
    "\n",
    "\n",
    "\n",
    "`equalized_b = [1,-,-,-,1,-,-,-,0,-,-,-]`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original rhythm: [1, 0, 0, 1, 0, 0, 1, 0]    Target rhythm: [1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1]\n",
      "lcm=104, original_scale_factor=12, target_scale_factor=7\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(104, 104)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# \"Equalize\" (i.e. scale rhythms so they're of equal length)\n",
    "def equalize_rhythm_subdivisions(original_rhythm, target_rhythm, delimiter=\"-\"):\n",
    "    original_length = len(original_rhythm)\n",
    "    target_length = len(target_rhythm)\n",
    "    lcm = (original_length*target_length) / fibonaccistretch.euclid(original_length, target_length)\n",
    "    original_scale_factor = (lcm / original_length) - 1\n",
    "    target_scale_factor = (lcm / target_length) - 1\n",
    "    \n",
    "    print(\"lcm={}, original_scale_factor={}, target_scale_factor={}\").format(lcm, original_scale_factor, target_scale_factor)\n",
    "    \n",
    "    delimiter = str(delimiter)\n",
    "    original_rhythm = list((delimiter*original_scale_factor).join([str(x) for x in original_rhythm]))\n",
    "    target_rhythm = list((delimiter*target_scale_factor).join([str(x) for x in target_rhythm]))\n",
    "    \n",
    "    original_rhythm.extend(list(delimiter*original_scale_factor))\n",
    "    target_rhythm.extend(list(delimiter*target_scale_factor))\n",
    "\n",
    "    \n",
    "    return (original_rhythm, target_rhythm)\n",
    "\n",
    "# print(scale_rhythm_subdivisions(original_rhythm, target_rhythm))\n",
    "original_rhythm, target_rhythm = generate_original_and_target_rhythms(3, 8, 13)\n",
    "print(\"Original rhythm: {}    Target rhythm: {}\".format(original_rhythm, target_rhythm))\n",
    "equalized_original_rhythm, equalized_target_rhythm = equalize_rhythm_subdivisions(original_rhythm, target_rhythm)\n",
    "(len(equalized_original_rhythm), len(equalized_target_rhythm))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Get pulse indices so we can see how the equalized original and target relate. In particular, our goal is to create a relationship such that the original pulse indices always come first (so that they're bufferable in real-time)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "([0, 39, 78], [0, 16, 24, 40, 56, 64, 80, 96])"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def get_pulse_indices_for_rhythm(rhythm, pulse_symbols=[1]):\n",
    "    pulse_symbols = [str(s) for s in pulse_symbols]\n",
    "    rhythm = [str(x) for x in rhythm]\n",
    "    pulse_indices = [i for i,symbol in enumerate(rhythm) if symbol in pulse_symbols]\n",
    "    return pulse_indices\n",
    "\n",
    "equalized_original_pulse_indices = get_pulse_indices_for_rhythm(equalized_original_rhythm)\n",
    "equalized_target_pulse_indices = get_pulse_indices_for_rhythm(equalized_target_rhythm)\n",
    "(equalized_original_pulse_indices, equalized_target_pulse_indices)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For original we'll actually use ALL the steps instead of just pulses though. So:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "([0, 13, 26, 39, 52, 65, 78, 91], [0, 16, 24, 40, 56, 64, 80, 96])\n"
     ]
    }
   ],
   "source": [
    "equalized_original_pulse_indices = get_pulse_indices_for_rhythm(equalized_original_rhythm, [1,0])\n",
    "equalized_target_pulse_indices = get_pulse_indices_for_rhythm(equalized_target_rhythm, [1])\n",
    "print(equalized_original_pulse_indices, equalized_target_pulse_indices)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can check to see if all the original pulse indices come first (this is our goal):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Oh no; original pulse at 26 comes after target pulse at 24 (diff=2)\n",
      "Oh no; original pulse at 65 comes after target pulse at 64 (diff=1)\n"
     ]
    }
   ],
   "source": [
    "for i in range(len(equalized_original_pulse_indices)):\n",
    "    opi = equalized_original_pulse_indices[i]\n",
    "    tpi = equalized_target_pulse_indices[i]\n",
    "    if (opi > tpi):\n",
    "        print(\"Oh no; original pulse at {} comes after target pulse at {} (diff={})\".format(opi, tpi, opi-tpi))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Oh no... how do we fix this??\n",
    "\n",
    "- One solution is to just nudge them over, especially since they only differ by 1/104 to 2/104ths of a measure in this case. \n",
    "- Another solution would be to use the same data from the original pulse if there's not a new pulse available. Hmmmm\n",
    "- Or use as much of the original buffer as we can...?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## First pass at format_rhythm(), without fixing rhythm\n",
    "\n",
    "But first let's define a function for end-to-end formatting:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# Format original and target rhythms for real-time manipulation\n",
    "def format_rhythms(original_rhythm, target_rhythm):\n",
    "    # Equalize rhythm lengths and get pulse indices\n",
    "    eor, etr = equalize_rhythm_subdivisions(original_rhythm, target_rhythm)\n",
    "    eopi = get_pulse_indices_for_rhythm(eor, pulse_symbols=[1,0])\n",
    "    etpi = get_pulse_indices_for_rhythm(etr, pulse_symbols=[1])\n",
    "\n",
    "    # Find all the ones with problematic pulses (note that we're using *pulses* of target but *steps* of original)\n",
    "    for i in range(min(len(eopi), len(etpi))):\n",
    "        opi = eopi[i]\n",
    "        tpi = etpi[i]\n",
    "        if (opi > tpi):\n",
    "            print(\"Oh no; original pulse at {} comes after target pulse at {} (diff={})\".format(opi, tpi, opi-tpi))\n",
    "            \n",
    "    # TODO: Fix problematic pulses\n",
    "    #\n",
    "    \n",
    "    print(\"Formatted original: {}\".format(rtos(eor)))\n",
    "    print(\"Formatted target:   {}\".format(rtos(etr)))\n",
    "    \n",
    "    return (eor, etr)\n",
    "\n",
    "# Rhythm to string\n",
    "def rtos(rhythm):\n",
    "    return \"\".join(rhythm)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Alright let's try this out:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=24, original_scale_factor=2, target_scale_factor=7\n",
      "Formatted original: 1--0--0--1--0--0--1--0--\n",
      "Formatted target:   1-------0-------1-------\n"
     ]
    }
   ],
   "source": [
    "# len(original) > len(target)\n",
    "formatted = format_rhythms([1,0,0,1,0,0,1,0], [1,0,1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=88, original_scale_factor=10, target_scale_factor=7\n",
      "Formatted original: 1----------0----------0----------1----------0----------0----------1----------0----------\n",
      "Formatted target:   1-------0-------0-------1-------1-------0-------0-------1-------1-------1-------1-------\n"
     ]
    }
   ],
   "source": [
    "# len(original) < len(target)\n",
    "formatted = format_rhythms([1,0,0,1,0,0,1,0], [1,0,0,1,1,0,0,1,1,1,1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=20, original_scale_factor=4, target_scale_factor=1\n",
      "Formatted original: 1----0----1----0----\n",
      "Formatted target:   1-0-0-1-0-0-1-0-0-0-\n",
      "--------\n",
      "lcm=10, original_scale_factor=4, target_scale_factor=0\n",
      "Oh no; original pulse at 5 comes after target pulse at 3 (diff=2)\n",
      "Formatted original: 1----1----\n",
      "Formatted target:   1001001000\n"
     ]
    }
   ],
   "source": [
    "# Trying [1,0,1,0] and [1,1] as originals, with the same target\n",
    "formatted = format_rhythms([1,0,1,0], [1,0,0,1,0,0,1,0,0,0])\n",
    "print(\"--------\")\n",
    "formatted = format_rhythms([1,1], [1,0,0,1,0,0,1,0,0,0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To make things a bit clearer maybe we'll try the `abcd` format for `rtos()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# Rhythm to string\n",
    "# Method: \"str\", \"alphabet\"\n",
    "def rtos(rhythm, format_method=\"str\", pulse_symbols=[\"1\"]):\n",
    "    pulse_symbols = [str(s) for s in pulse_symbols]\n",
    "    \n",
    "    if format_method == \"str\":\n",
    "        return \"\".join(rhythm)\n",
    "    elif format_method == \"alphabet\":\n",
    "        alphabet = list(\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\")\n",
    "        output = []\n",
    "        ai = 0\n",
    "        for i,x in enumerate(rhythm):\n",
    "            if str(x) in pulse_symbols:\n",
    "                output.append(alphabet[ai%len(alphabet)])\n",
    "                ai += 1\n",
    "            else:\n",
    "                output.append(x)\n",
    "        return \"\".join(output)\n",
    "    else:\n",
    "        return rhythm\n",
    "    \n",
    "# Format original and target rhythms for real-time manipulation\n",
    "def format_rhythms(original_rhythm, target_rhythm, format_method=\"str\", pulse_symbols=[\"1\"]):\n",
    "    # Equalize rhythm lengths and get pulse indices\n",
    "    eor, etr = equalize_rhythm_subdivisions(original_rhythm, target_rhythm)\n",
    "    eopi = get_pulse_indices_for_rhythm(eor, pulse_symbols=[1,0])\n",
    "    etpi = get_pulse_indices_for_rhythm(etr, pulse_symbols=[1])\n",
    "\n",
    "    # Find all the ones with problematic pulses (note that we're using *pulses* of target but *steps* of original)\n",
    "    for i in range(min(len(eopi), len(etpi))):\n",
    "        opi = eopi[i]\n",
    "        tpi = etpi[i]\n",
    "        if (opi > tpi):\n",
    "            print(\"Oh no; original pulse at {} comes after target pulse at {} (diff={})\".format(opi, tpi, opi-tpi))\n",
    "            \n",
    "    # TODO: Fix problematic pulses\n",
    "    #\n",
    "    \n",
    "    print(\"\")\n",
    "    print(\"Original: {}\".format(rtos(eor, format_method=format_method, pulse_symbols=[1,0])))\n",
    "    print(\"Target:   {}\".format(rtos(etr, format_method=format_method, pulse_symbols=[1])))\n",
    "    \n",
    "    return (eor, etr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=20, original_scale_factor=4, target_scale_factor=1\n",
      "\n",
      "Original: A----B----C----D----\n",
      "Target:   A-0-0-B-0-0-C-0-0-0-\n",
      "\n",
      "--------\n",
      "\n",
      "lcm=10, original_scale_factor=4, target_scale_factor=0\n",
      "Oh no; original pulse at 5 comes after target pulse at 3 (diff=2)\n",
      "\n",
      "Original: A----B----\n",
      "Target:   A00B00C000\n"
     ]
    }
   ],
   "source": [
    "# Trying [1,0,1,0] and [1,1] as originals, with the same target\n",
    "formatted = format_rhythms([1,0,1,0], [1,0,0,1,0,0,1,0,0,0], format_method=\"alphabet\")\n",
    "print(\"\\n--------\\n\")\n",
    "formatted = format_rhythms([1,1], [1,0,0,1,0,0,1,0,0,0], format_method=\"alphabet\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=104, original_scale_factor=12, target_scale_factor=7\n",
      "Oh no; original pulse at 26 comes after target pulse at 24 (diff=2)\n",
      "Oh no; original pulse at 65 comes after target pulse at 64 (diff=1)\n",
      "\n",
      "Original: A------------B------------C------------D------------E------------F------------G------------H------------\n",
      "Target:   A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n"
     ]
    }
   ],
   "source": [
    "formatted = format_rhythms(original_rhythm, target_rhythm, format_method=\"alphabet\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exploring adjustment options\n",
    "\n",
    "Let's use this example to explore adjustment options:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original rhythm: [1, 0, 0, 1, 0, 0, 1, 0]\n",
      "Target rhythm:   [1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1]\n",
      "\n",
      "lcm=104, original_scale_factor=12, target_scale_factor=7\n",
      "Oh no; original pulse at 26 comes after target pulse at 24 (diff=2)\n",
      "Oh no; original pulse at 65 comes after target pulse at 64 (diff=1)\n",
      "\n",
      "Original: A------------B------------C------------D------------E------------F------------G------------H------------\n",
      "Target:   A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n"
     ]
    }
   ],
   "source": [
    "print(\"Original rhythm: {}\\nTarget rhythm:   {}\\n\".format(original_rhythm, target_rhythm))\n",
    "formatted = format_rhythms(original_rhythm, target_rhythm, format_method=\"alphabet\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In all the following cases only the target changes, not the original:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1. For every problematic pulse (e.g. C), just re-use the previous pulse\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "becomes\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------B-------0-------C-------0-------D-------E-------0-------F-------0-------G-------\n",
    "```\n",
    "\n",
    "Pros:\n",
    "\n",
    "- It's simple\n",
    "\n",
    "Cons:\n",
    "\n",
    "- We end up losing pulse **H**.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2. For every problematic pulse, re-use prev pulse, but on the next step (i.e. 0) use newest pulse\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "becomes\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------B-------C-------D-------0-------E-------E-------F-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "Pros:\n",
    "\n",
    "- We use all the pulses (if we have enough 0s)\n",
    "\n",
    "Cons:\n",
    "\n",
    "- We kind of end up obfuscating the pulses actual desired target rhythm. In this case\n",
    "        10110 10110 101\n",
    "  becomes\n",
    "        10111 10111 101"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3. For every problematic pulse, just nudge the corresponding target pulse\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "becomes\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B---------C-----0-------D-------0-------E--------F------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "Pros:\n",
    "\n",
    "- We avoid having to repeat any pulses (repeated pulses could sound weird)\n",
    "- We use all the pulses\n",
    "\n",
    "Cons:\n",
    "\n",
    "- The rhythm becomes metrically incorrect and could sound unnatural/bad."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 4. Subdivide original rhythm further until we don't have problematic pulses anymore"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=104, original_scale_factor=12, target_scale_factor=7\n",
      "Oh no; original pulse at 26 comes after target pulse at 24 (diff=2)\n",
      "Oh no; original pulse at 65 comes after target pulse at 64 (diff=1)\n",
      "\n",
      "Original: A------------B------------C------------D------------E------------F------------G------------H------------\n",
      "Target:   A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n",
      "\n",
      "--------\n",
      "\n",
      "lcm=208, original_scale_factor=12, target_scale_factor=15\n",
      "\n",
      "Original: A------------B------------C------------D------------E------------F------------G------------H------------I------------J------------K------------L------------M------------N------------O------------P------------\n",
      "Target:   A---------------0---------------B---------------C---------------0---------------D---------------0---------------E---------------F---------------0---------------G---------------0---------------H---------------\n"
     ]
    }
   ],
   "source": [
    "formatted = format_rhythms(original_rhythm, target_rhythm, format_method=\"alphabet\")\n",
    "print(\"\\n--------\\n\")\n",
    "formatted = format_rhythms([1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0], target_rhythm, format_method=\"alphabet\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Needs more work."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 5. Method 4, but use 0s of target rhythm as pulses too\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------I------------J------------K------------L------------M------------N------------O------------P------------\n",
    "Trgt: A---------------0---------------B---------------C---------------0---------------D---------------0---------------E---------------F---------------0---------------G---------------0---------------H---------------\n",
    "```\n",
    "\n",
    "becomes\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------I------------J------------K------------L------------M------------N------------O------------P------------\n",
    "Trgt: A---------------B---------------C---------------D---------------E---------------F---------------G---------------H---------------I---------------J---------------K---------------L---------------M---------------\n",
    "```\n",
    "\n",
    "Pros:\n",
    "\n",
    "- We preserve both rhythms AND avoid repeating segments, which is good\n",
    "\n",
    "Cons:\n",
    "\n",
    "- We end up scrapping a lot of the latter pulses (pulses **N**, **O**, and **P**)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 6. Use 0s of target rhythm as pulses too, if necessary\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 7. Just always use the most recent pulse we have, when needed\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "becomes\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------B-------0-------D-------0-------E-------E-------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "Pros:\n",
    "\n",
    "- Preserve rhythm shape, and it's simple\n",
    "\n",
    "Cons:\n",
    "\n",
    "- We lose intermediate pulses, in this case **C** and **F**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 8. Combine methods 4 and 7\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------I------------J------------K------------L------------M------------N------------O------------P------------\n",
    "Trgt: A---------------0---------------B---------------C---------------0---------------D---------------0---------------E---------------F---------------0---------------G---------------0---------------H---------------\n",
    "```\n",
    "\n",
    "becomes\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------I------------J------------K------------L------------M------------N------------O------------P------------\n",
    "Trgt: A---------------B---------------C---------------D---------------E---------------G---------------H---------------I---------------J---------------K---------------M---------------N---------------O---------------\n",
    "```\n",
    "\n",
    "Pros:\n",
    "\n",
    "- Again, we preserve rhythm shape\n",
    "\n",
    "Cons:\n",
    "\n",
    "- We lose intermediate pulses **F**, **L**, and **P**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 9. Method 7 (use most recent available pulse), but stretch instead of repeat pulses\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "becomes\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------0-------0-------D-------0-------E-------0-------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "Pros:\n",
    "\n",
    "- Avoid having to repeat pulses\n",
    "\n",
    "Cons:\n",
    "\n",
    "- We lose intermediate pulses, in this case **C** and **F**\n",
    "- We lose rhythm shape, as\n",
    "        10110 10110 101\n",
    "  becomes\n",
    "        10100 10100 101\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 10. Method 2, but stretch instead of repeat. So stretch, and use new pulse on next available step (i.e. 0)\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "becomes\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------0-------C-------D-------0-------E-------0-------F-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "Pros:\n",
    "\n",
    "- Avoid having to repeat pulses\n",
    "- We use all the pulses!\n",
    "\n",
    "Cons:\n",
    "\n",
    "- We lose rhythm shape, as\n",
    "        10110 10110 101\n",
    "  becomes\n",
    "        10101 10101 101\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Method 11: Just don't let the user create target rhythms with problematic pulses\n",
    "\n",
    "Mmhmm din"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### So What?\n",
    "\n",
    "So it's a tradeoff between:\n",
    "\n",
    "-  a) Using all the pulses\n",
    "-  b) Preserving target rhythm shape\n",
    "-  c) Avoiding repeating of pulses (which could sound unnatural)\n",
    "\n",
    "Method 10 achieves (a) and (c).\n",
    "\n",
    "Method 5 achieves (b) and (c).\n",
    "\n",
    "Method 2 achieves (a).\n",
    "\n",
    "Method 7 achieves (b), while 9 achieves (c).\n",
    "\n",
    "\n",
    "## Trying out method 10\n",
    "\n",
    "Let's try **Method 10** for now. We'll redefine our `rtos` method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# Rhythm to string\n",
    "# Method: \"str\", \"alphabet\"\n",
    "def rtos(rhythm, format_method=\"str\", pulse_symbols=[\"1\"]):\n",
    "    pulse_symbols = [str(s) for s in pulse_symbols]\n",
    "    rhythm = [str(x) for x in rhythm]\n",
    "    \n",
    "    if format_method == \"str\":\n",
    "        return \"\".join(rhythm)\n",
    "    elif format_method == \"alphabet\":\n",
    "        alphabet = list(\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\")\n",
    "        output = []\n",
    "        ai = 0\n",
    "        for i,x in enumerate(rhythm):\n",
    "            if str(x) in pulse_symbols:\n",
    "                output.append(alphabet[ai%len(alphabet)])\n",
    "                ai += 1\n",
    "            else:\n",
    "                output.append(x)\n",
    "        return \"\".join(output)\n",
    "    else:\n",
    "        return rhythm\n",
    "    \n",
    "# Format original and target rhythms for real-time manipulation\n",
    "def format_rhythms(original_rhythm, target_rhythm, format_method=\"str\", original_pulse_symbols=[1,0], target_pulse_symbols=[\"1\"], delimiter=\"-\"):\n",
    "    \n",
    "    # Convert all arrays to string arrays\n",
    "    original_rhythm = [str(x) for x in original_rhythm]\n",
    "    target_rhythm = [str(x) for x in target_rhythm]\n",
    "    original_pulse_symbols = [str(x) for x in original_pulse_symbols]\n",
    "    target_pulse_symbols = [str(x) for x in target_pulse_symbols]\n",
    "    \n",
    "    # Adjust target rhythm if inappropriate number of pulses\n",
    "    if len(original_rhythm) > len([x for x in target_rhythm if x in target_pulse_symbols]):\n",
    "        while len(target_rhythm) < len(original_rhythm):\n",
    "            target_rhythm.extend(target_rhythm)\n",
    "        target_rhythm = bjorklund.bjorklund(pulses=len(original_rhythm), steps=len(target_rhythm))\n",
    "        target_rhythm = [str(x) for x in target_rhythm]\n",
    "    \n",
    "    # Equalize rhythm lengths and get pulse indices\n",
    "    eq_original_rhythm, eq_target_rhythm = equalize_rhythm_subdivisions(original_rhythm, target_rhythm, delimiter=delimiter)\n",
    "    eq_original_step_indices = get_pulse_indices_for_rhythm(eq_original_rhythm, pulse_symbols=[1,0])\n",
    "    eq_target_pulse_indices = get_pulse_indices_for_rhythm(eq_target_rhythm, pulse_symbols=[1])\n",
    "    \n",
    "    # Assertion that may not hold up\n",
    "    # assert(len(eq_original_step_indices) == len(target_pulse_indices))\n",
    "\n",
    "    # Find all the ones with problematic pulses (note that we're using *pulses* of target but *steps* of original)\n",
    "    # for i in range(min(len(eq_original_step_indices), len(eq_target_pulse_indices))):\n",
    "    #     osi = eq_original_step_indices[i]\n",
    "    #     tpi = eq_target_pulse_indices[i]\n",
    "    #     if (osi > tpi):\n",
    "    #         print(\"Oh no; original pulse at {} comes after target pulse at {} (diff={})\".format(osi, tpi, osi-tpi))\n",
    "            \n",
    "    # Fix problematic pulses using method 10\n",
    "    # (Starting to do this in a more C++ style so it's easier to port)\n",
    "    \n",
    "    fixed_eq_target_rhythm = list(delimiter * len(eq_target_rhythm))\n",
    "    eq_target_step_indices = get_pulse_indices_for_rhythm(eq_target_rhythm, pulse_symbols=[1,0])\n",
    "    \n",
    "    print(\"eq_original_step_indices: {}\".format(eq_original_step_indices))\n",
    "    print(\"eq_target_pulse_indices:  {}\".format(eq_target_pulse_indices))\n",
    "    print(\"eq_target_step_indices:   {}\".format(eq_target_step_indices))\n",
    "    \n",
    "    osi_idx = -1\n",
    "    tpi_idx = 0\n",
    "    \n",
    "    for i in range(len(eq_target_rhythm)):\n",
    "        # print(i)\n",
    "\n",
    "        # Update index for original step indices\n",
    "        if i in eq_original_step_indices:\n",
    "            # osi_idx = min(osi_idx+1, len(eq_original_step_indices)-1)\n",
    "            osi_idx += 1\n",
    "            \n",
    "            \n",
    "        # Adjust\n",
    "        if i in eq_target_step_indices:\n",
    "            osi = eq_original_step_indices[osi_idx]\n",
    "            tpi = eq_target_pulse_indices[tpi_idx]\n",
    "            print(\"{}: osi@{}={}, tpi@{}={}\".format(i, osi_idx, osi, tpi_idx, tpi))\n",
    "            \n",
    "            # Make sure current position isn't earlier than original's,\n",
    "            # and that target pulse position isn't ahead of original pulse position\n",
    "            if i >= osi and i >= tpi and osi_idx >= tpi_idx:\n",
    "                fixed_eq_target_rhythm[i] = 1\n",
    "                tpi_idx += 1\n",
    "                print(\"set to 1\")\n",
    "                continue\n",
    "            else:\n",
    "                fixed_eq_target_rhythm[i] = 0\n",
    "                print(\"set to 0\")\n",
    "                continue\n",
    "\n",
    "        # Otherwise, it's a delimiter, so we just put a delimiter there\n",
    "        fixed_eq_target_rhythm[i] = delimiter\n",
    "    \n",
    "    print(\"\")\n",
    "    print(\"OrigPlse: {}\").format(rtos(eq_original_rhythm, format_method=\"str\", pulse_symbols=[1]))\n",
    "    print(\"OrigStep: {}\".format(rtos(eq_original_rhythm, format_method=format_method, pulse_symbols=[1,0])))\n",
    "    print(\"TrgtPlse: {}\".format(rtos(eq_target_rhythm, format_method=format_method, pulse_symbols=[1])))\n",
    "    print(\"FixdPlse: {}\".format(rtos(fixed_eq_target_rhythm, format_method=format_method, pulse_symbols=[1])))\n",
    "    \n",
    "    return (eq_original_rhythm, fixed_eq_target_rhythm)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original rhythm: [1, 0, 0, 1, 0, 0, 1, 0]\n",
      "Target rhythm:   [1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1]\n",
      "\n",
      "lcm=104, original_scale_factor=12, target_scale_factor=7\n",
      "eq_original_step_indices: [0, 13, 26, 39, 52, 65, 78, 91]\n",
      "eq_target_pulse_indices:  [0, 16, 24, 40, 56, 64, 80, 96]\n",
      "eq_target_step_indices:   [0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96]\n",
      "0: osi@0=0, tpi@0=0\n",
      "set to 1\n",
      "8: osi@0=0, tpi@1=16\n",
      "set to 0\n",
      "16: osi@1=13, tpi@1=16\n",
      "set to 1\n",
      "24: osi@1=13, tpi@2=24\n",
      "set to 0\n",
      "32: osi@2=26, tpi@2=24\n",
      "set to 1\n",
      "40: osi@3=39, tpi@3=40\n",
      "set to 1\n",
      "48: osi@3=39, tpi@4=56\n",
      "set to 0\n",
      "56: osi@4=52, tpi@4=56\n",
      "set to 1\n",
      "64: osi@4=52, tpi@5=64\n",
      "set to 0\n",
      "72: osi@5=65, tpi@5=64\n",
      "set to 1\n",
      "80: osi@6=78, tpi@6=80\n",
      "set to 1\n",
      "88: osi@6=78, tpi@7=96\n",
      "set to 0\n",
      "96: osi@7=91, tpi@7=96\n",
      "set to 1\n",
      "\n",
      "OrigPlse: 1------------0------------0------------1------------0------------0------------1------------0------------\n",
      "OrigStep: A------------B------------C------------D------------E------------F------------G------------H------------\n",
      "TrgtPlse: A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n",
      "FixdPlse: A-------0-------B-------0-------C-------D-------0-------E-------0-------F-------G-------0-------H-------\n"
     ]
    }
   ],
   "source": [
    "print(\"Original rhythm: {}\\nTarget rhythm:   {}\\n\".format(original_rhythm, target_rhythm))\n",
    "formatted = format_rhythms(original_rhythm, target_rhythm, format_method=\"alphabet\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That looks more like it! Let's try a few more:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false,
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=20, original_scale_factor=4, target_scale_factor=3\n",
      "eq_original_step_indices: [0, 5, 10, 15]\n",
      "eq_target_pulse_indices:  [0, 4, 8, 12]\n",
      "eq_target_step_indices:   [0, 4, 8, 12, 16]\n",
      "0: osi@0=0, tpi@0=0\n",
      "set to 1\n",
      "4: osi@0=0, tpi@1=4\n",
      "set to 0\n",
      "8: osi@1=5, tpi@1=4\n",
      "set to 1\n",
      "12: osi@2=10, tpi@2=8\n",
      "set to 1\n",
      "16: osi@3=15, tpi@3=12\n",
      "set to 1\n",
      "\n",
      "OrigPlse: 1----0----1----1----\n",
      "OrigStep: A----B----C----D----\n",
      "TrgtPlse: A---B---C---D---0---\n",
      "FixdPlse: A---0---B---C---D---\n"
     ]
    }
   ],
   "source": [
    "formatted = format_rhythms([1,0,1,1], [1,0,1,0,1], format_method=\"alphabet\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=12, original_scale_factor=2, target_scale_factor=1\n",
      "eq_original_step_indices: [0, 3, 6, 9]\n",
      "eq_target_pulse_indices:  [0, 4, 8, 10]\n",
      "eq_target_step_indices:   [0, 2, 4, 6, 8, 10]\n",
      "0: osi@0=0, tpi@0=0\n",
      "set to 1\n",
      "2: osi@0=0, tpi@1=4\n",
      "set to 0\n",
      "4: osi@1=3, tpi@1=4\n",
      "set to 1\n",
      "6: osi@2=6, tpi@2=8\n",
      "set to 0\n",
      "8: osi@2=6, tpi@2=8\n",
      "set to 1\n",
      "10: osi@3=9, tpi@3=10\n",
      "set to 1\n",
      "\n",
      "OrigPlse: 1--0--1--1--\n",
      "OrigStep: A--B--C--D--\n",
      "TrgtPlse: A-0-B-0-C-D-\n",
      "FixdPlse: A-0-B-0-C-D-\n"
     ]
    }
   ],
   "source": [
    "formatted = format_rhythms([1,0,1,1], [1,0,1,0,1,1], format_method=\"alphabet\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=42, original_scale_factor=6, target_scale_factor=5\n",
      "eq_original_step_indices: [0, 7, 14, 21, 28, 35]\n",
      "eq_target_pulse_indices:  [0, 6, 12, 18, 24, 30]\n",
      "eq_target_step_indices:   [0, 6, 12, 18, 24, 30, 36]\n",
      "0: osi@0=0, tpi@0=0\n",
      "set to 1\n",
      "6: osi@0=0, tpi@1=6\n",
      "set to 0\n",
      "12: osi@1=7, tpi@1=6\n",
      "set to 1\n",
      "18: osi@2=14, tpi@2=12\n",
      "set to 1\n",
      "24: osi@3=21, tpi@3=18\n",
      "set to 1\n",
      "30: osi@4=28, tpi@4=24\n",
      "set to 1\n",
      "36: osi@5=35, tpi@5=30\n",
      "set to 1\n",
      "\n",
      "OrigPlse: 1------0------1------1------0------1------\n",
      "OrigStep: A------B------C------D------E------F------\n",
      "TrgtPlse: A-----B-----C-----D-----E-----F-----0-----\n",
      "FixdPlse: A-----0-----B-----C-----D-----E-----F-----\n"
     ]
    }
   ],
   "source": [
    "formatted = format_rhythms([1,0,1,1,0,1], [1,0,1,1,0,1,0], format_method=\"alphabet\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=24, original_scale_factor=3, target_scale_factor=2\n",
      "eq_original_step_indices: [0, 4, 8, 12, 16, 20]\n",
      "eq_target_pulse_indices:  [0, 3, 6, 12, 15, 18]\n",
      "eq_target_step_indices:   [0, 3, 6, 9, 12, 15, 18, 21]\n",
      "0: osi@0=0, tpi@0=0\n",
      "set to 1\n",
      "3: osi@0=0, tpi@1=3\n",
      "set to 0\n",
      "6: osi@1=4, tpi@1=3\n",
      "set to 1\n",
      "9: osi@2=8, tpi@2=6\n",
      "set to 1\n",
      "12: osi@3=12, tpi@3=12\n",
      "set to 1\n",
      "15: osi@3=12, tpi@4=15\n",
      "set to 0\n",
      "18: osi@4=16, tpi@4=15\n",
      "set to 1\n",
      "21: osi@5=20, tpi@5=18\n",
      "set to 1\n",
      "\n",
      "OrigPlse: 1---0---1---1---0---1---\n",
      "OrigStep: A---B---C---D---E---F---\n",
      "TrgtPlse: A--B--C--0--D--E--F--0--\n",
      "FixdPlse: A--0--B--C--D--0--E--F--\n"
     ]
    }
   ],
   "source": [
    "formatted = format_rhythms([1,0,1,1,0,1], [0], format_method=\"alphabet\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the previous two examples, the fixed rhythm doesn't resemble the target rhythm at all...\n",
    "I guess this is because we're using the Bjorklund fix too, to fix pulses.\n",
    "\n",
    "But if we're going through all this trouble to \"fix\" rhythms, maybe we should just place constraints on what the user can do in the first place?\n",
    "\n",
    "I guess we should have both.\n",
    "\n",
    "e.g. if they put a pulse before the original position, it'll show up red or something.\n",
    "\n",
    "Hmm, but in *that* case, maybe we *do* want to preserve the original rhythm. And just have it repeat or something?\n",
    "In other words, use method **7**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Trying out method 7"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# TODO: Try with method 7 vs. method 10"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Misc noodles"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "np.indices?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'2-3-4'"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"0\"*4\n",
    "\"-\".join([str(x) for x in [2,3,4]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a\n",
      "b\n"
     ]
    }
   ],
   "source": [
    "print(\"a\"); print(\"b\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
<commit_msg>add comment about why we use len(original_rhythm) instead of num_pulses<commit_after>{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Formatting rhythms for real-time stretch\n",
    "\n",
    "The goal of this notebook is to come up with an algorithm to re-format original and target rhythms such that real-time time-stretching (or even real-time stuttering) is possible."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import librosa\n",
    "import numpy as np\n",
    "\n",
    "import pardir; pardir.pardir() # Allow imports from parent directory\n",
    "import bjorklund\n",
    "import fibonaccistretch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "librosa.effects.time_stretch??\n",
    "librosa.core.phase_vocoder??\n",
    "fibonaccistretch.euclidean_stretch??"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Original rhythm: [1, 0, 0, 1, 0, 0, 1, 0]    Target rhythm: [1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1]'"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Generate rhythms based on parameters\n",
    "def generate_original_and_target_rhythms(num_pulses, original_length, target_length):\n",
    "    original_rhythm = bjorklund.bjorklund(pulses=num_pulses, steps=original_length)\n",
    "    target_rhythm = bjorklund.bjorklund(pulses=len(original_rhythm), steps=target_length) # We use len(original_rhythm) instead of num_pulses because we're doing Euclidean stretch here\n",
    "    return (original_rhythm, target_rhythm)\n",
    "\n",
    "original_rhythm, target_rhythm = generate_original_and_target_rhythms(3, 8, 13)\n",
    "\n",
    "\"Original rhythm: {}    Target rhythm: {}\".format(original_rhythm, target_rhythm)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "104"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lcm = (8*13) / fibonaccistretch.euclid(8, 13)\n",
    "lcm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "104"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "8*13"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## \"Equalize\": Make rhythms the same length\n",
    "\n",
    "Use LCM to \"equalize\" rhythms so that they're of equal length.\n",
    "\n",
    "e.g.\n",
    "\n",
    "`a = [1,0,0,1]`\n",
    "\n",
    "\n",
    "\n",
    "`b = [1,1,0]`\n",
    "\n",
    "become\n",
    "\n",
    "`equalized_a = [1,-,-,0,-,-,0,-,-,1,-,-]`\n",
    "\n",
    "\n",
    "\n",
    "`equalized_b = [1,-,-,-,1,-,-,-,0,-,-,-]`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original rhythm: [1, 0, 0, 1, 0, 0, 1, 0]    Target rhythm: [1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1]\n",
      "lcm=104, original_scale_factor=12, target_scale_factor=7\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(104, 104)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# \"Equalize\" (i.e. scale rhythms so they're of equal length)\n",
    "def equalize_rhythm_subdivisions(original_rhythm, target_rhythm, delimiter=\"-\"):\n",
    "    original_length = len(original_rhythm)\n",
    "    target_length = len(target_rhythm)\n",
    "    lcm = (original_length*target_length) / fibonaccistretch.euclid(original_length, target_length)\n",
    "    original_scale_factor = (lcm / original_length) - 1\n",
    "    target_scale_factor = (lcm / target_length) - 1\n",
    "    \n",
    "    print(\"lcm={}, original_scale_factor={}, target_scale_factor={}\").format(lcm, original_scale_factor, target_scale_factor)\n",
    "    \n",
    "    delimiter = str(delimiter)\n",
    "    original_rhythm = list((delimiter*original_scale_factor).join([str(x) for x in original_rhythm]))\n",
    "    target_rhythm = list((delimiter*target_scale_factor).join([str(x) for x in target_rhythm]))\n",
    "    \n",
    "    original_rhythm.extend(list(delimiter*original_scale_factor))\n",
    "    target_rhythm.extend(list(delimiter*target_scale_factor))\n",
    "\n",
    "    \n",
    "    return (original_rhythm, target_rhythm)\n",
    "\n",
    "# print(scale_rhythm_subdivisions(original_rhythm, target_rhythm))\n",
    "original_rhythm, target_rhythm = generate_original_and_target_rhythms(3, 8, 13)\n",
    "print(\"Original rhythm: {}    Target rhythm: {}\".format(original_rhythm, target_rhythm))\n",
    "equalized_original_rhythm, equalized_target_rhythm = equalize_rhythm_subdivisions(original_rhythm, target_rhythm)\n",
    "(len(equalized_original_rhythm), len(equalized_target_rhythm))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Get pulse indices so we can see how the equalized original and target relate. In particular, our goal is to create a relationship such that the original pulse indices always come first (so that they're bufferable in real-time)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "([0, 39, 78], [0, 16, 24, 40, 56, 64, 80, 96])"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def get_pulse_indices_for_rhythm(rhythm, pulse_symbols=[1]):\n",
    "    pulse_symbols = [str(s) for s in pulse_symbols]\n",
    "    rhythm = [str(x) for x in rhythm]\n",
    "    pulse_indices = [i for i,symbol in enumerate(rhythm) if symbol in pulse_symbols]\n",
    "    return pulse_indices\n",
    "\n",
    "equalized_original_pulse_indices = get_pulse_indices_for_rhythm(equalized_original_rhythm)\n",
    "equalized_target_pulse_indices = get_pulse_indices_for_rhythm(equalized_target_rhythm)\n",
    "(equalized_original_pulse_indices, equalized_target_pulse_indices)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For original we'll actually use ALL the steps instead of just pulses though. So:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "([0, 13, 26, 39, 52, 65, 78, 91], [0, 16, 24, 40, 56, 64, 80, 96])\n"
     ]
    }
   ],
   "source": [
    "equalized_original_pulse_indices = get_pulse_indices_for_rhythm(equalized_original_rhythm, [1,0])\n",
    "equalized_target_pulse_indices = get_pulse_indices_for_rhythm(equalized_target_rhythm, [1])\n",
    "print(equalized_original_pulse_indices, equalized_target_pulse_indices)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can check to see if all the original pulse indices come first (this is our goal):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Oh no; original pulse at 26 comes after target pulse at 24 (diff=2)\n",
      "Oh no; original pulse at 65 comes after target pulse at 64 (diff=1)\n"
     ]
    }
   ],
   "source": [
    "for i in range(len(equalized_original_pulse_indices)):\n",
    "    opi = equalized_original_pulse_indices[i]\n",
    "    tpi = equalized_target_pulse_indices[i]\n",
    "    if (opi > tpi):\n",
    "        print(\"Oh no; original pulse at {} comes after target pulse at {} (diff={})\".format(opi, tpi, opi-tpi))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Oh no... how do we fix this??\n",
    "\n",
    "- One solution is to just nudge them over, especially since they only differ by 1/104 to 2/104ths of a measure in this case. \n",
    "- Another solution would be to use the same data from the original pulse if there's not a new pulse available. Hmmmm\n",
    "- Or use as much of the original buffer as we can...?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## First pass at format_rhythm(), without fixing rhythm\n",
    "\n",
    "But first let's define a function for end-to-end formatting:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# Format original and target rhythms for real-time manipulation\n",
    "def format_rhythms(original_rhythm, target_rhythm):\n",
    "    # Equalize rhythm lengths and get pulse indices\n",
    "    eor, etr = equalize_rhythm_subdivisions(original_rhythm, target_rhythm)\n",
    "    eopi = get_pulse_indices_for_rhythm(eor, pulse_symbols=[1,0])\n",
    "    etpi = get_pulse_indices_for_rhythm(etr, pulse_symbols=[1])\n",
    "\n",
    "    # Find all the ones with problematic pulses (note that we're using *pulses* of target but *steps* of original)\n",
    "    for i in range(min(len(eopi), len(etpi))):\n",
    "        opi = eopi[i]\n",
    "        tpi = etpi[i]\n",
    "        if (opi > tpi):\n",
    "            print(\"Oh no; original pulse at {} comes after target pulse at {} (diff={})\".format(opi, tpi, opi-tpi))\n",
    "            \n",
    "    # TODO: Fix problematic pulses\n",
    "    #\n",
    "    \n",
    "    print(\"Formatted original: {}\".format(rtos(eor)))\n",
    "    print(\"Formatted target:   {}\".format(rtos(etr)))\n",
    "    \n",
    "    return (eor, etr)\n",
    "\n",
    "# Rhythm to string\n",
    "def rtos(rhythm):\n",
    "    return \"\".join(rhythm)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Alright let's try this out:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=24, original_scale_factor=2, target_scale_factor=7\n",
      "Formatted original: 1--0--0--1--0--0--1--0--\n",
      "Formatted target:   1-------0-------1-------\n"
     ]
    }
   ],
   "source": [
    "# len(original) > len(target)\n",
    "formatted = format_rhythms([1,0,0,1,0,0,1,0], [1,0,1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=88, original_scale_factor=10, target_scale_factor=7\n",
      "Formatted original: 1----------0----------0----------1----------0----------0----------1----------0----------\n",
      "Formatted target:   1-------0-------0-------1-------1-------0-------0-------1-------1-------1-------1-------\n"
     ]
    }
   ],
   "source": [
    "# len(original) < len(target)\n",
    "formatted = format_rhythms([1,0,0,1,0,0,1,0], [1,0,0,1,1,0,0,1,1,1,1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=20, original_scale_factor=4, target_scale_factor=1\n",
      "Formatted original: 1----0----1----0----\n",
      "Formatted target:   1-0-0-1-0-0-1-0-0-0-\n",
      "--------\n",
      "lcm=10, original_scale_factor=4, target_scale_factor=0\n",
      "Oh no; original pulse at 5 comes after target pulse at 3 (diff=2)\n",
      "Formatted original: 1----1----\n",
      "Formatted target:   1001001000\n"
     ]
    }
   ],
   "source": [
    "# Trying [1,0,1,0] and [1,1] as originals, with the same target\n",
    "formatted = format_rhythms([1,0,1,0], [1,0,0,1,0,0,1,0,0,0])\n",
    "print(\"--------\")\n",
    "formatted = format_rhythms([1,1], [1,0,0,1,0,0,1,0,0,0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To make things a bit clearer maybe we'll try the `abcd` format for `rtos()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# Rhythm to string\n",
    "# Method: \"str\", \"alphabet\"\n",
    "def rtos(rhythm, format_method=\"str\", pulse_symbols=[\"1\"]):\n",
    "    pulse_symbols = [str(s) for s in pulse_symbols]\n",
    "    \n",
    "    if format_method == \"str\":\n",
    "        return \"\".join(rhythm)\n",
    "    elif format_method == \"alphabet\":\n",
    "        alphabet = list(\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\")\n",
    "        output = []\n",
    "        ai = 0\n",
    "        for i,x in enumerate(rhythm):\n",
    "            if str(x) in pulse_symbols:\n",
    "                output.append(alphabet[ai%len(alphabet)])\n",
    "                ai += 1\n",
    "            else:\n",
    "                output.append(x)\n",
    "        return \"\".join(output)\n",
    "    else:\n",
    "        return rhythm\n",
    "    \n",
    "# Format original and target rhythms for real-time manipulation\n",
    "def format_rhythms(original_rhythm, target_rhythm, format_method=\"str\", pulse_symbols=[\"1\"]):\n",
    "    # Equalize rhythm lengths and get pulse indices\n",
    "    eor, etr = equalize_rhythm_subdivisions(original_rhythm, target_rhythm)\n",
    "    eopi = get_pulse_indices_for_rhythm(eor, pulse_symbols=[1,0])\n",
    "    etpi = get_pulse_indices_for_rhythm(etr, pulse_symbols=[1])\n",
    "\n",
    "    # Find all the ones with problematic pulses (note that we're using *pulses* of target but *steps* of original)\n",
    "    for i in range(min(len(eopi), len(etpi))):\n",
    "        opi = eopi[i]\n",
    "        tpi = etpi[i]\n",
    "        if (opi > tpi):\n",
    "            print(\"Oh no; original pulse at {} comes after target pulse at {} (diff={})\".format(opi, tpi, opi-tpi))\n",
    "            \n",
    "    # TODO: Fix problematic pulses\n",
    "    #\n",
    "    \n",
    "    print(\"\")\n",
    "    print(\"Original: {}\".format(rtos(eor, format_method=format_method, pulse_symbols=[1,0])))\n",
    "    print(\"Target:   {}\".format(rtos(etr, format_method=format_method, pulse_symbols=[1])))\n",
    "    \n",
    "    return (eor, etr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=20, original_scale_factor=4, target_scale_factor=1\n",
      "\n",
      "Original: A----B----C----D----\n",
      "Target:   A-0-0-B-0-0-C-0-0-0-\n",
      "\n",
      "--------\n",
      "\n",
      "lcm=10, original_scale_factor=4, target_scale_factor=0\n",
      "Oh no; original pulse at 5 comes after target pulse at 3 (diff=2)\n",
      "\n",
      "Original: A----B----\n",
      "Target:   A00B00C000\n"
     ]
    }
   ],
   "source": [
    "# Trying [1,0,1,0] and [1,1] as originals, with the same target\n",
    "formatted = format_rhythms([1,0,1,0], [1,0,0,1,0,0,1,0,0,0], format_method=\"alphabet\")\n",
    "print(\"\\n--------\\n\")\n",
    "formatted = format_rhythms([1,1], [1,0,0,1,0,0,1,0,0,0], format_method=\"alphabet\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=104, original_scale_factor=12, target_scale_factor=7\n",
      "Oh no; original pulse at 26 comes after target pulse at 24 (diff=2)\n",
      "Oh no; original pulse at 65 comes after target pulse at 64 (diff=1)\n",
      "\n",
      "Original: A------------B------------C------------D------------E------------F------------G------------H------------\n",
      "Target:   A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n"
     ]
    }
   ],
   "source": [
    "formatted = format_rhythms(original_rhythm, target_rhythm, format_method=\"alphabet\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exploring adjustment options\n",
    "\n",
    "Let's use this example to explore adjustment options:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original rhythm: [1, 0, 0, 1, 0, 0, 1, 0]\n",
      "Target rhythm:   [1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1]\n",
      "\n",
      "lcm=104, original_scale_factor=12, target_scale_factor=7\n",
      "Oh no; original pulse at 26 comes after target pulse at 24 (diff=2)\n",
      "Oh no; original pulse at 65 comes after target pulse at 64 (diff=1)\n",
      "\n",
      "Original: A------------B------------C------------D------------E------------F------------G------------H------------\n",
      "Target:   A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n"
     ]
    }
   ],
   "source": [
    "print(\"Original rhythm: {}\\nTarget rhythm:   {}\\n\".format(original_rhythm, target_rhythm))\n",
    "formatted = format_rhythms(original_rhythm, target_rhythm, format_method=\"alphabet\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In all the following cases only the target changes, not the original:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1. For every problematic pulse (e.g. C), just re-use the previous pulse\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "becomes\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------B-------0-------C-------0-------D-------E-------0-------F-------0-------G-------\n",
    "```\n",
    "\n",
    "Pros:\n",
    "\n",
    "- It's simple\n",
    "\n",
    "Cons:\n",
    "\n",
    "- We end up losing pulse **H**.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2. For every problematic pulse, re-use prev pulse, but on the next step (i.e. 0) use newest pulse\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "becomes\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------B-------C-------D-------0-------E-------E-------F-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "Pros:\n",
    "\n",
    "- We use all the pulses (if we have enough 0s)\n",
    "\n",
    "Cons:\n",
    "\n",
    "- We kind of end up obfuscating the pulses actual desired target rhythm. In this case\n",
    "        10110 10110 101\n",
    "  becomes\n",
    "        10111 10111 101"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3. For every problematic pulse, just nudge the corresponding target pulse\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "becomes\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B---------C-----0-------D-------0-------E--------F------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "Pros:\n",
    "\n",
    "- We avoid having to repeat any pulses (repeated pulses could sound weird)\n",
    "- We use all the pulses\n",
    "\n",
    "Cons:\n",
    "\n",
    "- The rhythm becomes metrically incorrect and could sound unnatural/bad."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 4. Subdivide original rhythm further until we don't have problematic pulses anymore"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=104, original_scale_factor=12, target_scale_factor=7\n",
      "Oh no; original pulse at 26 comes after target pulse at 24 (diff=2)\n",
      "Oh no; original pulse at 65 comes after target pulse at 64 (diff=1)\n",
      "\n",
      "Original: A------------B------------C------------D------------E------------F------------G------------H------------\n",
      "Target:   A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n",
      "\n",
      "--------\n",
      "\n",
      "lcm=208, original_scale_factor=12, target_scale_factor=15\n",
      "\n",
      "Original: A------------B------------C------------D------------E------------F------------G------------H------------I------------J------------K------------L------------M------------N------------O------------P------------\n",
      "Target:   A---------------0---------------B---------------C---------------0---------------D---------------0---------------E---------------F---------------0---------------G---------------0---------------H---------------\n"
     ]
    }
   ],
   "source": [
    "formatted = format_rhythms(original_rhythm, target_rhythm, format_method=\"alphabet\")\n",
    "print(\"\\n--------\\n\")\n",
    "formatted = format_rhythms([1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0], target_rhythm, format_method=\"alphabet\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Needs more work."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 5. Method 4, but use 0s of target rhythm as pulses too\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------I------------J------------K------------L------------M------------N------------O------------P------------\n",
    "Trgt: A---------------0---------------B---------------C---------------0---------------D---------------0---------------E---------------F---------------0---------------G---------------0---------------H---------------\n",
    "```\n",
    "\n",
    "becomes\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------I------------J------------K------------L------------M------------N------------O------------P------------\n",
    "Trgt: A---------------B---------------C---------------D---------------E---------------F---------------G---------------H---------------I---------------J---------------K---------------L---------------M---------------\n",
    "```\n",
    "\n",
    "Pros:\n",
    "\n",
    "- We preserve both rhythms AND avoid repeating segments, which is good\n",
    "\n",
    "Cons:\n",
    "\n",
    "- We end up scrapping a lot of the latter pulses (pulses **N**, **O**, and **P**)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 6. Use 0s of target rhythm as pulses too, if necessary\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 7. Just always use the most recent pulse we have, when needed\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "becomes\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------B-------0-------D-------0-------E-------E-------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "Pros:\n",
    "\n",
    "- Preserve rhythm shape, and it's simple\n",
    "\n",
    "Cons:\n",
    "\n",
    "- We lose intermediate pulses, in this case **C** and **F**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 8. Combine methods 4 and 7\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------I------------J------------K------------L------------M------------N------------O------------P------------\n",
    "Trgt: A---------------0---------------B---------------C---------------0---------------D---------------0---------------E---------------F---------------0---------------G---------------0---------------H---------------\n",
    "```\n",
    "\n",
    "becomes\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------I------------J------------K------------L------------M------------N------------O------------P------------\n",
    "Trgt: A---------------B---------------C---------------D---------------E---------------G---------------H---------------I---------------J---------------K---------------M---------------N---------------O---------------\n",
    "```\n",
    "\n",
    "Pros:\n",
    "\n",
    "- Again, we preserve rhythm shape\n",
    "\n",
    "Cons:\n",
    "\n",
    "- We lose intermediate pulses **F**, **L**, and **P**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 9. Method 7 (use most recent available pulse), but stretch instead of repeat pulses\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "becomes\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------0-------0-------D-------0-------E-------0-------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "Pros:\n",
    "\n",
    "- Avoid having to repeat pulses\n",
    "\n",
    "Cons:\n",
    "\n",
    "- We lose intermediate pulses, in this case **C** and **F**\n",
    "- We lose rhythm shape, as\n",
    "        10110 10110 101\n",
    "  becomes\n",
    "        10100 10100 101\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 10. Method 2, but stretch instead of repeat. So stretch, and use new pulse on next available step (i.e. 0)\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "becomes\n",
    "\n",
    "```\n",
    "Orig: A------------B------------C------------D------------E------------F------------G------------H------------\n",
    "Trgt: A-------0-------B-------0-------C-------D-------0-------E-------0-------F-------G-------0-------H-------\n",
    "```\n",
    "\n",
    "Pros:\n",
    "\n",
    "- Avoid having to repeat pulses\n",
    "- We use all the pulses!\n",
    "\n",
    "Cons:\n",
    "\n",
    "- We lose rhythm shape, as\n",
    "        10110 10110 101\n",
    "  becomes\n",
    "        10101 10101 101\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Method 11: Just don't let the user create target rhythms with problematic pulses\n",
    "\n",
    "Mmhmm din"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### So What?\n",
    "\n",
    "So it's a tradeoff between:\n",
    "\n",
    "-  a) Using all the pulses\n",
    "-  b) Preserving target rhythm shape\n",
    "-  c) Avoiding repeating of pulses (which could sound unnatural)\n",
    "\n",
    "Method 10 achieves (a) and (c).\n",
    "\n",
    "Method 5 achieves (b) and (c).\n",
    "\n",
    "Method 2 achieves (a).\n",
    "\n",
    "Method 7 achieves (b), while 9 achieves (c).\n",
    "\n",
    "\n",
    "## Trying out method 10\n",
    "\n",
    "Let's try **Method 10** for now. We'll redefine our `rtos` method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# Rhythm to string\n",
    "# Method: \"str\", \"alphabet\"\n",
    "def rtos(rhythm, format_method=\"str\", pulse_symbols=[\"1\"]):\n",
    "    pulse_symbols = [str(s) for s in pulse_symbols]\n",
    "    rhythm = [str(x) for x in rhythm]\n",
    "    \n",
    "    if format_method == \"str\":\n",
    "        return \"\".join(rhythm)\n",
    "    elif format_method == \"alphabet\":\n",
    "        alphabet = list(\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\")\n",
    "        output = []\n",
    "        ai = 0\n",
    "        for i,x in enumerate(rhythm):\n",
    "            if str(x) in pulse_symbols:\n",
    "                output.append(alphabet[ai%len(alphabet)])\n",
    "                ai += 1\n",
    "            else:\n",
    "                output.append(x)\n",
    "        return \"\".join(output)\n",
    "    else:\n",
    "        return rhythm\n",
    "    \n",
    "# Format original and target rhythms for real-time manipulation\n",
    "def format_rhythms(original_rhythm, target_rhythm, format_method=\"str\", original_pulse_symbols=[1,0], target_pulse_symbols=[\"1\"], delimiter=\"-\"):\n",
    "    \n",
    "    # Convert all arrays to string arrays\n",
    "    original_rhythm = [str(x) for x in original_rhythm]\n",
    "    target_rhythm = [str(x) for x in target_rhythm]\n",
    "    original_pulse_symbols = [str(x) for x in original_pulse_symbols]\n",
    "    target_pulse_symbols = [str(x) for x in target_pulse_symbols]\n",
    "    \n",
    "    # Adjust target rhythm if inappropriate number of pulses\n",
    "    if len(original_rhythm) > len([x for x in target_rhythm if x in target_pulse_symbols]):\n",
    "        while len(target_rhythm) < len(original_rhythm):\n",
    "            target_rhythm.extend(target_rhythm)\n",
    "        target_rhythm = bjorklund.bjorklund(pulses=len(original_rhythm), steps=len(target_rhythm))\n",
    "        target_rhythm = [str(x) for x in target_rhythm]\n",
    "    \n",
    "    # Equalize rhythm lengths and get pulse indices\n",
    "    eq_original_rhythm, eq_target_rhythm = equalize_rhythm_subdivisions(original_rhythm, target_rhythm, delimiter=delimiter)\n",
    "    eq_original_step_indices = get_pulse_indices_for_rhythm(eq_original_rhythm, pulse_symbols=[1,0])\n",
    "    eq_target_pulse_indices = get_pulse_indices_for_rhythm(eq_target_rhythm, pulse_symbols=[1])\n",
    "    \n",
    "    # Assertion that may not hold up\n",
    "    # assert(len(eq_original_step_indices) == len(target_pulse_indices))\n",
    "\n",
    "    # Find all the ones with problematic pulses (note that we're using *pulses* of target but *steps* of original)\n",
    "    # for i in range(min(len(eq_original_step_indices), len(eq_target_pulse_indices))):\n",
    "    #     osi = eq_original_step_indices[i]\n",
    "    #     tpi = eq_target_pulse_indices[i]\n",
    "    #     if (osi > tpi):\n",
    "    #         print(\"Oh no; original pulse at {} comes after target pulse at {} (diff={})\".format(osi, tpi, osi-tpi))\n",
    "            \n",
    "    # Fix problematic pulses using method 10\n",
    "    # (Starting to do this in a more C++ style so it's easier to port)\n",
    "    \n",
    "    fixed_eq_target_rhythm = list(delimiter * len(eq_target_rhythm))\n",
    "    eq_target_step_indices = get_pulse_indices_for_rhythm(eq_target_rhythm, pulse_symbols=[1,0])\n",
    "    \n",
    "    print(\"eq_original_step_indices: {}\".format(eq_original_step_indices))\n",
    "    print(\"eq_target_pulse_indices:  {}\".format(eq_target_pulse_indices))\n",
    "    print(\"eq_target_step_indices:   {}\".format(eq_target_step_indices))\n",
    "    \n",
    "    osi_idx = -1\n",
    "    tpi_idx = 0\n",
    "    \n",
    "    for i in range(len(eq_target_rhythm)):\n",
    "        # print(i)\n",
    "\n",
    "        # Update index for original step indices\n",
    "        if i in eq_original_step_indices:\n",
    "            # osi_idx = min(osi_idx+1, len(eq_original_step_indices)-1)\n",
    "            osi_idx += 1\n",
    "            \n",
    "            \n",
    "        # Adjust\n",
    "        if i in eq_target_step_indices:\n",
    "            osi = eq_original_step_indices[osi_idx]\n",
    "            tpi = eq_target_pulse_indices[tpi_idx]\n",
    "            print(\"{}: osi@{}={}, tpi@{}={}\".format(i, osi_idx, osi, tpi_idx, tpi))\n",
    "            \n",
    "            # Make sure current position isn't earlier than original's,\n",
    "            # and that target pulse position isn't ahead of original pulse position\n",
    "            if i >= osi and i >= tpi and osi_idx >= tpi_idx:\n",
    "                fixed_eq_target_rhythm[i] = 1\n",
    "                tpi_idx += 1\n",
    "                print(\"set to 1\")\n",
    "                continue\n",
    "            else:\n",
    "                fixed_eq_target_rhythm[i] = 0\n",
    "                print(\"set to 0\")\n",
    "                continue\n",
    "\n",
    "        # Otherwise, it's a delimiter, so we just put a delimiter there\n",
    "        fixed_eq_target_rhythm[i] = delimiter\n",
    "    \n",
    "    print(\"\")\n",
    "    print(\"OrigPlse: {}\").format(rtos(eq_original_rhythm, format_method=\"str\", pulse_symbols=[1]))\n",
    "    print(\"OrigStep: {}\".format(rtos(eq_original_rhythm, format_method=format_method, pulse_symbols=[1,0])))\n",
    "    print(\"TrgtPlse: {}\".format(rtos(eq_target_rhythm, format_method=format_method, pulse_symbols=[1])))\n",
    "    print(\"FixdPlse: {}\".format(rtos(fixed_eq_target_rhythm, format_method=format_method, pulse_symbols=[1])))\n",
    "    \n",
    "    return (eq_original_rhythm, fixed_eq_target_rhythm)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original rhythm: [1, 0, 0, 1, 0, 0, 1, 0]\n",
      "Target rhythm:   [1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1]\n",
      "\n",
      "lcm=104, original_scale_factor=12, target_scale_factor=7\n",
      "eq_original_step_indices: [0, 13, 26, 39, 52, 65, 78, 91]\n",
      "eq_target_pulse_indices:  [0, 16, 24, 40, 56, 64, 80, 96]\n",
      "eq_target_step_indices:   [0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96]\n",
      "0: osi@0=0, tpi@0=0\n",
      "set to 1\n",
      "8: osi@0=0, tpi@1=16\n",
      "set to 0\n",
      "16: osi@1=13, tpi@1=16\n",
      "set to 1\n",
      "24: osi@1=13, tpi@2=24\n",
      "set to 0\n",
      "32: osi@2=26, tpi@2=24\n",
      "set to 1\n",
      "40: osi@3=39, tpi@3=40\n",
      "set to 1\n",
      "48: osi@3=39, tpi@4=56\n",
      "set to 0\n",
      "56: osi@4=52, tpi@4=56\n",
      "set to 1\n",
      "64: osi@4=52, tpi@5=64\n",
      "set to 0\n",
      "72: osi@5=65, tpi@5=64\n",
      "set to 1\n",
      "80: osi@6=78, tpi@6=80\n",
      "set to 1\n",
      "88: osi@6=78, tpi@7=96\n",
      "set to 0\n",
      "96: osi@7=91, tpi@7=96\n",
      "set to 1\n",
      "\n",
      "OrigPlse: 1------------0------------0------------1------------0------------0------------1------------0------------\n",
      "OrigStep: A------------B------------C------------D------------E------------F------------G------------H------------\n",
      "TrgtPlse: A-------0-------B-------C-------0-------D-------0-------E-------F-------0-------G-------0-------H-------\n",
      "FixdPlse: A-------0-------B-------0-------C-------D-------0-------E-------0-------F-------G-------0-------H-------\n"
     ]
    }
   ],
   "source": [
    "print(\"Original rhythm: {}\\nTarget rhythm:   {}\\n\".format(original_rhythm, target_rhythm))\n",
    "formatted = format_rhythms(original_rhythm, target_rhythm, format_method=\"alphabet\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That looks more like it! Let's try a few more:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false,
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=20, original_scale_factor=4, target_scale_factor=3\n",
      "eq_original_step_indices: [0, 5, 10, 15]\n",
      "eq_target_pulse_indices:  [0, 4, 8, 12]\n",
      "eq_target_step_indices:   [0, 4, 8, 12, 16]\n",
      "0: osi@0=0, tpi@0=0\n",
      "set to 1\n",
      "4: osi@0=0, tpi@1=4\n",
      "set to 0\n",
      "8: osi@1=5, tpi@1=4\n",
      "set to 1\n",
      "12: osi@2=10, tpi@2=8\n",
      "set to 1\n",
      "16: osi@3=15, tpi@3=12\n",
      "set to 1\n",
      "\n",
      "OrigPlse: 1----0----1----1----\n",
      "OrigStep: A----B----C----D----\n",
      "TrgtPlse: A---B---C---D---0---\n",
      "FixdPlse: A---0---B---C---D---\n"
     ]
    }
   ],
   "source": [
    "formatted = format_rhythms([1,0,1,1], [1,0,1,0,1], format_method=\"alphabet\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=12, original_scale_factor=2, target_scale_factor=1\n",
      "eq_original_step_indices: [0, 3, 6, 9]\n",
      "eq_target_pulse_indices:  [0, 4, 8, 10]\n",
      "eq_target_step_indices:   [0, 2, 4, 6, 8, 10]\n",
      "0: osi@0=0, tpi@0=0\n",
      "set to 1\n",
      "2: osi@0=0, tpi@1=4\n",
      "set to 0\n",
      "4: osi@1=3, tpi@1=4\n",
      "set to 1\n",
      "6: osi@2=6, tpi@2=8\n",
      "set to 0\n",
      "8: osi@2=6, tpi@2=8\n",
      "set to 1\n",
      "10: osi@3=9, tpi@3=10\n",
      "set to 1\n",
      "\n",
      "OrigPlse: 1--0--1--1--\n",
      "OrigStep: A--B--C--D--\n",
      "TrgtPlse: A-0-B-0-C-D-\n",
      "FixdPlse: A-0-B-0-C-D-\n"
     ]
    }
   ],
   "source": [
    "formatted = format_rhythms([1,0,1,1], [1,0,1,0,1,1], format_method=\"alphabet\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=42, original_scale_factor=6, target_scale_factor=5\n",
      "eq_original_step_indices: [0, 7, 14, 21, 28, 35]\n",
      "eq_target_pulse_indices:  [0, 6, 12, 18, 24, 30]\n",
      "eq_target_step_indices:   [0, 6, 12, 18, 24, 30, 36]\n",
      "0: osi@0=0, tpi@0=0\n",
      "set to 1\n",
      "6: osi@0=0, tpi@1=6\n",
      "set to 0\n",
      "12: osi@1=7, tpi@1=6\n",
      "set to 1\n",
      "18: osi@2=14, tpi@2=12\n",
      "set to 1\n",
      "24: osi@3=21, tpi@3=18\n",
      "set to 1\n",
      "30: osi@4=28, tpi@4=24\n",
      "set to 1\n",
      "36: osi@5=35, tpi@5=30\n",
      "set to 1\n",
      "\n",
      "OrigPlse: 1------0------1------1------0------1------\n",
      "OrigStep: A------B------C------D------E------F------\n",
      "TrgtPlse: A-----B-----C-----D-----E-----F-----0-----\n",
      "FixdPlse: A-----0-----B-----C-----D-----E-----F-----\n"
     ]
    }
   ],
   "source": [
    "formatted = format_rhythms([1,0,1,1,0,1], [1,0,1,1,0,1,0], format_method=\"alphabet\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lcm=24, original_scale_factor=3, target_scale_factor=2\n",
      "eq_original_step_indices: [0, 4, 8, 12, 16, 20]\n",
      "eq_target_pulse_indices:  [0, 3, 6, 12, 15, 18]\n",
      "eq_target_step_indices:   [0, 3, 6, 9, 12, 15, 18, 21]\n",
      "0: osi@0=0, tpi@0=0\n",
      "set to 1\n",
      "3: osi@0=0, tpi@1=3\n",
      "set to 0\n",
      "6: osi@1=4, tpi@1=3\n",
      "set to 1\n",
      "9: osi@2=8, tpi@2=6\n",
      "set to 1\n",
      "12: osi@3=12, tpi@3=12\n",
      "set to 1\n",
      "15: osi@3=12, tpi@4=15\n",
      "set to 0\n",
      "18: osi@4=16, tpi@4=15\n",
      "set to 1\n",
      "21: osi@5=20, tpi@5=18\n",
      "set to 1\n",
      "\n",
      "OrigPlse: 1---0---1---1---0---1---\n",
      "OrigStep: A---B---C---D---E---F---\n",
      "TrgtPlse: A--B--C--0--D--E--F--0--\n",
      "FixdPlse: A--0--B--C--D--0--E--F--\n"
     ]
    }
   ],
   "source": [
    "formatted = format_rhythms([1,0,1,1,0,1], [0], format_method=\"alphabet\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the previous two examples, the fixed rhythm doesn't resemble the target rhythm at all...\n",
    "I guess this is because we're using the Bjorklund fix too, to fix pulses.\n",
    "\n",
    "But if we're going through all this trouble to \"fix\" rhythms, maybe we should just place constraints on what the user can do in the first place?\n",
    "\n",
    "I guess we should have both.\n",
    "\n",
    "e.g. if they put a pulse before the original position, it'll show up red or something.\n",
    "\n",
    "Hmm, but in *that* case, maybe we *do* want to preserve the original rhythm. And just have it repeat or something?\n",
    "In other words, use method **7**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Trying out method 7"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# TODO: Try with method 7 vs. method 10"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Misc noodles"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "np.indices?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'2-3-4'"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"0\"*4\n",
    "\"-\".join([str(x) for x in [2,3,4]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a\n",
      "b\n"
     ]
    }
   ],
   "source": [
    "print(\"a\"); print(\"b\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}

{'fraction_non_alphanumeric': 0.30344278078417053, 'fraction_numerical': 0.03005654969401193, 'mean_word_length': 1.5587993770352542, 'pattern_counts': {'":': 752, '<': 7, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7542966', 'n_tokens_mistral': 34767, 'n_tokens_neox': 32704, 'n_words': 6437}