;FamiTone audio library v1.24 ;by Shiru (shiru@mail.ru) 06'11 ;The name is suggested by Memblers from NesDev ;Feel free to do anything you want with this code, consider it Public Domain ;aliases for APU registers APU_PL1_VOL = $4000 APU_PL1_SWEEP = $4001 APU_PL1_LO = $4002 APU_PL1_HI = $4003 APU_PL2_VOL = $4004 APU_PL2_SWEEP = $4005 APU_PL2_LO = $4006 APU_PL2_HI = $4007 APU_TRI_LINEAR = $4008 APU_TRI_LO = $400a APU_TRI_HI = $400b APU_NOISE_VOL = $400c APU_NOISE_LO = $400e APU_NOISE_HI = $400f APU_DMC_FREQ = $4010 APU_DMC_RAW = $4011 APU_DMC_START = $4012 APU_DMC_LEN = $4013 APU_SND_CHN = $4015 ;all the FamiTone variables take 112+15*FT_SFX_STREAMS bytes in a RAM page FT_FRAME_CNT = FT_BASE_ADR FT_SONG_SPEED = FT_BASE_ADR+1 FT_INSTRUMENT_L = FT_BASE_ADR+2 FT_INSTRUMENT_H = FT_BASE_ADR+3 FT_PULSE1_PREV = FT_BASE_ADR+4 FT_PULSE2_PREV = FT_BASE_ADR+5 FT_CHANNELS = FT_BASE_ADR+6 FT_CH1_VARS = FT_CHANNELS FT_CH2_VARS = FT_CHANNELS+9 FT_CH3_VARS = FT_CHANNELS+18 FT_CH4_VARS = FT_CHANNELS+27 FT_CH5_VARS = FT_CHANNELS+36 FT_ENVELOPES = FT_BASE_ADR+51 FT_CH1_ENVS = FT_ENVELOPES ;three envelopes (5*3 bytes) for pulse and triangle FT_CH2_ENVS = FT_ENVELOPES+15 FT_CH3_ENVS = FT_ENVELOPES+30 FT_CH4_ENVS = FT_ENVELOPES+45 ;only two envelopes (5*2 bytes) for noise FT_DPCM_TABLE_L = FT_BASE_ADR+106 FT_DPCM_TABLE_H = FT_BASE_ADR+107 FT_DPCM_EFFECT = FT_BASE_ADR+108 FT_SFX_ADR_L = FT_BASE_ADR+109 FT_SFX_ADR_H = FT_BASE_ADR+110 FT_PAL_ADJUST = FT_BASE_ADR+111 ;envelope variables offsets, every envelope uses 5 bytes FT_ENV_STRUCT_SIZE = 5 FT_ENV_VALUE = FT_BASE_ADR+0 FT_ENV_REPEAT = FT_BASE_ADR+1 FT_ENV_ADR_L = FT_BASE_ADR+2 FT_ENV_ADR_H = FT_BASE_ADR+3 FT_ENV_PTR = FT_BASE_ADR+4 ;channels variables offsets, every channel uses 9 bytes FT_CHN_STRUCT_SIZE = 9 FT_CHN_REPEAT = FT_BASE_ADR+0 FT_CHN_NOTE = FT_BASE_ADR+1 FT_CHN_INSTRUMENT = FT_BASE_ADR+2 FT_CHN_DUTY = FT_BASE_ADR+3 FT_CHN_PTR_L = FT_BASE_ADR+4 FT_CHN_PTR_H = FT_BASE_ADR+5 FT_CHN_RETURN_L = FT_BASE_ADR+6 FT_CHN_RETURN_H = FT_BASE_ADR+7 FT_CHN_REF_LEN = FT_BASE_ADR+8 ;aliases for outputs FT_CH1_NOTE = FT_CH1_VARS+.lobyte(FT_CHN_NOTE) FT_CH2_NOTE = FT_CH2_VARS+.lobyte(FT_CHN_NOTE) FT_CH3_NOTE = FT_CH3_VARS+.lobyte(FT_CHN_NOTE) FT_CH4_NOTE = FT_CH4_VARS+.lobyte(FT_CHN_NOTE) FT_CH5_NOTE = FT_CH5_VARS+.lobyte(FT_CHN_NOTE) FT_CH1_VOLUME = FT_CH1_ENVS+.lobyte(FT_ENV_VALUE) FT_CH2_VOLUME = FT_CH2_ENVS+.lobyte(FT_ENV_VALUE) FT_CH3_VOLUME = FT_CH3_ENVS+.lobyte(FT_ENV_VALUE) FT_CH4_VOLUME = FT_CH4_ENVS+.lobyte(FT_ENV_VALUE) FT_CH1_NOTE_OFF = FT_CH1_ENVS+.lobyte(FT_ENV_VALUE)+FT_ENV_STRUCT_SIZE FT_CH2_NOTE_OFF = FT_CH2_ENVS+.lobyte(FT_ENV_VALUE)+FT_ENV_STRUCT_SIZE FT_CH3_NOTE_OFF = FT_CH3_ENVS+.lobyte(FT_ENV_VALUE)+FT_ENV_STRUCT_SIZE FT_CH4_NOTE_OFF = FT_CH4_ENVS+.lobyte(FT_ENV_VALUE)+FT_ENV_STRUCT_SIZE FT_CH1_PITCH_OFF = FT_CH1_ENVS+.lobyte(FT_ENV_VALUE)+FT_ENV_STRUCT_SIZE*2 FT_CH2_PITCH_OFF = FT_CH2_ENVS+.lobyte(FT_ENV_VALUE)+FT_ENV_STRUCT_SIZE*2 FT_CH3_PITCH_OFF = FT_CH3_ENVS+.lobyte(FT_ENV_VALUE)+FT_ENV_STRUCT_SIZE*2 FT_CH1_DUTY = FT_CH1_VARS+.lobyte(FT_CHN_DUTY) FT_CH2_DUTY = FT_CH2_VARS+.lobyte(FT_CHN_DUTY) FT_CH3_DUTY = FT_CH3_VARS+.lobyte(FT_CHN_DUTY) FT_CH4_DUTY = FT_CH4_VARS+.lobyte(FT_CHN_DUTY) ;output buffer, used then sound effects are enabled FT_OUT_BUF = FT_BASE_ADR+112 ;11 bytes ;aliases for APU registers for music FamiTone .if(!FT_SFX_ENABLE) ;if sound effects are disabled, just write to APU FT_MR_PULSE1_V = APU_PL1_VOL FT_MR_PULSE1_L = APU_PL1_LO FT_MR_PULSE1_H = APU_PL1_HI FT_MR_PULSE2_V = APU_PL2_VOL FT_MR_PULSE2_L = APU_PL2_LO FT_MR_PULSE2_H = APU_PL2_HI FT_MR_TRI_V = APU_TRI_LINEAR FT_MR_TRI_L = APU_TRI_LO FT_MR_TRI_H = APU_TRI_HI FT_MR_NOISE_V = APU_NOISE_VOL FT_MR_NOISE_F = APU_NOISE_LO .else ;otherwise write to output buffer FT_MR_PULSE1_V = FT_OUT_BUF FT_MR_PULSE1_L = FT_OUT_BUF+1 FT_MR_PULSE1_H = FT_OUT_BUF+2 FT_MR_PULSE2_V = FT_OUT_BUF+3 FT_MR_PULSE2_L = FT_OUT_BUF+4 FT_MR_PULSE2_H = FT_OUT_BUF+5 FT_MR_TRI_V = FT_OUT_BUF+6 FT_MR_TRI_L = FT_OUT_BUF+7 FT_MR_TRI_H = FT_OUT_BUF+8 FT_MR_NOISE_V = FT_OUT_BUF+9 FT_MR_NOISE_F = FT_OUT_BUF+10 .endif ;sound effect stream variables, 15 bytes per stream FT_SFX_BASE_ADR = FT_BASE_ADR+123 FT_SFX_STRUCT_SIZE = 15 FT_SFX_REPEAT = FT_SFX_BASE_ADR FT_SFX_PTR_L = FT_SFX_BASE_ADR+1 FT_SFX_PTR_H = FT_SFX_BASE_ADR+2 FT_SFX_OFF = FT_SFX_BASE_ADR+3 FT_SFX_BUF = FT_SFX_BASE_ADR+4 ;11 bytes ;aliases for channels to use in user calls FT_SFX_CH0 = 0 FT_SFX_CH1 = FT_SFX_STRUCT_SIZE FT_SFX_CH2 = FT_SFX_STRUCT_SIZE*2 FT_SFX_CH3 = FT_SFX_STRUCT_SIZE*3 ;reset APU, initialize FamiTone ;in: A 0 for PAL, not 0 for NTSC FamiToneInit: cmp #0 beq @pal lda #$ff @pal: sta FT_PAL_ADJUST lda #$0f ;enable channels, stop DMC sta APU_SND_CHN lda #$81 ;disable triangle length counter sta APU_TRI_LINEAR lda #$01 ;load noise length sta APU_NOISE_HI lda #$30 ;volumes to 0 sta APU_PL1_VOL sta APU_PL2_VOL sta APU_NOISE_VOL lda #$08 ;no sweep sta APU_PL1_SWEEP sta APU_PL2_SWEEP lda #$ff sta FT_PULSE1_PREV sta FT_PULSE2_PREV ;stop music that currently plays (it is a part of FamiToneInit as well) FamiToneMusicStop: ldx #.lobyte(FT_CHANNELS) ;reset all the channels variables ldy #5 @setChannels: lda #0 sta FT_CHN_PTR_L,x sta FT_CHN_PTR_H,x sta FT_CHN_REPEAT,x sta FT_CHN_REF_LEN,x sta FT_CHN_INSTRUMENT,x lda #63 sta FT_CHN_NOTE,x lda #$30 sta FT_CHN_DUTY,x txa clc adc #FT_CHN_STRUCT_SIZE tax dey bne @setChannels sty FT_CH1_VOLUME ;reset volumes sty FT_CH2_VOLUME sty FT_CH3_VOLUME sty FT_CH4_VOLUME sty FT_DPCM_EFFECT ;no DPCM effect playing sty FT_SONG_SPEED ;no music playing rts ;start playing a music ;in: X,Y address of the module (LSB,MSB) FamiToneMusicStart: lda #0 sta FT_SONG_SPEED ;stop music update stx