Algorithmic Music

Basic and Machine Language

Moderator: Moderators

Post Reply
edinky
Vic 20 Newbie
Posts: 18
Joined: Sat Apr 07, 2018 10:34 am

Algorithmic Music

Post by edinky »

Hey has anyone ever tried anything like this on the vic?

I would love to be able to make some algorithmic music in order to have music without taking up too much code, but not sure how to translate this stuff into vic asm:

https://www.youtube.com/watch?v=qlrs2Vorw2Y
groepaz
Vic 20 Scientist
Posts: 1187
Joined: Wed Aug 25, 2010 5:30 pm

Re: Algorithmic Music

Post by groepaz »

The biggest problem with those examples is that they generate samples... To achieve a reasonable samplerate (of like 8khz or so) you must generate one sample every 2 scanlines or so, roughly 100 cycles to do whatever you need to do - that will not work with the formulas used there.

You could experiment with something similar though that uses the regular VIC sound however - there was a similar thing for the C64 long ago: https://csdb.dk/release/?id=26158 ...
I'm just a Software Guy who has no Idea how the Hardware works. Don't listen to me.
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Algorithmic Music

Post by chysn »

One common generative technique in modular synthesis involves the use of a shift register. Tom Whitwell of Music Thing Modular developed a module called Turing Machine that uses a pair of 4015 dual 4-bit shift register chips and a digital to analog converter, and works something like this:

(1) Randomize a 16-bit register
(2) Get the high 8 bits of that register
(3) Send this high byte to the DAC. For the Turing Machine, this becomes a voltage from 0 to 10V, and that's usually sent to a voltage-controlled oscillator to generate a melody
(4) When the module receives a trigger signal, shift the register left by one bit, carrying bit 15 back to bit 0
(5) Generate a random number and compare it to a user-defined threshold. If the threshold is met, flip bit 0 of the register (0 to 1 or 1 to 0)
(6) Go back to step 2

The trigger signal can be any event, but in this demo, I'll just use a FOR-NEXT loop for delay. A simple VIC-20 implementation of the shift-register concept is something like this:

1 P=.05 : D = 60 // p = .05, meaning that 5% of the time, bit 15 will be flipped when shifted to bit 0
5 POKE 36878, 15
10 R = INT(RND(0) * 65536) // Step 1
20 N = INT(R / 256) // Step 2
30 POKE 36875, N OR 128 // Step 3; the OR here is to ensure proper value range
40 FOR I = 0 TO D : NEXT I
50 R=R*2 // Step 4
60 C=N AND 128 // C is a carry flag, which will either be returned to bit 0 of the register as-is, or flipped
70 IF C > 0 THEN R = R - 65536
80 B = 0 : IF C > 0 THEN B = 1 // Get the value of bit 15 prior to the shift
90 IF P > RND(0) THEN B = 1 - B // Step 5
100 R = R + B
110 GOTO 20 // Step 6

There's a lot to play with using this basic structure. You can have several shift registers running at once and use them for volume as well as pitch.

This is one simple way of creating algorithmic (or generative) melodies that's commonly employed by modular synthesists, and there are many, many others.
Last edited by chysn on Tue Oct 22, 2019 10:36 pm, edited 3 times in total.
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Algorithmic Music

Post by chysn »

I can mention a couple additional tricks. The BASIC program I wrote above is a 16-step evolving sequence. You can get a 32-step sequence by setting P = 1. In this situation, the last bit is always flipped, so you get 32 repeating steps.

You can set fewer than 16 steps by changing which bit is the carry bit. Bitwise arithmetic in VIC-20 BASIC is annoying, so I didn't implement that above, but it's possible. Machine language would be way more suitable for this algorithm, and I might do that, too, if there's any interest.
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
edinky
Vic 20 Newbie
Posts: 18
Joined: Sat Apr 07, 2018 10:34 am

Re: Algorithmic Music

Post by edinky »

chysn wrote: Tue Oct 22, 2019 10:34 pm I can mention a couple additional tricks. The BASIC program I wrote above is a 16-step evolving sequence. You can get a 32-step sequence by setting P = 1. In this situation, the last bit is always flipped, so you get 32 repeating steps.

You can set fewer than 16 steps by changing which bit is the carry bit. Bitwise arithmetic in VIC-20 BASIC is annoying, so I didn't implement that above, but it's possible. Machine language would be way more suitable for this algorithm, and I might do that, too, if there's any interest.
wow this is great, exactly what i was looking for, thank you chysn!!! i can play around with values and see what comes up. would be good to play with the other DACs and see if we can get some kind of harmonies or percussion in there.

i like how a simple value could change the output enough for a new level of a game etc. cool!!

and yes ASM would be ideal of course!
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Algorithmic Music

Post by chysn »

edinky wrote: Sun Nov 17, 2019 3:30 am and yes ASM would be ideal of course!
Sorry for the delay on this! Holidays came and went, while other projects popped on and off my radar. But here's an assembly language implementation of the 16-bit shift register, as described above. The advantage over the BASIC version is that you can set pattern length. What "pattern length" actually means (and what it meant in Tom Whitwell's original hardware) is that the specified bit is used as the carry bit, optionally instead of bit 15.

Code: Select all

BASRND = $E094      ; Routine for BASIC's RND() function
RNDNUM = $8C        ; One of the result storage locations for RND()
REG_L  = $FB        ; \ Storage for the 16-bit shift register
REG_H  = $FC        ; /
L_SRCH = $FD        ; Used for length search
NOTE   = $FE        ; Examine this location after calling NXNOTE to determine
                    ; which note to play. This would be something along the lines
                    ; of POKE 36875, PEEK(254) OR 128, with the extra 128 being
                    ; the gate on bit for the voice.

; INIT initializes the shift register with a random 16-bit value. This is optional;
; you may feel free to use what's already in the register locations, or set your own
; initial value, or use some other method to set REG_L and REG_H.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
INIT:   JSR BASRND  ; Call the BASIC RND() function
        LDA RNDNUM
        STA REG_L   ; Use it to set the low byte of the shift register
        JSR BASRND
        LDA RNDNUM
        STA REG_H   ; And the high byte with another RND() call
        RTS

; NXNOTE stores the current note value in the NOTE location (see above). Then,        
; shift the 16-bit register one bit to the left. Do a probability check
; to determine whether the last bit is moved back to bit 0, or is flipped.
;
; Preparations:
;     (1) Set Y register to the probability value (0-127)
;         Probability of 0 means never change the pattern; 127 means always flip bit 0
;     (2) Set X register to the sequence length (1-16)
;         Determines which bit is carried back to bit 0 of the sequence. This is 1-indexed,
;         so a value of 8 means "carry bit 7", or "8 steps long."
;     (3) JSR NXNOTE
;
; Example usage after call:
;     LDA NOTE
;     ORA #$80 ; To gate the note
;     STA {VIC sound register}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
NXNOTE: LDA REG_H   ; Since the VIC-20's note values are 7 bits, use the top
        LSR         ; seven bits of the shift register as the note value, to be
        STA NOTE    ; saved in memory for use by the caller upon return.

        TYA
        PHA         ; Put probability (0-127) in Y onto the stack for later use
FINDCB: DEX         ; The caller passes length, but the routine uses (length - 1)
                    ; so that AND can be used to mask off steps 9-16 below
        TXA
        LDX REG_H
        CMP #$08    ; Select the high byte or the low byte for length in X; if
                    ; X is 9-16, use the high byte. Otherwise, use the low byte.
        BCS COUNT
        LDX REG_L
COUNT:  STX L_SRCH  ; This is the byte we'll be searching for the last bit
        AND #$07    ; We're counting from bits 0 to 7 within the L_SRCH byte
        TAX
        INX         ; X will now be the actual length, but within the selected byte
        LDA #$01    ; A will now be set to indicate the last bit of the pattern
ADVANC: DEX
        BEQ FOUND
        ASL
        CLC
        BCC ADVANC
FOUND:  BIT L_SRCH
        BEQ ONE
        LDX #$00    ; The last bit of the pattern is 0
        BEQ ROT_H
ONE:    LDX #$01    ; The last bit of the pattern is 1

ROT_H:  ASL REG_H   ; Shift high byte left
ROT_L   ASL REG_L   ; Shift low byte left
        BCC ROLL
        LDA #$01    ; Set bit 0 of high byte if bit 7 of the low byte was set
        ORA REG_H
        STA REG_H
ROLL:   CPX #$01
        BEQ CHKPR   ; If the last bit of the pattern was set, roll it to bit 0 of the low
        LDA #$01    ; byte using the same method as before, with an OR to set bit 0
        ORA REG_L
        STA REG_L
CHKPR:  JSR BASRND  ; Make the probability check
        PLA         ; Pull probability from the stack
        CMP RNDNUM
        BEQ DONE
        BCC DONE    ; If A > random number, flip the bit. The higher A is,
                    ; the more likely it is that the bit gets flipped.
        LDA #$01    ; When the probability says flip the bit, do that
        EOR REG_L   ; by XORing the register low bit with value 0000 0001
        STA REG_L   ; and saving the register with the flipped bit

DONE:   RTS
It would be easy to build a USR routine around this for integration with BASIC.
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
edinky
Vic 20 Newbie
Posts: 18
Joined: Sat Apr 07, 2018 10:34 am

Re: Algorithmic Music

Post by edinky »

this is so cool, thanks! i'm going to dig into this and test it. maybe i can work out how to create a harmonising note with a different voice and some percussion with the noise voice. wish me luck!
Post Reply