This commit is contained in:
Eknous-P 2023-07-20 20:48:27 +04:00
commit 2c09b0c872
17 changed files with 173 additions and 178 deletions

View File

@ -4,5 +4,5 @@ The SCC/Bubble System WSG instrument editor consists of these macros:
- **Volume**: volume sequence - **Volume**: volume sequence
- **Arpeggio**: pitch sequence - **Arpeggio**: pitch sequence
- **Waveform**: spicifies wavetables sequence - **Waveform**: specifies wavetable sequence
- **Pitch**: fine pitch - **Pitch**: fine pitch

View File

@ -1,23 +1,122 @@
# wavetables
Wavetable synthesizers, in context of Furnace, are sound sources that operate on extremely short n-bit PCM streams. By extremely short, no more than 256 bytes. This amount of space is nowhere near enough to store an actual sampled sound, it allows certain amount of freedom to define a waveform shape.
Maximum wave width (length) is 256 bytes, and maximum wave height (depth) is 256 steps.
Each chip has its own maximum size, shown in the following table. If a larger wave is defined for these chips, it will be squashed to fit within the constraints of the chips. Some hardware doesn't work well with the wavetable synthesizer (described below); these systems are marked in the "synth?" column.
system | width | height | synth?
--------------------|------:|:-------|:-----:
Bubble System | 32 | 16 |
Game Boy | 32 | 16 |
Game.com | 32 | 16 |
Namco WSG | 32 | 16 |
WonderSwan | 32 | 16 |
Namco N163 | ≤128 | 16 |
Super Nintendo | ≤256 | 16 |
PC Engine | 32 | 32 |
Virtual Boy | 32 | 64 | no
Famicom Disk System | 64 | 64 |
Konami SCC | 32 | 256 |
Seta X1-010 | 128 | 256 |
# wavetable editor # wavetable editor
Wavetable synthesizers, in context of Furnace, are sound sources that operate on extremely short n-bit PCM streams. By extremely short, no more than 256 bytes. This amount of space is nowhere near enough to store an actual sampled sound, it allows certain amount of freedom to define a waveform shape. As of Furnace 0.6pre4, wavetable editor affects PC Engine, WonderSwan, Namco WSGs, Virtual Boy, Game.com, SCC, FDS, Seta X1-010, Konami Bubble System WSG, SNES, Amiga and channel 3 of Game Boy. ![wavetable editor](wave-editor.png)
Furnace's wavetable editor is rather simple, you can draw the waveform using mouse or by pasting an MML bit stream in the input field. Maximum wave width (length) is 256 bytes, and maximum wave height (depth) is 256. NOTE: Game Boy, PCE, WonderSwan, Namco WSG, N163, Game.com, Virtual Boy and Bubble System can handle max 32 byte waveforms, X1-010 can handle max 128 byte waveforms as of now, with 16-level height for GB, X1-010 Envelope, WS, Bubble System, SNES, Namco WSG and N163, 32-level height for PCE and 64-level height for Virtual Boy. If a larger wave is defined for these chips, it will be squashed to fit within the constraints of the chips. controls across the top line:
- waveform number. the `-` and `+` buttons step through the list.
- open.
- save.
- **Steps**: view waveform as discrete blocks.
- **Lines**: view waveform as a continuous line.
- **Width**: length of the waveform data. maximum is 256.
- **Height**: depth of the waveform. maximum is 256.
- `<` / `>`: toggle tabs (described below).
Furnace's wavetable editor features multiple ways of creating desired waveform shape: waveform display:
- the waveform is directly editable with the mouse.
- hovering will display a tooltip with the waveform position and value.
- **Shape** tab allows you to select a few predefined basic shapes and indirectly edit it via "Duty", "Exponent" and "XOR Point" sliders: controls across the bottom line:
- **Duty**: Affects mainly pulse waves, determining its wisth, like on C64/VRC6 - **Dec**: view MML stream as decimal.
- **Exponent**: Powers the waveform in the mathematical sense of the word (^2, ^3 and so on) - **Hex**: view MML stream as hexadecimal.
- **XOR Point**: Determines the point where the waveform gets negated. - `+` / `±`: toggle MML stream as unsigned/signed. also adjusts waveform display.
- _TODO:_ amplitude/phase part - MML stream: waveform data as an editable numeric sequence.
- **FM** for creating the waveform with frequency modulation synthesis principles: One can set carrier/modulation levels, frquency multiplier, connection between operators and FM waveforms of these operators.
- **WaveTools**: Allows user to fine-tune the waveform: scale said waveform in both X and Y axes, smoothen, amplify, normalize, convert to signed/unisgned, invert or even randomize the wavetable.
## wavetable synthesizer ## tabs
Furnace contains a mode for wavetable instruments that allows you to modulate or combine 1 or 2 waves to create unique "animated" sounds. Think of it like a VST or a plugin, as it's basically an extension of regular wavetable soundchips that still allow it to run on real hardware. each tab provides different ways of creating or altering a waveform.
### Shapes
![wavetable shape tab](wave-editor-shapes.png)
this creates a waveform by adding together a few predefined basic wave shapes.
- shape: select shape from sine, triangle, saw, and square.
- **Duty**: only affects pulse waves, determining their width
- **Exponent**: applies an exponent (power) to the waveform (^2, ^3 and so on).
- **XOR Point**: determines the point where the waveform gets negated.
- **Amplitude/Phase**: add together up to 16 instances of the shape.
- **Amplitude**: height of the shape.
- **Phase**: position along the shape. for example, 0.250 starts the shape a quarter of the way along.
### FM
![wavetable FM tab](wave-editor-FM.png)
this creates a waveform using frequency modulation synthesis with up to four operators.
one can set carrier/modulation levels, frequency multipliers, connections between operators and FM waveforms of these operators.
### WaveTools
![wavetable tools tab](wave-editor-tools.png)
these are useful editing tools to fine-tune the waveform:
- **Scale X**: stretches the waveform to a new length.
- interpolation method: filters the waveform when stretching. choose from none, linear, cosine, and cubic interpolation.
- **Scale Y**: resizes the waveform to a new height. it will clip at the top and bottom.
- **Offset X**: slides the the waveform forward or back. it will wrap around.
- **Offset Y**: slides the waveform up or down. it will clip at the top and bottom.
- **Smooth**. smooths waveform.
- **Amplify**. changes the volume of the waveform. it will clip at the top and bottom.
- **Normalize**: stretches waveform to maximum within the wavetable height.
- **Invert**: flips waveform vertically.
- **Half**: halves the waveform's frequency by stretching its first half to fill the waveform length.
- **Double**: doubles the waveform's frequency by squashing it to half length then repeating it.
- **Convert Signed/Unsigned**. worth trying if an imported wave sounds corrupted.
- **Randomize**: generate a completely random waveform.
# wavetable synthesizer
Within the "Wavetable" tab of the instrument editor, Furnace allows you to modulate or combine 1 or 2 waves to create unique "animated" sounds. Think of it like a VST or a plugin, as it's basically an extension of regular wavetable soundchips that still allow it to run on real hardware.
This is accomplished by selecting a wave or two, a mode, and adjusting the settings as needed until you come up with a sound that you like, without taking up a load of space. This allows you to create unique sound effects or instruments, that, when used well, almost sound like they're Amiga samples. This is accomplished by selecting a wave or two, a mode, and adjusting the settings as needed until you come up with a sound that you like, without taking up a load of space. This allows you to create unique sound effects or instruments, that, when used well, almost sound like they're Amiga samples.
Unfortunately, on chips like the HuC6280, you cannot use the wavetable synth to animate waveforms and have them sound smooth, as the chip resets the channel's phase when a waveform is changed while the channel is playing. On certain frequencies, this can be avoided, but not on most, unfortunately. Unfortunately, on some chips like the HuC6280, you cannot use the wavetable synth to animate waveforms and have them sound smooth, as the chip resets the channel's phase when a waveform is changed while the channel is playing. On certain frequencies, this can be avoided, but not on most, unfortunately.
![instrument wavetable tab](instrument-wavetable.png)
input waveforms should match the size of the wavetable or unexpected results may occur.
- **Enable synthesizer**: must be on for the rest of this to work.
- synthesizer type: selects the synthesis algorithm.
- waveform displays.
- **Wave 1**: selects input waveform.
- **Wave 2**: selects second input waveform. only appears when a dual-waveform synthesizer is selected.
- **Pause preview**: toggles live waveform preview.
- **Restart preview**: restarts preview from initial state.
- **Copy to new wavetable**: copies the currently displayed output waveform into the wavetable as a new entry.
- (width×height): size of wavetable.
- **Update Rate**: time in ticks between waveform changes.
- **Speed**: rate of change with each update.
- **Amount**: strength of synthesizer function.
- **Power**: only appears when synthesizer type is "Phase Modulation".
- **Global**:
- if disabled, each note resets the synthesizer to the start.
- if enabled, synthesis continues unbroken from note to note.

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
doc/5-wave/wave-editor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -14,6 +14,7 @@ this is a list of sound chips that Furnace supports, including effects.
- [Famicom Disk System](fds.md) - [Famicom Disk System](fds.md)
- [Game Boy](game-boy.md) - [Game Boy](game-boy.md)
- [Konami K007232](k007232.md) - [Konami K007232](k007232.md)
- [Konami K056320](k056320.md)
- [Konami SCC](scc.md) - [Konami SCC](scc.md)
- [Konami VRC6](vrc6.md) - [Konami VRC6](vrc6.md)
- [Atari Lynx](lynx.md) - [Atari Lynx](lynx.md)

9
doc/7-systems/k056320.md Normal file
View File

@ -0,0 +1,9 @@
# Konami 056320
the 056320 is a sample-based chip that featured in a number of Konami arcade games, notably _Sunset Riders_ and _Teenage Mutant Ninja Turtles: Turtles in Time_. it has four channels of audio and stereo output, and can access up to 2MB of samples in 8-bit PCM or 4-bit ADPCM formats.
# effects
- `DFxx`: Set sample playback direction. `0` is normal; `1` is reverse.

View File

@ -83,45 +83,6 @@ reference: [NESdev](https://www.nesdev.org/wiki/APU_Noise)
## length counter table ## length counter table
<!-- organized by input value... of little use -->
<!--
value | raw | NTSC | PAL | Dendy | NTSC 5-step | PAL 5-step | Dendy 5-step
-----:|----:|------:|------:|------:|------------:|-----------:|-------------:
`00` | 10 | 83ms | 100ms | 84ms | 104ms | 125ms | 105ms
`01` | 254 | 2.1s | 2.5s | 2.1s | 2.6s | 3.2s | 2.7s
`02` | 20 | 166ms | 200ms | 168ms | 208ms | 250ms | 210ms
`03` | 2 | 17ms | 20ms | 17ms | 21ms | 25ms | 21ms
`04` | 40 | 333ms | 400ms | 336ms | 417ms | 500ms | 421ms
`05` | 4 | 33ms | 40ms | 34ms | 42ms | 50ms | 42ms
`06` | 80 | 667ms | 800ms | 673ms | 833ms | 1.0s | 841ms
`07` | 6 | 50ms | 60ms | 50ms | 63ms | 75ms | 63ms
`08` | 160 | 1.3s | 1.6s | 1.3s | 1.7s | 2.0s | 1.7s
`09` | 8 | 67ms | 80ms | 67ms | 83ms | 100ms | 84ms
`0A` | 60 | 500ms | 600ms | 505ms | 625ms | 750ms | 631ms
`0B` | 10 | 83ms | 100ms | 84ms | 104ms | 125ms | 105ms
`0C` | 14 | 117ms | 140ms | 118ms | 146ms | 175ms | 147ms
`0D` | 12 | 100ms | 120ms | 101ms | 125ms | 150ms | 126ms
`0E` | 26 | 217ms | 260ms | 219ms | 271ms | 325ms | 273ms
`0F` | 14 | 117ms | 140ms | 118ms | 145ms | 175ms | 147ms
`10` | 12 | 100ms | 120ms | 101ms | 125ms | 150ms | 126ms
`11` | 16 | 133ms | 160ms | 135ms | 167ms | 200ms | 168ms
`12` | 24 | 200ms | 240ms | 202ms | 250ms | 300ms | 252ms
`13` | 18 | 150ms | 180ms | 151ms | 188ms | 225ms | 189ms
`14` | 48 | 400ms | 480ms | 404ms | 500ms | 600ms | 505ms
`15` | 20 | 167ms | 200ms | 168ms | 208ms | 250ms | 210ms
`16` | 96 | 800ms | 960ms | 807ms | 1.0s | 1.2s | 1.0s
`17` | 22 | 183ms | 220ms | 185ms | 229ms | 275ms | 231ms
`18` | 192 | 1.6s | 1.9s | 1.6s | 2.0s | 2.4s | 2.0s
`19` | 24 | 200ms | 240ms | 202ms | 250ms | 300ms | 252ms
`1A` | 72 | 600ms | 720ms | 606ms | 750ms | 900ms | 757ms
`1B` | 26 | 217ms | 260ms | 219ms | 271ms | 325ms | 273ms
`1C` | 16 | 133ms | 160ms | 135ms | 167ms | 200ms | 168ms
`1D` | 28 | 233ms | 280ms | 235ms | 292ms | 350ms | 294ms
`1E` | 32 | 267ms | 320ms | 269ms | 333ms | 400ms | 336ms
`1F` | 30 | 250ms | 300ms | 252ms | 313ms | 375ms | 315ms
-->
<!-- organized by resulting times... more useful! -->
value | raw | NTSC | PAL | Dendy | NTSC 5-step | PAL 5-step | Dendy 5-step value | raw | NTSC | PAL | Dendy | NTSC 5-step | PAL 5-step | Dendy 5-step
-----:|----:|------:|------:|------:|------------:|-----------:|-------------: -----:|----:|------:|------:|------:|------------:|-----------:|-------------:
`03` | 2 | 17ms | 20ms | 17ms | 21ms | 25ms | 21ms `03` | 2 | 17ms | 20ms | 17ms | 21ms | 25ms | 21ms
@ -161,21 +122,25 @@ reference: [NESdev](https://www.nesdev.org/wiki/APU_Length_Counter)
## DPCM frequency table ## DPCM frequency table
value | NTSC | PAL "value" is for DefleMask compatability.
------|----------:|----------:
`00` | 4181.7Hz | 4177.4Hz value | tracker | NTSC freq | NTSC pitch | PAL freq | PAL pitch
`01` | 4709.9Hz | 4696.6Hz -----:|:-------:|----------:|:----------:|----------:|:----------:
`02` | 5264.0Hz | 5261.4Hz `00` | `C-3` | 4181.7Hz | C-8 - 2¢ | 4177.4Hz | C-8 - 4¢
`03` | 5593.0Hz | 5579.2Hz `01` | `D-3` | 4709.9Hz | D-8 + 4¢ | 4696.6Hz | D-8 - 1¢
`04` | 6257.9Hz | 6023.9Hz `02` | `E-3` | 5264.0Hz | E-8 - 3¢ | 5261.4Hz | E-8 - 4¢
`05` | 7046.3Hz | 7044.9Hz `03` | `F-3` | 5593.0Hz | F-8 + 2¢ | 5579.2Hz | F-8 - 3¢
`06` | 7919.3Hz | 7917.2Hz `04` | `G-3` | 6258.0Hz | G-8 - 4¢ | 6023.9Hz | G-8 - 70¢
`07` | 8363.4Hz | 8397.0Hz `05` | `A-3` | 7046.4Hz | A-8 + 2¢ | 7044.9Hz | A-8 + 1¢
`08` | 9419.9Hz | 9446.6Hz `06` | `B-3` | 7919.4Hz | B-8 + 4¢ | 7917.2Hz | B-8 + 3¢
`09` | 11186.1Hz | 11233.8Hz `07` | `C-4` | 8363.4Hz | C-9 - 2¢ | 8397.0Hz | C-9 + 5¢
`0A` | 12604.0Hz | 12595.5Hz `08` | `D-4` | 9419.9Hz | D-9 + 4¢ | 9446.6Hz | D-9 + 9¢
`0B` | 13982.6Hz | 14089.9Hz `09` | `F-4` | 11186.1Hz | F-9 + 2¢ | 11233.8Hz | F-9 + 9¢
`0C` | 16884.6Hz | 16965.4Hz `0A` | `G-4` | 12604.0Hz | G-9 + 8¢ | 12595.5Hz | G-9 + 7¢
`0D` | 21306.8Hz | 21315.5Hz `0B` | `A-4` | 13982.6Hz | A-9 - 12¢ | 14089.9Hz | A-9 + 1¢
`0E` | 24858.0Hz | 25191.0Hz `0C` | `C-5` | 16884.6Hz | C-10 + 15¢ | 16965.4Hz | C-10 + 23¢
`0F` | 33143.9Hz | 33252.1Hz `0D` | `E-5` | 21306.8Hz | E-10 + 17¢ | 21315.5Hz | E-10 + 18¢
`0E` | `G-5` | 24858.0Hz | G-10 - 16¢ | 25191.0Hz | G-10 + 7¢
`0F` | `C-6` | 33143.9Hz | C-11 - 18¢ | 33252.1Hz | C-11 - 12¢
reference: [NESdev](https://www.nesdev.org/wiki/APU_DMC#Pitch_table)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 55 KiB

View File

@ -22,7 +22,6 @@
#include "../../ta-log.h" #include "../../ta-log.h"
#include <math.h> #include <math.h>
#define rRead(a,v) n163.addr_w(a); n163.data_r(v);
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} } #define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
#define rWriteMask(a,v,m) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v,m)); if (dumpWrites) {addWrite(a,v);} } #define rWriteMask(a,v,m) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v,m)); if (dumpWrites) {addWrite(a,v);} }
#define chWrite(c,a,v) \ #define chWrite(c,a,v) \
@ -198,8 +197,8 @@ void DivPlatformN163::tick(bool sysTick) {
chan[i].freqChanged=true; chan[i].freqChanged=true;
} }
if (chan[i].std.duty.had) { if (chan[i].std.duty.had) {
if (chan[i].wavePos!=chan[i].std.duty.val) { if (chan[i].curWavePos!=chan[i].std.duty.val) {
chan[i].wavePos=chan[i].std.duty.val; chan[i].curWavePos=chan[i].std.duty.val;
if (chan[i].waveMode&0x2) { if (chan[i].waveMode&0x2) {
chan[i].waveUpdated=true; chan[i].waveUpdated=true;
} }
@ -225,8 +224,8 @@ void DivPlatformN163::tick(bool sysTick) {
chan[i].freqChanged=true; chan[i].freqChanged=true;
} }
if (chan[i].std.ex1.had) { if (chan[i].std.ex1.had) {
if (chan[i].waveLen!=(chan[i].std.ex1.val&0xfc)) { if (chan[i].curWaveLen!=(chan[i].std.ex1.val&0xfc)) {
chan[i].waveLen=chan[i].std.ex1.val&0xfc; chan[i].curWaveLen=chan[i].std.ex1.val&0xfc;
chan[i].ws.setWidth(chan[i].waveLen); chan[i].ws.setWidth(chan[i].waveLen);
if (chan[i].waveMode&0x2) { if (chan[i].waveMode&0x2) {
chan[i].waveUpdated=true; chan[i].waveUpdated=true;
@ -250,35 +249,6 @@ void DivPlatformN163::tick(bool sysTick) {
} }
} }
} }
if (chan[i].std.ex3.had) {
if (chan[i].loadWave!=chan[i].std.ex3.val) {
chan[i].loadWave=chan[i].std.ex3.val;
if (chan[i].loadMode&0x2) {
updateWave(i,chan[i].loadWave,chan[i].loadPos,chan[i].loadLen&0xfc);
}
}
}
if (chan[i].std.alg.had) {
if (chan[i].loadPos!=chan[i].std.alg.val) {
chan[i].loadPos=chan[i].std.alg.val;
}
}
if (chan[i].std.fb.had) {
if (chan[i].loadLen!=(chan[i].std.fb.val&0xfc)) {
chan[i].loadLen=chan[i].std.fb.val&0xfc;
}
}
if (chan[i].std.fms.had) {
if ((chan[i].loadMode&0x2)!=(chan[i].std.fms.val&0x2)) { // load when every waveform changes
chan[i].loadMode=(chan[i].loadMode&~0x2)|(chan[i].std.fms.val&0x2);
}
if ((chan[i].loadMode&0x1)!=(chan[i].std.fms.val&0x1)) { // load now
chan[i].loadMode=(chan[i].loadMode&~0x1)|(chan[i].std.fms.val&0x1);
if (chan[i].loadMode&0x1) { // rising edge
updateWave(i,chan[i].loadWave,chan[i].loadPos,chan[i].loadLen&0xfc);
}
}
}
if (chan[i].volumeChanged) { if (chan[i].volumeChanged) {
if (chan[i].active && !isMuted[i]) { if (chan[i].active && !isMuted[i]) {
chWriteMask(i,0x7,chan[i].resVol&0xf,0xf); chWriteMask(i,0x7,chan[i].resVol&0xf,0xf);
@ -288,7 +258,7 @@ void DivPlatformN163::tick(bool sysTick) {
chan[i].volumeChanged=false; chan[i].volumeChanged=false;
} }
if (chan[i].waveChanged) { if (chan[i].waveChanged) {
chWrite(i,0x6,chan[i].wavePos); chWrite(i,0x6,chan[i].curWavePos);
if (chan[i].active) { if (chan[i].active) {
chan[i].freqChanged=true; chan[i].freqChanged=true;
} }
@ -309,7 +279,7 @@ void DivPlatformN163::tick(bool sysTick) {
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
// TODO: what is this mess? // TODO: what is this mess?
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE);
chan[i].freq=(((chan[i].freq*chan[i].waveLen)*(chanMax+1))/16); chan[i].freq=(((chan[i].freq*chan[i].curWaveLen)*(chanMax+1))/16);
if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].freq>0x3ffff) chan[i].freq=0x3ffff; if (chan[i].freq>0x3ffff) chan[i].freq=0x3ffff;
if (chan[i].keyOn) { if (chan[i].keyOn) {
@ -325,7 +295,7 @@ void DivPlatformN163::tick(bool sysTick) {
} }
chWrite(i,0x0,chan[i].freq&0xff); chWrite(i,0x0,chan[i].freq&0xff);
chWrite(i,0x2,chan[i].freq>>8); chWrite(i,0x2,chan[i].freq>>8);
chWrite(i,0x4,((256-chan[i].waveLen)&0xfc)|((chan[i].freq>>16)&3)); chWrite(i,0x4,((256-chan[i].curWaveLen)&0xfc)|((chan[i].freq>>16)&3));
if (chan[i].keyOn) chan[i].keyOn=false; if (chan[i].keyOn) chan[i].keyOn=false;
if (chan[i].keyOff) chan[i].keyOff=false; if (chan[i].keyOff) chan[i].keyOff=false;
chan[i].freqChanged=false; chan[i].freqChanged=false;
@ -342,7 +312,9 @@ int DivPlatformN163::dispatch(DivCommand c) {
chan[c.chan].wavePos=ins->n163.wavePos; chan[c.chan].wavePos=ins->n163.wavePos;
chan[c.chan].waveLen=ins->n163.waveLen; chan[c.chan].waveLen=ins->n163.waveLen;
chan[c.chan].waveMode=ins->n163.waveMode; chan[c.chan].waveMode=ins->n163.waveMode;
chan[c.chan].ws.init(NULL,chan[c.chan].waveLen,15,false); chan[c.chan].curWavePos=chan[c.chan].wavePos;
chan[c.chan].curWaveLen=chan[c.chan].waveLen;
chan[c.chan].ws.init(NULL,chan[c.chan].waveLen,15,true);
chan[c.chan].ws.changeWave1(chan[c.chan].wave); chan[c.chan].ws.changeWave1(chan[c.chan].wave);
chan[c.chan].waveChanged=true; chan[c.chan].waveChanged=true;
if (chan[c.chan].waveMode&0x3 || ins->ws.enabled) { if (chan[c.chan].waveMode&0x3 || ins->ws.enabled) {
@ -438,14 +410,14 @@ int DivPlatformN163::dispatch(DivCommand c) {
chan[c.chan].keyOn=true; chan[c.chan].keyOn=true;
break; break;
case DIV_CMD_N163_WAVE_POSITION: case DIV_CMD_N163_WAVE_POSITION:
chan[c.chan].wavePos=c.value; chan[c.chan].curWavePos=c.value;
if (chan[c.chan].waveMode&0x2) { if (chan[c.chan].waveMode&0x2) {
chan[c.chan].waveUpdated=true; chan[c.chan].waveUpdated=true;
} }
chan[c.chan].waveChanged=true; chan[c.chan].waveChanged=true;
break; break;
case DIV_CMD_N163_WAVE_LENGTH: case DIV_CMD_N163_WAVE_LENGTH:
chan[c.chan].waveLen=c.value&0xfc; chan[c.chan].curWaveLen=c.value&0xfc;
if (chan[c.chan].waveMode&0x2) { if (chan[c.chan].waveMode&0x2) {
chan[c.chan].waveUpdated=true; chan[c.chan].waveUpdated=true;
} }
@ -458,42 +430,13 @@ int DivPlatformN163::dispatch(DivCommand c) {
chan[c.chan].waveChanged=true; chan[c.chan].waveChanged=true;
} }
break; break;
case DIV_CMD_N163_WAVE_LOAD:
chan[c.chan].loadWave=c.value;
if (chan[c.chan].loadMode&0x2) { // load when every waveform changes
updateWave(c.chan,chan[c.chan].loadWave,chan[c.chan].loadPos,chan[c.chan].loadLen);
}
break;
case DIV_CMD_N163_WAVE_LOADPOS:
chan[c.chan].loadPos=c.value;
break;
case DIV_CMD_N163_WAVE_LOADLEN:
chan[c.chan].loadLen=c.value&0xfc;
break;
case DIV_CMD_N163_WAVE_LOADMODE:
chan[c.chan].loadMode=c.value&0x3;
if (chan[c.chan].loadMode&0x1) { // load now
updateWave(c.chan,chan[c.chan].loadWave,chan[c.chan].loadPos,chan[c.chan].loadLen);
}
break;
case DIV_CMD_N163_GLOBAL_WAVE_LOAD: case DIV_CMD_N163_GLOBAL_WAVE_LOAD:
loadWave=c.value; loadWave=c.value;
if (loadMode&0x2) { // load when every waveform changes // TODO: load wave here
updateWave(c.chan,loadWave,loadPos,loadLen);
}
break; break;
case DIV_CMD_N163_GLOBAL_WAVE_LOADPOS: case DIV_CMD_N163_GLOBAL_WAVE_LOADPOS:
loadPos=c.value; loadPos=c.value;
break; break;
case DIV_CMD_N163_GLOBAL_WAVE_LOADLEN:
loadLen=c.value&0xfc;
break;
case DIV_CMD_N163_GLOBAL_WAVE_LOADMODE:
loadMode=c.value&0x3;
if (loadMode&0x3) { // load now
updateWave(c.chan,loadWave,loadPos,loadLen);
}
break;
case DIV_CMD_N163_CHANNEL_LIMIT: case DIV_CMD_N163_CHANNEL_LIMIT:
if (chanMax!=(c.value&0x7)) { if (chanMax!=(c.value&0x7)) {
chanMax=c.value&0x7; chanMax=c.value&0x7;
@ -619,8 +562,6 @@ void DivPlatformN163::reset() {
chanMax=initChanMax; chanMax=initChanMax;
loadWave=-1; loadWave=-1;
loadPos=0; loadPos=0;
loadLen=0;
loadMode=0;
rWrite(0x7f,initChanMax<<4); rWrite(0x7f,initChanMax<<4);
} }

View File

@ -29,9 +29,8 @@ class DivPlatformN163: public DivDispatch {
struct Channel: public SharedChannel<signed char> { struct Channel: public SharedChannel<signed char> {
signed char resVol; signed char resVol;
short wave, wavePos, waveLen; short wave, wavePos, waveLen;
short curWavePos, curWaveLen;
unsigned char waveMode; unsigned char waveMode;
short loadWave, loadPos, loadLen;
unsigned char loadMode;
bool volumeChanged; bool volumeChanged;
bool waveChanged, waveUpdated; bool waveChanged, waveUpdated;
DivWaveSynth ws; DivWaveSynth ws;
@ -41,11 +40,9 @@ class DivPlatformN163: public DivDispatch {
wave(-1), wave(-1),
wavePos(0), wavePos(0),
waveLen(0), waveLen(0),
curWavePos(0),
curWaveLen(0),
waveMode(0), waveMode(0),
loadWave(-1),
loadPos(0),
loadLen(0),
loadMode(0),
volumeChanged(false), volumeChanged(false),
waveChanged(false), waveChanged(false),
waveUpdated(false) {} waveUpdated(false) {}
@ -63,8 +60,7 @@ class DivPlatformN163: public DivDispatch {
FixedQueue<QueuedWrite,2048> writes; FixedQueue<QueuedWrite,2048> writes;
unsigned char initChanMax; unsigned char initChanMax;
unsigned char chanMax; unsigned char chanMax;
short loadWave, loadPos, loadLen; short loadWave, loadPos;
unsigned char loadMode;
bool multiplex; bool multiplex;
n163_core n163; n163_core n163;

View File

@ -1005,20 +1005,16 @@ void DivEngine::registerSystems() {
{DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE},
{DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163}, {DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163},
{}, {},
{
{0x18, {DIV_CMD_N163_CHANNEL_LIMIT, "180x: Change channel limits (0 to 7, x + 1)"}}
},
{ {
{0x10, {DIV_CMD_WAVE, "10xx: Select waveform"}}, {0x10, {DIV_CMD_WAVE, "10xx: Select waveform"}},
{0x11, {DIV_CMD_N163_WAVE_POSITION, "11xx: Set waveform position in RAM (single nibble unit)"}}, {0x11, {DIV_CMD_N163_WAVE_POSITION, "11xx: Set waveform position in RAM (single nibble unit)"}},
{0x12, {DIV_CMD_N163_WAVE_LENGTH, "12xx: Set waveform length in RAM (04 to FC, 4 nibble unit)"}}, {0x12, {DIV_CMD_N163_WAVE_LENGTH, "12xx: Set waveform length in RAM (04 to FC, 4 nibble unit)"}},
{0x13, {DIV_CMD_N163_WAVE_MODE, "130x: Change waveform update mode (0: off; bit 0: update now; bit 1: update when every waveform changes)"}}, {0x13, {DIV_CMD_N163_WAVE_MODE, "130x: Change waveform update mode (0: off; bit 0: update now; bit 1: update when every waveform changes)"}},
{0x14, {DIV_CMD_N163_WAVE_LOAD, "14xx: Select waveform for load to RAM"}}, {0x20, {DIV_CMD_N163_GLOBAL_WAVE_LOAD, "20xx: Load a waveform into memory"}},
{0x15, {DIV_CMD_N163_WAVE_LOADPOS, "15xx: Set waveform position for load to RAM (single nibble unit)"}}, {0x21, {DIV_CMD_N163_GLOBAL_WAVE_LOADPOS, "21xx: Set position for wave load"}},
{0x16, {DIV_CMD_N163_WAVE_LOADLEN, "16xx: Set waveform length for load to RAM (04 to FC, 4 nibble unit)"}},
{0x17, {DIV_CMD_N163_WAVE_LOADMODE, "170x: Change waveform load mode (0: off; bit 0: load now; bit 1: load when every waveform changes)"}},
{0x18, {DIV_CMD_N163_CHANNEL_LIMIT, "180x: Change channel limits (0 to 7, x + 1)"}},
{0x20, {DIV_CMD_N163_GLOBAL_WAVE_LOAD, "20xx: (Global) Select waveform for load to RAM"}},
{0x21, {DIV_CMD_N163_GLOBAL_WAVE_LOADPOS, "21xx: (Global) Set waveform position for load to RAM (single nibble unit)"}},
{0x22, {DIV_CMD_N163_GLOBAL_WAVE_LOADLEN, "22xx: (Global) Set waveform length for load to RAM (04 to FC, 4 nibble unit)"}},
{0x23, {DIV_CMD_N163_GLOBAL_WAVE_LOADMODE, "230x: (Global) Change waveform load mode (0: off; bit 0: load now; bit 1: load when every waveform changes)"}},
} }
); );

View File

@ -462,8 +462,6 @@ void putDispatchChip(void* data, int type) {
ImGui::Text("- chanMax: %d",ch->chanMax); ImGui::Text("- chanMax: %d",ch->chanMax);
ImGui::Text("- loadWave: %d",ch->loadWave); ImGui::Text("- loadWave: %d",ch->loadWave);
ImGui::Text("- loadPos: %d",ch->loadPos); ImGui::Text("- loadPos: %d",ch->loadPos);
ImGui::Text("- loadLen: %d",ch->loadLen);
ImGui::Text("- loadMode: %d",ch->loadMode);
COMMON_CHIP_DEBUG_BOOL; COMMON_CHIP_DEBUG_BOOL;
ImGui::TextColored(ch->multiplex?colorOn:colorOff,">> Multiplex"); ImGui::TextColored(ch->multiplex?colorOn:colorOff,">> Multiplex");
break; break;
@ -876,10 +874,6 @@ void putDispatchChan(void* data, int chanNum, int type) {
ImGui::Text("- wavepos: %d",ch->wavePos); ImGui::Text("- wavepos: %d",ch->wavePos);
ImGui::Text("- wavelen: %d",ch->waveLen); ImGui::Text("- wavelen: %d",ch->waveLen);
ImGui::Text("- wavemode: %d",ch->waveMode); ImGui::Text("- wavemode: %d",ch->waveMode);
ImGui::Text("- loadwave: %d",ch->loadWave);
ImGui::Text("- loadpos: %d",ch->loadPos);
ImGui::Text("- loadlen: %d",ch->loadLen);
ImGui::Text("- loadmode: %d",ch->loadMode);
ImGui::Text("- resVol: %.2x",ch->resVol); ImGui::Text("- resVol: %.2x",ch->resVol);
COMMON_CHAN_DEBUG_BOOL; COMMON_CHAN_DEBUG_BOOL;
ImGui::TextColored(ch->volumeChanged?colorOn:colorOff,">> VolumeChanged"); ImGui::TextColored(ch->volumeChanged?colorOn:colorOff,">> VolumeChanged");

View File

@ -5442,10 +5442,10 @@ void FurnaceGUI::drawInsEdit() {
dutyLabel="Duty"; dutyLabel="Duty";
dutyMax=63; dutyMax=63;
} }
/*if (ins->type==DIV_INS_N163) { if (ins->type==DIV_INS_N163) {
dutyLabel="Waveform pos."; dutyLabel="Wave Pos";
dutyMax=255; dutyMax=255;
}*/ }
if (ins->type==DIV_INS_VRC6) { if (ins->type==DIV_INS_VRC6) {
dutyLabel="Duty"; dutyLabel="Duty";
dutyMax=ins->amiga.useSample?0:7; dutyMax=ins->amiga.useSample?0:7;
@ -5755,8 +5755,6 @@ void FurnaceGUI::drawInsEdit() {
if (ex2Max>0) { if (ex2Max>0) {
if (ins->type==DIV_INS_C64) { if (ins->type==DIV_INS_C64) {
macroList.push_back(FurnaceGUIMacroDesc("Resonance",&ins->std.ex2Macro,0,ex2Max,64,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Resonance",&ins->std.ex2Macro,0,ex2Max,64,uiColors[GUI_COLOR_MACRO_OTHER]));
/*} else if (ins->type==DIV_INS_N163) {
macroList.push_back(FurnaceGUIMacroDesc("Wave Update",&ins->std.ex2Macro,0,ex2Max,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,n163UpdateBits));*/
} else if (ins->type==DIV_INS_FDS) { } else if (ins->type==DIV_INS_FDS) {
macroList.push_back(FurnaceGUIMacroDesc("Mod Speed",&ins->std.ex2Macro,0,ex2Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Mod Speed",&ins->std.ex2Macro,0,ex2Max,160,uiColors[GUI_COLOR_MACRO_OTHER]));
} else if (ins->type==DIV_INS_SU) { } else if (ins->type==DIV_INS_SU) {
@ -5786,12 +5784,6 @@ void FurnaceGUI::drawInsEdit() {
macroList.push_back(FurnaceGUIMacroDesc("Noise AND Mask",&ins->std.fbMacro,0,8,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); macroList.push_back(FurnaceGUIMacroDesc("Noise AND Mask",&ins->std.fbMacro,0,8,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc("Noise OR Mask",&ins->std.fmsMacro,0,8,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); macroList.push_back(FurnaceGUIMacroDesc("Noise OR Mask",&ins->std.fmsMacro,0,8,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
} }
if (ins->type==DIV_INS_N163) {
/*macroList.push_back(FurnaceGUIMacroDesc("WaveLoad Wave",&ins->std.ex3Macro,0,255,160,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc("WaveLoad Pos",&ins->std.algMacro,0,255,160,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc("WaveLoad Len",&ins->std.fbMacro,0,252,160,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc("WaveLoad Trigger",&ins->std.fmsMacro,0,2,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,n163UpdateBits));*/
}
if (ins->type==DIV_INS_FDS) { if (ins->type==DIV_INS_FDS) {
macroList.push_back(FurnaceGUIMacroDesc("Mod Position",&ins->std.ex3Macro,0,127,160,uiColors[GUI_COLOR_MACRO_OTHER])); macroList.push_back(FurnaceGUIMacroDesc("Mod Position",&ins->std.ex3Macro,0,127,160,uiColors[GUI_COLOR_MACRO_OTHER]));
} }

View File

@ -128,8 +128,10 @@ void FurnaceGUI::drawNewSong() {
for (size_t chipIndex=0; chipIndex<chips.size(); chipIndex++) { for (size_t chipIndex=0; chipIndex<chips.size(); chipIndex++) {
DivSystem chip=chips[chipIndex]; DivSystem chip=chips[chipIndex];
const DivSysDef* sysDef=e->getSystemDef(chip); const DivSysDef* sysDef=e->getSystemDef(chip);
ImGui::PushTextWrapPos(MIN(scrW*dpiScale,400.0f*dpiScale));
ImGui::Text("%s (x%d): ",sysDef->name,chipCounts[chip]); ImGui::Text("%s (x%d): ",sysDef->name,chipCounts[chip]);
ImGui::TextWrapped("%s",sysDef->description); ImGui::Text("%s",sysDef->description);
ImGui::PopTextWrapPos();
if (chipIndex+1<chips.size()) { if (chipIndex+1<chips.size()) {
ImGui::Separator(); ImGui::Separator();
} }