'Wave Editing Tool v3.4 for FamiTracker
'Written by Alex Allsopp (Necrophageon)

CONST pi = 3.14159265#          'It's delicious! :)

'ÛÛÛÛÛÛÛÛÛÛÛMAIN-LINEÛÛÛÛÛÛÛÛÛÛÛÛÛ
DO                              'Û
GOSUB wave.length               'Û - Wave length menu
GOSUB wave.function             'Û - Asks what to do with the waveforms
                                'Û
LET wave.id = 1                 'Û
GOSUB wave.select               'Û - Wave type selection Menu
GOSUB build.wave                'Û - Builds the waveform, or requests
                                'Û   manual input if "custom" is selected
IF funct <> 3 THEN              'Û
LET wave.id = 2                 'Û
DO                              'Û
        GOSUB wave.select       'Û - Same as the first one, second waveform
        GOSUB build.wave        'Û - Builds the second waveform like the first
        GOSUB wave.check        'Û - Checks for identical waves when shape-
LOOP WHILE check = w.len        'Û   shift is selected
END IF                          'Û
                                'Û
GOSUB file.name                 'Û - Rudamentary filename prompt subroutine
OPEN filename$ FOR OUTPUT AS #1 'Û
GOSUB get.steps                 'Û - Prompts the user for number of steps
SELECT CASE funct               'Û
        CASE 1                  'Û
          GOSUB dist.ask        'Û
          GOSUB wave.plus       'Û - Combines the waveforms and auto-phases
        CASE 2                  'Û
          GOSUB dist.ask        'Û
          GOSUB wave.change     'Û - Combines the waveforms and shape-shifts
        CASE 3                  'Û
          GOSUB dist.range      'Û
          GOSUB dist.loop       'Û
END SELECT                      'Û
CLOSE #1                        'Û
GOSUB finish                    'Û - Cleans up to avoid a mess when looping
LOOP WHILE again = 1            'Û
END                             'Û
'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ                      

'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛWaveform Selection DataÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
DATA "6.25% pulse", "12.5% pulse", "18.75% pulse", "25% pulse", "31.25% pulse"
DATA "37.5% pulse", "43.75% pulse", "50% pulse", "Triangle", "Saw", "Sine", "Custom"
'==================================================================

'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛwave.lengthÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
wave.length:
CLS
LET title$ = "Welcome to Necrophageon's Wave Workshop!"
LET question$ = "What size wave would you like?"
LOCATE 5, (40 - INT(LEN(title$) / 2))
PRINT title$
LOCATE 8, (40 - INT(LEN(question$) / 2))
PRINT question$
FOR i = 1 TO 6
        LET question$ = "[" + STR$(i) + "] - " + STR$((i * 4) + 8)
        LOCATE 9 + i, 30
        PRINT question$
NEXT i
LOCATE 16, 30
PRINT "[ 7] -  64 (FDS)"
DO UNTIL keypress$ > "0" AND keypress$ < "8"
        LET keypress$ = INKEY$
        LET lent$ = STR$((VAL(keypress$) * 4) + 8)
LOOP
IF keypress$ = "7" THEN LET lent$ = "64"
LOCATE 20, 30
PRINT "You have selected "; lent$
LET w.len = VAL(lent$)
IF w.len = 64 THEN
        LET max.vol = 63
ELSE
        LET max.vol = 15
END IF
DIM wave.form1(w.len)
DIM wave.form2(w.len)
DIM wave.form3(w.len)
DIM wave.final(w.len)
SLEEP 1
RETURN
'==================================================================

'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛwave.functionÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
wave.function:
CLS
LOCATE 10, 30
PRINT "What would you like to do?"
LOCATE 12, 30
PRINT "[ 1] - Auto-Phase"
LOCATE 13, 30
PRINT "[ 2] - Shape-Shift"
LOCATE 14, 30
PRINT "[ 3] - Distort"
DO
        LET keypress$ = INKEY$
LOOP UNTIL keypress$ >= "1" AND keypress$ <= "3"
LET funct = VAL(keypress$)
RETURN
'==================================================================

'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛwave.selectÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
wave.select:
CLS
LET question1$ = "What type of waveform?"
IF wave.id = 1 THEN
        LET question$ = "Select first waveform"
ELSE
        LET question$ = "Select second waveform"
END IF
LOCATE 2, (40 - FIX(LEN(question$) / 2))
PRINT question$
RESTORE
FOR i = 1 TO 12
        READ w$
        LET question$ = "[" + STR$(i) + "] - " + w$
        LOCATE (3 + i), 30
        PRINT question$
NEXT i
LET x# = 0
DO
        LOCATE 17, 30
        INPUT "Your selection"; x#
LOOP WHILE x# < 1 OR x# > 12
RESTORE
FOR i = 1 TO x#
        READ w$
NEXT i
LOCATE 19, 30

PRINT "You have selected '"; w$; "'"

IF x# <> 12 THEN
LET y# = 0
DO WHILE y# < 4 OR y# > max.vol
        IF w.len = 64 THEN
                LET question$ = "Wave volume 4 - 63"
                IF funct = 1 THEN
                        LET question$ = question$ + " (31 recommended)"
                ELSE
                        LET question$ = question$ + " (63 recommended)"
                END IF
        ELSE
                LET question$ = "Wave volume 4 - 15"
                IF funct = 1 THEN
                        LET question$ = question$ + " (7 recommended)"
                ELSE
                        LET question$ = question$ + " (15 recommended)"
                END IF
               
        END IF
        LOCATE 21, 30
        PRINT question$
        LOCATE 21, 30 + LEN(question$)
        INPUT y#
LOOP
END IF
RETURN
'==================================================================

'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛbuild.waveÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
build.wave:
CLS
IF x# < 9 THEN                  'This builds squares
        LET pulse.high = INT(w.len * (x# / 16))
        IF pulse.high = 0 THEN pulse.high = 1
        LET pulse.low = w.len - pulse.high
        FOR i = 1 TO pulse.high
                IF wave.id = 1 THEN
                        LET wave.form1(i) = y#
                ELSE
                        LET wave.form2(i) = y#
                END IF
        NEXT i
        FOR i = 1 TO pulse.low
                IF wave.id = 1 THEN
                        LET wave.form1(i + pulse.high) = 0
                ELSE
                        LET wave.form2(i + pulse.high) = 0
                END IF
        NEXT i
END IF
IF x# = 9 THEN                  'This builds half a triangle
        DIM tri.temp(w.len)
        LET half = w.len / 2
        FOR i = 1 TO half
                LET tri.temp(i) = FIX((i / half) * y#)
        NEXT i
        FOR i = 1 TO half       'This mirrors the first half to the second
                LET tri.temp(i + half) = tri.temp(half - (i - 1))
        NEXT i
        LET stepper = (w.len / 4)
        FOR i = 1 TO w.len      'This pushes the whole wave back 25% for shape-shifting compatibility
                LET wave.step = stepper + i
                IF wave.step > w.len THEN LET wave.step = wave.step - w.len
                IF wave.id = 1 THEN
                        LET wave.form1(i) = tri.temp(wave.step)
                ELSE
                        LET wave.form2(i) = tri.temp(wave.step)
                END IF
        NEXT i
ERASE tri.temp
END IF
IF x# = 10 THEN                 'Sawtooth builder
        FOR i = 1 TO w.len
                IF wave.id = 1 THEN
                        LET wave.form1(i) = INT((i / w.len) * y#)
                ELSE
                        LET wave.form2(i) = INT((i / w.len) * y#)
                END IF
        NEXT i
END IF
IF x# = 11 THEN                 'Sine builder
        FOR i = 1 TO w.len
                IF wave.id = 1 THEN
                        LET wave.form1(i) = (INT(SIN((i / w.len) * (2 * pi)) * (y# / 2)) + 1) + FIX(y# / 2)
                ELSE
                        LET wave.form2(i) = (INT(SIN((i / w.len) * (2 * pi)) * (y# / 2)) + 1) + FIX(y# / 2)
                END IF
        NEXT i
END IF
IF x# = 12 THEN                 'Custom wave input
        CLS
        FOR i = 1 TO w.len
                PRINT "Position "; i; "/"; w.len
                DO
                INPUT position#
                LOOP UNTIL position# >= 0 AND position# <= max.vol
                IF wave.id = 1 THEN
                        LET wave.form1(i) = position#
                ELSE
                        LET wave.form2(i) = position#
                END IF
        NEXT i
END IF
RETURN
'==================================================================

'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛwave.checkÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
wave.check:
LET check = 0
FOR i = 1 TO w.len
        IF wave.form1(i) = wave.form2(i) AND funct = 2 THEN LET check = check + 1
NEXT i
IF check = w.len THEN
        CLS
        LOCATE 12, 25
        PRINT "Identical wave forms - restarting"
        SLEEP 2
END IF
RETURN
'==================================================================

'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛget.stepsÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
get.steps:
CLS
IF w.len = 64 THEN LET max.steps = 64
IF w.len <= 32 AND w.len >= 16 THEN LET max.steps = 16
IF w.len = 12 THEN
        IF funct = 1 THEN LET max.steps = 12
        IF funct = 2 THEN LET max.steps = 16
END IF
LOCATE 12, 24
PRINT "How many steps (Min 3 / Max"; max.steps; ")"
DO
LET fail = 0
LOCATE 12, 56
INPUT fw.len
IF fw.len < 3 OR fw.len > max.steps THEN LET fail = 1
LOOP UNTIL fail = 0
RETURN
'==================================================================

'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛdist.askÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
dist.ask:
CLS
LOCATE 12, 23
PRINT "Would you like to add distortion (y/n)?"
DO
LET keypress$ = INKEY$
LOOP UNTIL LCASE$(keypress$) = "y" OR LCASE$(keypress$) = "n"
LET dist = 0
IF LCASE$(keypress$) = "y" THEN GOSUB dist.range
RETURN
'==================================================================

'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛdist.rangeÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
dist.range:
LET dist = 1
DIM dist.array(w.len)
RANDOMIZE TIMER / 3
CLS
LOCATE 10, 30
PRINT "Enter level of distortion:"
LOCATE 12, 30
PRINT "[ 1] - Low"
LOCATE 13, 30
PRINT "[ 2] - High"
DO
LET keypress$ = INKEY$
LOOP UNTIL keypress$ >= "1" AND keypress$ <= "2"
LET dist.level = VAL(keypress$) * ((max.vol + 1) / 16)
RETURN
'==================================================================

'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛwave.plusÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
wave.plus:
FOR i = 1 TO fw.len
        LET wave.step = FIX(i * (w.len / fw.len))
        FOR f = 1 TO w.len
                LET wave.x = (wave.step + f) - 1
                IF wave.x > w.len THEN LET wave.x = wave.x - w.len
                IF wave.form1(f) + wave.form2(wave.x) > max.vol THEN
                        LET wave.final(f) = max.vol
                ELSE
                        LET wave.final(f) = wave.form1(f) + wave.form2(wave.x)
                END IF
        NEXT f
        GOSUB wave.write
NEXT i
RETURN
'==================================================================

'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛwave.changeÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
wave.change:
FOR i = 1 TO fw.len
        LET wave.step = (INT((100 * (i - 1)) / (fw.len - 1)) / 100)
        FOR f = 1 TO w.len
                IF i = 1 THEN LET wave.final(f) = wave.form1(f)
                IF i = fw.len THEN LET wave.final(f) = wave.form2(f)
                IF ((wave.form2(f) - wave.form1(f)) * wave.step) + wave.form1(f) + .5 >= INT((wave.form2(f) - wave.form1(f)) * wave.step) + wave.form1(f) + 1 THEN
                        LET wave.pos = INT((wave.form2(f) - wave.form1(f)) * wave.step) + wave.form1(f) + 1
                ELSE
                        LET wave.pos = INT((wave.form2(f) - wave.form1(f)) * wave.step) + wave.form1(f)
                END IF
                IF i > 1 AND i < fw.len THEN LET wave.final(f) = wave.pos
        NEXT f
        GOSUB wave.write
NEXT i
RETURN
'==================================================================

'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛdist.loopÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
dist.loop:
FOR i = 1 TO fw.len
        FOR t = 1 TO w.len
                LET wave.final(t) = wave.form1(t)
        NEXT t
        GOSUB wave.write
NEXT i
RETURN
'==================================================================

'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛdist.addÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
dist.add:
FOR d = 1 TO w.len
        LET dist.gen = INT(RND * (dist.level * 2) - dist.level)
        LET add = dist.gen + wave.final(d)
        SELECT CASE add
                CASE IS > max.vol
                        LET wave.final(d) = max.vol
                CASE IS < 0
                        LET wave.final(d) = 0
                CASE ELSE
                        LET wave.final(d) = add
        END SELECT
NEXT d
RETURN
'==================================================================

'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛwave.writeÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
wave.write:
IF dist = 1 THEN GOSUB dist.add
LET wave.output$ = ""
FOR t = 1 TO w.len
        LET wave.output$ = wave.output$ + STR$(wave.final(t))
NEXT t
PRINT #1, wave.output$
RETURN
'==================================================================

'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛfile.nameÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
file.name:
INPUT "Save as (max 8 characters) "; filename$
IF LCASE$(RIGHT$(filename$, 4)) <> ".txt" THEN
        LET leng = LEN(filename$)
ELSE
        LET leng = LEN(filename$) - 4
END IF
IF leng > 8 THEN LET leng = 8
LET filename$ = LEFT$(filename$, leng) + ".txt"
RETURN
'==================================================================

'ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛfinishÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
finish:
CLS
LOCATE 12, 35
PRINT "Job's done!"
LOCATE 14, 25
PRINT "Would you like to make another?"
DO
LET keypress$ = INKEY$
LOOP UNTIL keypress$ = "y" OR keypress$ = "n"
IF keypress$ = "y" THEN
        LET again = 1
        LET dist = 0
        ERASE wave.form1
        ERASE wave.form2
        ERASE wave.form3
        ERASE wave.final
        ERASE dist.array
ELSE
        LET again = 0
END IF
RETURN
'==================================================================

