cbcompose.py

download this file here: cbcompose.py

#!/usr/bin/python
# -*- coding: utf-8 -*-

# ---------- cbcompose ------------ #

# 2018-08-26 - 2018-08-28
# writen in vim

# about:
# general idea and some functions based on Tom Whitwell's python script
# that does more or less the same thing
# <https://gist.github.com/TomWhitwell/12d06d39571c3e19fb7457dc17c4915c>
# used it as a reference to write my own version
# mine adds random selections from tools like instruments, software, hardware, samples, etc.
# creates a random block of ascii art
# picks random samples from a list
# picks random words from a list
# and writes everything to a markdown file

# purpose:
# serve as a tool for musical inspiration
# create a set of constraints within in which I must create some new music
# also: get me to use my various instruments in ways I may not have thought of,
# which should help me become even more familiar with each instrument  
# i.e. use the Casio SK-1 as a bassline, which is something I may not have done on my own accord, but I'll do it if the instructions tell me to

# --------------------------------- #

# import things we need
import random
import string
import os
import subprocess
from subprocess import call
import datetime

##########################################################
# CONFIGURATION

# decide how many tracks we should use
upperLimit = 7 # the most tracks we want to use
lowerLimit = 3 # the fewest tracks we want to use

# decide the length range for the name of the track
nameUpperLimit = 15
nameLowerLimit = 5

# decide the BPM range
bpmLowerLimit = 80
bpmUpperLimit = 160

# what is the maximum number of samples we want to load?
maxSamples = 4 # not using this setting right now

# how wide and how tall should the ASCII art be?
width = 10
height = 5

# should we include acoustic/non-electronic instruments?
includeAcousticInstruments = False

# are we on a mac or linux?
# the result of this will be True (on a mac) or False (not on a mac, meaning it's probably linux)  
# this affects where the output file will be saved
mac = os.path.exists("/Users/chris/")

# Samples list 

# for randomly choosing samples, we need a file that has a list
# of full paths to all our samples, or at least the ones we want to use
# to generate this list: 
# cd to the sample directory you want to use
# run this: find . -type f -exec ls {} \; 2> /dev/null > samples_list.txt
# remove lines containing some phrases:
# sed -i '/impulse/d' samples_list.txt
# reove the first 1 character of every line:
# sed 's/^.//'
# I go between mac and linux every day. if the computer where the script is running doesn't have a /Users/chris folder, there's a good chance it's a linux machine  

if mac == True:
    samplesList = "/Users/chris/Nextcloud/Samples/cb_samples_list.txt"
else:
    samplesList = "/home/chris/Nextcloud/Samples/cb_samples_list.txt"

# Word list
# get some words for inspiration
# I got mostly words related to visual art because for me making music is visual
# I got words from these places:
# <https://describingwords.net/describing-words-for-art/>
# <https://www.thoughtco.com/art-words-list-2577414>
# <https://describingwords.net/music/>
# <https://describingwords.net/words-to-describe-sound/> 
# and also added a bunch myself

# how many mood words to get?
wordUpperLimit = 3
wordLowerLimit = 1

if mac == True:
    wordlist = "/Users/chris/Dropbox/Apps/cb_notes/musicmaking/cbcompose.py/wordlist.txt"
else:
    wordlist = "/home/chris/Dropbox/Apps/cb_notes/musicmaking/cbcompose.py/wordlist.txt"

# Sample media
# TODO: generate a list of all the music in my music library
# idea: choose a random track to sample
# more: how do we include vinyl as well?

# Output
# where to save the results? we will create a new file that lives in this directory:
# (see above for figuring out whether we are on mac or linux)
if mac == True:
    outputpath = "/Users/chris/Dropbox/Apps/cb_notes/musicmaking/cbcompose.py/scores/"
else:
    outputpath = "/home/chris/Dropbox/Apps/cb_notes/musicmaking/cbcompose.py/scores/"

##########################################################
##########################################################
######### end of configuration ###########################
##########################################################

# get the current date
# TODO: fix some bugs here, weird hour and minute display for single-digits
i = datetime.datetime.now()
print ("dd/mm/yyyy format =  %s/%s/%s" % (i.day, i.month, i.year) )
print ("%s/%s/%s %s:%s:%s" % (i.year, i.month, i.day, i.hour, i.minute, i.second) )

# Functions

# random name function
# usage: randomname(NUM_OF_CHARACTERS)
def randomname(length = 6):
    vowels = ['a','e','i','o','u','', ' ']
    consonants = ['b','c','d','f','g','h','j','k','l','m','n','p','q','r','s','t','v','w','x','y','z'] 
    a = random.sample(consonants,length/2)
    x = 0
    while x < length:
        a.insert(x,random.choice(vowels))
        x = x+2
    return "".join(a).title()

# rhythmic motif function
# usage: rhythm(length=NUM)
# create an extremely short graphic "score" that is very open to interpretation
# what do the symbols mean? that's up to the person reading them
def rhythm(length = 6):
        rests = [' ', ' ', ' ', '  ', '  ', '   ', ' ', ' ', ' ', ' ', ' ', ' ']
        notes = ['+', '+', '+', '+', '*', '*', '>', '+', '+', '+', '^', 'o', 'O']
        a = random.sample(notes,length/2)
        x = 0
        while x < length:
                a.insert(x,random.choice(rests))
                x = x+2
        return "".join(a).title()

# random line from file function
# read a random line from a given file
def random_line(afile):
        line = next(afile)
        for num, aline in enumerate(afile):
            if random.randrange(num + 2): continue
            line = aline
        return line

# generate some random ASCII art
#characters = ['-','=','%','#',' ','_','^','&','$','@']
characters = [' ','■', '▄', '░', '▒','▓','▓','┘','┌','▀', ' ', ' ', ' ', ' ', '%','@','#','X','x','O','0','+','o','°', '≡','╣','*','®', ' ',' ',' ',' ',' ',' ',' ',' ',' ','!','¡','#']
y = 0
line = ""
asciart = ""
while y < height:
    line = ""
    x = 0
    #print "y: ", y
    while x < width:
        #print "x =", x
        line = line + random.choice(characters)
        #print "line:", line
        x = x + 1
    asciart = asciart + "\n" + line
    y = y + 1

#print asciart
# clear the screen
os.system('clear')
# show us some stuff when we run this script in a terminal
print ""
print "//////////////// CB's Composition Decider Script ////////////////////"
print ""


# generate a random song name
nameLength = random.randint(nameLowerLimit,nameUpperLimit)
songname = randomname(nameLength)
# clean it up - remove leading and trailing spaces
# could probably put this into one line but I'm not sure how
songname = songname.lstrip()
songname = songname.rstrip()

# about writing to files with python, FYI to myself:
# <http://docs.hyperion-rt.org/en/stable/tutorials/python_writing.html> 
# Line returns have to be explicitly included using \n, i.e.:
# f.write("Line 1\n")
# And files should be closed with:
# f.close()
# writing variables to files:
# The best way to write out variables with this technique is to use string formatting
# more info: <http://docs.python.org/library/stdtypes.html#string-formatting>  
# format % variables
# write stuff to a markdown file, because I <3 markdown
outputfile = outputpath + songname + ".md"
print "will save to: ", outputfile 
f = open(outputfile,"w+")

numberOfTracks = random.randint(lowerLimit,upperLimit)

#### OPTIONS
## here we set up our pool of choices, including moods, instruments, modules, sound functions, everything

# moods
# used this at first, but then started using a much longer wordlist.txt file
mood = [ 'happy', 'angry', 'brooding', 'silly', 'quiet']

# instruments: hardware musical instruments
# FULL
#if includeAcousticInstruments == True:
# TODO: add a if then else here to have modes with and without acoustic instruments 
# for making music late at night while people are sleeping
physicalInstrument = [ 'Fender Rhodes', 'Korg Volca Keys', 'Yamaha VL-70m', 'modular synth', 'Casio SK-1', 'Nord Electro 2', 'Casiotone MT-520', 'Korg M1', 'modular synth', 'modular synth', 'modular synth', 'modular synth', 'modular synth', 'modular synth', 'radio', 'recorder/sampling']
# ABBREVIATED
#physicalInstrument = [ 'Korg Volca Keys', 'modular synth', 'Casio sampling keyboard', 'Korg M1', 'modular synth', 'modular synth'] 

# some of the modules on my modular
module = [ 'PT2399 delay', 'Growl amp', 'stepped triangle', 'shift register', 'VCO', '10 step sequencer', 'mixer', 'multiplexer', 'arduino VCO', 'fidget spinner', 'cmos noise', 'low pass gate', '4066 VCA', 'sequential switch', 'slope generator', 'space noise', 'sample & hold', 'low pass filter w resonance', 'XOR gate', 'comparator', 'control voltage processor', 'spring thingy', 'r/2r ladder', 'jkLOL', 'schmidt trigger', 'RCA input', 'square to 8-bit saw converter', 'envelope follower', 'gate junction', 'external input']

# types of modules in VCV Rack
rackModule = [ 'state variable filter', 'distortion', 'VCO', 'filter', 'shift register']

# sound types: the function of a track
soundtype = [ 'rhythmic', 'pad/harmonic', 'bassline', 'melodic', 'ambience']

# places, rooms of the house: places you can go sample a sound
place = [ 'bathroom', 'bedroom', 'basement', 'closet', 'kitchen', 'back yard', 'front yard', 'stairwell', 'deck', 'driveway']

# sample from media
# TODO: get a list of music in my library and choose a random track to sample

# styles
# DEPRECATED
# at first I thought using styles would be a good way to get inspired, but I find it more restricing than anything. I'd rather come to my own conclusions about stylistic choices based on other, more ambiguous prompts (like the ascii art, words, song title, etc.)  
style = [ 'techno', 'hip-hop', 'dub', 'house', 'funk', 'r&b', 'musique concrete', 'ambient', 'drum and bass', 'trap', 'jazz', 'cumbia', 'electro', 'minimalist', 'disco', 'glitch', 'noise']

# software to use
# FULL
#software = [ 'Patterning (iPad)', 'Tweaky Beats (iOS)', 'Pure Data', 'VCV Rack', 'Bitwig Studio', 'Tidal Cycles']\
# ABBREVIATED
software = [ 'Tweaky Beats (iOS)', 'VCV Rack', 'Bitwig Studio', 'Patterning (iPad)' ]

# hardware effects to use 
# the more "none" items there are, the less frequently a track will use a hardware effect
hardwareEffect = [ 'Spring reverb', 'Plate reverb', 'Alesis Microverb', 'Vintage Delay pedal', 'none', 'none', 'none', 'none', 'none']

# software effects to use
softwareEffect = [ 'distortion', 'delay', 'flanger', 'pitch', 'none', 'none', 'none', 'none']

# bitwig instruments to use
bitwigInstrument = [ 'polysynth', 'phase-4', 'FM4', 'sampler']

# nord electro instruments to use
nordInstrument = [ 'wurly', 'rhodes', 'clav', 'organ']

# time signatures
beatsPerBar = [1, 2, 3, 4, 4, 4, 5, 6, 7, 8, 9, 'no pulse', 'no pulse']

# radio channels
# from <https://www.radiolineup.com/locate/Grand-Rapids-MI>  
# these are radio stations that I might be able to pick up in my basement
radioFq = [ '88.1 FM', '88.3 FM', '88.9 FM', '91.9 FM', '92.5 FM', '92.5 FM', '95.3 FM', '96.5 FM', '96.9 FM', '100.9 FM', '107.9 FM', '590 AM', '810 AM', '850 AM', '1140 AM', '1410 AM', '1580 AM', '1660 AM']

# types of tracks
# instrument: hardware or physical instrument
# software: a software program that makes noise
# sampler: a bitwig sampler instrument
# multiple samples: a bitwig drum machine instrument loaded with multiple samples
trackType = [ 'physical instrument', 'software', 'sampler'] # other option: 'multiple samples'

mood = random.choice(mood) # deprecated

# start writing things to our file
f.write("# %s \n\n" % songname)
f.write("_generated with cbcompose.py  ")
f.write ("%s/%s/%s %s:%s:%s_\n\n" % (i.year, i.month, i.day, i.hour, i.minute, i.second) )
f.write("## Song details\n\n")

#print "mood: ", mood
#print "style influences: ", random.choice(style), ",", random.choice(style)
f.write("BPM: %s  \n" % random.randint(bpmLowerLimit, bpmUpperLimit))
f.write("beats per bar: %s  \n" % random.choice(beatsPerBar))
f.write("number of tracks: %s  \n" % numberOfTracks)
funrhythm = rhythm(6)
f.write("rhythm: %s  \n\n" % funrhythm)

# get some words
f.write("words: ")
for count in range(wordUpperLimit):
    f.write("%s " % random.choice(open(wordlist).readlines()).rstrip())
    count = count + 1
f.write("\n\n")

f.write("## Some ASCII art\n\n")
f.write("")
f.write(asciart)
f.write("\n")
f.write("")
f.write("\n\n")

## here we decide what sounds will go on which tracks
f.write("## Tracks\n\n")

sampleTracks = 0
multiSampleTracks = 0

for trackNumber in range(numberOfTracks):
    count = 0
    trackNumber = trackNumber + 1
    f.write("### track %s\n\n" % trackNumber)
    # pick a type of track (from the trackType array above)
    trackKind = random.choice(trackType)

    if trackKind == 'physical instrument':
        # if the trackKind is instrument, choose an instrument
        instrument = random.choice(physicalInstrument)
        f.write("instrument: %s  \n" % instrument)

        if instrument == "modular synth":
            # if we are using the modular synth, pick a module we must use
            useModules = random.choice(module), "and", random.choice(module)
            f.write("use at least these modules: %s and %s  \n" % (random.choice(module),random.choice(module)))

        elif instrument == "Nord Electro 2":
            f.write("use this sound: %s \n" % random.choice(nordInstrument))

        elif instrument == "radio":
            f.write("use this radio frequency: %s  \n" % random.choice(radioFq))

        elif instrument == "recorder/sampling":
            f.write("go to this place and sample something: %s  \n" % random.choice(place))

    elif trackKind == 'software':
        softwareChoice = random.choice(software)
        f.write("software: %s  \n" % softwareChoice)

        if softwareChoice == "Bitwig Studio":
            # if the software is bitwig, choose a bitwig instrument
            f.write("bitwig instrument: %s  \n" % random.choice(bitwigInstrument))

    elif trackKind == 'sampler':
        # if the trackKind is a sampler instrument, choose a sample
        # the rstrip() removes newlines
        f.write("sample: %s\n" % random.choice(open(samplesList).readlines()).rstrip())
        sampleTracks = sampleTracks + 1

    elif trackKind == 'multiple samples':
        # if the trackKind is multiple samples, choose some samples for us to use
        # the rstrip() removes newlines
        multiSampleTracks = multiSampleTracks + 1
        for count in range(maxSamples):
            f.write("sample: %s  \n" % random.choice(open(samplesList).readlines()).rstrip())
            count = count + 1

    # you can comment lines here that you don't want
    f.write("sound function: %s  \n" % random.choice(soundtype))
    #print "hardware effect: ", random.choice(hardwareEffect)
    #print "software effect: ", random.choice(softwareEffect)

    f.write("\n")

f.close

print ""
print "////////////////// results /////////////////////////////"
print ""

# ok, done!
# since we're probably running this in a terminal,
# show us what we got (read the contents of the file we just generated)
# this is nicer than having to open it up and look ourselves
with open(outputfile) as f:
    print f.read()

 

One response

Comments are closed.