GenJam-HubNet
Do you have questions or comments about this model? Ask them here! (You'll first need to log in.)
WHAT IS IT?
This model aims to demonstrate the concept of a genetic algorithm in the context of rhythm production. Unlike the other GenJam models, this model doesn't rely on fitness functions inspired by ethnomusicology research. instead, we still have low, medium, and high-drums, but each user gets to decide the fitness of their pattern in order to work in concert with evolution to create jamming beats. This is a 'duple' version, where there are 4 'beats' per chromosome.
HOW IT WORKS
There are 16 drums, 5 high drummers (INSTRUMENT), 5 medium drummers (INSTRUMENT), and 6 low drummers (INSTRUMENT). Note that we use "low", "medium", and "high" to describe the pitch and tembre of the drum. Each drummer has a set of "rhythm chromosomes" which dictate what pattern it plays.
This model works exactly like GenJam-Duple (Triple and Mixed) except that fitness is determined by each of the Hubnet Clients!
HOW TO USE IT
The whole idea of this model is to experiement with the parameters in order to build a "great" rhythm. What makes a great rhythm is entirely up to you.
There are two interface elements that must be set before pressing the SETUP button:
- NUM-CHROMOSOMES This specifies the number of rhythmic chromosomes each player has
- SHUFFLE-PARTS? This is simply a GUI change that shuffles around the player's lines on the view (just to make things look cooler)
GO-ONCE is used to ask all the turtles to play their pattern exactly once and then evolve GO-ONCE-NO-EVOLVE can be used to play any particular pattern exactly once with no evolution
SOUND? is used to toggle sound output
The following are options that the learner can tweak to modify the evolution process:
TEMPO-BPM changes how fast each pattern is played (measured in beats (or chromosomes) per minute) HIT-LIMIT defines how long a turtle can go without being forced to evolve in someway (Note: again, this is a deviation from the traditional genetic algorithm but can be used to escape 'stale' rhythms) HIT-DENSITY-MODIFIER is a modifier to specify how "dense" a pattern should be (hits vs rests) NUM-MUTATIONS is how many mutations are applied to an off-springs chromosomes MUTATION-STRENGTH dictates how much a particular chromosome can mutate SOLOER is a chooser that allows you to single out a player SOLO? dictates whether or not to allow SOLOER to solo
The following are outputs of the model:
GENERATIONS counts how many generations of players we've seen DENSITY represents the current ratio of hits to rests
The following are plots of the model:
AVERAGE FITNESS plots the average fitness of each type of turtle and the average overall fitness over time HITS SINCE EVOLUTION is a histogram showing how long it has been since each player has 'evolved' HITS PER DRUMMER is a histogram showing how many hits each player has done
THINGS TO NOTICE
When you first start the model, everyone starts with the same chromosomes. So if you disable mutation by making MUTATION-STRENGTH 0, your model won't evolve! That's because the same chromosomes are just being combined together for each player.
Notice that sometimes, a single turtle sticks around for a really long time (because it's super fit!)
Checkout the Hits per Drummer graph. Do you see any general trends over the course of running the model? Why might that trend be taking place?
Notice that the SOLO function doesn't just make one player louder, it makes it so that player doesn't evolve or mutate. How does that affect the model?
THINGS TO TRY
Use the SOLO function to force a rhythm to evolve around a single stagnant player.
Try and make a rhythm where all three types of players are "equally" fit!
Try to evolve a rhythm where the high drums are very sparse.
See how creating a "complex" meter changes the way you hear the beat. An easy way to do this is to set the NUMBER-CHROMOSOMES to 5 and see if you can hear the musical phrase.
EXTENDING THE MODEL
HubNet Models are pretty complicated, but see if you can make it so n clients can connect!
NETLOGO FEATURES
Notice that this model purposefully slows down the NetLogo world. That's because music doesn't happen as fast as possible! Unfortunately, this doesn't work for the first tick, so the space between the "first beat" and "second beat" is not equal to the space betwen the rest of the beats.
In addition, we do some HubNet magic to only show each person's pattern on their HubNet Client view.
RELATED MODELS
To play with other rhythmic feels, check out GenJam-Duple, GenJam-Triple and GenJam-Mixed. Also, check out the Sound Machines and Drum Machine models!
CREDITS AND REFERENCES
Author: Connor Bain Written for Multi-agent Modeling 2016.
Comments and Questions
extensions [ sound ] globals [ chromosomes ;; list of all possible chromosomes random-whos ;; keeps a shuffled list for pretty-ness generations ;; number of generations we've seen available-drummers ;; list of currently-available drummers fake-who ;; global variable to fix hubnet bug ] turtles-own [ my-who ;; variable to fix hubnet my-fitness ;; fitness of a drummer my-chromosomes my-pattern ;; lists to hold the chromosomes and hit-pattern of a drummer my-velocity my-instrument ;; the (int) velocity value and (string) MIDI instrument for that turtle mutation-rate ;; variables to control "reproduction" hits ;; counts the number of drum hits for a turtle hits-since-evolve ;; the number of hits since a mutation or evolution ] breed [ students student ] ;; for hubnet students-own [ my-drummer-who ;; the turtle player that is associated with this student user-id ;; students choose a user name so we keep that here ] breed [ low-drums low-drummer ] breed [ med-drums med-drummer ] breed [ high-drums high-drummer ] ;; Hubnet Code to startup hubnet-reset end ;; when a new user logs in create a student turtle ;; this turtle will store any state on the client ;; values of sliders, etc. to create-new-student create-students 1 [ ;; store the message-source in user-id now ;; so when you get messages from this client ;; later you will know which turtle it affects set user-id hubnet-message-source set label user-id ;; initialize turtle variables to the default ;; value of the corresponding widget in the client interface set my-drummer-who one-of available-drummers set available-drummers remove my-drummer-who available-drummers ;; SET DEFAULT INSTRUMENT if is-low-drummer? one-of turtles with [(not is-student? self) and who = [my-drummer-who] of myself] [ ask one-of turtles with [(not is-student? self) and who = [my-drummer-who] of myself] [ set my-instrument "BASS DRUM 1" ] ] if is-med-drummer? one-of turtles with [(not is-student? self) and who = [my-drummer-who] of myself] [ ask one-of turtles with [(not is-student? self) and who = [my-drummer-who] of myself] [ set my-instrument "LOW CONGA" ] ] if is-high-drummer? one-of turtles with [(not is-student? self) and who = [my-drummer-who] of myself] [ ask one-of turtles with [(not is-student? self) and who = [my-drummer-who] of myself] [ set my-instrument "CLOSED HI HAT" ] ] ;; update the clients with any information you have set send-info-to-clients ] end to listen-clients ;; KICK ANYONE THAT IS OVER THE 16 ;; as long as there are more messages from the clients ;; keep processing them. while [ hubnet-message-waiting? ] [ ;; get the first message in the queue hubnet-fetch-message ifelse hubnet-enter-message? and not empty? available-drummers ;; when clients enter we get a special message [ create-new-student ] [ ifelse hubnet-exit-message? ;; when clients exit we get a special message [ remove-student ] ;; need to add this drummer back to the available list [ ask students with [user-id = hubnet-message-source] [ execute-command hubnet-message-tag ] ;; otherwise the message means that the user has ] ;; done something in the interface hubnet-message-tag ;; is the name of the widget that was changed ] ] end ;; Other messages correspond to users manipulating the ;; client interface, handle these individually. to execute-command [command] ;; turtle procedure ;; you should have one if statement for each widget that ;; can affect the outcome of the model, buttons, sliders, switches ;; choosers and the view, if the user clicks on the view you will receive ;; a message with the tag "View" and the hubnet-message will be a ;; two item list of the coordinates if command = "my-instrument" [ ;; note that the hubnet-message will vary depending on ;; the type of widget that corresponds to the tag ;; for example if the widget is a slider the message ;; will be a number, of the widget is switch the message ;; will be a boolean value ask one-of turtles with [(not is-student? self) and who = [my-drummer-who] of myself] [set my-instrument hubnet-message] stop ] if command = "my-fitness" [ ask one-of turtles with [(not is-student? self) and who = [my-drummer-who] of myself] [ set my-fitness hubnet-message stop ] ] end ;; whenever something in world changes that should be displayed in ;; a monitor on the client send the information back to the client to send-info-to-clients ;; turtle procedure hubnet-send user-id "hits-since-evolved" [hits-since-evolve] of one-of turtles with [(not is-student? self) and who = [my-drummer-who] of myself] hubnet-send user-id "my-instrument" [my-instrument] of one-of turtles with [(not is-student? self) and who = [my-drummer-who] of myself] let temp "" if is-low-drummer? one-of turtles with [(not is-student? self) and who = [my-drummer-who] of myself] [ set temp "low" ] if is-med-drummer? one-of turtles with [(not is-student? self) and who = [my-drummer-who] of myself] [ set temp "med" ] if is-high-drummer? one-of turtles with [(not is-student? self) and who = [my-drummer-who] of myself] [ set temp "high" ] hubnet-send user-id "my-part" temp end ;; when a user logs out make sure to clean up the turtle ;; that was associated with that user (so you don't try to ;; send messages to it after it is gone) also if any other ;; turtles of variables reference this turtle make sure to clean ;; up those references too. to remove-student ask students with [user-id = hubnet-message-source] [ set available-drummers fput my-drummer-who available-drummers die ] end to setup hubnet-kick-all-clients ca cp cd clear-output ;; Make the view big enough to show 16 lines and however many 'beats' resize-world 0 ((num-chromosomes * 4) - 1) 0 15 set-globals set-initial-turtle-variables reset-ticks update-view end ;; Method to play a pattern without any evolution to go-no-evolve listen-clients ask turtles with [not is-student? self] [ play ;; includes end of life ] update-view ask students [send-info-to-clients] tick wait 60 / TEMPO-BPM / 4 end ;; Method to play a pattern with evolution to go listen-clients ;; If we've reached the end of a pattern, do some evolution! if (ticks mod ((num-chromosomes * 4)) = 0) and (ticks != 0) [ set generations generations + 1 go-evolve ] foreach available-drummers [ ask one-of turtles with [my-who = ?] [ set my-fitness random 100 ] ] ask turtles with [not is-student? self] [ play ] update-view ask students [send-info-to-clients] tick wait 60 / TEMPO-BPM / 4 ;; This roughly sets temp end ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; PLAY FUNCTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; to play-my-drum ;; turtle proceudre let temp my-velocity if sound? [ if solo? [ ;; If you're a soloer, play out! Otherwise, SHHHH. ifelse my-who = soloer [ set temp my-velocity + 50 ] [ set temp my-velocity - 50 ] ] sound:play-drum my-instrument temp ] end to play ;; turtle procedure if is-low-drummer? self [ if item (ticks mod (num-chromosomes * 4)) my-pattern = 1 [ play-my-drum set hits hits + 1 set hits-since-evolve hits-since-evolve + 1 ] ] if is-med-drummer? self [ if item (ticks mod (num-chromosomes * 4)) my-pattern = 1 [ play-my-drum set hits hits + 1 set hits-since-evolve hits-since-evolve + 1 ] ] if is-high-drummer? self [ if item (ticks mod (num-chromosomes * 4)) my-pattern = 1 [ play-my-drum set hits hits + 1 set hits-since-evolve hits-since-evolve + 1 ] ] end ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; END PLAY FUNCTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; EVOLUTION FUNCTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; to go-evolve ;; If there isn't a soloist, ask 2 of each type to evolve ifelse not solo? [ ask n-of 2 low-drums [ evolve ] ask n-of 2 med-drums [ evolve ] ask n-of 2 high-drums [ evolve ] ;; If a drummer hasn't changed in a while, mutate ask turtles with [(not is-student? self) and hits-since-evolve > hit-limit] [ mutate set hits-since-evolve 0 ] ] [ ;; If there is a soloist, do the same, but don't include the soloer ask n-of 2 low-drums with [my-who != soloer] [ evolve ] ask n-of 2 med-drums with [my-who != soloer] [ evolve ] ask n-of 2 high-drums with [my-who != soloer] [ evolve ] ;; If a drummer hasn't changed in a while, mutate ask turtles with [(not is-student? self) and hits-since-evolve > hit-limit and my-who != soloer] [ mutate set hits-since-evolve 0 ] ] end to evolve ;; turtle procedure let mate nobody let list-of-fitnesses [] let search-fitness 0 if is-low-drummer? self [ set list-of-fitnesses [fitness] of other breed set search-fitness select-random-weighted-fitness list-of-fitnesses set mate one-of other breed with [fitness = search-fitness] ] if is-med-drummer? self [ set list-of-fitnesses [fitness] of turtles with [(not is-student? self) and breed != [breed] of myself] set search-fitness select-random-weighted-fitness list-of-fitnesses set mate one-of turtles with [(breed != [breed] of myself) and (fitness = search-fitness)] ] if is-high-drummer? self [ set list-of-fitnesses [fitness] of other breed set search-fitness select-random-weighted-fitness list-of-fitnesses set mate one-of other breed with [fitness = search-fitness] ] let offspring-chromosomes reproduce-with mate ask min-one-of other breed with [(not is-student? self) and my-who != soloer] [fitness] [ set my-chromosomes offspring-chromosomes update-pattern set hits-since-evolve 0 ] end ;; This is where the basic genetic algorithm comes in to-report reproduce-with [mate] ;; turtle procedure ;; ASKER IS 1st Parent MATE is 2nd parent ;;; my-chromosomes let her-chromosomes [my-chromosomes] of mate ;; Pick a random cross-over point let crossover-point random length my-chromosomes ;; Combine the chromosomes let baby-chromosomes sentence (sublist my-chromosomes 0 crossover-point) (sublist her-chromosomes crossover-point length her-chromosomes) ;; Do a little mutation let mutation-chance 0 if is-low-drummer? self [ set mutation-chance 50 ] if is-med-drummer? self [ set mutation-chance 25 ] if is-high-drummer? self [ set mutation-chance 10 ] ;; Maybe actually mutate if random 100 > mutation-chance [ set baby-chromosomes mutate-chromosomes baby-chromosomes ] report baby-chromosomes end ;; FITNESS FUNCTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Dependent on breed, because you can lose fitness or gain fitness by fitting to your particular proclivities to-report fitness ;; turtle procedure ;; Arbirtrary 10% window around target density ;; use add 1 smoothing report my-fitness + 1 end to mutate ;; turtle procedure set my-chromosomes mutate-chromosomes my-chromosomes update-pattern end ;; Method to mutate a chromosome to-report mutate-chromosomes [the-chromosomes] ;; basically picks a chromosome, mutates it, returns a new set let new-chromosomes the-chromosomes repeat num-mutations [ let temp random num-chromosomes set new-chromosomes replace-item temp new-chromosomes ((round (random-normal (item temp new-chromosomes) mutation-strength)) mod 16) ] report new-chromosomes end ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; END EVOLUTION FUNCTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; HELPER FUNCTIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Called after a chromosome update in order to redefine that turtle's pattern to update-pattern ;; turtle method set my-pattern [] foreach my-chromosomes [ set my-pattern sentence my-pattern (get-chromosome ?) ] end to set-globals set fake-who 0 set available-drummers n-values 16 [?] set generations 0 set random-whos n-values 16 [?] ;; this is just for looks. if shuffle-parts? [ set random-whos shuffle random-whos set random-whos shuffle random-whos ] set chromosomes [] ;; CHROMOSOME LIBRARY let c0 [0 0 0 0] let c1 [1 0 0 0] let c2 [0 1 0 0] let c3 [0 0 1 0] let c4 [0 0 0 1] let c5 [1 1 0 0] let c6 [1 0 1 0] let c7 [1 0 0 1] let c8 [0 1 1 0] let c9 [0 1 0 1] let c10 [0 0 1 1] let c11 [1 1 1 0] let c12 [1 0 1 1] let c13 [1 1 0 1] let c14 [0 1 1 1] let c15 [1 1 1 1] set chromosomes (list c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15) ;; END CHROMOSOME LIBRARY end to set-initial-turtle-variables create-low-drums 6 [ set my-who fake-who set fake-who fake-who + 1 set my-instrument "LOW CONGA" set my-velocity 85 set color red set mutation-rate 64 ] create-med-drums 5 [ set my-who fake-who set fake-who fake-who + 1 set my-instrument "Bass Drum 1" set color green set my-velocity 85 set mutation-rate 32 ] create-high-drums 5 [ set my-who fake-who set fake-who fake-who + 1 set my-instrument "Closed Hi Hat" set color blue set my-velocity 85 set mutation-rate 16 ] ask turtles with [(not is-student? self)] [ set my-pattern [] set my-chromosomes (n-values num-chromosomes [1]) ht update-pattern set hits 0 ] end ;; Method to update the view (simplified music notation) to update-view ask turtles with [not is-student? self] [ let temp 0 let row item my-who random-whos foreach my-pattern [ ifelse ? = 1 [ ifelse solo? and (soloer = my-who) [ ask patch temp row [set pcolor white] ] [ ask patch temp row [set pcolor [color] of myself] ] ] [ ask patch temp row [set pcolor black] ] set temp temp + 1 ] ] ask patches with [pxcor = (ticks mod (num-chromosomes * 4))] [set pcolor yellow] ask students [ let row item my-drummer-who random-whos hubnet-send-override user-id patches with [pycor != row] "pcolor" [ black ] ] end ;; Method to get a chromosomes pattern from the library to-report get-chromosome [index] report item index chromosomes end ;; This is my version picking a weighted random turtle to-report select-random-weighted-fitness [theList] let weighted-list [] foreach theList [ ;; add one smoothing let temp ? foreach n-values round ((? / sum theList * 100) + 1) [?] [ set weighted-list fput temp weighted-list ] ] report item (random length weighted-list) weighted-list end ;;; OLD CODE ;breed [ low-drums low-drummer ] ;;; Atsimevu ;;; 64 Low Conga ;;; 63 Open Hi Conga ;;; 52 Mute Hi Conga ;;; open-hand open-fingers slap-fingers slap-two-fingers ;; Options for hit ; ;;; Sogo or Kidi ;;; MIDI INSTRUMENT 117. Taiko Drum ;breed [ med-drums med-drummer ] ; ;;; Kagan ;;; 65. Hi Timbale ;breed [ high-drums high-drummer ]
There is only one version of this model, created about 9 years ago by Connor Bain.
Attached files
File | Type | Description | Last updated | |
---|---|---|---|---|
GenJam-HubNet.png | preview | Preview for 'GenJam-HubNet' | about 9 years ago, by Connor Bain | Download |
This model does not have any ancestors.
This model does not have any descendants.