Sound driver fun

Discussion of hardware and software development for Super NES and Super Famicom.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
Augustus Blackheart
Posts: 56
Joined: Sat Jul 26, 2014 9:50 am

Re: Sound driver fun

Post by Augustus Blackheart » Tue Dec 22, 2015 2:26 pm

KungFuFurby wrote:Your SNESMod has a bug that I had fixed (and got incorporated into an actual updated version of SNESMod)...

The bug that occurs has to do with loading sound effects. It happens when loop points go bad and cause the carry flag to accidentally get set, resulting in the sample pointers to be out of alignment by one byte.
I've added this fix. I haven't updated the windows binary yet and I was hoping to get the increase in patterns, instruments and samples working in this latest update. Maybe next time. I did add your bugfix for ultra low pitch. Thanks for that!

Augustus Blackheart
Posts: 56
Joined: Sat Jul 26, 2014 9:50 am

Re: Sound driver fun

Post by Augustus Blackheart » Wed Dec 30, 2015 3:10 pm

To save space I think I'll have the vibrato waveform set the tremolo and panbrello waveform as well. I've attached a short tremolo demo to the first post.

KungFuFurby
Posts: 259
Joined: Wed Jul 09, 2008 8:46 pm

Re: Sound driver fun

Post by KungFuFurby » Wed Dec 30, 2015 4:17 pm

The negative octave handling code is not present in snesmod-1.2.asm. In addition, I added a clrc opcode just past asl a in oct_cont so that negative octaves don't mess anything else up.

Augustus Blackheart
Posts: 56
Joined: Sat Jul 26, 2014 9:50 am

Re: Sound driver fun

Post by Augustus Blackheart » Wed Dec 30, 2015 5:38 pm

Yeah, I was a bit sloppy there... the full fix was only in one of the drivers. They're all updated now.

alekmaul
Posts: 54
Joined: Tue Apr 24, 2012 12:22 pm
Contact:

Re: Sound driver fun

Post by alekmaul » Wed Dec 30, 2015 10:10 pm

Well, have a 404 not found when I try to download your updated version Augustus Blackheart... :(

KungFuFurby
Posts: 259
Joined: Wed Jul 09, 2008 8:46 pm

Re: Sound driver fun

Post by KungFuFurby » Thu Dec 31, 2015 5:25 am

alekmaul wrote:Well, have a 404 not found when I try to download your updated version Augustus Blackheart... :(
I cheated by replacing a 22 with a 30, realizing that the URL looked wrong since usually it indicated when the file was last updated.

Augustus Blackheart
Posts: 56
Joined: Sat Jul 26, 2014 9:50 am

Re: Sound driver fun

Post by Augustus Blackheart » Thu Dec 31, 2015 12:02 pm

Today's update has reduced redundancy in the vibrato routine from the ABNOFX driver applied to all of the sound drivers. I'm rewriting how the special features use zeropage. After I make sure all those routine are working properly under the new system I'll be adding tremolo, panbrello, noise frequency sweeps, and possibly swing tempo if I have time. Then I'll rewrite the filter sweep routine. Next up should be... phase inversion and pitch modulation to modify direct gain.

KungFuFurby
Posts: 259
Joined: Wed Jul 09, 2008 8:46 pm

Re: Sound driver fun

Post by KungFuFurby » Mon Jan 04, 2016 10:48 am

There are a couple of thoughts coming through my mind in regard to envelopes...

- There is a bug in regard to a note on with a delay freezing the previous instrument's envelope (this existed in the original version as well prior to your modifications).

- Perhaps there should be a "psuedo-gain" method of adjusting the gain on a per-step basis during SNESMod's idle loop (when the driver checks for commands and any ticks) for each of the channels rather than doing so instantly to reduce crackle. Obviously the rate won't be consistent this way, but one thing I do know is that direct gain has double precision of any attack-related gain/ADSR modes (except for bent gain, but only when loud enough). The psuedo-gain can also be applied to the panning, which has no such controls.

You don't need to implement this next part, but I have an extra piece of theory to apply to gain (especially if direct gain gives this a zero where in reality the value is less than a one, but not a zero):

- Exponential decrease can pull off volume levels that direct gain can't touch due to precision. The side effect of pulling this off is that it is prone to hiccups when the volume is increased since there is up to 15 sample's worth of delay in there after setup (especially if we're going all the way down to the lowest possible volume value gain can ever do, which is a sixteenth of what direct gain can pull off for an extreme low). This is because direct gain goes first and is set to 01.

I also have a bugfix in regards to surround failing to be disabled when the volume column uses a panning command:

Code: Select all

;--------------------------------------------------------
; 128-192 set pan
;--------------------------------------------------------
vcmd_pan:
	cmp	mod_tick, #0		; set panning
	bne	exit_vcmd		;
	push	a			;
	mov	a, y			;
	sbc	a, #128			;
	mov	ch_panning+x, a		;
	mov	a, ch_flags+x		; Bugfix by KungFuFurby 12/20/15
	and	a, #~CF_SURROUND	; Surround should be disabled
	mov	ch_flags+x, a		; when panning is set via volume
	pop	a			; column.
	ret				;

Augustus Blackheart
Posts: 56
Joined: Sat Jul 26, 2014 9:50 am

Re: Sound driver fun

Post by Augustus Blackheart » Thu Jan 07, 2016 6:27 pm

KungFuFurby wrote: - Perhaps there should be a "psuedo-gain" method of adjusting the gain on a per-step basis during SNESMod's idle loop (when the driver checks for commands and any ticks) for each of the channels rather than doing so instantly to reduce crackle. Obviously the rate won't be consistent this way, but one thing I do know is that direct gain has double precision of any attack-related gain/ADSR modes (except for bent gain, but only when loud enough). The psuedo-gain can also be applied to the panning, which has no such controls.

You don't need to implement this next part, but I have an extra piece of theory to apply to gain (especially if direct gain gives this a zero where in reality the value is less than a one, but not a zero):

- Exponential decrease can pull off volume levels that direct gain can't touch due to precision. The side effect of pulling this off is that it is prone to hiccups when the volume is increased since there is up to 15 sample's worth of delay in there after setup (especially if we're going all the way down to the lowest possible volume value gain can ever do, which is a sixteenth of what direct gain can pull off for an extreme low). This is because direct gain goes first and is set to 01.
Anything that reduces crackle is great. I'll have to try it out.
I also have a bugfix in regards to surround failing to be disabled when the volume column uses a panning command:
Nice. I have it call the duplicate code from within Command_SetPanning which conviently ends with ret.

Awhile back I changed the default master volume to be much higher and as such, in my own version of the driver, I got rid of the Echo Volume scaled by Master Volume routine. Do you think it's worth keeping that as an option in Super SNESMod? It might be useful is someone is manually setting the master volume lower, however, it takes up a lot of space... although I don't know why people couldn't just use different echo values if it's a concern.

KungFuFurby
Posts: 259
Joined: Wed Jul 09, 2008 8:46 pm

Re: Sound driver fun

Post by KungFuFurby » Fri Jan 08, 2016 9:36 am

Augustus Blackheart wrote:Awhile back I changed the default master volume to be much higher and as such, in my own version of the driver, I got rid of the Echo Volume scaled by Master Volume routine. Do you think it's worth keeping that as an option in Super SNESMod? It might be useful is someone is manually setting the master volume lower, however, it takes up a lot of space... although I don't know why people couldn't just use different echo values if it's a concern.
I actually have something running through my head that might allow you to cut any effects that end up on the chopping block in the actual IT file (and also allows you to circumnavigate the assembler, not counting constant memory locations). Every single IT file must be scanned in the scenario of a soundbank though (or the user will have to manually define which ones to cut and/or keep... where all of them are on by default).

This is a lovely little file format that I have invented that allows you to pull off relocatable SPC700 code on the SNES (and in your C++ code if you want to make some adjustments) while minding constant memory locations (as defined by the assembler):
SPC700 Portable Code Format
Allows you to make relocatable modules of SPC700 code.
Works for blocks of code up to 32,767 bytes in length (unless you want to bend the rules and define where constant memory locations begin).

Most likely SNES implementation is to construct the code in WRAM, then send it to the SPC700. In theory it can also be done on the fly

It is highly recommended to have a master pointer array on hand for the SPC700, as this format works best with re-loadable "modules" (think of a collection of note effects, for example).

Header is the following:
(For standard variety, which has a 32,767 byte limit)
- 15-bit filesize (in SNES bytes, not SPC700 bytes) + 1 bit flag (flag indicates whether or not there are extra details within the code to either modify or handle in a non-standard method)
(For non-standard variety, which requires that you define a constant indicating where constant memory locations end... this is not put in the actual SPC700 file, and is instead usually hard-coded on the SNES's end)
- 2 byte filesize (in SNES bytes, not SPC700 bytes)
- 1 byte flag containing the following bits...
-- (Bit 7) Whether or not the counters are 16-bit.
-- (Bits 6-5 are not defined yet.)
-- (Bit 4) Whether or not there are non-constant values not auto-detected by the code as two-byte pointer offsets
-- (Bit 3) Whether or not there are non-constant values not auto-detected by the code as scattered bytes
-- (Bit 2) Whether or not there are SLEEP opcodes that should be sent to the SPC700
-- (Bit 1) Whether or not there are STOP opcodes that should be sent to the SPC700
-- (Bit 0) Whether or not there are a series of two-byte pointers

The one-byte flag is also there in the standard variety, but it is optional since the one-bit flag will indicate whether or not this flag exists.

The filesize can be omitted, but in substitution for there being no filesize, the file must be ended with a STOP SPC700 opcode. In these scenarios, the one-byte flag is required.

When flag bits are set to account for non-auto-detected opcodes, then extra collections of data will show up after the flag byte.

Each collection contains a counter for the number of pointers to deal with. Counters are 8-bit by default, but they can be set to 16 bits by setting the highest bit for the flag (highly doubtful unless you somehow use more than 256 pointers in one set).

These extra data shall show up when the corresponding bit is set...
- (Bit 4) Two-byte pointers to SPC700 RAM offsets
- (Bit 3) Four-byte pointers to SPC700 RAM offsets (two bytes per pointer to a byte in the order of low byte, followed by high byte)
- (Bit 2) Two-byte pointers to SLEEP opcodes that should not be skipped (this directly affects SPC700 RAM offsets)
- (Bit 1) Two-byte pointers to STOP opcodes that should not be skipped
- (Bit 0) Four-byte pointers to a collection of SPC700 RAM offsets (two bytes indicate the start point, and two bytes indicate the end point)

All opcodes with !a parameters (including indexed parameters) are defined as following...
- Positive values indicate a constant memory location
- Negative values (which in this case means the sign bit is set) indicate an offset from the starting memory location of the code (these values are EORed by $FFFF when sent to the SPC700... meaning $FFFF stands for a zero offset). These offsets are NOT relative to the start of the file on the SNES, as bytes can be skipped and modified.

A SLEEP opcode (which normally should not be executed on the SPC700) does not get sent to the SPC700, and instead marks the beginning of non-SPC700 code. Following the SLEEP opcode is a two-byte filesize indicating how many bytes are not SPC700 code.

A STOP opcode marks the end of the file, and also does not get sent to the SPC700.
This might work well for SNESMod in particular (and it also cuts down on the number of copies of code you have to maintain in the process).

Augustus Blackheart
Posts: 56
Joined: Sat Jul 26, 2014 9:50 am

Re: Sound driver fun

Post by Augustus Blackheart » Thu Apr 14, 2016 7:16 pm

KungFuFurby wrote:I actually have something running through my head that might allow you to cut any effects that end up on the chopping block in the actual IT file (and also allows you to circumnavigate the assembler, not counting constant memory locations). Every single IT file must be scanned in the scenario of a soundbank though (or the user will have to manually define which ones to cut and/or keep... where all of them are on by default).
That would be a much better way of doing things. Speaking of cutting down on multiple versions...

There are a lot of silly errors in that last version of SNESMod. Update coming soon.

Augustus Blackheart
Posts: 56
Joined: Sat Jul 26, 2014 9:50 am

Re: Sound driver fun

Post by Augustus Blackheart » Fri Jan 13, 2017 2:14 pm

I have two different issues I can't seem to figure out:

Issue 1:

I have a version of SNESMod that never pushes anything to the stack (except calls of course but it's not like there are a ton of nested calls). In this driver I put 144 bytes of sample data starting at 0100h. This works fine for most of my songs but there are a couple where it is fine for half of the song and than at a certain point something occurs and the stack stops hovering around 235-237 and keeps dropping very rapidly wiping out the 144 bytes at 0100h, continuing to decrease and wraparound endlessly. Other than losing that sample data, the song continues to play fine.

Although I only know the value of the stack register with the SPC player, the samples at 0100h do get destroyed in every emulator I've tried and on real hardware. Other songs tested did not have this issue. Any thoughts on what would cause this to occur? Everything else about the song plays perfectly fine so if it's some sort of bug it is only specific to whatever is going on with the stack. (I've been looking over song data but there was nothing especially noteworthy about this song it uses all of the same commands as other songs I've tested use.)

Anyway, it's not the biggest deal, but it would be nice to save some bytes for module data. In the mean time I've gone back to storing that particular sample data outside of stack space. Maybe this is something that is not possible to do...

Issue 2:

I set up a system to swap out 144 bytes (8 32 sample BRR) in SNESMod. This is stored outside of stack space. It works. Kind of. Here is the test I ran:

1: load sample
2: play module
3: change sample data
4: jump to previous pattern
repeat 3-4

The first two transfers always work, the third is unsuccessful, the fourth transfer works, and after that only the 2nd and 4th transfers are successful. When a transfer is unsuccessful, the result is either a short burst of noise or what sounds like a low pitch sine-ish waveform.

Ideally, I would like to be able to change samples at any time that a song is playing. I'm not sure what the proper way to do this is. If anybody has suggestions for what I should (or shouldn't) be doing that would be helpful. I thought about modifying the SPC streaming code from SNESMod for this task if I can't get it to work using this code, but I'd like to understand why this method isn't working consistently.

code on the SNES side:

Code: Select all

; increment memory pointer by 2
.macro incptr
.scope
	iny
	iny
	
	bne	_catch_overflow
	inc	spc_ptr+2

_catch_overflow:
.endscope
.endmacro

;----------------------------------------------------------------------
spcLoadSample:			; y = sample address
;----------------------------------------------------------------------

	phx
	jsr	spcFlush	; flush fifo!
	plx

	sty	z:spc_ptr
	lda	sample_bank
	sta	z:spc_ptr+2
	
	lda	z:spc_v		; wait for spc
	pha
:	cmp	REG_APUIO1
	bne	:-
	lda	#CMD_SAMPLE	; send SAMPLE message
	sta	REG_APUIO0
	pla
	eor	#80h
	ora	#01h
	sta	z:spc_v
	sta	REG_APUIO1
:	cmp	REG_APUIO1	; wait for spc
	bne	:-

	ldx	#144/2	; x = word length
	ldy	#0		; y = index
	jsr	do_transfer
	rts
	
;--------------------------------------------------------------
; spc_ptr+y: source address
; x = length of transfer (WORDS)
;--------------------------------------------------------------
transfer_again:
	eor	#80h
	sta	REG_APUIO1
	sta	spc_v
	incptr
:	cmp	REG_APUIO1
	bne	:-
;--------------------------------------------------------------
do_transfer:
;--------------------------------------------------------------
	rep	#20h		; transfer 1 word
	lda	[spc_ptr], y
	sta	REG_APUIO2
	sep	#20h
	lda	spc_v
	dex
	bne	transfer_again
	
	incptr

end_transfer:
	lda	#0	; final word was transferred
	sta	REG_APUIO1	; write p1=0 to terminate
	sta	spc_v
:	cmp	REG_APUIO1
	bne	:-
	sta	spc_pr+1
	rts
code on the SPC-700 side:

Code: Select all

;-------------------------------------------------------------------
CopySample:
;-------------------------------------------------------------------
	mov	xfer_address, #Sample01&0ffh
	mov	xfer_address+1, #Sample01>>8
	call	StartTransfer
	mov	SPC_PORT1, comms_v
	ret
;-------------------------------------------------------------------
StartTransfer:
;-------------------------------------------------------------------
	mov	x, comms_v		; start transfer
	mov	y, #0
	mov	SPC_PORT1, x
;-------------------------------------------------------------------
DoTransfer:
;-------------------------------------------------------------------
	cmp	x, SPC_PORT1		; wait for data
	beq	DoTransfer
	mov	x, SPC_PORT1

	mov	a, SPC_PORT2		; copy data
	mov	[xfer_address]+y, a
	mov	a, SPC_PORT3
	mov	SPC_PORT1, x		;<- reply to snes
	inc	y
	mov	[xfer_address]+y, a
	inc	y
	beq	_inc_address		; catch index overflow
_cont1:	cmp	x, #0			; loop until x=0
	bne	DoTransfer

	mov	m0, y
	clrc
	adc	xfer_address, m0
	adc	xfer_address+1, #0
	mov	comms_v, x
	ret

_inc_address:
	inc	xfer_address+1
	bra	_cont1

Revenant
Posts: 440
Joined: Sat Apr 25, 2015 1:47 pm
Location: FL

Re: Sound driver fun

Post by Revenant » Fri Jan 13, 2017 2:31 pm

I have something of a working SPC debugger/tracer, if you'd like me to figure out what's going on with the first issue.

I don't have any ideas right off the bat regarding the second one, but I can take a closer look at the code later and see if anything seems suspect.

Augustus Blackheart
Posts: 56
Joined: Sat Jul 26, 2014 9:50 am

Re: Sound driver fun

Post by Augustus Blackheart » Fri Dec 15, 2017 6:53 pm

I fixed some of the clicking in SNESMod by using some code from XM2SNES. The issue I was having was with a bass sample and it would play fine unless 8th or 16th notes were used. This has been fixed!

SNESMod is a little less clicky than it was, but still more clicky than XM2SNES. This is still something I hope to fix, I don't have a lot of patience for this sort of thing, I'm always more interested in adding shiny new things than fixing broken things.

Post Reply