Walkthrough of the code

#!/usr/bin/python

import atexit
import signal
import RPi.GPIO as GPIO
import time

# Activate GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(True)

# Variables
steps = 1500 # Steps per sequence - keep this high for perfect speed control, and low for immediate control over speed from the toggler
staging = 100 # How many steps do we need to accelerate the stepper motor?
delay = 0 # Current delay
previous = 0.01 # This just makes sure we're staging the stepper motor
microsteps = 1 # How many micro-steps per step should we take?
current = 0 # Store our current
offset = 0 # Store the offset in time between what we're trying to achieve and what we actually achieved according to the Pi-clock. Paramount for perfect timing!
speed = 0 # What speed are we at?
death = 0 # At what time should we die?
speed_1 = 33.3 # First level speed
speed_2 = 45 # Second level speed
time_1 = 22 # How many minutes are in an LP?
time_2 = 15 # How many minutes are in a Single?
status = False # Record player status

# GPIO pins we're using
enable_a = 21 # Stepper driver enable pins
enable_b = 13
a1 = 20 # Stepper driver pins, for each coil
a2 = 16
b1 = 26
b2 = 19
toggle_speed_1 = 17 # Toggle button pins
toggle_speed_2 = 27

# Set pin states
GPIO.setup(enable_a, GPIO.OUT) # Make stepper pins output pins!
GPIO.setup(enable_b, GPIO.OUT)
GPIO.setup(a1, GPIO.OUT)
GPIO.setup(a2, GPIO.OUT)
GPIO.setup(b1, GPIO.OUT)
GPIO.setup(b2, GPIO.OUT)
GPIO.setup(toggle_speed_1, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Make ground detected, so the pin isn't floating
GPIO.setup(toggle_speed_2, GPIO.IN, pull_up_down=GPIO.PUD_UP)

# Function for step sequence
def adjust(pin, state, delay, step):
    # Get how many steps we're taking per step
    calc = int(microsteps*step)

    # If less than two, one step is just a step. Take that step, and then sleep
    if calc<2:
        GPIO.output(pin, state)
        time.sleep(delay)

    # Otherwise, we'll go ahead and bit-bang a micro-stepping sequence
    else:
        # What's the delay we need per step?
        microdelay = float(delay)/float(calc)

        # Go through each microstep
        for a in range(0, calc):
            # How far into the stepping sequence are we?
            percentage = float(a)/float(calc)

            # Unset the pin
            GPIO.output(pin, not state)

            # Sleep a bit
            time.sleep(float(microdelay)*(1-percentage))

            # Set the pin
            GPIO.output(pin, state)

            # Sleep a bit more
            time.sleep(float(microdelay)*(percentage))


# Handle abortions safely, if this program fails, we don't want the stepper motor to keep drawing current
def abort():
    GPIO.output(enable_a, False)
    GPIO.output(enable_b, False)

atexit.register(abort)
signal.signal(signal.SIGTSTP, abort)

# Loop through step sequence based on number of steps
while True:
    # Store current time
    io_timer = time.clock()
    death_timer = time.time()

    # Check toggle state. If it's on, reset all variables
    if (GPIO.input(toggle_speed_1) and GPIO.input(toggle_speed_2)) or not (GPIO.input(toggle_speed_1) or GPIO.input(toggle_speed_2)):
        status = False
        death = 0
        previous = 0.01
        offset = 0
        speed = 0
        delay = 0

    # Otherwise, pins are on
    else:
        # If we're running at lower speed
        if GPIO.input(toggle_speed_1):
            # We've changed speed since last time!
            if speed != speed_1:
                speed = speed_1

                # Set a death timer, for when the record is finished
                death = (time_1*60)+death_timer

        # Otherwise, we're running at the higher speed
        else:
            # We've changed speed since last time
            if speed != speed_2:
                speed = speed_2

                # Set a death timer, for when the record is finished
                death = (time_2*60)+death_timer

        # If we're still allowed to run, record isn't finished
        if death_timer < death:
            # Define current time, and activate runner
            current = 1/float((30.0/7.0)*200*2/60.0*float(speed))

            # Set the record to running!
            status = True

            # Do we have a sample time?
            if offset:
                # What's the error (i.e. how slow is the Pi compared to our wished benchmark?
                error = offset/float(steps)/4.0

                # If it's obscenely high, we need to set it to a reasonable level. The Pi still needs its sleep
                if current<error:
                    current = 0.00010

                #Otherwise, it's all good! Adjust accordingly
                else:
                    current -= error

        # Otherwise, let's deactivate motor. The record is finished!
        else:
            status = False

    # If we've got green light, run
    if(status):
        # Enable the stepper motor!
        GPIO.output(enable_a, True)
        GPIO.output(enable_b, True)

        # Go through motor in X steps
        for i in range(0, steps):
            # If we're not up to speed, stage the speed
            if delay != current:
                if i<staging:
                    delay = current
                    delay -= ((current-previous)/(staging))*(staging-i)
                else:
                    delay = current

            # Move to step
            adjust(b1, False, delay, 1)
            adjust(b2, True, delay, 1)

            adjust(a1, False, delay, 1)
            adjust(a2, True, delay, 1)

            adjust(b2, False, delay, 1)
            adjust(b1, True, delay, 1)

            adjust(a2, False, delay, 1)
            adjust(a1, True, delay, 1)

        # Store the offset
        offset = time.clock()-io_timer
        previous = current

    # If we've got red light, let's disable steppers, and hope we've got new instructions soon!
    else:
        GPIO.output(enable_a, False)
        GPIO.output(enable_b, False)

        time.sleep(1)