What's the most common way to implement vibrato?

Discuss NSF files, FamiTracker, MML tools, or anything else related to NES music.

Moderator: Moderators

User avatar
olddb
Posts: 188
Joined: Thu Oct 26, 2017 12:29 pm
Contact:

What's the most common way to implement vibrato?

Post by olddb »

What's the most common way to implement vibrato for an nes music engine? Specifically for the pulse channels.
I'm using Famitracker.

I tried using APU Sweep, but cant get it quiet right.

Thank you.
...
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: What's the most common way to implement vibrato?

Post by tepples »

In general, vibrato is a sine function added to the note's period, looping maybe every 12 frames.

Are you looking to implement any sort of vibrato, or are you trying to exactly emulate FamiTracker's formulas for vibrato rate and depth? The latter might be more expensive in CPU time and/or ROM space.
Drag
Posts: 1615
Joined: Mon Sep 27, 2004 2:57 pm
Contact:

Re: What's the most common way to implement vibrato?

Post by Drag »

You're going to want the music engine itself to drive the pitch registers manually to do vibrato. I've seen two different methods:

A) Have a table of pitch offsets that you add to the pitch each frame; the pitch offsets will usually look like a triangle wave (e.g., 0, +1, +2, +3, +2, +1, 0 -1, -2, -3, -2, -1, loop) but I've seen other waveforms. Hirokazu Tanaka's engine and Capcom's engine do something like this.

B) Choose two pitch offsets and switch back and forth between them; this looks like a square wave and is what Konami, Natsume, and Akita Nakatsuka's engine (Zelda II) does.

Avoid writing to the 4th register of the channel (i.e., the most significant bits of the pitch) until those bits absolutely change, otherwise, you'll get crackling or buzzing artifacts since writing to that register resets the phase of the channel's waveform each time you write to it. You can freely write to the 3rd register all you want though.
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: What's the most common way to implement vibrato?

Post by rainwarrior »

olddb wrote: Fri Apr 30, 2021 3:50 pm What's the most common way to implement vibrato for an nes music engine? Specifically for the pulse channels.
I'm using Famitracker.

I tried using APU Sweep, but cant get it quiet right.
At the engine level, Famitracker makes use of the sweep as part of its vibrato, but in an obtuse way (a way to adjust the high byte of period without a pop). It's meant to be hidden from the user though. (Edit: or maybe it doesn't... I don't see it happening in 0.4.6. There are engines that do this though.)

Famitracker does have a vibratio effect you can just enter in the effect column. If you're using Famitracker but a different engine, it's kind of up to what that engine can do. Some support the vibrato effect but many don't.

In Lizard (FTMs) I just used pitch envelopes to accomplish vibrato, rather than a dedicated effect. Generally this is a symmetrical envelope, looped where it crosses a 0 offset. The important part is that it should be symmetrical (i.e. have the same amount of positive and negative contribution). Talking about a sine wave is maybe a red herring since we have so little precision to work with. Anything symmetrical and smooth-ish should be okay.

Examples:

Code: Select all

light vibrato (5 frames long)
| -1 0 0 0 1 1 0 0 0 -1

0 0 0 -1 -1 | 1 1 1 1 0 -1 -1 -1 -1 0 
wider vibrato
note: the loop point starts at -3, and the loop goes up and down by 6, so it remains centred
-1 -1 -1 | 1 1 2 1 1 -1 -1 -2 -1 -1

medium vibrato (range of 4)
0 0 0 -1 -1 | 1 1 1 1 0 -1 -1 -1 -1 0 

faster vibrato (4 frames long)
0 -1 -1 -1 | 1 2 2 1 -1 -2 -2 -1
So the speed is controlled by how long the loop is. It's kept centred by having the same count of positive and negative, and making sure the loop either starts at 0 or has a lead-in that makes it start at the bottom or top, appropriately.

In these examples I do mostly try to approximate a sine by moving the pitch fastest as it crosses 0, but it's not really necessary to be sine-like. (That medium vibrato maybe should have had the 0s in the middle like 1 1 0 1 1 -1 -1 0 -1 -1, but it doesn't really matter.)

Also, like Drag said, if you're using an engine that does not do Famtracker's sweep trick to manipulate the high bits of period, you'll get pops if you put a wide vibrato next to a high-bit crossing. A-3 in particular, since it's so close to 256.
User avatar
olddb
Posts: 188
Joined: Thu Oct 26, 2017 12:29 pm
Contact:

Re: What's the most common way to implement vibrato?

Post by olddb »

tepples wrote: Fri Apr 30, 2021 10:03 pm Are you looking to implement any sort of vibrato, or are you trying to exactly emulate FamiTracker's formulas for vibrato rate and depth? The latter might be more expensive in CPU time and/or ROM space.
This is what I am trying to do: Get it as close as possible to Famitrackers values.

Thank you all for the great answer. Is good to have options.
...
User avatar
olddb
Posts: 188
Joined: Thu Oct 26, 2017 12:29 pm
Contact:

Re: What's the most common way to implement vibrato?

Post by olddb »

...
Last edited by olddb on Wed May 05, 2021 9:47 pm, edited 1 time in total.
...
puppydrum64
Posts: 160
Joined: Sat Apr 24, 2021 7:25 am

Re: What's the most common way to implement vibrato?

Post by puppydrum64 »

olddb wrote: Sat May 01, 2021 2:55 pm
tepples wrote: Fri Apr 30, 2021 10:03 pm Are you looking to implement any sort of vibrato, or are you trying to exactly emulate FamiTracker's formulas for vibrato rate and depth? The latter might be more expensive in CPU time and/or ROM space.
This is what I am trying to do: Get it as close as possible to Famitrackers values.

Thank you all for the great answer. Is good to have options.
I had been trying to do the same, e.g. recreate fx code 4 74 using Famitracker's pitch and arpeggio envelopes. I tried blind guessing and listening to figure it out but I was hoping there would be a chart somewhere
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: What's the most common way to implement vibrato?

Post by rainwarrior »

puppydrum64 wrote: Wed May 05, 2021 12:18 pmI was hoping there would be a chart somewhere
FT 0.4.6 driver.s line 650 wrote:

Code: Select all

; Vibrato table (256 bytes)
ft_vibrato_table:
	.byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
	.byte $00, $00, $00, $00, $00, $00, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01
	.byte $00, $00, $00, $00, $01, $01, $01, $01, $02, $02, $02, $02, $02, $02, $02, $02
	.byte $00, $00, $00, $01, $01, $01, $02, $02, $02, $03, $03, $03, $03, $03, $03, $03
	.byte $00, $00, $00, $01, $01, $02, $02, $03, $03, $03, $04, $04, $04, $04, $04, $04
	.byte $00, $00, $01, $02, $02, $03, $03, $04, $04, $05, $05, $06, $06, $06, $06, $06
	.byte $00, $00, $01, $02, $03, $04, $05, $06, $07, $07, $08, $08, $09, $09, $09, $09
	.byte $00, $01, $02, $03, $04, $05, $06, $07, $08, $09, $09, $0A, $0B, $0B, $0B, $0B
	.byte $00, $01, $02, $04, $05, $06, $07, $08, $09, $0A, $0B, $0C, $0C, $0D, $0D, $0D
	.byte $00, $01, $03, $04, $06, $08, $09, $0A, $0C, $0D, $0E, $0E, $0F, $10, $10, $10
	.byte $00, $02, $04, $06, $08, $0A, $0C, $0D, $0F, $11, $12, $13, $14, $15, $15, $15
	.byte $00, $02, $05, $08, $0B, $0E, $10, $13, $15, $17, $18, $1A, $1B, $1C, $1D, $1D
	.byte $00, $04, $08, $0C, $10, $14, $18, $1B, $1F, $22, $24, $26, $28, $2A, $2B, $2B
	.byte $00, $06, $0C, $12, $18, $1E, $23, $28, $2D, $31, $35, $38, $3B, $3D, $3E, $3F
	.byte $00, $09, $12, $1B, $24, $2D, $35, $3C, $43, $4A, $4F, $54, $58, $5B, $5E, $5F
	.byte $00, $0C, $18, $25, $30, $3C, $47, $51, $5A, $62, $6A, $70, $76, $7A, $7D, $7F
The 16 rows are the 16 depth settings. The 16 columns are the phase of the effect when it's used.

i.e. vibrato $37 uses row $3 (depth) and adds $7 (speed) to the accumulator each frame.

Bits 0-3 of the accumulator select a column, but bits 4-5 of the accumulator reflect the direction or depth, i.e. $0X reads the row in increasing order, $1X reads it backwards, $2X is increasing but inverted result, $3X is backwards order but inverted.

So the maximum length of a vibrato's period (i.e. value $X1) is 64 frames in total. Higher speeds skip over steps (e.g. $X4 is 16 frames in total).

The code is in effect.s line 523 if you want to review it.
Last edited by rainwarrior on Wed May 05, 2021 4:28 pm, edited 2 times in total.
puppydrum64
Posts: 160
Joined: Sat Apr 24, 2021 7:25 am

Re: What's the most common way to implement vibrato?

Post by puppydrum64 »

rainwarrior wrote: Wed May 05, 2021 12:39 pm
puppydrum64 wrote: Wed May 05, 2021 12:18 pmI was hoping there would be a chart somewhere
FT 0.4.6 driver.s line 650 wrote:

Code: Select all

; Vibrato table (256 bytes)
ft_vibrato_table:
	.byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00
	.byte $00, $00, $00, $00, $00, $00, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01
	.byte $00, $00, $00, $00, $01, $01, $01, $01, $02, $02, $02, $02, $02, $02, $02, $02
	.byte $00, $00, $00, $01, $01, $01, $02, $02, $02, $03, $03, $03, $03, $03, $03, $03
	.byte $00, $00, $00, $01, $01, $02, $02, $03, $03, $03, $04, $04, $04, $04, $04, $04
	.byte $00, $00, $01, $02, $02, $03, $03, $04, $04, $05, $05, $06, $06, $06, $06, $06
	.byte $00, $00, $01, $02, $03, $04, $05, $06, $07, $07, $08, $08, $09, $09, $09, $09
	.byte $00, $01, $02, $03, $04, $05, $06, $07, $08, $09, $09, $0A, $0B, $0B, $0B, $0B
	.byte $00, $01, $02, $04, $05, $06, $07, $08, $09, $0A, $0B, $0C, $0C, $0D, $0D, $0D
	.byte $00, $01, $03, $04, $06, $08, $09, $0A, $0C, $0D, $0E, $0E, $0F, $10, $10, $10
	.byte $00, $02, $04, $06, $08, $0A, $0C, $0D, $0F, $11, $12, $13, $14, $15, $15, $15
	.byte $00, $02, $05, $08, $0B, $0E, $10, $13, $15, $17, $18, $1A, $1B, $1C, $1D, $1D
	.byte $00, $04, $08, $0C, $10, $14, $18, $1B, $1F, $22, $24, $26, $28, $2A, $2B, $2B
	.byte $00, $06, $0C, $12, $18, $1E, $23, $28, $2D, $31, $35, $38, $3B, $3D, $3E, $3F
	.byte $00, $09, $12, $1B, $24, $2D, $35, $3C, $43, $4A, $4F, $54, $58, $5B, $5E, $5F
	.byte $00, $0C, $18, $25, $30, $3C, $47, $51, $5A, $62, $6A, $70, $76, $7A, $7D, $7F
The 16 rows are the 16 depth settings. The 16 columns are the phase of the effect when it's used.

i.e. vibrato $37 uses row $3 (depth) and adds $7 (speed) to the accumulator each frame.

Bits 0-3 of the accumulator select a column, but bits 4-5 of the accumulator reflect the direction or depth, i.e. $0X reads the row in increasing order, $1X reads it backwards, $2X is increasing but inverted result, $3X is backwards order but inverted.

So the maximum length of a vibrato's period (i.e. value $X1) is 64 frames in total. Higher speeds skip over steps (e.g. $X4 is 16 frames in total).

The code is in effect.s line 523 if you want to review it.
I'll admit that kind of went over my head. :?
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: What's the most common way to implement vibrato?

Post by rainwarrior »

puppydrum64 wrote: Wed May 05, 2021 1:17 pmI'll admit that kind of went over my head. :?
Pick a depth. Go down that many rows in the table.

Pick a speed, go through the row left to right at that speed. Those are your pitch offsets. If you're using vibrato speed 1, step 1 column at a time. If using speed 2, step 2 columns, etc...

After going left to right, go right to left, then do the same but upside down, like this:

Code: Select all

0X 1X 2X 3X (accumulator)
| /\  |  |  |
|/ |\ |  |  |
/  | \|  |  |
|  |  \  | /|
|  |  |\ |/ |
|  |  | \/  |
I believe the rows of the table are equivalent to the first 1/4 of a sine wave, scaled and rounded to various sizes, but I haven't checked.
Last edited by rainwarrior on Wed May 05, 2021 4:27 pm, edited 3 times in total.
puppydrum64
Posts: 160
Joined: Sat Apr 24, 2021 7:25 am

Re: What's the most common way to implement vibrato?

Post by puppydrum64 »

rainwarrior wrote: Wed May 05, 2021 1:46 pm
puppydrum64 wrote: Wed May 05, 2021 1:17 pmI'll admit that kind of went over my head. :?
Pick a depth. Go down that many rows in the table.

Pick a speed, go through the row left to right at that speed. Those are your pitch offsets. If you're using vibrato speed 1, step 1 column at a time. If using speed 2, step 2 columns, etc...

After going left to right, go right to left, then do the same but upside down, like this:

Code: Select all

0X 1X 2X 3X (accumulator)
| /\  |  |  |
|/ |\ |  |  |
/  | \|  |  |
|  |  \  | /|
|  |  |\ |/ |
|  |  | \/  |
I believe the rows of the table are equivalent to the first 1/4 of a sine wave, scaled and rounded to various sizes, but I haven't checked.
So famitracker fx code 4 74 equals "Start at row 7, then increment by 4?"
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: What's the most common way to implement vibrato?

Post by rainwarrior »

Yes.
puppydrum64
Posts: 160
Joined: Sat Apr 24, 2021 7:25 am

Re: What's the most common way to implement vibrato?

Post by puppydrum64 »

I didn't know that Famitracker had an actual NES-compatible driver. Would this code work if I copied and pasted it into a .asm file and assembled it as an NES rom?
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: What's the most common way to implement vibrato?

Post by tepples »

The FamiTracker driver is fairly large in ROM and RAM size. You'd also need to add some layer on top to make sound effects.

There are other drivers that are smaller and better suited to games in general, such as Pently, FamiTone2, FamiTone4, and GGSound. All have available tools to convert the text export from FamiTracker, though all have limits to some extent on what effects and envelope features will and won't survive translation.
puppydrum64
Posts: 160
Joined: Sat Apr 24, 2021 7:25 am

Re: What's the most common way to implement vibrato?

Post by puppydrum64 »

tepples wrote: Wed May 05, 2021 5:53 pm The FamiTracker driver is fairly large in ROM and RAM size. You'd also need to add some layer on top to make sound effects.

There are other drivers that are smaller and better suited to games in general, such as Pently, FamiTone2, FamiTone4, and GGSound. All have available tools to convert the text export from FamiTracker, though all have limits to some extent on what effects and envelope features will and won't survive translation.
I've only ever heard of GGsound, it's fairly easy to use but doesn't work with the effects table or expansion audio, both of which I really want to have in my game's music
Post Reply