Skip to content

Instantly share code, notes, and snippets.

@MegaLoler
Created September 3, 2017 00:34
Show Gist options
  • Save MegaLoler/99a317abc3d6346ae97746ada5e6a38a to your computer and use it in GitHub Desktop.
Save MegaLoler/99a317abc3d6346ae97746ada5e6a38a to your computer and use it in GitHub Desktop.
A 4 channel softsynth for AVR and a tracker in C that communicates with it over serial
.define NOTE_C 262
.define NOTE_Cs 277
.define NOTE_D 294
.define NOTE_Ds 311
.define NOTE_E 330
.define NOTE_F 349
.define NOTE_Fs 370
.define NOTE_G 392
.define NOTE_Gs 415
.define NOTE_A 440
.define NOTE_As 466
.define NOTE_B 494
.def LEFT_BUF = r2
.def RIGHT_BUF = r3
.def CHAN1_OSCL = r4
.def CHAN1_OSCH = r5
.def CHAN2_OSCL = r6
.def CHAN2_OSCH = r7
.def CHAN3_OSCL = r8
.def CHAN3_OSCH = r9
.def CHAN4_OSCL = r10
.def CHAN4_OSCH = r11
.def CHAN1_FRQL = r12
.def CHAN1_FRQH = r13
.def CHAN2_FRQL = r14
.def CHAN2_FRQH = r15
.def CHAN3_FRQL = r24
.def CHAN3_FRQH = r25
.def CHAN4_FRQL = r26
.def CHAN4_FRQH = r27
.def NOISEL = r0
.def NOISEH = r1
.NOLIST
.include "m48def.inc"
.LIST
.cseg
.org 0x00
rjmp start
.org 0x09
rjmp timer
.org 0x12
rjmp recieve
start:
ldi r16, LOW(RAMEND)
out spl, r16
ldi r16, HIGH(RAMEND)
out sph, r16
ldi r16, 1
sts TIMSK2, r16
ldi r16, 0b001
sts TCCR2B, r16
ldi r16, 0b11111100
out ddrd, r16
ldi r16, 0xFF
out ddrc, r16
ldi r16, 0xFF
mov LEFT_BUF, r16
mov RIGHT_BUF, r16
ldi r16, 0b0011
sts enable, r16
ldi r16, 0b00000000
sts waveform, r16
ldi r16, 0x80
sts chan1_tdut, r16
sts chan2_tdut, r16
sts chan3_tdut, r16
sts chan4_tdut, r16
ldi r16, 0xFF
sts chan1_tvol, r16
sts chan2_tvol, r16
sts chan3_tvol, r16
sts chan4_tvol, r16
ldi r16, 0x01
ldi r17, 0x00
mov NOISEL, r16
mov NOISEH, r17
ldi r16, 0x00
sts tick_timer, r16
sts tick_timer + 1, r16
ldi r16, 0xF0
sts chan1_env + 1, r16
sts chan2_env + 1, r16
sts chan3_env + 1, r16
sts chan4_env + 1, r16
ldi r16, 0x44
sts chan1_notes, r16
ldi r16, 0x00
sts chan2_notes, r16
ldi r16, 0x00
sts chan3_notes, r16
ldi r16, 0x00
sts chan4_notes, r16
ldi r16, 0x00
sts chan1_notes + 1, r16
ldi r16, 0x00
sts chan2_notes + 1, r16
ldi r16, 0x00
sts chan3_notes + 1, r16
ldi r16, 0x00
sts chan4_notes + 1, r16
ldi r16, 0b0000
sts note_on, r16
;ldi r16, 0b00100000
;ldi r17, 0b10000010
;rcall command
;ldi r16, 0b00100010
;ldi r17, 0b11111110
;rcall command
;ldi r16, 0b01011100
;ldi r17, 0b11111110
;rcall command
;ldi r16, 0b01110110
;ldi r17, 0b11111110
;rcall command
;ldi r16, 0b01111001
;ldi r17, 0b10000001
;rcall command
ldi r16, LOW(103)
ldi r17, HIGH(103)
sts UBRR0L, r16
sts UBRR0H, r17
ldi r16, (1 << RXEN0) | (1 << RXCIE0)
sts UCSR0B, r16
ldi r16, (1 << USBS0) | (3 << UCSZ00)
sts UCSR0C, r16
sei
loop:
rjmp loop
recieve:
lds r16, UDR0
cpi r16, 0xFF
breq handle_command
lds r17, command_buf
sts command_buf + 1, r17
sts command_buf, r16
reti
handle_command:
lds r16, command_buf
lds r17, command_buf + 1
rcall command
reti
command:
sbrs r17, 7
ret
mov r18, r16
lsr r18
andi r18, 0b00000011 ;channel
sbrs r16, 0
rjmp note_command
effect_command:
mov r19, r16
lsl r19
andi r19, 0xF0 ;effect
mov r20, r17
lsl r20
sbrc r16, 7
ori r20, 0b00000001 ;argument
cpi r19, 0x00
breq apply_arp
cpi r19, 0x10
breq apply_pitch_slide_up
cpi r19, 0x20
breq apply_pitch_slide_down_a
cpi r19, 0x30
breq apply_portamento_a
cpi r19, 0x40
breq apply_vibrato_a
cpi r19, 0x50
breq apply_waveform_a
cpi r19, 0x60
breq apply_duty_cycle_a
cpi r19, 0x70
breq apply_tremolo_a
cpi r19, 0x80
breq apply_pwm_a
cpi r19, 0xA0
breq apply_volume_slide_up_a
cpi r19, 0xB0
breq apply_volume_slide_down_a
cpi r19, 0xC0
breq apply_volume_a
cpi r19, 0xF0
breq apply_fine_a
rjmp done
apply_arp:
cpi r18, 0b00
breq apply_arp1
cpi r18, 0b01
breq apply_arp2
cpi r18, 0b10
breq apply_arp3
cpi r18, 0b11
breq apply_arp4
rjmp done
apply_arp1:
sts chan1_notes + 1, r20
rjmp done
apply_arp2:
sts chan2_notes + 1, r20
rjmp done
apply_arp3:
sts chan3_notes + 1, r20
rjmp done
apply_arp4:
sts chan4_notes + 1, r20
rjmp done
apply_pitch_slide_down_a:
rjmp apply_pitch_slide_down
apply_portamento_a:
rjmp apply_portamento
apply_vibrato_a:
rjmp apply_vibrato
apply_waveform_a:
rjmp apply_waveform
apply_duty_cycle_a:
rjmp apply_duty_cycle
apply_tremolo_a:
rjmp apply_tremolo
apply_pwm_a:
rjmp apply_pwm
apply_volume_slide_up_a:
rjmp apply_volume_slide_up
apply_volume_slide_down_a:
rjmp apply_volume_slide_down
apply_volume_a:
rjmp apply_volume_eff
apply_fine_a:
rjmp apply_fine
apply_pitch_slide_up:
cpi r18, 0b00
breq apply_pitch_slide_up1
cpi r18, 0b01
breq apply_pitch_slide_up2
cpi r18, 0b10
breq apply_pitch_slide_up3
cpi r18, 0b11
breq apply_pitch_slide_up4
rjmp done
apply_pitch_slide_up1:
sts chan1_pit_slide, r20
lds r16, pit_slide_dir
ori r16, 0b0001
sts pit_slide_dir, r16
rjmp done
apply_pitch_slide_up2:
sts chan2_pit_slide, r20
lds r16, pit_slide_dir
ori r16, 0b0010
sts pit_slide_dir, r16
rjmp done
apply_pitch_slide_up3:
sts chan3_pit_slide, r20
lds r16, pit_slide_dir
ori r16, 0b0100
sts pit_slide_dir, r16
rjmp done
apply_pitch_slide_up4:
sts chan4_pit_slide, r20
lds r16, pit_slide_dir
ori r16, 0b1000
sts pit_slide_dir, r16
rjmp done
apply_pitch_slide_down:
cpi r18, 0b00
breq apply_pitch_slide_down1
cpi r18, 0b01
breq apply_pitch_slide_down2
cpi r18, 0b10
breq apply_pitch_slide_down3
cpi r18, 0b11
breq apply_pitch_slide_down4
rjmp done
apply_pitch_slide_down1:
sts chan1_pit_slide, r20
lds r16, pit_slide_dir
andi r16, 0b1110
sts pit_slide_dir, r16
rjmp done
apply_pitch_slide_down2:
sts chan2_pit_slide, r20
lds r16, pit_slide_dir
andi r16, 0b1101
sts pit_slide_dir, r16
rjmp done
apply_pitch_slide_down3:
sts chan3_pit_slide, r20
lds r16, pit_slide_dir
andi r16, 0b1011
sts pit_slide_dir, r16
rjmp done
apply_pitch_slide_down4:
sts chan4_pit_slide, r20
lds r16, pit_slide_dir
andi r16, 0b0111
sts pit_slide_dir, r16
rjmp done
apply_portamento:
cpi r18, 0b00
breq apply_portamento1
cpi r18, 0b01
breq apply_portamento2
cpi r18, 0b10
breq apply_portamento3
cpi r18, 0b11
breq apply_portamento4
rjmp done
apply_portamento1:
sts chan1_port, r20
rjmp done
apply_portamento2:
sts chan2_port, r20
rjmp done
apply_portamento3:
sts chan3_port, r20
rjmp done
apply_portamento4:
sts chan4_port, r20
rjmp done
apply_vibrato:
cpi r18, 0b00
breq apply_vibrato1
cpi r18, 0b01
breq apply_vibrato2
cpi r18, 0b10
breq apply_vibrato3
cpi r18, 0b11
breq apply_vibrato4
rjmp done
apply_vibrato1:
sts chan1_vib, r20
rjmp done
apply_vibrato2:
sts chan2_vib, r20
rjmp done
apply_vibrato3:
sts chan3_vib, r20
rjmp done
apply_vibrato4:
sts chan4_vib, r20
rjmp done
apply_waveform:
lds r16, waveform
cpi r18, 0b00
breq apply_waveform1
cpi r18, 0b01
breq apply_waveform2
cpi r18, 0b10
breq apply_waveform3
cpi r18, 0b11
breq apply_waveform4
rjmp done_waveform
apply_waveform1:
andi r16, 0b11111100
or r16, r20
rjmp done_waveform
apply_waveform2:
andi r16, 0b11110011
lsl r20
lsl r20
or r16, r20
rjmp done_waveform
apply_waveform3:
andi r16, 0b11001111
lsl r20
lsl r20
lsl r20
lsl r20
or r16, r20
rjmp done_waveform
apply_waveform4:
andi r16, 0b00111111
lsl r20
lsl r20
lsl r20
lsl r20
lsl r20
lsl r20
or r16, r20
done_waveform:
sts waveform, r16
rjmp done
apply_duty_cycle:
cpi r18, 0b00
breq apply_duty_cycle1
cpi r18, 0b01
breq apply_duty_cycle2
cpi r18, 0b10
breq apply_duty_cycle3
cpi r18, 0b11
breq apply_duty_cycle4
rjmp done
apply_duty_cycle1:
sts chan1_tdut, r20
rjmp done
apply_duty_cycle2:
sts chan2_tdut, r20
rjmp done
apply_duty_cycle3:
sts chan3_tdut, r20
rjmp done
apply_duty_cycle4:
sts chan4_tdut, r20
rjmp done
apply_tremolo:
cpi r18, 0b00
breq apply_tremolo1
cpi r18, 0b01
breq apply_tremolo2
cpi r18, 0b10
breq apply_tremolo3
cpi r18, 0b11
breq apply_tremolo4
rjmp done
apply_tremolo1:
sts chan1_trm, r20
rjmp done
apply_tremolo2:
sts chan2_trm, r20
rjmp done
apply_tremolo3:
sts chan3_trm, r20
rjmp done
apply_tremolo4:
sts chan4_trm, r20
rjmp done
apply_pwm:
cpi r18, 0b00
breq apply_pwm1
cpi r18, 0b01
breq apply_pwm2
cpi r18, 0b10
breq apply_pwm3
cpi r18, 0b11
breq apply_pwm4
rjmp done
apply_pwm1:
sts chan1_pwm, r20
rjmp done
apply_pwm2:
sts chan2_pwm, r20
rjmp done
apply_pwm3:
sts chan3_pwm, r20
rjmp done
apply_pwm4:
sts chan4_pwm, r20
rjmp done
apply_volume_slide_up:
cpi r18, 0b00
breq apply_volume_slide_up1
cpi r18, 0b01
breq apply_volume_slide_up2
cpi r18, 0b10
breq apply_volume_slide_up3
cpi r18, 0b11
breq apply_volume_slide_up4
rjmp done
apply_volume_slide_up1:
sts chan1_vol_slide, r20
lds r16, vol_slide_dir
ori r16, 0b0001
sts vol_slide_dir, r16
rjmp done
apply_volume_slide_up2:
sts chan2_vol_slide, r20
lds r16, vol_slide_dir
ori r16, 0b0010
sts vol_slide_dir, r16
rjmp done
apply_volume_slide_up3:
sts chan3_vol_slide, r20
lds r16, vol_slide_dir
ori r16, 0b0100
sts vol_slide_dir, r16
rjmp done
apply_volume_slide_up4:
sts chan4_vol_slide, r20
lds r16, vol_slide_dir
ori r16, 0b1000
sts vol_slide_dir, r16
rjmp done
apply_volume_slide_down:
cpi r18, 0b00
breq apply_volume_slide_down1
cpi r18, 0b01
breq apply_volume_slide_down2
cpi r18, 0b10
breq apply_volume_slide_down3
cpi r18, 0b11
breq apply_volume_slide_down4
rjmp done
apply_volume_slide_down1:
sts chan1_vol_slide, r20
lds r16, vol_slide_dir
andi r16, 0b1110
sts vol_slide_dir, r16
rjmp done
apply_volume_slide_down2:
sts chan2_vol_slide, r20
lds r16, vol_slide_dir
andi r16, 0b1101
sts vol_slide_dir, r16
rjmp done
apply_volume_slide_down3:
sts chan3_vol_slide, r20
lds r16, vol_slide_dir
andi r16, 0b1011
sts vol_slide_dir, r16
rjmp done
apply_volume_slide_down4:
sts chan4_vol_slide, r20
lds r16, vol_slide_dir
andi r16, 0b0111
sts vol_slide_dir, r16
rjmp done
apply_volume_eff:
cpi r18, 0b00
breq apply_volume1_eff
cpi r18, 0b01
breq apply_volume2_eff
cpi r18, 0b10
breq apply_volume3_eff
cpi r18, 0b11
breq apply_volume4_eff
rjmp done
apply_volume1_eff:
sts chan1_tvol, r20
rjmp done
apply_volume2_eff:
sts chan1_tvol, r20
rjmp done
apply_volume3_eff:
sts chan1_tvol, r20
rjmp done
apply_volume4_eff:
sts chan1_tvol, r20
rjmp done
apply_fine:
cpi r18, 0b00
breq apply_fine1
cpi r18, 0b01
breq apply_fine2
cpi r18, 0b10
breq apply_fine3
cpi r18, 0b11
breq apply_fine4
rjmp done
apply_fine1:
sts chan1_fine, r20
rjmp done
apply_fine2:
sts chan2_fine, r20
rjmp done
apply_fine3:
sts chan3_fine, r20
rjmp done
apply_fine4:
sts chan4_fine, r20
rjmp done
note_command:
mov r19, r16
lsl r19
andi r19, 0xF0 ;note
mov r20, r17
lsl r20
sbrc r16, 7
ori r20, 0b00000001
lsl r17
andi r17, 0b11111000 ;volume
andi r20, 0x0F
or r20, r19 ;note/octave
cpi r18, 0b00
breq set_chan1
cpi r18, 0b01
breq set_chan2
cpi r18, 0b10
breq set_chan3a
cpi r18, 0b11
breq set_chan4a
rjmp done
set_chan1:
cpi r19, 0x00
breq skip_note1
cpi r19, 0x10
breq reset_note1
cpi r19, 0x20
breq enable_note1
cpi r19, 0x30
breq disable_note1
lds r16, enable
ori r16, 0b0001
sts enable, r16
lds r16, note_on
ori r16, 0b0001
sts note_on, r16
sts chan1_notes, r20
rjmp skip_note1
reset_note1:
clr CHAN1_OSCL
clr CHAN1_OSCH
rjmp skip_note1
enable_note1:
lds r16, enable
ori r16, 0b0001
sts enable, r16
rjmp skip_note1
disable_note1:
lds r16, enable
andi r16, 0b1110
sts enable, r16
rjmp skip_note1
skip_note1:
cpi r17, 0x00
breq done_a
sts chan1_tvol, r17
rjmp done
set_chan3a:
rjmp set_chan3
set_chan4a:
rjmp set_chan4b
set_chan2:
cpi r19, 0x00
breq skip_note2
cpi r19, 0x10
breq reset_note2
cpi r19, 0x20
breq enable_note2
cpi r19, 0x30
breq disable_note2
lds r16, enable
ori r16, 0b0010
sts enable, r16
lds r16, note_on
ori r16, 0b0010
sts note_on, r16
sts chan2_notes, r20
rjmp skip_note2
reset_note2:
clr CHAN2_OSCL
clr CHAN2_OSCH
rjmp skip_note2
enable_note2:
lds r16, enable
ori r16, 0b0010
sts enable, r16
rjmp skip_note2
disable_note2:
lds r16, enable
andi r16, 0b1101
sts enable, r16
rjmp skip_note2
skip_note2:
cpi r17, 0x00
breq done_a
sts chan2_tvol, r17
rjmp done
set_chan4b:
rjmp set_chan4
done_a:
rjmp done
set_chan3:
cpi r19, 0x00
breq skip_note3
cpi r19, 0x10
breq reset_note3
cpi r19, 0x20
breq enable_note3
cpi r19, 0x30
breq disable_note3
lds r16, enable
ori r16, 0b0100
sts enable, r16
lds r16, note_on
ori r16, 0b0100
sts note_on, r16
sts chan3_notes, r20
rjmp skip_note3
reset_note3:
clr CHAN3_OSCL
clr CHAN3_OSCH
rjmp skip_note3
enable_note3:
lds r16, enable
ori r16, 0b0100
sts enable, r16
rjmp skip_note3
disable_note3:
lds r16, enable
andi r16, 0b1011
sts enable, r16
rjmp skip_note3
skip_note3:
cpi r17, 0x00
breq done
sts chan3_tvol, r17
rjmp done
set_chan4:
cpi r19, 0x00
breq skip_note4
cpi r19, 0x10
breq reset_note4
cpi r19, 0x20
breq enable_note4
cpi r19, 0x30
breq disable_note4
lds r16, enable
ori r16, 0b1000
sts enable, r16
lds r16, note_on
ori r16, 0b1000
sts note_on, r16
sts chan4_notes, r20
rjmp skip_note4
reset_note4:
clr CHAN3_OSCL
clr CHAN3_OSCH
rjmp skip_note4
enable_note4:
lds r16, enable
ori r16, 0b1000
sts enable, r16
rjmp skip_note4
disable_note4:
lds r16, enable
andi r16, 0b0111
sts enable, r16
rjmp skip_note4
skip_note4:
cpi r17, 0x00
breq done
sts chan4_tvol, r17
rjmp done
done:
ret
timer:
push r16
push r17
push r18
push r19
push r20
push r21
push r22
push r23
mov r16, LEFT_BUF
andi r16, 0b11111100
mov r17, RIGHT_BUF
lsr r17
lsr r17
out portd, r16
out portc, r17
clr LEFT_BUF
clr RIGHT_BUF
add CHAN1_OSCL, CHAN1_FRQL
adc CHAN1_OSCH, CHAN1_FRQH
add CHAN2_OSCL, CHAN2_FRQL
adc CHAN2_OSCH, CHAN2_FRQH
add CHAN3_OSCL, CHAN3_FRQL
adc CHAN3_OSCH, CHAN3_FRQH
add CHAN4_OSCL, CHAN4_FRQL
adc CHAN4_OSCH, CHAN4_FRQH
lds r16, waveform
lds r22, enable
mov r18, r16
andi r18, 0b00000011
mov r19, CHAN1_OSCH
lds r20, chan1_vol
lds r21, chan1_dut
rcall generate
sbrc r22, 0
add LEFT_BUF, r17
lsr r16
lsr r16
mov r18, r16
andi r18, 0b00000011
mov r19, CHAN2_OSCH
lds r20, chan2_vol
lds r21, chan2_dut
rcall generate
sbrc r22, 1
add LEFT_BUF, r17
lsr r16
lsr r16
mov r18, r16
andi r18, 0b00000011
mov r19, CHAN3_OSCH
lds r20, chan3_vol
lds r21, chan3_dut
rcall generate
sbrc r22, 2
add RIGHT_BUF, r17
lsr r16
lsr r16
mov r18, r16
andi r18, 0b00000011
mov r19, CHAN4_OSCH
lds r20, chan4_vol
lds r21, chan4_dut
rcall generate
sbrc r22, 3
add RIGHT_BUF, r17
lds r16, tick_timer
cpi r16, 0x00
brne skip22
lds r20, vol_slide_dir
lds r16, chan1_tvol
lds r17, chan1_vol_slide
sbrc r20, 0
rjmp up1b
sbc r16, r17
brcc down1b
clr r16
up1b:
add r16, r17
brcc down1b
ser r16
down1b:
sts chan1_tvol, r16
sts chan1_ptrm, r16
lds r16, chan2_tvol
lds r17, chan2_vol_slide
sbrc r20, 1
rjmp up2b
sbc r16, r17
brcc down2b
clr r16
up2b:
add r16, r17
brcc down2b
ser r16
down2b:
sts chan2_tvol, r16
sts chan2_ptrm, r16
rjmp skip23
skip22:
rjmp skip2
skip23:
lds r16, chan3_tvol
lds r17, chan3_vol_slide
sbrc r20, 2
rjmp up3b
sbc r16, r17
brcc down3b
clr r16
up3b:
add r16, r17
brcc down3b
ser r16
down3b:
sts chan3_tvol, r16
sts chan3_ptrm, r16
lds r16, chan4_tvol
lds r17, chan4_vol_slide
sbrc r20, 3
rjmp up4b
sbc r16, r17
brcc down4b
clr r16
up4b:
add r16, r17
brcc down4b
ser r16
down4b:
sts chan4_tvol, r16
sts chan4_ptrm, r16
rjmp skip3
skip2:
rjmp skip
skip3:
lds r16, note_on
lds r17, chan1_notes + 1
cpi r17, 0x00
breq skip11
ori r16, 0b0001
skip11:
sbrs r16, 0
rjmp skip7
andi r16, 0b1110
sts note_on, r16
lds r16, chan1_notes
lds r17, chan1_notes + 1
rcall handle_note
sts chan1_tpit, r16
sts chan1_tpit + 1, r17
skip7:
lds r16, note_on
lds r17, chan2_notes + 1
cpi r17, 0x00
breq skip12
ori r16, 0b0010
skip12:
sbrs r16, 1
rjmp skip8
andi r16, 0b1101
sts note_on, r16
lds r16, chan2_notes
lds r17, chan2_notes + 1
rcall handle_note
sts chan2_tpit, r16
sts chan2_tpit + 1, r17
skip8:
lds r16, note_on
lds r17, chan3_notes + 1
cpi r17, 0x00
breq skip13
ori r16, 0b0100
skip13:
sbrs r16, 2
rjmp skip9
andi r16, 0b1011
sts note_on, r16
lds r16, chan3_notes
lds r17, chan3_notes + 1
rcall handle_note
sts chan3_tpit, r16
sts chan3_tpit + 1, r17
skip9:
lds r16, note_on
lds r17, chan4_notes + 1
cpi r17, 0x00
breq skip14
ori r16, 0b1000
skip14:
sbrs r16, 3
rjmp skip10
andi r16, 0b0111
sts note_on, r16
lds r16, chan4_notes
lds r17, chan4_notes + 1
rcall handle_note
sts chan4_tpit, r16
sts chan4_tpit + 1, r17
skip10:
lds CHAN1_FRQL, chan1_pvib
lds CHAN1_FRQH, chan1_pvib + 1
lds CHAN2_FRQL, chan2_pvib
lds CHAN2_FRQH, chan2_pvib + 1
lds CHAN3_FRQL, chan3_pvib
lds CHAN3_FRQH, chan3_pvib + 1
lds CHAN4_FRQL, chan4_pvib
lds CHAN4_FRQH, chan4_pvib + 1
lds r21, slide_timer
inc r21
cpi r21, 0x4
brne skip15
clr r21
skip15:
sts slide_timer, r21
cpi r21, 0x00
brne skip16
ser r21
rjmp skip17
skip16:
clr r21
skip17:
lds r20, pit_slide_dir
lds r16, chan1_tpit
lds r17, chan1_tpit + 1
sbrs r21, 0
rjmp skip18
lds r18, chan1_pit_slide
ldi r19, 0x00
sbrc r20, 0
rjmp up1
cpi r17, 0x00
brne down1a
cpi r16, 0x00
breq down1
down1a:
sbc r16, r18
brcc down1
dec r17
rjmp down1
up1:
add r16, r18
adc r17, r19
down1:
sts chan1_tpit, r16
sts chan1_tpit + 1, r17
skip18:
sbrs r21, 0
rjmp port1
lds r18, chan1_port
cpi r18, 0x00
breq skip_port1
mov r23, CHAN1_FRQL
cpi r23, 0x00
brne ne1
mov r23, CHAN1_FRQH
cpi r23, 0x00
breq skip_port1
ne1:
cp CHAN1_FRQH, r17
brlo higher1
cp r17, CHAN1_FRQH
brlo lower1
cp CHAN1_FRQL, r16
brlo higher1
cp r16, CHAN1_FRQL
brlo lower1
rjmp skip_port1
higher1:
ldi r19, 0x00
add CHAN1_FRQL, r18
adc CHAN1_FRQH, r19
cp r17, CHAN1_FRQH
brlo skip_port1
cp CHAN1_FRQH, r17
brlo port1
cp r16, CHAN1_FRQL
brlo skip_port1
rjmp port1
lower1:
clc
sbc CHAN1_FRQL, r18
brcc lower1a
dec CHAN1_FRQH
lower1a:
cp CHAN1_FRQH, r17
brlo skip_port1
cp r17, CHAN1_FRQH
brlo port1
cp CHAN1_FRQL, r16
brlo skip_port1
rjmp port1
skip_port1:
mov CHAN1_FRQL, r16
mov CHAN1_FRQH, r17
port1:
lds r16, chan2_tpit
lds r17, chan2_tpit + 1
sbrs r21, 0
rjmp skip19
lds r18, chan2_pit_slide
ldi r19, 0x00
sbrc r20, 1
rjmp up2
cpi r17, 0x00
brne down2a
cpi r16, 0x00
breq down2
down2a:
sbc r16, r18
brcc down2
dec r17
rjmp down2
up2:
add r16, r18
adc r17, r19
down2:
sts chan2_tpit, r16
sts chan2_tpit + 1, r17
skip19:
sbrs r21, 0
rjmp port2
lds r18, chan2_port
cpi r18, 0x00
breq skip_port2
mov r23, CHAN2_FRQL
cpi r23, 0x00
brne ne2
mov r23, CHAN2_FRQH
cpi r23, 0x00
breq skip_port2
ne2:
cp CHAN2_FRQH, r17
brlo higher2
cp r17, CHAN2_FRQH
brlo lower2
cp CHAN2_FRQL, r16
brlo higher2
cp r16, CHAN2_FRQL
brlo lower2
rjmp skip_port2
higher2:
ldi r19, 0x00
add CHAN2_FRQL, r18
adc CHAN2_FRQH, r19
cp r17, CHAN2_FRQH
brlo skip_port2
cp CHAN2_FRQH, r17
brlo port2
cp r16, CHAN2_FRQL
brlo skip_port2
rjmp port2
lower2:
clc
sbc CHAN2_FRQL, r18
brcc lower2a
dec CHAN2_FRQH
lower2a:
cp CHAN2_FRQH, r17
brlo skip_port2
cp r17, CHAN2_FRQH
brlo port2
cp CHAN2_FRQL, r16
brlo skip_port2
rjmp port2
skip_port2:
mov CHAN2_FRQL, r16
mov CHAN2_FRQH, r17
port2:
lds r16, chan3_tpit
lds r17, chan3_tpit + 1
sbrs r21, 0
rjmp skip20
lds r18, chan3_pit_slide
ldi r19, 0x00
sbrc r20, 2
rjmp up3
cpi r17, 0x00
brne down3a
cpi r16, 0x00
breq down3
down3a:
sbc r16, r18
brcc down3
dec r17
rjmp down3
up3:
add r16, r18
adc r17, r19
down3:
sts chan3_tpit, r16
sts chan3_tpit + 1, r17
skip20:
sbrs r21, 0
rjmp port3
lds r18, chan3_port
cpi r18, 0x00
breq skip_port3
mov r23, CHAN3_FRQL
cpi r23, 0x00
brne ne3
mov r23, CHAN3_FRQH
cpi r23, 0x00
breq skip_port3
ne3:
cp CHAN3_FRQH, r17
brlo higher3
cp r17, CHAN3_FRQH
brlo lower3
cp CHAN3_FRQL, r16
brlo higher3
cp r16, CHAN3_FRQL
brlo lower3
rjmp skip_port3
higher3:
ldi r19, 0x00
add CHAN3_FRQL, r18
adc CHAN3_FRQH, r19
cp r17, CHAN3_FRQH
brlo skip_port3
cp CHAN3_FRQH, r17
brlo port3
cp r16, CHAN3_FRQL
brlo skip_port3
rjmp port3
lower3:
clc
sbc CHAN3_FRQL, r18
brcc lower3a
dec CHAN3_FRQH
lower3a:
cp CHAN3_FRQH, r17
brlo skip_port3
cp r17, CHAN3_FRQH
brlo port3
cp CHAN3_FRQL, r16
brlo skip_port3
rjmp port3
skip_port3:
mov CHAN3_FRQL, r16
mov CHAN3_FRQH, r17
port3:
lds r16, chan4_tpit
lds r17, chan4_tpit + 1
sbrs r21, 0
rjmp skip21
lds r18, chan4_pit_slide
ldi r19, 0x00
sbrc r20, 3
rjmp up4
cpi r17, 0x00
brne down4a
cpi r16, 0x00
breq down4
down4a:
sbc r16, r18
brcc down4
dec r17
rjmp down4
up4:
add r16, r18
adc r17, r19
down4:
sts chan4_tpit, r16
sts chan4_tpit + 1, r17
skip21:
sbrs r21, 0
rjmp port4
lds r18, chan4_port
cpi r18, 0x00
breq skip_port4
mov r23, CHAN4_FRQL
cpi r23, 0x00
brne ne4
mov r23, CHAN4_FRQH
cpi r23, 0x00
breq skip_port4
ne4:
cp CHAN4_FRQH, r17
brlo higher4
cp r17, CHAN4_FRQH
brlo lower4
cp CHAN4_FRQL, r16
brlo higher4
cp r16, CHAN4_FRQL
brlo lower4
rjmp skip_port4
higher4:
ldi r19, 0x00
add CHAN4_FRQL, r18
adc CHAN4_FRQH, r19
cp r17, CHAN4_FRQH
brlo skip_port4
cp CHAN4_FRQH, r17
brlo port4
cp r16, CHAN4_FRQL
brlo skip_port4
rjmp port4
lower4:
clc
sbc CHAN4_FRQL, r18
brcc lower4a
dec CHAN4_FRQH
lower4a:
cp CHAN4_FRQH, r17
brlo skip_port4
cp r17, CHAN4_FRQH
brlo port4
cp CHAN4_FRQL, r16
brlo skip_port4
rjmp port4
skip_port4:
mov CHAN4_FRQL, r16
mov CHAN4_FRQH, r17
port4:
sts chan1_pvib, CHAN1_FRQL
sts chan1_pvib + 1, CHAN1_FRQH
sts chan2_pvib, CHAN2_FRQL
sts chan2_pvib + 1, CHAN2_FRQH
sts chan3_pvib, CHAN3_FRQL
sts chan3_pvib + 1, CHAN3_FRQH
sts chan4_pvib, CHAN4_FRQL
sts chan4_pvib + 1, CHAN4_FRQH
lds r21, vibrato_timer
inc r21
cpi r21, 0x8
brne skip27
clr r21
skip27:
sts vibrato_timer, r21
push r0
push r1
lds r16, chan1_tvib
lds r17, chan1_vib
mov r19, r17
andi r17, 0xF0
lsr r17
andi r19, 0x0F
cpi r21, 0x00
brne skip26
add r16, r17
sts chan1_tvib, r16
skip26:
sbrc r16, 7
com r16
lsr r16
lsr r16
lsr r16
mul r16, r19
mov r16, r0
lsr r16
lsr r16
lsr r16
ldi r19, 0x00
lds r18, chan1_fine
add r16, r18
add CHAN1_FRQL, r16
adc CHAN1_FRQH, r19
lds r16, chan2_tvib
lds r17, chan2_vib
mov r19, r17
andi r17, 0xF0
lsr r17
andi r19, 0x0F
cpi r21, 0x00
brne skip30
add r16, r17
sts chan2_tvib, r16
skip30:
sbrc r16, 7
com r16
lsr r16
lsr r16
lsr r16
mul r16, r19
mov r16, r0
lsr r16
lsr r16
lsr r16
ldi r19, 0x00
lds r18, chan2_fine
add r16, r18
add CHAN2_FRQL, r16
adc CHAN2_FRQH, r19
lds r16, chan3_tvib
lds r17, chan3_vib
mov r19, r17
andi r17, 0xF0
lsr r17
andi r19, 0x0F
cpi r21, 0x00
brne skip28
add r16, r17
sts chan3_tvib, r16
skip28:
sbrc r16, 7
com r16
lsr r16
lsr r16
lsr r16
mul r16, r19
mov r16, r0
lsr r16
lsr r16
lsr r16
ldi r19, 0x00
lds r18, chan3_fine
add r16, r18
add CHAN3_FRQL, r16
adc CHAN3_FRQH, r19
lds r16, chan4_tvib
lds r17, chan4_vib
mov r19, r17
andi r17, 0xF0
lsr r17
andi r19, 0x0F
cpi r21, 0x00
brne skip29
add r16, r17
sts chan4_tvib, r16
skip29:
sbrc r16, 7
com r16
lsr r16
lsr r16
lsr r16
mul r16, r19
mov r16, r0
lsr r16
lsr r16
lsr r16
ldi r19, 0x00
lds r18, chan4_fine
add r16, r18
add CHAN4_FRQL, r16
adc CHAN4_FRQH, r19
lds r16, chan1_ttrm
lds r17, chan1_trm
mov r19, r17
andi r17, 0xF0
lsr r17
andi r19, 0x0F
lsl r19
lsl r19
lsl r19
lsl r19
cpi r21, 0x00
brne skip31
add r16, r17
sts chan1_ttrm, r16
skip31:
sbrc r16, 7
com r16
lsl r16
mul r16, r19
mov r16, r1
lds r18, chan1_ptrm
clc
sbc r18, r16
brcc skip32
clr r18
skip32:
sts chan1_vol, r18
lds r16, chan2_ttrm
lds r17, chan2_trm
mov r19, r17
andi r17, 0xF0
lsr r17
andi r19, 0x0F
lsl r19
lsl r19
lsl r19
lsl r19
cpi r21, 0x00
brne skip33
add r16, r17
sts chan2_ttrm, r16
skip33:
sbrc r16, 7
com r16
lsl r16
mul r16, r19
mov r16, r1
lds r18, chan2_ptrm
clc
sbc r18, r16
brcc skip34
clr r18
skip34:
sts chan2_vol, r18
lds r16, chan3_ttrm
lds r17, chan3_trm
mov r19, r17
andi r17, 0xF0
lsr r17
andi r19, 0x0F
lsl r19
lsl r19
lsl r19
lsl r19
cpi r21, 0x00
brne skip38
add r16, r17
sts chan3_ttrm, r16
skip38:
sbrc r16, 7
com r16
lsl r16
mul r16, r19
mov r16, r1
lds r18, chan3_ptrm
clc
sbc r18, r16
brcc skip35
clr r18
skip35:
sts chan3_vol, r18
lds r16, chan4_ttrm
lds r17, chan4_trm
mov r19, r17
andi r17, 0xF0
lsr r17
andi r19, 0x0F
lsl r19
lsl r19
lsl r19
lsl r19
cpi r21, 0x00
brne skip36
add r16, r17
sts chan4_ttrm, r16
skip36:
sbrc r16, 7
com r16
lsl r16
mul r16, r19
mov r16, r1
lds r18, chan4_ptrm
clc
sbc r18, r16
brcc skip37
clr r18
skip37:
sts chan4_vol, r18
lds r16, chan1_tpwm
lds r17, chan1_pwm
mov r19, r17
andi r17, 0xF0
lsr r17
lsr r17
lsr r17
lsr r17
andi r19, 0x0F
lsl r19
lsl r19
lsl r19
lsl r19
cpi r21, 0x00
brne skip39
add r16, r17
sts chan1_tpwm, r16
skip39:
sbrc r16, 7
com r16
mul r16, r19
mov r16, r1
lds r18, chan1_tdut
add r18, r16
sts chan1_dut, r18
lds r16, chan2_tpwm
lds r17, chan2_pwm
mov r19, r17
andi r17, 0xF0
lsr r17
lsr r17
lsr r17
lsr r17
andi r19, 0x0F
lsl r19
lsl r19
lsl r19
lsl r19
cpi r21, 0x00
brne skip40
add r16, r17
sts chan2_tpwm, r16
skip40:
sbrc r16, 7
com r16
mul r16, r19
mov r16, r1
lds r18, chan2_tdut
add r18, r16
sts chan2_dut, r18
lds r16, chan3_tpwm
lds r17, chan3_pwm
mov r19, r17
andi r17, 0xF0
lsr r17
lsr r17
lsr r17
lsr r17
andi r19, 0x0F
lsl r19
lsl r19
lsl r19
lsl r19
cpi r21, 0x00
brne skip41
add r16, r17
sts chan3_tpwm, r16
skip41:
sbrc r16, 7
com r16
mul r16, r19
mov r16, r1
lds r18, chan3_tdut
add r18, r16
sts chan3_dut, r18
lds r16, chan4_tpwm
lds r17, chan4_pwm
mov r19, r17
andi r17, 0xF0
lsr r17
lsr r17
lsr r17
lsr r17
andi r19, 0x0F
lsl r19
lsl r19
lsl r19
lsl r19
cpi r21, 0x00
brne skip42
add r16, r17
sts chan4_tpwm, r16
skip42:
sbrc r16, 7
com r16
mul r16, r19
mov r16, r1
lds r18, chan4_tdut
add r18, r16
sts chan4_dut, r18
pop r1
pop r0
skip:
lds r16, tick_timer
inc r16
sts tick_timer, r16
pop r23
pop r22
pop r21
pop r20
pop r19
pop r18
pop r17
pop r16
reti
handle_note:
lds r18, arp_timer
inc r18
mov r19, r18
lsr r19
lsr r19
lsr r19
lsr r19
lsr r19
andi r19, 0b11
cpi r19, 0b11
brne skip5
ldi r18, 0x0
skip5:
sts arp_timer, r18
sbrc r19, 0
rjmp arp1
sbrc r19, 1
rjmp arp2
rjmp skip6
arp1:
andi r17, 0xF0
add r16, r17
brcc skip6
ldi r18, 0x41
add r16, r18
arp2:
lsl r17
lsl r17
lsl r17
lsl r17
add r16, r17
brcc skip6
ldi r18, 0x41
add r16, r18
skip6:
rjmp note_freq
note_freq:
mov r19, r16
lsr r16
lsr r16
lsr r16
lsr r16
cpi r16, 0x04
breq setc
cpi r16, 0x05
breq setcs
cpi r16, 0x06
breq setd
cpi r16, 0x07
breq setds
cpi r16, 0x08
breq sete
cpi r16, 0x09
breq setf
cpi r16, 0x0A
breq setfs
cpi r16, 0x0B
breq setg
cpi r16, 0x0C
breq setgs
cpi r16, 0x0D
breq seta
cpi r16, 0x0E
breq setas
cpi r16, 0x0F
breq setb
ret
setc:
ldi r16, LOW(NOTE_C)
ldi r17, HIGH(NOTE_C)
rjmp skip4
setcs:
ldi r16, LOW(NOTE_Cs)
ldi r17, HIGH(NOTE_Cs)
rjmp skip4
setd:
ldi r16, LOW(NOTE_D)
ldi r17, HIGH(NOTE_D)
rjmp skip4
setds:
ldi r16, LOW(NOTE_Ds)
ldi r17, HIGH(NOTE_Ds)
rjmp skip4
sete:
ldi r16, LOW(NOTE_E)
ldi r17, HIGH(NOTE_E)
rjmp skip4
setf:
ldi r16, LOW(NOTE_F)
ldi r17, HIGH(NOTE_F)
rjmp skip4
setfs:
ldi r16, LOW(NOTE_Fs)
ldi r17, HIGH(NOTE_Fs)
rjmp skip4
setg:
ldi r16, LOW(NOTE_G)
ldi r17, HIGH(NOTE_G)
rjmp skip4
setgs:
ldi r16, LOW(NOTE_Gs)
ldi r17, HIGH(NOTE_Gs)
rjmp skip4
seta:
ldi r16, LOW(NOTE_A)
ldi r17, HIGH(NOTE_A)
rjmp skip4
setas:
ldi r16, LOW(NOTE_As)
ldi r17, HIGH(NOTE_As)
rjmp skip4
setb:
ldi r16, LOW(NOTE_B)
ldi r17, HIGH(NOTE_B)
rjmp skip4
skip4:
andi r19, 0x0F
cpi r19, 0x01
breq seto1
cpi r19, 0x02
breq seto2
cpi r19, 0x03
breq seto3
cpi r19, 0x04
breq seto4
cpi r19, 0x05
breq seto5
cpi r19, 0x06
breq seto6
cpi r19, 0x07
breq seto7
cpi r19, 0x08
breq seto8
ret
seto1:
lsr r17
ror r16
lsr r17
ror r16
lsr r17
ror r16
ret
seto2:
lsr r17
ror r16
lsr r17
ror r16
ret
seto3:
lsr r17
ror r16
ret
seto4:
ret
seto5:
lsl r16
rol r17
ret
seto6:
lsl r16
rol r17
lsl r16
rol r17
ret
seto7:
lsl r16
rol r17
lsl r16
rol r17
lsl r16
rol r17
ret
seto8:
lsl r16
rol r17
lsl r16
rol r17
lsl r16
rol r17
lsl r16
rol r17
ret
generate:
clr r17
cpi r18, 0b00
breq gen_pulse
cpi r18, 0b01
breq gen_triangle
cpi r18, 0b10
breq gen_saw
cpi r18, 0b11
breq gen_noise
ret
gen_pulse:
cp r19, r21
brlo return
mov r17, r20
lsr r17
lsr r17
return:
ret
gen_triangle:
mov r17, r19
sbrc r19, 7
com r17
rjmp apply_volume
ret
gen_saw:
mov r17, r19
lsr r17
lsr r17
rjmp apply_volume
ret
gen_noise:
clr r21
clr r19
sbrc NOISEH, 6
ser r21
sbrc NOISEL, 3
ser r19
eor r19, r21
clr r21
lsl NOISEH
lsl NOISEL
adc NOISEH, r21
sbrc r19, 0
inc NOISEL
mov r17, NOISEL
lsr r17
rjmp apply_volume
ret
apply_volume:
push r0
push r1
mul r17, r20
mov r17, r1
pop r1
pop r0
ret
.dseg
command_buf: .byte 2
chan1_vol: .byte 1
chan2_vol: .byte 1
chan3_vol: .byte 1
chan4_vol: .byte 1
chan1_dut: .byte 1
chan2_dut: .byte 1
chan3_dut: .byte 1
chan4_dut: .byte 1
waveform: .byte 1
enable: .byte 1
tick_timer: .byte 1
arp_timer: .byte 1
slide_timer: .byte 1
vibrato_timer: .byte 1
note_on: .byte 1
; 0 - 3: note
; 4 - 7: octave
; 8 - 11: arp 1
; 12 - 15: arp 2
chan1_notes: .byte 2
chan2_notes: .byte 2
chan3_notes: .byte 2
chan4_notes: .byte 2
chan1_tpit: .byte 2
chan2_tpit: .byte 2
chan3_tpit: .byte 2
chan4_tpit: .byte 2
chan1_tvol: .byte 1
chan2_tvol: .byte 1
chan3_tvol: .byte 1
chan4_tvol: .byte 1
chan1_tdut: .byte 1
chan2_tdut: .byte 1
chan3_tdut: .byte 1
chan4_tdut: .byte 1
; 0 - down
; 1 - up
vol_slide_dir: .byte 1
pit_slide_dir: .byte 1
chan1_vol_slide: .byte 1
chan2_vol_slide: .byte 1
chan3_vol_slide: .byte 1
chan4_vol_slide: .byte 1
chan1_pit_slide: .byte 1
chan2_pit_slide: .byte 1
chan3_pit_slide: .byte 1
chan4_pit_slide: .byte 1
chan1_port: .byte 1
chan2_port: .byte 1
chan3_port: .byte 1
chan4_port: .byte 1
; 0 - 3: rate
; 4 - 7: depth
chan1_vib: .byte 1
chan2_vib: .byte 1
chan3_vib: .byte 1
chan4_vib: .byte 1
chan1_trm: .byte 1
chan2_trm: .byte 1
chan3_trm: .byte 1
chan4_trm: .byte 1
chan1_pwm: .byte 1
chan2_pwm: .byte 1
chan3_pwm: .byte 1
chan4_pwm: .byte 1
chan1_pvib: .byte 2
chan2_pvib: .byte 2
chan3_pvib: .byte 2
chan4_pvib: .byte 2
chan1_ptrm: .byte 2
chan2_ptrm: .byte 2
chan3_ptrm: .byte 2
chan4_ptrm: .byte 2
chan1_tvib: .byte 1
chan2_tvib: .byte 1
chan3_tvib: .byte 1
chan4_tvib: .byte 1
chan1_ttrm: .byte 1
chan2_ttrm: .byte 1
chan3_ttrm: .byte 1
chan4_ttrm: .byte 1
chan1_tpwm: .byte 1
chan2_tpwm: .byte 1
chan3_tpwm: .byte 1
chan4_tpwm: .byte 1
; 0 - 3: attack
; 4 - 7: decay
; 8 - 11: sustain
; 12 - 15: release
chan1_env: .byte 2
chan2_env: .byte 2
chan3_env: .byte 2
chan4_env: .byte 2
chan1_fine: .byte 1
chan2_fine: .byte 1
chan3_fine: .byte 1
chan4_fine: .byte 1
// Feature complete!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ncurses.h>
#define NOTE_NULL 0x0
#define NOTE_RESET 0x1
#define NOTE_ON 0x2
#define NOTE_OFF 0x3
#define NOTE_C 0x4
#define NOTE_Cs 0x5
#define NOTE_Db 0x5
#define NOTE_D 0x6
#define NOTE_Ds 0x7
#define NOTE_Eb 0x7
#define NOTE_E 0x8
#define NOTE_F 0x9
#define NOTE_Fs 0xA
#define NOTE_Gb 0xA
#define NOTE_G 0xB
#define NOTE_Gs 0xC
#define NOTE_Ab 0xC
#define NOTE_A 0xD
#define NOTE_As 0xE
#define NOTE_Bb 0xE
#define NOTE_B 0xF
#define EFF_ARPEGGIO 0x0
#define EFF_PITCH_SLIDE_UP 0x1
#define EFF_PITCH_SLIDE_DOWN 0x2
#define EFF_PORTAMENTO 0x3
#define EFF_VIBRATO 0x4
#define EFF_WAVEFORM 0x5
#define EFF_DUTY_CYCLE 0x6
#define EFF_TREMOLO 0x7
#define EFF_PWM 0x8
#define EFF_PITCH 0x9
#define EFF_VOLUME_SLIDE_UP 0xA
#define EFF_VOLUME_SLIDE_DOWN 0xB
#define EFF_VOLUME 0xC
#define EFF_ENVELOPE_ATTACK_DECAY 0xD
#define EFF_ENVELOPE_SUSTAIN_RELEASE 0xE
#define EFF_FINE_TUNE 0xF
FILE *out;
char serialPort[255];
typedef struct
{
unsigned char enable;
unsigned char effect;
unsigned char argument;
} Effect;
typedef struct
{
unsigned char note;
unsigned char octave;
unsigned char volume;
unsigned char applyInstrument;
unsigned char instrument;
} NoteCommand;
typedef struct
{
NoteCommand noteCommand;
Effect effect;
} Entry;
typedef struct
{
Entry entries[4];
} Frame;
typedef struct
{
Effect effects[8];
} Instrument;
typedef struct
{
Frame frames[32];
} Order;
typedef struct
{
unsigned char fps;
unsigned char orderCount;
unsigned char repeat;
unsigned char instrumentCount;
Instrument *instruments;
Order *orders;
} Song;
Song song;
void printTabs(int tabs)
{
int i;
for(i = 0; i < tabs; i++)
{
printf("\t");
}
}
void printEffect(Effect *effect, int tabs)
{
int s1, s2;
s1 = (effect->argument) >> 4;
s2 = (effect->argument) & 0x0F;
printTabs(tabs);
if(effect->enable)
{
switch(effect->effect)
{
case 0x0:
if(s1 || s2)
{
printf("Arpeggio: Semitones: %i, %i\n", s1, s2);
}
else
{
printf("Arpeggio Off\n");
}
break;
case 0x1:
printf("Pitch Slide Up: Rate: %i\n", effect->argument);
break;
case 0x2:
printf("Pitch Slide Down: Rate: %i\n", effect->argument);
break;
case 0x3:
if(effect->argument)
{
printf("Portamento: Rate: %i\n", effect->argument);
}
else
{
printf("Portamento Off");
}
break;
case 0x4:
printf("Vibrato: Rate: %i, Depth: %i\n", s1, s2);
break;
case 0x5:
printf("Waveform: ");
switch(effect->argument)
{
case 0:
printf("Pulse\n");
break;
case 1:
printf("Triangle\n");
break;
case 2:
printf("Sawtooth\n");
break;
case 3:
printf("Noise\n");
break;
default:
printf("Unknown\n");
}
break;
case 0x6:
printf("Duty Cycle: %i\%\n", effect->argument / 256 * 100);
break;
case 0x7:
printf("Tremolo: Rate: %i, Depth: %i\n", s1, s2);
break;
case 0x8:
printf("Pulse Width Modulation: Rate: %i, Depth: %i\n", s1, s2);
break;
case 0x9:
printf("Pitch: %i\n", effect->argument);
break;
case 0xA:
printf("Volume Slide Up: Rate: %i\n", effect->argument);
break;
case 0xB:
printf("Volume Slide Down: Rate: %i\n", effect->argument);
break;
case 0xC:
printf("Volume: %i\n", effect->argument);
break;
case 0xD:
printf("Envelope: Attack: %i, Decay: %i\n", s1, s2);
break;
case 0xE:
printf("Envelope: Sustain: %i\%, Release: %i\n", s1 / 16 * 100, s2);
break;
case 0xF:
printf("Fine Tune: %i\n", effect->argument);
break;
default:
printf("Unknown Effect\n");
}
}
else
{
printf("No Effect\n");
}
}
void printInstrumentInfo(Instrument *instrument, int tabs)
{
int i;
for(i = 0; i < sizeof(instrument->effects) / sizeof(Effect); i++)
{
printTabs(tabs);
printf("Effect #%i:\n", i + 1);
printEffect(&(instrument->effects[i]), tabs + 1);
}
}
void printNote(unsigned char note, unsigned char octave, int tabs)
{
printTabs(tabs);
switch(note)
{
case 0x0:
printf("No Note\n");
break;
case 0x1:
printf("Reset Oscillator Timer\n");
break;
case 0x2:
printf("Enable Oscillator\n");
break;
case 0x3:
printf("Disable Oscillator\n");
break;
case 0x4:
printf("Play Note C%i\n", octave);
break;
case 0x5:
printf("Play Note C#%i\n", octave);
break;
case 0x6:
printf("Play Note D%i\n", octave);
break;
case 0x7:
printf("Play Note D#%i\n", octave);
break;
case 0x8:
printf("Play Note E%i\n", octave);
break;
case 0x9:
printf("Play Note F%i\n", octave);
break;
case 0xA:
printf("Play Note F#%i\n", octave);
break;
case 0xB:
printf("Play Note G%i\n", octave);
break;
case 0xC:
printf("Play Note G#%i\n", octave);
break;
case 0xD:
printf("Play Note A%i\n", octave);
break;
case 0xE:
printf("Play Note A#%i\n", octave);
break;
case 0xF:
printf("Play Note B%i\n", octave);
break;
default:
printf("Unknown Command\n");
}
}
void printVolume(unsigned char volume, int tabs)
{
printTabs(tabs);
if(volume)
{
printf("Volume: %i\%\n", volume / 32 * 100);
}
else
{
printf("Ignore Volume\n");
}
}
void printInstrumentID(unsigned char applyInstrument, unsigned char instrument, int tabs)
{
printTabs(tabs);
if(applyInstrument)
{
printf("Apply Instrument #%i\n", instrument + 1);
}
else
{
printf("Ignore Instrument\n");
}
}
void printNoteCommand(NoteCommand *noteCommand, int tabs)
{
printNote(noteCommand->note, noteCommand->octave, tabs);
printVolume(noteCommand->volume, tabs);
printInstrumentID(noteCommand->applyInstrument, noteCommand->instrument, tabs);
}
void printEntry(Entry *entry, int tabs)
{
printNoteCommand(&(entry->noteCommand), tabs);
printEffect(&(entry->effect), tabs);
}
void printFrame(Frame *frame, int tabs)
{
int i;
for(i = 0; i < sizeof(frame->entries) / sizeof(Entry); i++)
{
printTabs(tabs);
printf("Channel #%i:\n", i + 1);
printEntry(&(frame->entries[i]), tabs + 1);
}
}
void printOrderInfo(Order *order, int tabs)
{
int i;
for(i = 0; i < sizeof(order->frames) / sizeof(Frame); i++)
{
printTabs(tabs);
printf("Frame #%i:\n", i + 1);
printFrame(&(order->frames[i]), tabs + 1);
}
}
void printSongInfo(Song *song)
{
printf("Speed: %i FPS\n", song->fps);
printf("Length: %i Orders\n", song->orderCount);
printf("Repeat: Order #%i\n", song->repeat + 1);
printf("Instrument Count: %i\n", song->instrumentCount);
printf("Instruments:\n");
int i;
for(i = 0; i < song->instrumentCount; i++)
{
printf("\tInstrument #%i:\n", i + 1);
printInstrumentInfo(&(song->instruments[0]), 2);
}
printf("Orders:\n");
for(i = 0; i < song->orderCount; i++)
{
printf("\tOrder #%i:\n", i + 1);
printOrderInfo(&(song->orders[0]), 2);
}
}
void playNote(unsigned char note, unsigned char octave, unsigned char volume, unsigned char osc)
{
note &= 0x0F;
octave &= 0b00000111;
volume &= 0b00011110; // weird bug causing the last bit to make the octave go really high
osc &= 0b00000011;
char highByte = (octave >> 1) | (volume << 2) | (1 << 7);
char lowByte = (osc << 1) | (note << 3) | (octave << 7);
fputc(highByte, out);
fputc(lowByte, out);
fputc(0xFF, out);
fclose(out);
out = fopen(serialPort, "w");
}
void playEffect(unsigned char effect, unsigned char argument, unsigned char osc)
{
effect &= 0x0F;
osc &= 0b00000011;
char highByte = (argument >> 1) | (1 << 7);
char lowByte = 1 | (osc << 1) | (effect << 3) | (argument << 7);
fputc(highByte, out);
fputc(lowByte, out);
fputc(0xFF, out);
fclose(out);
out = fopen(serialPort, "w");
}
void sendEffectCommand(Effect *effect, unsigned char osc)
{
if(effect->enable)
{
playEffect(effect->effect, effect->argument, osc);
}
}
void sendNoteCommand(NoteCommand *noteCommand, unsigned char osc)
{
if(noteCommand->note || noteCommand->octave || noteCommand->volume)
{
if(noteCommand->applyInstrument)
{
int i;
for(i = 0; i < 8; i++)
{
Effect effect = song.instruments[noteCommand->instrument].effects[i];
sendEffectCommand(&effect, osc);
}
}
playNote(noteCommand->note, noteCommand->octave, noteCommand->volume, osc);
}
}
void playEntry(Entry *entry, unsigned char osc)
{
sendEffectCommand(&entry->effect, osc);
sendNoteCommand(&entry->noteCommand, osc);
}
void playFrame(Frame *frame)
{
playEntry(&frame->entries[0], 0);
playEntry(&frame->entries[1], 1);
playEntry(&frame->entries[2], 2);
playEntry(&frame->entries[3], 3);
}
NoteCommand newNoteCommand(unsigned char note, unsigned char octave, unsigned char volume, unsigned char applyInstrument, unsigned char instrument)
{
NoteCommand command;
command.note = note;
command.octave = octave;
command.volume = volume;
command.applyInstrument = applyInstrument;
command.instrument = instrument;
return command;
}
NoteCommand newSimpleNoteCommand(unsigned char note, unsigned char octave, unsigned char volume)
{
return newNoteCommand(note, octave, volume, 0, 0);
}
NoteCommand newSimplestNoteCommand(unsigned char note, unsigned char octave)
{
return newSimpleNoteCommand(note, octave, 0);
}
NoteCommand newNullNoteCommand()
{
return newSimplestNoteCommand(NOTE_NULL, 0);
}
NoteCommand newResetNoteCommand()
{
return newSimplestNoteCommand(NOTE_RESET, 0);
}
NoteCommand newNoteOnCommand()
{
return newSimplestNoteCommand(NOTE_ON, 0);
}
NoteCommand newNoteOffCommand()
{
return newSimplestNoteCommand(NOTE_OFF, 0);
}
Effect newEffect(unsigned char effect, unsigned char argument)
{
Effect new_effect;
new_effect.enable = 1;
new_effect.effect = effect;
new_effect.argument = argument;
return new_effect;
}
Effect newNullEffect()
{
Effect effect;
effect.enable = 0;
effect.effect = 0;
effect.argument = 0;
return effect;
}
Entry newEntry(NoteCommand command, Effect effect)
{
Entry entry;
entry.noteCommand = command;
entry.effect = effect;
return entry;
}
Entry newSimpleEntry(NoteCommand command)
{
return newEntry(command, newNullEffect());
}
Entry newNullEntry()
{
return newEntry(newNullNoteCommand(), newNullEffect());
}
Frame newFrame(Entry entry1, Entry entry2, Entry entry3, Entry entry4)
{
Frame frame;
frame.entries[0] = entry1;
frame.entries[1] = entry2;
frame.entries[2] = entry3;
frame.entries[3] = entry4;
return frame;
}
Frame newUnisonFrame(Entry entry)
{
return newFrame(entry, entry, entry, entry);
}
Frame newQuietFrame()
{
return newUnisonFrame(newSimpleEntry(newNoteOffCommand()));
}
Order *newEmptyOrder()
{
Order *order;
int i;
for(i = 0; i < sizeof(order->frames) / sizeof(Frame); i++)
{
order->frames[i] = newQuietFrame();
}
return order;
}
WINDOW *orderWindow;
WINDOW *instrumentWindow;
WINDOW *effectWindow;
WINDOW *trackerWindow;
int w1, w2, h1, h2;
#define WIN_ORDER 0
#define WIN_INSTR 1
#define WIN_EFFECT 2
#define WIN_TRACKER 3
int curWindow = WIN_TRACKER;
#define MODE_EDIT 0
#define MODE_PLAY 1
#define MODE_JAM 2
int curMode = MODE_EDIT;
#define MAX_ORDERS 64
#define MAX_INSTRUMENTS 16
int curOrder = 0;
int curInstrument = 0;
int curEffect = 0;
int orderScroll = 0;
int instrumentScroll = 0;
int curOsc = 0;
int curFrame = 0;
int curCol = 0;
int trackerScroll = 0;
int curOctave = 4;
void placeCursor()
{
move(LINES - 1, COLS - 1);
}
void printTitle(WINDOW *window, char *title)
{
int h, w;
getmaxyx(window, h, w);
wattron(window, A_BOLD);
mvwprintw(window, 0,( w - strlen(title)) / 2, "%s", title);
}
void drawOrderWindow()
{
wclear(orderWindow);
wattrset(orderWindow, A_NORMAL);
if(curWindow == WIN_ORDER && curMode == MODE_EDIT) wattrset(orderWindow, A_REVERSE);
box(orderWindow, 0, 0);
char t[20];
sprintf(t, "Orders (%i)", song.orderCount);
printTitle(orderWindow, t);
wattrset(orderWindow, A_NORMAL);
int i;
int a;
for(i = orderScroll; i < song.orderCount && i < h1 - 2 + orderScroll; i++)
{
wattrset(orderWindow, curOrder == i ? A_REVERSE : A_NORMAL);
for(a = 0; a < w1 - 2; a++)
{
mvwprintw(orderWindow, i + 1 - orderScroll, a + 1, " ");
}
mvwprintw(orderWindow, i + 1 - orderScroll, 1, "%c 0x%x: ", song.repeat == i ? '*' : ' ', i);
}
wrefresh(orderWindow);
placeCursor();
}
void drawInstrumentWindow()
{
wclear(instrumentWindow);
wattrset(instrumentWindow, A_NORMAL);
if(curWindow == WIN_INSTR && curMode == MODE_EDIT) wattrset(instrumentWindow, A_REVERSE);
box(instrumentWindow, 0, 0);
char t[20];
sprintf(t, "Instruments (%i)", song.instrumentCount);
printTitle(instrumentWindow, t);
wattrset(instrumentWindow, A_NORMAL);
int i;
int a;
for(i = instrumentScroll; i < song.instrumentCount && i < h1 - 2 + instrumentScroll; i++)
{
wattrset(instrumentWindow, curInstrument == i ? A_REVERSE : A_NORMAL);
for(a = 0; a < w1 - 2; a++)
{
mvwprintw(instrumentWindow, i + 1 - instrumentScroll, a + 1, " ");
}
mvwprintw(instrumentWindow, i + 1 - instrumentScroll, 1, "0x%x: ", i);
}
wrefresh(instrumentWindow);
placeCursor();
}
void drawEffectWindow()
{
wclear(effectWindow);
wattrset(effectWindow, A_NORMAL);
if(curWindow == WIN_EFFECT && curMode == MODE_EDIT) wattrset(effectWindow, A_REVERSE);
box(effectWindow, 0, 0);
printTitle(effectWindow, "Effects");
wattrset(effectWindow, A_NORMAL);
Instrument *instrument = &song.instruments[curInstrument];
int i;
int a;
for(i = 0; i < sizeof(instrument->effects) / sizeof(Effect); i++)
{
Effect effect = instrument->effects[i];
wattrset(effectWindow, curEffect == i ? A_REVERSE : A_NORMAL);
for(a = 0; a < w1 - 2; a++)
{
mvwprintw(effectWindow, i + 1, a + 1, " ");
}
if(!effect.enable)
{
mvwprintw(effectWindow, i + 1, 1, "0x%x: DISABLED", i);
}
else
{
char eff[20];
switch(effect.effect)
{
case 0x0:
strcpy(eff, "ARPEGGIO: ");
break;
case 0x1:
strcpy(eff, "PORT UP: ");
break;
case 0x2:
strcpy(eff, "PORT DOWN: ");
break;
case 0x3:
strcpy(eff, "PORTAMENTO:");
break;
case 0x4:
strcpy(eff, "VIBRATO: ");
break;
case 0x5:
strcpy(eff, "WAVEFORM: ");
break;
case 0x6:
strcpy(eff, "DUTY CYCLE:");
break;
case 0x7:
strcpy(eff, "TREMOLO: ");
break;
case 0x8:
strcpy(eff, "PWM: ");
break;
case 0x9:
strcpy(eff, "PITCH: ");
break;
case 0xA:
strcpy(eff, "VOL UP: ");
break;
case 0xB:
strcpy(eff, "VOL DOWN: ");
break;
case 0xC:
strcpy(eff, "SET VOL: ");
break;
case 0xD:
strcpy(eff, "ATCK/DECAY:");
break;
case 0xE:
strcpy(eff, "SUS/RELEAS:");
break;
case 0xF:
strcpy(eff, "FINE TUNE: ");
break;
}
mvwprintw(effectWindow, i + 1, 1, "0x%x: (0x%x) %s 0x%x%x", i, effect.effect, eff, (effect.argument & 0xF0) >> 4, effect.argument & 0xF);
}
}
wrefresh(effectWindow);
placeCursor();
}
void drawTrackerWindow()
{
wclear(trackerWindow);
wattrset(trackerWindow, A_NORMAL);
if(curWindow == WIN_TRACKER && curMode == MODE_EDIT) wattrset(trackerWindow, A_REVERSE);
wattron(trackerWindow, A_BOLD);
box(trackerWindow, 0, 0);
wattroff(trackerWindow, A_BOLD);
printTitle(trackerWindow, "Tracker");
wattrset(trackerWindow, A_NORMAL);
int i;
for(i = -2 + trackerScroll; i <= 32 && i < h2 - 4 + trackerScroll; i++)
{
wattrset(trackerWindow, A_BOLD);
if(i - trackerScroll >= 0 && i - trackerScroll < 32)
{
if(curFrame == i) wattron(trackerWindow, A_REVERSE);
mvwprintw(trackerWindow, i + 3 - trackerScroll, 1, "0x%x%x ", (i & 0xF0) >> 4, i & 0xF);
}
else if(i - trackerScroll == -2)
{
mvwprintw(trackerWindow, i + 3 - trackerScroll, 1, "(0x%x)", curOrder);
}
else if(i - trackerScroll == -1 || i == 32)
{
wattrset(trackerWindow, A_NORMAL);
wmove(trackerWindow, i + 3 - trackerScroll, 1);
int a;
for(a = 0; a < 5; a++)
{
waddch(trackerWindow, ACS_HLINE);
}
}
wattrset(trackerWindow, A_NORMAL);
waddch(trackerWindow, ACS_VLINE);
int a;
for(a = 0; a < 4; a++)
{
if(i - trackerScroll == -2)
{
wattrset(trackerWindow, A_BOLD);
if(curOsc == a) wattron(trackerWindow, A_REVERSE);
mvwprintw(trackerWindow, i + 3 - trackerScroll, a * 10 + 7, "Osc #%i ", a + 1);
wattrset(trackerWindow, A_NORMAL);
}
else if(i - trackerScroll == -1 || i == 32)
{
wmove(trackerWindow, i + 3 - trackerScroll, a * 10 + 7);
int a;
for(a = 0; a < 9; a++)
{
waddch(trackerWindow, ACS_HLINE);
}
}
else
{
Entry entry = song.orders[curOrder].frames[i].entries[a];
wattrset(trackerWindow, curFrame == i && curOsc == a && curCol == 0 ? A_REVERSE : A_NORMAL);
switch(entry.noteCommand.note)
{
case 0x0:
mvwprintw(trackerWindow, i + 3 - trackerScroll, a * 10 + 7, "---");
break;
case 0x1:
mvwprintw(trackerWindow, i + 3 - trackerScroll, a * 10 + 7, "RES");
break;
case 0x2:
mvwprintw(trackerWindow, i + 3 - trackerScroll, a * 10 + 7, "+++");
break;
case 0x3:
mvwprintw(trackerWindow, i + 3 - trackerScroll, a * 10 + 7, "xxx");
break;
case 0x4:
mvwprintw(trackerWindow, i + 3 - trackerScroll, a * 10 + 7, "C-%x", entry.noteCommand.octave);
break;
case 0x5:
mvwprintw(trackerWindow, i + 3 - trackerScroll, a * 10 + 7, "C#%x", entry.noteCommand.octave);
break;
case 0x6:
mvwprintw(trackerWindow, i + 3 - trackerScroll, a * 10 + 7, "D-%x", entry.noteCommand.octave);
break;
case 0x7:
mvwprintw(trackerWindow, i + 3 - trackerScroll, a * 10 + 7, "D#%x", entry.noteCommand.octave);
break;
case 0x8:
mvwprintw(trackerWindow, i + 3 - trackerScroll, a * 10 + 7, "E-%x", entry.noteCommand.octave);
break;
case 0x9:
mvwprintw(trackerWindow, i + 3 - trackerScroll, a * 10 + 7, "F-%x", entry.noteCommand.octave);
break;
case 0xA:
mvwprintw(trackerWindow, i + 3 - trackerScroll, a * 10 + 7, "F#%x", entry.noteCommand.octave);
break;
case 0xB:
mvwprintw(trackerWindow, i + 3 - trackerScroll, a * 10 + 7, "G-%x", entry.noteCommand.octave);
break;
case 0xC:
mvwprintw(trackerWindow, i + 3 - trackerScroll, a * 10 + 7, "G#%x", entry.noteCommand.octave);
break;
case 0xD:
mvwprintw(trackerWindow, i + 3 - trackerScroll, a * 10 + 7, "A-%x", entry.noteCommand.octave);
break;
case 0xE:
mvwprintw(trackerWindow, i + 3 - trackerScroll, a * 10 + 7, "A#%x", entry.noteCommand.octave);
break;
case 0xF:
mvwprintw(trackerWindow, i + 3 - trackerScroll, a * 10 + 7, "B-%x", entry.noteCommand.octave);
break;
}
wattrset(trackerWindow, curFrame == i && curOsc == a && curCol == 1 ? A_REVERSE : A_NORMAL);
if(entry.noteCommand.applyInstrument)
{
wprintw(trackerWindow, "%x", entry.noteCommand.instrument);
}
else
{
wprintw(trackerWindow, "-", entry.noteCommand.instrument);
}
wattrset(trackerWindow, curFrame == i && curOsc == a && curCol == 2 ? A_REVERSE : A_NORMAL);
if(entry.noteCommand.volume)
{
wprintw(trackerWindow, "%x%x", entry.noteCommand.volume >> 4, entry.noteCommand.volume & 0xF);
}
else
{
wprintw(trackerWindow, "--");
}
if(entry.effect.enable)
{
wattrset(trackerWindow, curFrame == i && curOsc == a && curCol == 3 ? A_REVERSE : A_NORMAL);
wprintw(trackerWindow, "%x", entry.effect.effect & 0xF);
wattrset(trackerWindow, curFrame == i && curOsc == a && curCol == 4 ? A_REVERSE : A_NORMAL);
wprintw(trackerWindow, "%x%x", entry.effect.argument >> 4, entry.effect.argument & 0xF);
}
else
{
wattrset(trackerWindow, curFrame == i && curOsc == a && curCol == 3 ? A_REVERSE : A_NORMAL);
wprintw(trackerWindow, "-");
wattrset(trackerWindow, curFrame == i && curOsc == a && curCol == 4 ? A_REVERSE : A_NORMAL);
wprintw(trackerWindow, "--");
}
}
wattrset(trackerWindow, A_NORMAL);
waddch(trackerWindow, ACS_VLINE);
}
}
wrefresh(trackerWindow);
placeCursor();
}
void drawWindows()
{
drawOrderWindow();
drawInstrumentWindow();
drawEffectWindow();
drawTrackerWindow();
placeCursor();
refresh();
}
void spam(int c, int y)
{
move(y, 0);
int i;
for(i = 0; i < COLS; i++)
{
printw("%c", c);
}
}
void drawStatus()
{
char modeString[32];
switch(curMode)
{
case MODE_EDIT:
strcpy(modeString, "Edit");
break;
case MODE_PLAY:
strcpy(modeString, "Play");
break;
case MODE_JAM:
strcpy(modeString, "Jam");
break;
}
attrset(A_NORMAL);
spam(' ', LINES - 1);
mvprintw(LINES - 1, 0, "MODE: %s | OCTAVE: %i | SPEED: %i FPS | REPEAT: 0x%x | ORDERS: %i | INSTRUMENTS: %i", modeString, curOctave, song.fps, song.repeat, song.orderCount, song.instrumentCount);
placeCursor();
refresh();
}
void initWindows()
{
w1 = COLS / 3;
h1 = 10;
w2 = w1 * 3;
h2 = LINES - h1 - 1;
orderWindow = newwin(h1, w1, 0, 0);
instrumentWindow = newwin(h1, w1, 0, w1);
effectWindow = newwin(h1, w1, 0, w1 * 2);
trackerWindow = newwin(h2, w2, h1, 0);
clear();
refresh();
drawWindows();
drawStatus();
placeCursor();
refresh();
}
void scrollTracker()
{
while(curFrame > h2 / 2 + trackerScroll) trackerScroll++;
while(curFrame < trackerScroll) trackerScroll--;
}
void down()
{
curFrame = (curFrame + 1) % 32;
scrollTracker();
}
void up()
{
curFrame--;
if(curFrame < 0) curFrame = 31;
scrollTracker();
}
Order clipboard;
void editInput(int c)
{
Entry *entry = &song.orders[curOrder].frames[curFrame].entries[curOsc];
switch(c)
{
case '\t':
curWindow = (curWindow + 1) % 4;
drawWindows();
break;
default:
switch(curWindow)
{
case WIN_ORDER:
switch(c)
{
case KEY_DOWN:
curOrder = (curOrder + 1) % song.orderCount;
while(curOrder >= orderScroll + h1 - 2)
{
orderScroll++;
}
while(curOrder < orderScroll)
{
orderScroll--;
}
drawOrderWindow();
drawTrackerWindow();
break;
case KEY_UP:
curOrder = (curOrder - 1);
while(curOrder < 0) curOrder += song.orderCount;
while(curOrder >= orderScroll + h1 - 2)
{
orderScroll++;
}
while(curOrder < orderScroll)
{
orderScroll--;
}
drawOrderWindow();
drawTrackerWindow();
break;
case 'r':
song.repeat = curOrder;
drawOrderWindow();
break;
case 'c':
clipboard = song.orders[curOrder];
break;
case 'v':
song.orders[curOrder] = clipboard;
drawTrackerWindow();
case '=':
song.orderCount++;
if(song.orderCount > MAX_ORDERS) song.orderCount = MAX_ORDERS;
drawOrderWindow();
drawStatus();
break;
case '-':
song.orderCount--;
if(song.orderCount <= 0) song.orderCount = 1;
if(curOrder >= song.orderCount) curOrder = song.orderCount - 1;
while(curOrder >= orderScroll + h1 - 2)
{
orderScroll++;
}
while(curOrder < orderScroll)
{
orderScroll--;
}
if(song.repeat >= song.orderCount) song.repeat--;
drawOrderWindow();
drawStatus();
break;
}
break;
case WIN_INSTR:
switch(c)
{
case KEY_DOWN:
curInstrument = (curInstrument + 1) % song.instrumentCount;
while(curInstrument >= instrumentScroll + h1 - 2)
{
instrumentScroll++;
}
while(curInstrument < instrumentScroll)
{
instrumentScroll--;
}
drawInstrumentWindow();
drawEffectWindow();
break;
case KEY_UP:
curInstrument = (curInstrument - 1);
while(curInstrument < 0) curInstrument += song.instrumentCount;
while(curInstrument >= instrumentScroll + h1 - 2)
{
instrumentScroll++;
}
while(curInstrument < instrumentScroll)
{
instrumentScroll--;
}
drawInstrumentWindow();
drawEffectWindow();
break;
case '=':
song.instrumentCount++;
if(song.instrumentCount > MAX_INSTRUMENTS) song.instrumentCount = MAX_INSTRUMENTS;
drawInstrumentWindow();
drawStatus();
break;
case '-':
song.instrumentCount--;
if(song.instrumentCount <= 0) song.instrumentCount = 1;
if(curInstrument >= song.instrumentCount) curInstrument = song.instrumentCount - 1;
while(curInstrument >= instrumentScroll + h1 - 2)
{
orderScroll++;
}
while(curInstrument < instrumentScroll)
{
instrumentScroll--;
}
drawInstrumentWindow();
drawStatus();
break;
}
break;
case WIN_EFFECT:
switch(c)
{
case KEY_DOWN:
curEffect = (curEffect + 1) % 8;
drawEffectWindow();
break;
case KEY_UP:
curEffect = (curEffect - 1);
while(curEffect < 0) curEffect += 8;
drawEffectWindow();
break;
case KEY_RIGHT:
if(song.instruments[curInstrument].effects[curEffect].enable)
{
song.instruments[curInstrument].effects[curEffect].effect++;
if(song.instruments[curInstrument].effects[curEffect].effect > 0xF) song.instruments[curInstrument].effects[curEffect].effect = 0xF;
}
else
{
song.instruments[curInstrument].effects[curEffect].enable = 1;
}
drawEffectWindow();
break;
case KEY_LEFT:
if(song.instruments[curInstrument].effects[curEffect].enable)
{
song.instruments[curInstrument].effects[curEffect].effect--;
if(song.instruments[curInstrument].effects[curEffect].effect > 0xF)
{
song.instruments[curInstrument].effects[curEffect].effect = 0;
song.instruments[curInstrument].effects[curEffect].enable = 0;
}
drawEffectWindow();
}
break;
case '=':
if(song.instruments[curInstrument].effects[curEffect].enable)
{
song.instruments[curInstrument].effects[curEffect].argument++;
drawEffectWindow();
}
break;
case '-':
if(song.instruments[curInstrument].effects[curEffect].enable)
{
song.instruments[curInstrument].effects[curEffect].argument--;
drawEffectWindow();
}
break;
case ']':
if(song.instruments[curInstrument].effects[curEffect].enable)
{
song.instruments[curInstrument].effects[curEffect].argument += 0x10;
drawEffectWindow();
}
break;
case '[':
if(song.instruments[curInstrument].effects[curEffect].enable)
{
song.instruments[curInstrument].effects[curEffect].argument -= 0x10;
drawEffectWindow();
}
break;
}
break;
case WIN_TRACKER:
switch(c)
{
case KEY_F(1):
curOsc = 0;
drawTrackerWindow();
break;
case KEY_F(2):
curOsc = 1;
drawTrackerWindow();
break;
case KEY_F(3):
curOsc = 2;
drawTrackerWindow();
break;
case KEY_F(4):
curOsc = 3;
drawTrackerWindow();
break;
case KEY_RIGHT:
curCol++;
if(curCol > 4)
{
curCol = 0;
curOsc = (curOsc + 1) % 4;
}
drawTrackerWindow();
break;
case KEY_LEFT:
curCol--;
if(curCol < 0)
{
curCol = 4;
curOsc--;
if(curOsc < 0) curOsc = 3;
}
drawTrackerWindow();
break;
case KEY_DOWN:
down();
drawTrackerWindow();
break;
case KEY_UP:
up();
drawTrackerWindow();
break;
case 'p':
playEntry(entry, curOsc);
break;
case KEY_BACKSPACE:
up();
default:
switch(curCol)
{
case 0:
switch(c)
{
case KEY_BACKSPACE:
entry->noteCommand.note = NOTE_NULL;
break;
case '`':
if(entry->noteCommand.note == NOTE_OFF)
{
entry->noteCommand.note = NOTE_ON;
}
else
{
entry->noteCommand.note = NOTE_OFF;
}
down();
break;
case '0':
entry->noteCommand.octave = 0;
break;
case '1':
entry->noteCommand.octave = 1;
break;
case '2':
entry->noteCommand.octave = 2;
break;
case '3':
entry->noteCommand.octave = 3;
break;
case '4':
entry->noteCommand.octave = 4;
break;
case '5':
entry->noteCommand.octave = 5;
break;
case '6':
entry->noteCommand.octave = 6;
break;
case '7':
entry->noteCommand.octave = 7;
break;
case 'a':
entry->noteCommand.octave = curOctave;
entry->noteCommand.note = NOTE_C;
down();
break;
case 'w':
entry->noteCommand.octave = curOctave;
entry->noteCommand.note = NOTE_Cs;
down();
break;
case 's':
entry->noteCommand.octave = curOctave;
entry->noteCommand.note = NOTE_D;
down();
break;
case 'e':
entry->noteCommand.octave = curOctave;
entry->noteCommand.note = NOTE_Ds;
down();
break;
case 'd':
entry->noteCommand.octave = curOctave;
entry->noteCommand.note = NOTE_E;
down();
break;
case 'f':
entry->noteCommand.octave = curOctave;
entry->noteCommand.note = NOTE_F;
down();
break;
case 't':
entry->noteCommand.octave = curOctave;
entry->noteCommand.note = NOTE_Fs;
down();
break;
case 'g':
entry->noteCommand.octave = curOctave;
entry->noteCommand.note = NOTE_G;
down();
break;
case 'y':
entry->noteCommand.octave = curOctave;
entry->noteCommand.note = NOTE_Gs;
down();
break;
case 'h':
entry->noteCommand.octave = curOctave;
entry->noteCommand.note = NOTE_A;
down();
break;
case 'u':
entry->noteCommand.octave = curOctave;
entry->noteCommand.note = NOTE_As;
down();
break;
case 'j':
entry->noteCommand.octave = curOctave;
entry->noteCommand.note = NOTE_B;
down();
break;
case 'k':
entry->noteCommand.octave = curOctave + 1;
entry->noteCommand.note = NOTE_C;
down();
break;
case 'r':
entry->noteCommand.note = NOTE_RESET;
break;
case '=':
if(entry->noteCommand.note == NOTE_B)
{
entry->noteCommand.note = NOTE_C;
entry->noteCommand.octave = (entry->noteCommand.octave + 1) % 8;
}
else if(entry->noteCommand.note > 3)
{
entry->noteCommand.note++;
}
break;
case '-':
if(entry->noteCommand.note == 4)
{
entry->noteCommand.note = 15;
if(entry->noteCommand.octave == 0)
{
entry->noteCommand.octave = 7;
}
else
{
entry->noteCommand.octave--;
}
}
else if(entry->noteCommand.note > 4)
{
entry->noteCommand.note--;
}
break;
case ']':
entry->noteCommand.octave = (entry->noteCommand.octave + 1) % 8;
break;
case '[':
if(entry->noteCommand.octave == 0)
{
entry->noteCommand.octave = 7;
}
else
{
entry->noteCommand.octave--;
}
break;
}
break;
case 1:
switch(c)
{
case KEY_BACKSPACE:
entry->noteCommand.applyInstrument = 0;
break;
case '0':
entry->noteCommand.instrument = 0;
entry->noteCommand.applyInstrument = 1;
down();
break;
case '1':
entry->noteCommand.instrument = 1;
entry->noteCommand.applyInstrument = 1;
down();
break;
case '2':
entry->noteCommand.instrument = 2;
entry->noteCommand.applyInstrument = 1;
down();
break;
case '3':
entry->noteCommand.instrument = 3;
entry->noteCommand.applyInstrument = 1;
down();
break;
case '4':
entry->noteCommand.instrument = 4;
entry->noteCommand.applyInstrument = 1;
down();
break;
case '5':
entry->noteCommand.instrument = 5;
entry->noteCommand.applyInstrument = 1;
down();
break;
case '6':
entry->noteCommand.instrument = 6;
entry->noteCommand.applyInstrument = 1;
down();
break;
case '7':
entry->noteCommand.instrument = 7;
entry->noteCommand.applyInstrument = 1;
down();
break;
case '=':
entry->noteCommand.instrument = (entry->noteCommand.instrument + 1) % 8;
break;
case '-':
if(entry->noteCommand.instrument == 0)
{
entry->noteCommand.instrument = 7;
}
else
{
entry->noteCommand.instrument--;
}
break;
}
break;
case 2:
switch(c)
{
case KEY_BACKSPACE:
entry->noteCommand.volume = 0;
break;
case '0':
entry->noteCommand.volume = 0;
down();
break;
case '`':
entry->noteCommand.volume = 31;
down();
break;
case '=':
entry->noteCommand.volume = (entry->noteCommand.volume + 1) % 32;
break;
case '-':
if(entry->noteCommand.volume == 0)
{
entry->noteCommand.volume = 31;
}
else
{
entry->noteCommand.volume--;
}
break;
case ']':
entry->noteCommand.volume = (entry->noteCommand.volume + 0x10) % 32;
break;
case '[':
if(entry->noteCommand.volume <= 0x10)
{
entry->noteCommand.volume = 31;
}
else
{
entry->noteCommand.volume -= 0x10;
}
break;
}
break;
case 3:
switch(c)
{
case KEY_BACKSPACE:
entry->effect.enable = 0;
break;
case '0':
entry->effect.effect = 0;
entry->effect.enable = 1;
down();
break;
case '1':
entry->effect.effect = 1;
entry->effect.enable = 1;
down();
break;
case '2':
entry->effect.effect = 2;
entry->effect.enable = 1;
down();
break;
case '3':
entry->effect.effect = 3;
entry->effect.enable = 1;
down();
break;
case '4':
entry->effect.effect = 4;
entry->effect.enable = 1;
down();
break;
case '5':
entry->effect.effect = 5;
entry->effect.enable = 1;
down();
break;
case '6':
entry->effect.effect = 6;
entry->effect.enable = 1;
down();
break;
case '7':
entry->effect.effect = 7;
entry->effect.enable = 1;
down();
break;
case '8':
entry->effect.effect = 8;
entry->effect.enable = 1;
down();
break;
case '9':
entry->effect.effect = 9;
entry->effect.enable = 1;
down();
break;
case 'a':
entry->effect.effect = 0xA;
entry->effect.enable = 1;
down();
break;
case 'b':
entry->effect.effect = 0xB;
entry->effect.enable = 1;
down();
break;
case 'c':
entry->effect.effect = 0xC;
entry->effect.enable = 1;
down();
break;
case 'd':
entry->effect.effect = 0xD;
entry->effect.enable = 1;
down();
break;
case 'e':
entry->effect.effect = 0xE;
entry->effect.enable = 1;
down();
break;
case 'f':
entry->effect.effect = 0xF;
entry->effect.enable = 1;
down();
break;
case '=':
entry->effect.effect = (entry->effect.effect + 1) % 0x10;
entry->effect.enable = 1;
break;
case '-':
if(entry->effect.effect == 0)
{
entry->effect.effect = 0xF;
}
else
{
entry->effect.effect--;
}
entry->effect.enable = 1;
break;
}
break;
case 4:
switch(c)
{
case KEY_BACKSPACE:
entry->effect.enable = 0;
break;
case '0':
entry->effect.argument = 0;
entry->effect.enable = 1;
down();
break;
case '=':
entry->effect.argument++;
entry->effect.enable = 1;
break;
case '-':
entry->effect.argument--;
entry->effect.enable = 1;
break;
case ']':
entry->effect.argument += 0x10;
entry->effect.enable = 1;
break;
case '[':
entry->effect.argument -= 0x10;
entry->effect.enable = 1;
break;
}
break;
}
drawTrackerWindow();
}
break;
}
}
}
void playInput(int c)
{
}
void jamInput(int c)
{
}
void editMode()
{
curMode = MODE_EDIT;
drawWindows();
drawStatus();
}
void broadcastEffect(Effect effect)
{
sendEffectCommand(&effect, 0);
sendEffectCommand(&effect, 1);
sendEffectCommand(&effect, 2);
sendEffectCommand(&effect, 3);
}
void resetEffects()
{
broadcastEffect(newEffect(0x0, 0x00));
broadcastEffect(newEffect(0x1, 0x00));
broadcastEffect(newEffect(0x2, 0x00));
broadcastEffect(newEffect(0x3, 0x00));
broadcastEffect(newEffect(0x4, 0x00));
broadcastEffect(newEffect(0x5, 0x00));
broadcastEffect(newEffect(0x6, 0x7F));
broadcastEffect(newEffect(0x7, 0x00));
broadcastEffect(newEffect(0x8, 0x00));
broadcastEffect(newEffect(0x9, 0x00));
broadcastEffect(newEffect(0xA, 0x00));
broadcastEffect(newEffect(0xB, 0x00));
broadcastEffect(newEffect(0xC, 0xFF));
broadcastEffect(newEffect(0xD, 0x00));
broadcastEffect(newEffect(0xE, 0xF0));
broadcastEffect(newEffect(0xF, 0x00));
}
Frame quietFrame;
void reset()
{
playFrame(&quietFrame);
resetEffects();
}
int playing;
void playOrder(Order *order, int fps)
{
int i;
for(i = 0; i < sizeof(order->frames) / sizeof(Frame); i++)
{
curFrame = i;
scrollTracker();
drawTrackerWindow();
playFrame(&order->frames[i]);
usleep(1000000 / fps);
char c = getch();
if(c == '\n' || c == ' ' || c == KEY_F(8))
{
playing = 0;
reset();
editMode();
break;
}
}
}
void playMode()
{
curMode = MODE_PLAY;
drawWindows();
drawStatus();
int i;
playing = 1;
for(i = 0; playing; i++)
{
if(i == song.orderCount) i = song.repeat;
curOrder = i;
drawOrderWindow();
Order order = song.orders[i];
playOrder(&order, song.fps);
}
}
void jamMode()
{
curMode = MODE_JAM;
drawWindows();
drawStatus();
}
void playCurrentFrame()
{
Frame f = song.orders[curOrder].frames[curFrame];
playFrame(&f);
}
void playCurrentOrder()
{
Order o = song.orders[curOrder];
playOrder(&o, song.fps);
}
void save()
{
FILE *fp;
fp = fopen("chiptune.sav", "w");
fputc(song.fps, fp);
fputc(song.repeat, fp);
fputc(song.instrumentCount, fp);
fputc(song.orderCount, fp);
int i;
for(i = 0; i < song.instrumentCount; i++)
{
Instrument instrument = song.instruments[i];
int a;
for(a = 0; a < 8; a++)
{
Effect effect = instrument.effects[a];
fputc(effect.enable, fp);
fputc(effect.effect, fp);
fputc(effect.argument, fp);
}
}
for(i = 0; i < song.orderCount; i++)
{
Order order = song.orders[i];
int a;
for(a = 0; a < 32; a++)
{
Frame frame = order.frames[a];
int b;
for(b = 0; b < 4; b++)
{
Entry entry = frame.entries[b];
fputc(entry.noteCommand.note, fp);
fputc(entry.noteCommand.octave, fp);
fputc(entry.noteCommand.volume, fp);
fputc(entry.noteCommand.applyInstrument, fp);
fputc(entry.noteCommand.instrument, fp);
fputc(entry.effect.enable, fp);
fputc(entry.effect.effect, fp);
fputc(entry.effect.argument, fp);
}
}
}
fclose(fp);
}
void load()
{
FILE *fp;
fp = fopen("chiptune.sav", "r");
song.fps = fgetc(fp);
song.repeat = fgetc(fp);
song.instrumentCount = fgetc(fp);
song.orderCount = fgetc(fp);
int i;
for(i = 0; i < song.instrumentCount; i++)
{
int a;
for(a = 0; a < 8; a++)
{
song.instruments[i].effects[a].enable = fgetc(fp);
song.instruments[i].effects[a].effect = fgetc(fp);
song.instruments[i].effects[a].argument = fgetc(fp);
}
}
for(i = 0; i < song.orderCount; i++)
{
int a;
for(a = 0; a < 32; a++)
{
int b;
for(b = 0; b < 4; b++)
{
song.orders[i].frames[a].entries[b].noteCommand.note = fgetc(fp);
song.orders[i].frames[a].entries[b].noteCommand.octave = fgetc(fp);
song.orders[i].frames[a].entries[b].noteCommand.volume = fgetc(fp);
song.orders[i].frames[a].entries[b].noteCommand.applyInstrument = fgetc(fp);
song.orders[i].frames[a].entries[b].noteCommand.instrument = fgetc(fp);
song.orders[i].frames[a].entries[b].effect.enable = fgetc(fp);
song.orders[i].frames[a].entries[b].effect.effect = fgetc(fp);
song.orders[i].frames[a].entries[b].effect.argument = fgetc(fp);
}
}
}
fclose(fp);
curOrder = 0;
curInstrument = 0;
curEffect = 0;
curFrame = 0;
orderScroll = 0;
instrumentScroll = 0;
trackerScroll = 0;
drawWindows();
drawStatus();
}
void inputLoop()
{
quietFrame = newQuietFrame();
int e = 1;
while(e)
{
int c = getch();
switch(c)
{
case KEY_F(5):
reset();
break;
case KEY_F(6):
playNote(NOTE_C, 3, 0xFF, 0);
playNote(NOTE_E, 4, 0xFF, 1);
playNote(NOTE_G, 3, 0xFF, 2);
playNote(NOTE_Bb, 4, 0xFF, 3);
break;
case KEY_F(7):
playCurrentFrame();
break;
case KEY_F(8):
playCurrentOrder();
break;
case KEY_F(9):
save();
case KEY_F(10):
load();
break;
case 'q':
e = 0;
break;
case KEY_RESIZE:
initWindows();
break;
case '.':
curOctave = (curOctave + 1) % 8;
drawStatus();
break;
case ',':
if(curOctave == 0)
{
curOctave = 7;
}
else
{
curOctave--;
}
drawStatus();
break;
case '\'':
song.fps = (song.fps + 1) % 25;
if(song.fps <= 0) song.fps = 1;
drawStatus();
break;
case ';':
if(song.fps <= 1)
{
song.fps = 24;
}
else
{
song.fps--;
}
drawStatus();
break;
case '\n':
if(curMode == MODE_PLAY)
{
editMode();
}
else
{
playMode();
}
break;
case ' ':
if(curMode == MODE_JAM)
{
editMode();
}
else
{
jamMode();
}
break;
case ERR:
break;
default:
switch(curMode)
{
case MODE_EDIT:
editInput(c);
break;
case MODE_PLAY:
playInput(c);
break;
case MODE_JAM:
jamInput(c);
break;
}
}
}
}
void gui()
{
initscr();
clear();
noecho();
cbreak();
keypad(stdscr, TRUE);
nodelay(stdscr, TRUE);
refresh();
initWindows();
inputLoop();
endwin();
}
void initSong()
{
song.fps = 8;
song.repeat = 0;
song.orderCount = 1;
song.instrumentCount = 1;
Order orders[MAX_ORDERS];
song.orders = orders;
Instrument instruments[MAX_INSTRUMENTS];
song.instruments = instruments;
}
int main(int argc, char *argv[])
{
if(argc > 1)
{
strcpy(serialPort, argv[1]);
}
else
{
printf("Enter full path to serial device: ");
scanf("%s", serialPort);
}
if(out = fopen(serialPort, "w"))
{
initSong();
reset();
gui();
reset();
fclose(out);
}
else
{
printf("Unable to open file!\n");
exit(1);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment