Jump to content

A little scratch notation program I made (Nerdism inside)


mfsop

Recommended Posts

HOW IT WORKS:

First, you create a text file. In this example, it's a little 2 Click Flare / Tear Combo

Name
Flare Tear Combo

Number of beats = number of dashes in the graph
1

Record Motion - One line per beat
0/3 F B

Clicks
- C C - - -

Tears
- - - - T T

It works a bit like a step sequencer: The number of beats is a sub division of the complete scratch combo. For each beat you will need a separate line for the record motion, the click and the tear positions. The number of commands (F,-,C...) of each line determines the rhythm: 4 commands mean sixteenth notes resolution, 3 triplets and so on.

  • Record Motion: This line means just forward, then backward scratch. There are only 2 entries in this line (F and B), so it will be interpreted as a binary rhythm. You can use F, B, - for a pause, FS/BS for Stabs, FD/BD for Drags, U for Uzi, FL/BL for Lasers and FW/BW for Waves. A Stab will be plotted as a forward that goes at double speed, a drag at half speed. The first entry 0/3 is the starting position - you need to do a bit of math here to set the starting position of your record: 1/3 would mean that you start at one third in. You could also put in 3/3 B F - this would give you a reverse baby.
  • Clicks: In the example there are six entries - the first one is a pause (no click), then there are two clicks, then three pauses
  • Tears: 4 pauses, 2 tears

The program will read this file and return a graph like this:

 

Here's another example with stabs and drags to mark accents. It's an accented double-tear/quad with accents on every third sound, so you get a dotted quarter note polyrhythm with the record hand. The crossfader flares each sound of the tears once so it sounds a bit like a 3 click-orbit with accents.

I will add more information if anyone should feel like using it. You can do all kinds of things with it like clicks in triplets and record movements in sevens but that may cause the graphs to explode. Also I wrote this thing just yesterday so there may be some bugs in it.

 

This is about all you can do with it right now: Waves, Lasers, Uzis, Stabs, Drags, Tears and Clicks with triplets in the first beat and binary rhythm in the other two beats.

 

HOW TO RUN IT:

- Download this file and extract it: http://www.file-upload.net/download-7344986/scratchnotator.rar.html

- Put textfiles of your scratches into the folder "in" - make sure nothing else is in it. The example scratches are inside, too so that you have a starting point.

- Run scratch notator.exe and grab your pictures in the out-folder

- Post your favorite scratches or explain combos with it! :)

 

If you already have Python you can also just use the code below:

 

 

 

def notator():
    files_in=os.listdir(fol_in)
    for file_in in files_in:
        print file_in
        f_in=open(os.path.join(fol_in,file_in))
        f_in.readline()                 #'Name'
        title=str(f_in.readline()[:-1])
        f_in.readline()

        f_in.readline()                 #'Number of beats -> number of dashes'
        n_beats=int(f_in.readline())
        f_in.readline()

        f_in.readline()                 #'Record movement'

        #record movement
        x,y,x_i_list,y_i_list=[],[],[],[]
        y_last=np.zeros(n_beats)
        for i_beat in range(n_beats):
            line=f_in.readline()
            line=line.split()

            #number of beats in this beat - 3 for 8th-triplets, 4 for 16th notes
            n_sub=len(line[1:])

            #starting position of record
            line_0=line[0].split(r'/')
            x_i,y_i=np.zeros(n_sub),np.zeros(n_sub)           #record position
            x_i[0]=i_beat+1
            y_i[:]=float(line_0[0])/float(line_0[1])
            y_last[i_beat]=y_i[0]

            for i_sub in range(0,n_sub):
                #step points from movement
                move_type=line[i_sub+1] # forward, backward, pause?
                if (move_type=='F' or move_type=='FL' or move_type=='FW' or move_type=='FS' or move_type=='FD'):    #forward
                    m=1.0
                if (move_type=='-' or move_type=='U'): #pause or uzi
                    m=0.0
                if (move_type=='B' or move_type=='BL' or move_type=='BW' or move_type=='BS' or move_type=='BD'):    #backward
                    m=-1.0
                if (move_type[:2]=='TO'): #forward or backward to specific position
                    y_new_frac=move_type[2:].split(r'/')
                    y_new=float(y_new_frac[0])/float(y_new_frac[1])
                    m=(y_new-y_i[i_sub])/(1.0/n_sub)
                if (move_type[:3]=='WTO' or move_type[:3]=='LTO'): #laser or wave to specific position
                    y_new_frac=move_type[3:].split(r'/')
                    y_new=float(y_new_frac[0])/float(y_new_frac[1])
                    m=(y_new-y_i[i_sub])/(1.0/n_sub)

                x_cur=np.linspace(0,1.0,100)
                y_cur=np.zeros(100)

                #movement type between step points
                if move_type=='F' or move_type=='B' or move_type[:2]=='TO':  #forward/backward
                    y_cur=x_cur*m
                if move_type=='FS' or move_type=='BS':  #forward/backward stab
                    y_cur=x_cur*2*m
                if move_type=='FD' or move_type=='BD':  #forward/backward
                    y_cur=x_cur*.5*m
                if move_type=='FL' or move_type=='BL' or move_type[:3]=='LTO':  #laser
                    y_cur=m*(1-(x_cur-1)**4)
                if move_type=='FW' or move_type=='BW' or move_type[:3]=='WTO':  #wave
                    y_cur=x_cur*m+0.05*np.sin(1000*np.linspace(0,2*np.pi,100))
                if move_type=='U':
                    y_cur=x_cur*m+0.05*np.sin(1000*np.linspace(0,2*np.pi,100))
                if move_type=='-':
                    y_cur=y_cur+np.nan

                x_cur=x_cur*1.0/n_sub+1+i_beat+float(i_sub)/n_sub
                y_cur=y_cur*1.0/n_sub+y_i[i_sub]

                #complete list for later plotting
                for item in x_cur:
                    x.append(item)
                for item in y_cur:
                    y.append(item)
                    if np.isnan(item)==0:   #valid continue point for record position
                        y_last[i_beat]=item
                x_i[i_sub]=x_cur[0]
                if i_sub<n_sub-1:
                    y_i[i_sub+1:]=y_last[i_beat]
            x_i_list.append(np.copy(x_i))
            y_i_list.append(np.copy(y_i))
        x,y=np.array(x),np.array(y)
        x_i_list=np.array(x_i_list)
        f_in.readline()
        f_in.readline()

        #read clicks
        x_click,y_click=[],[]
        for i_beat in range(n_beats):
            line=f_in.readline()
            line=line.split()
            n_sub=len(line)
            for i_sub in range(n_sub):
                x_cur=1+i_beat+float(i_sub)/n_sub   #position of the click / no click in time
                x_click.append(x_cur)
                if line[i_sub]=='-':    #no click
                    y_click.append(np.nan)
                if line[i_sub]=='C':    #click
                    x_dist=abs(x-x_cur)
                    i=np.where(x_dist==x_dist.min())[0][0]
                    if np.isnan(y[i]):
                        rec_subs=len(x_i_list[i_beat])
                        j=int(float(i_sub)/n_sub*rec_subs)
                        y_click.append(y_i_list[i_beat][j])
                    else:
                        y_click.append(y[i])
        x_click,y_click=np.array(x_click),np.array(y_click)
        f_in.readline()
        f_in.readline()

        #read tears
        x_tear,y_tear=[],[]
        for i_beat in range(n_beats):
            line=f_in.readline()
            line=line.split()
            n_sub=len(line)
            for i_sub in range(n_sub):
                x_cur=1+i_beat+float(i_sub)/n_sub   #position of the tear / no tear in time
                x_tear.append(x_cur)
                if line[i_sub]=='-':    #no tear
                    y_tear.append(np.nan)
                if line[i_sub]=='T':    #tear
                    x_dist=abs(x-x_cur)
                    i=np.where(x_dist==x_dist.min())[0][0]
                    if np.isnan(y[i]):
                        rec_subs=len(x_i_list[i_beat])
                        j=int(float(i_sub)/n_sub*rec_subs)
                        y_tear.append(y_i_list[i_beat][j])      #use last position of record
                    else:
                        y_tear.append(y[i])
        x_tear,y_tear=np.array(x_tear),np.array(y_tear)

        #prepare plot
        fig=plt.figure()
        ax = fig.add_subplot(111)

        ax.set_title(title)
        plt.xticks(np.arange(n_beats+2))
        plt.yticks(np.arange(0))
        plt.grid(True)

        plt.plot(x,y,'k-')
        plt.plot(x_click,y_click,'k|',ms=80)
        plt.plot(x_tear,y_tear,'o',ms=20)

        plt.xlim(0.95,(n_beats+1.05))
        y_min=min(y[np.where(np.isnan(y)==0)[:][:]])
        y_max=max(y[np.where(np.isnan(y)==0)[:][:]])
        plt.ylim(y_min-(y_max-y_min)*.05,y_max+(y_max-y_min)*.05)

        plt.savefig(os.path.join(fol_out,file_in[:-4]+r'.png'))
        plt.close()

if __name__ == '__main__':
    print 'Scratch Notator'
    import pylab as plt
    import os
    import numpy as np

#folders
    fol_cur=os.getcwd()
    fol_in=os.path.join(fol_cur,r'in')
    fol_out=os.path.join(fol_cur,r'out')

    notator()
 

 

 

Edited by mfsop
  • Like 2
Link to comment
Share on other sites

Nice. I have been using Matplotlib a bit recently. I guess you could make a little GUI too. I'm still intending to get the Python combo generator off the ground again once I move house, though I'm still a bit of an amateur. Though I must be getting better as the code you posted made quite a bit of sense.

  • Like 1
Link to comment
Share on other sites

Glad to hear you like the idea! I did stumble upon an issue with clicks with pauses in the record hand before the click (the click wasn't shown). I've already fixed it in the code. I'll change the zip file later because I'm sure there will be some more unexpected things..

What I wanted to do is transcribe a variation of Tigerstyle's Butterfly scratch that someone explained on youtube (it was a French DJ, I think - no idea, who it was though):

 

As for a GUI: Well, I know absolutely nothing about making one but as you've already spilt the beans about knowing some Python, you could surely try it yourself. ;) But I'm not really sure if this will make it much easier because adjusting the rhyhtmic resolution and position of the record in a GUI seems like a major brain damager to me, right now.

Link to comment
Share on other sites

Guest broke

I did something like this with a flash GUI to draw ttm and store them in a database, then some php to render the ttms from the database.

 

Www.johnny1move.co.uk/scratch

 

I lost motivation to finish it off when I got a new job though...

 

I am disappoint

Link to comment
Share on other sites

I guess it's done for now. New version: http://www.file-upload.net/download-7344986/scratchnotator.rar.html

I've included a few new commands for the record hand. If you've done anything with the old version, your files should still work the same way - everything old stays the same. But now you can also move the record to a certain position to emphasize the pitch.

 

Also, some scratches like the Autobahn don't add up if you say that the record moves at the same time all the times because the last two sounds have longer record movements in the same time as the first seven ones.

There is a short tutorial with example files in a separate folder if you need it.

Now that should be enough to sketch out pretty much every idea without getting too complicated. Feel free to relate intervals to wave frequency ratios and notate accordingly if you want to ruin scratching for everyone.

Link to comment
Share on other sites

I did something like this with a flash GUI to draw ttm and store them in a database, then some php to render the ttms from the database.

 

Www.johnny1move.co.uk/scratch

 

I lost motivation to finish it off when I got a new job though...

 

 

I've been hitting this up recently using an old link you posted .. its a really great resource IMO, theres a good range of cuts to learn.

Link to comment
Share on other sites

I think there's only 2 things wrong with it...

 

The website is a bit flakey on different platforms which is far more of a ballache to sort out than it should be, and the flash Gui doesn't to tears although the php renderer will if you go into the data base and add them. I think I'd have to relearn action script to sort it now though!

Link to comment
Share on other sites

This is pretty cool!

 

I agree with Deft that it should have some sort of GUI front-end.

 

The GUI would simply create the text file in the correct format and place it in the inbox folder and execute notator.exe.

 

There are plenty of Python GUI Frameworks out there. Also if you don't care about having this a Windows Only application you can use C# .Net to create the GUI.

Link to comment
Share on other sites

  • 4 weeks later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...