Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

yamaha: new GEW7 and GEW12 machines, various GEW8/MultiPCM improvements #11105

Merged
merged 10 commits into from
Apr 22, 2023

Conversation

devinacker
Copy link
Contributor

@devinacker devinacker commented Apr 15, 2023

New working machines

Yamaha PSR-150 [Edward d-tech, Devin Acker]
Yamaha PSR-180 [Edward d-tech, Devin Acker]
Yamaha PSR-78 [Edward d-tech, Devin Acker]

New working clones

Yamaha PSR-110 [Edward d-tech, Devin Acker]
Yamaha PSR-75 [Edward d-tech, Devin Acker]
Yamaha PSR-76 [Edward d-tech, Devin Acker]
Yamaha PSS-11 [Edward d-tech, Devin Acker]
Yamaha PSS-21 [Edward d-tech, Devin Acker]
Yamaha PSS-31 [Edward d-tech, Devin Acker]
Yamaha PSS-6 [Devin Acker]

Machines promoted to working

Yamaha PSS-12 [Devin Acker]

New NOT_WORKING machines

Yamaha PSR-260 [Edward d-tech, Devin Acker]
Yamaha PSR-79 [Edward d-tech, Devin Acker]

New NOT_WORKING clones

Yamaha PSR-160 [Edward d-tech, Devin Acker]


GEW7 and MultiPCM now share a common base class that handles sample decoding, mixing, envelope generation, etc. In the process I fixed a handful of issues that affected both devices:

  • fixed potential out of bounds array reads due to unsigned wraparound when calculating envelope steps, which could lead to garbage output for certain octave/rate combinations (gew_pcm_device::get_rate, gew_pcm_device::envelope_generator_calc)
  • fixed envelopes getting stuck at max when D1R = DL = 0 and D2R > 0 (gew_pcm_device::envelope_generator_update for DECAY1)
  • fixed off-by-one octave value when calculating envelope rates, per OPL4 manual (gew_pcm_device::envelope_generator_calc)
  • fixed potential out-of-tune sample loops due to not accounting for the current sample position correctly (gew_pcm_device::sound_stream_update when offset >= end)
  • fixed sample lengths being off by one for MultiPCM (multipcm_device::init_sample)
  • added calls to sound_stream::update for MultiPCM where appropriate (register reads/writes)
  • added missing members to save states

The MU5 now sounds much better than before, though the demo still has some noticeable problems with at least one instrument having a much slower attack than it's supposed to, and probably other issues. I added the imperfect sound flag to gew_pcm_device as a result.

… fixes (envelope behavior, sample lengths, stream updates, save state members)

New working machines
----------
Yamaha PSR-150 [Edward d-tech, Devin Acker]
Yamaha PSR-180 [Edward d-tech, Devin Acker]
Yamaha PSR-78 [Edward d-tech, Devin Acker]

New working clones
----------
Yamaha PSR-110 [Edward d-tech, Devin Acker]
Yamaha PSR-75 [Edward d-tech, Devin Acker]
Yamaha PSR-76 [Edward d-tech, Devin Acker]
Yamaha PSS-11 [Edward d-tech, Devin Acker]
Yamaha PSS-21 [Edward d-tech, Devin Acker]
Yamaha PSS-31 [Edward d-tech, Devin Acker]
Yamaha PSS-6 [Devin Acker]

Machines promoted to working
----------
Yamaha PSS-12 [Devin Acker]

New NOT_WORKING machines
----------
Yamaha PSR-260 [Edward d-tech, Devin Acker]
Yamaha PSR-79 [Edward d-tech, Devin Acker]

New NOT_WORKING clones
----------
Yamaha PSR-160 [Edward d-tech, Devin Acker]
src/devices/cpu/m6502/gew12.h Outdated Show resolved Hide resolved
src/devices/cpu/m6502/gew7.h Outdated Show resolved Hide resolved
src/devices/cpu/m6502/gew7.h Outdated Show resolved Hide resolved
src/devices/machine/gew12_uart.h Outdated Show resolved Hide resolved
Copy link
Member

@cuavas cuavas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The internal timers shouldn’t be implemented using scheduler timers. You don’t want to be causing scheduler synchronisation for internal events like this, and you particularly don’t want to be causing scheduler syncrhonisation on every tick if possible, because that really hurts performance.

I already did the work to template the underlying CPU type of the 6502 microcontroller helper in 864bfe1. You can now use it with the 65C02 core, and use the 6500/1 timer as a guide for how to implement a lazily evaluated timer.

src/devices/cpu/m6502/gew12.cpp Outdated Show resolved Hide resolved
src/devices/cpu/m6502/gew12.cpp Outdated Show resolved Hide resolved
src/devices/cpu/m6502/gew12.cpp Outdated Show resolved Hide resolved
src/devices/cpu/m6502/gew12.h Outdated Show resolved Hide resolved
src/devices/cpu/m6502/gew7.cpp Outdated Show resolved Hide resolved
src/devices/sound/gew.cpp Outdated Show resolved Hide resolved
src/devices/sound/gew.h Outdated Show resolved Hide resolved
src/devices/sound/gew7.h Outdated Show resolved Hide resolved
src/devices/sound/gew7.h Outdated Show resolved Hide resolved
src/devices/sound/multipcm.h Outdated Show resolved Hide resolved
Copy link
Member

@cuavas cuavas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking a lot closer to ready now.

src/devices/cpu/m6502/gew12.cpp Outdated Show resolved Hide resolved
src/devices/cpu/m6502/gew12.cpp Outdated Show resolved Hide resolved
src/devices/cpu/m6502/gew12.cpp Outdated Show resolved Hide resolved
src/devices/cpu/m6502/gew7.cpp Outdated Show resolved Hide resolved
src/devices/cpu/m6502/gew7.h Outdated Show resolved Hide resolved
@devinacker devinacker requested a review from cuavas April 19, 2023 21:28
@rb6502 rb6502 merged commit 35cb398 into mamedev:master Apr 22, 2023
halcyon00 pushed a commit to halcyon00/mame that referenced this pull request May 9, 2023
…ts (mamedev#11105)

* yamaha: implement GEW7, partially implement GEW12, some GEW8/MultiPCM fixes (envelope behavior, sample lengths, stream updates, save state members)

New working machines
----------
Yamaha PSR-150 [Edward d-tech, Devin Acker]
Yamaha PSR-180 [Edward d-tech, Devin Acker]
Yamaha PSR-78 [Edward d-tech, Devin Acker]

New working clones
----------
Yamaha PSR-110 [Edward d-tech, Devin Acker]
Yamaha PSR-75 [Edward d-tech, Devin Acker]
Yamaha PSR-76 [Edward d-tech, Devin Acker]
Yamaha PSS-11 [Edward d-tech, Devin Acker]
Yamaha PSS-21 [Edward d-tech, Devin Acker]
Yamaha PSS-31 [Edward d-tech, Devin Acker]
Yamaha PSS-6 [Devin Acker]

Machines promoted to working
----------
Yamaha PSS-12 [Devin Acker]

New NOT_WORKING machines
----------
Yamaha PSR-260 [Edward d-tech, Devin Acker]
Yamaha PSR-79 [Edward d-tech, Devin Acker]

New NOT_WORKING clones
----------
Yamaha PSR-160 [Edward d-tech, Devin Acker]
@LIJI32
Copy link

LIJI32 commented May 10, 2024

A bit out of topic, but do you have any clue how key and button inputs work on the GEW12 (specifically PSR-260)? Looking at the firmware, it appears as-if certain addresses in RAM (e.g. 0x0FC, 0x4C7-0x4FF) are somehow populated with key/button information externally, but I can't seem to find any code in the firmware that configures any external "device" to use these addresses.

@galibert
Copy link
Member

The psr260 schematics are available on electrotanya.

https://elektrotanya.com/yamaha_psr260_262.pdf/download.html

There we see that the button inputs are on ports A & B, and the keyboard on C, D & E. Dunno how the GEW12 maps the ports though.

OG.

@LIJI32
Copy link

LIJI32 commented May 10, 2024

Thanks! I would have assumed they would be mapped in 0x1f2a to 0x1f2e (Like how Port L is mapped at 0x1f2f) but that doesn't seem to be the case.

@LIJI32
Copy link

LIJI32 commented May 30, 2024

So while I was reversing the firmware for the PSR-260, I wrote a quick-and-dirty emulator to allow me to debug it. While the code can't really be used in MAME, I did figure out a major chunk of the GEW12 hardware that is not emulated or documented elsewhere:

  1. Basic operation of the PSR-260 button scanner and PSR-160 button scanner – they seem to have direct access to RAM, and they read and write from addresses that are not configured by the ROM. Addresses differ between the two models.
  2. Basic operation of the key scanner of the same two models, including velocity sensing. Like with button scanning, key scanning seems directly read and write from RAM.
  3. Mapping most of the LCD CG graphics.
  4. Basic DSP emulation – I have at least rough understanding of most DSP registers.
  5. DSP data structures – a voice on the DSP is defined by a 16-byte structure, and I understand the majority of it.
  6. Waveform formats – other than 8-bit and 16-bit wave formats, GEW12 also supports a 10-bit sample format.

I'm not sure if GEW12 emulation is still being worked on or how much MAME contributors dug into the hardware by themselves already, but I could document what I know if it helps.

I'm attaching a short video demonstrating my current state of emulation. The most notable thing missing is the lack of volume envelopes.

Screen.Recording.2024-05-31.at.00.52.00.mp4

@rb6502
Copy link
Contributor

rb6502 commented May 31, 2024

I don't know if Devin's put further work into the GEW12 or PSR260, but I'd certainly like to see more :-)

@LIJI32
Copy link

LIJI32 commented Jun 2, 2024

So the key and button scanners operate this way: They have direct access to RAM, and I assume they halt the CPU while they access RAM (but I'm not entirely sure how and when they do so). Both key scanning the button scanning share a "control byte", at address 0xFC (PSR-260) and 0x4F9 (PSR-160).

Button scan codes are identical for both models, and follow this order:

TENKEY_MINUS
GRAND_PIANO
MULTIPAD_1
MULTIPAD_2
MULTIPAD_3
MULTIPAD_4

TENKEY_7
TENKEY_8
TENKEY_9
DJ
TENKEY_0
TENKEY_PLUS

TENKEY_1
TENKEY_2
TENKEY_3
TENKEY_4
TENKEY_5
TENKEY_6

METRONOME
DEMO
OVERALL
LESSON_R
DICTIONARY
TOUCH

UNUSED1
UNUSED2
PLAY_STOP
INTRO_END
MAIN_AB
TEMPO

SONG
STYLE
VOICE
ACCOMP
LESSON_L
SYNC_START

Button scanning seems to be enabled by bit 4 in the control byte. The button scanner sets bit 7 to 1 when changes are available, and the firmware resets it when it's done processing the changes.

Button state is stored differently across models. In PSR-160, the state is stored in 6 bytes, starting at 0x4e8. Each byte represents a 6-button group's state in bits 0-5, with 0 being released and 1 being down.

Button state in PSR-260 is scattered all over RAM. Each group takes 3 bytes, and only bites 6-7 are used to hold the state. The group start addresses are:

        0x4C8
        0x4D6
        0x4E4
        0x4F2
        0x0DF
        0x0ED

So the Voice button, for example, is stored in bit 6 of address 0xEE.

Key scanning seems to be enabled by bit 3 in the control byte. The key scanner sets bit 6 to 1 when events are available, and the firmware resets it when it's done processing the changes.

Events are stored in a buffer starting at address 0x488 in PSR-260 and 4A8 in PSR-160. The maximum length of this buffer is 63 bytes in PSR-260 and 64-bytes in PSR-160 (This is because an event is 3 bytes in PSR-260 and 2 bytes in PSR-160). The current length of the buffer is stored in address 0x4C7 in PSR-260 and 0x04E8 in PSR-160. I didn't look into the event format for PSR-160 other than it being 2-bytes long.

The PSR-260 event format is:

  • Byte 0 contains the scan code.
  • Byte 1's bit 7 is 0 for key presses and 1 for key releases.
  • Byte 1's bits 0-2 (MSB) and Byte 2 (LSB) are the velocity.

Velocity ranges from 0 to 0x401, where 0 is the fastest/loudest key press and 0x3FF is the slowest/most silent. Values 0x400 and 0x401 are used for exceptional values, such as pressing a key so slowly it's out of range. Both are treated the same by the firmware (playing near silent in most voices, unless touch sensitivity is disabled).

Scancodes seem to differ between PSR-260 and PSR-160. The scan codes for PSR-260, from C1 to C6 are:

        61,  67,  68,  69,  70,  71,  72,  78,  77,  76, 75, 74,
        73,  79,  80,  81,  82,  83,  84,  90,  89,  88, 87, 86,
        85,  91,  95,  92,  93,  94,  96,  102, 100, 97, 98, 99,
        101, 107, 104, 105, 106, 103, 108, 30,  29,  28, 27, 26,
        25,  31,  36,  32,  33,  34,  35,  41,  42,  40, 39, 38,
        37

That's it for now, I'll document the rest later.

@LIJI32
Copy link

LIJI32 commented Jun 12, 2024

Sound registers:
0x1001: Slot index.
The majority of the sound registers are "banked" per channel. This register's bits 1-4 determine which channel slot is to be modified.

0x1004-0x1007: Trigger register. This 4-byte register is a 16-bit bitmap (only the even bits, 0, 2, 4 and 6, are used) where each bit maps to a channel. Writing 1 to a bit will start the corresponding channel. Writing 0s has no effect. This design allows triggering up to 4 channels with a single write so they always remain in sync.

0x1008-0x100B: Release register. This is the same as the trigger register, but does releases rather than triggers

0x1010-0x1011: Pitch register. BE, 16-bit. Determines the pitch of the currently selected (see 0x1001) channel. Bits 0-11 determine the pitch, while bits 12-15 are a signed octave modifier (i.e. multiples the pitch by 2 ^ n, where n ranges from -8 to 7).

0x1013: Volume. Another banked register that controls the volume. 0x7F is completely silent, 0x00 is the maximum volume. I'm not sure how middle values are to be interpolated, but it's not linear interpolation.

0x1014 and 0x1015: Left and Right volume, respectively. These two are banked as well, and determine the panning. 0x3C is completely silent (for that output terminal) while 0x00 is maximum volume. Similarly, I'm not sure how the middle values are to be interpolated.

0x101C and 0x101D: I don't fully understand the meaning of these registers, but they appear to be related to the vibrato effect. They're banked.

0x1023: I don't fully understand the meaning of this registers, but they appear to be related to the release segment of the ADSR envelope. Unlike the other segments, the release envelop can be dynamically controlled after triggering a sound, which is how the firmware handles pedaling.

0x102D-0x102F: Sound definition pointer. Banked. Take this 24-bit, BE value and multiply it by two, and treat it as a ROM offset. The 16-byte structure at this offset determines the properties of the sound to be played on the channel. I'll describe this structure later, but it generally includes a pointer to the waveform, the waveform's format, looping information, and ADSR parameters.

LCD segments are a bit annoying to work with because their hardware ordering is all over the place. So instead, I adapted code from the firmware to convert the physical ordering into the more logical order used by the firmware:

bool PSR_260_get_lcd_segment(PSR_260_t *psr260, PSR_260_lcd_segment_t segment)
{
    static const struct {
        uint8_t offset;
        uint8_t mask;
    } mapping[256] = {
        {0x2C, 4},{0x2A, 2},{0x29, 2},{0x2A, 4}, {0x29, 4}, {6, 0x10},{6, 8},{0x3E, 1},{0x3A, 1},{0x3B, 1},{0x3F, 1},{0x3C, 1},
        {2, 8},{2, 4},{3, 4},{7, 8},{3, 0x10},{2, 0x10},{3, 8}, {2, 1},{0xA, 0x10},{0xB, 0x10},{7, 1},{3, 2},{2, 2},{3, 1},
        {0xA, 4},{0xA, 2},{0xB, 2},{0xF, 4},{0xB, 8},{0xA, 8},{0xB, 4}, {0x32, 4},{0x2B, 0x10},{0x2C, 0x10},{0x34, 4},{0x34, 8},
        {0x33, 8},{0x33, 4},{0x1A, 1},{0x23, 0x10},{0x24, 0x10},{0x1C, 1},{0x2C, 8},{0x2B, 8},{0x1B, 1},{0x3A, 8},{0x3B, 4},
        {0x3C, 4},{0x3C, 8},{0x3C, 0x10},{0x3B, 0x10},{0x3B, 8},{0x37, 0x10},{0x26, 8},{0x2A, 8},{0x34, 2},{0, 0x10},{0x34, 1},
        {0x33, 2},{7, 0x10},{7, 2},{0xF, 0x10},{0xC, 0x10},{4, 4},{7, 4},{4, 2},{0xF, 8},{0x1F, 8},{0xC, 2},{0xF, 2},{0xC, 8},
        {0x27, 8},{0x1C, 8},{0x1C, 2},{0x1C, 4},{0x1F, 1},{0x27, 0x10},{6, 2},{0x20, 8},{8, 2},{8, 8},{8, 0x10},{0, 4},{0x16, 4},
        {0, 1},{8, 4},{8, 1},{0x10, 0x10},{0x10, 8},{0x16, 2},{0x18, 8},{0x18, 4},{0x18, 2},{0x18, 1},{0x20, 0x10},{0x36, 2},
        {0x28, 4},{0x28, 2},{0x28, 1},{0x30, 0x10},{0x30, 8},{0x36, 1},{0x38, 8},{0x38, 2},{0x38, 1},{0x20, 1},{0x20, 2},{0x34, 0x10},
        {0x25, 4},{0x27, 4},{0x25, 8},{0x27, 2},{0xD, 2},{0xD, 8},{0x27, 1},{0xD, 0x10},{0x2F, 0x10},{5, 4},{0x2F, 8},{5, 0x10},
        {5, 2},{0x2F, 4},{5, 1},{0x2F, 2},{0xD, 4},{0xD, 1},{0x2F, 1},{0x15, 0x10},{0x37, 8},{0x15, 8},{0x37, 4},{0x15, 4},
        {0x1D, 0x10},{0x16, 1},{0x1D, 8},{0x3E, 4},{0x1D, 4},{0x1D, 2},{0x3E, 2},{0x1D, 1},{0x2E, 1},{0x25, 0x10},{0x36, 0x10},
        {0x15, 2},{0x15, 1},{0x36, 8},{0x2D, 4},{0x2E, 0x10},{0x2D, 2},{0x2E, 2},{0x2E, 8},{0x35, 0x10},{0x26, 0x10},{0x35, 8},
        {0x3E, 0x10},{0x35, 2},{0x35, 1},{0x2E, 4},{0x3D, 8},{0x2B, 4},{0x3D, 2},{0x3D, 1},{0x2B, 2},{0x25, 1},{0x2C, 2},{0x25, 2},
        {0x2C, 1},{0x24, 2},{0x2D, 1},{0x1A, 8},{0x32, 8},{0x2A, 0x10},{0x28, 8},{0x1C, 0x10},{0x14, 1},{0x14, 2},{0x14, 4},{0x14, 8},
        {0x14, 0x10},{0xC, 1},{0x31, 2},{0x31, 1},{0x39, 0x10},{0x39, 8},{0x11, 2},{0x11, 1},{0x39, 4},{0x39, 2},{0x29, 1},
        {0x31, 0x10},{0x21, 8},{0x11, 4},{0x31, 4},{0x29, 0x10},{0x29, 8},{0x1A, 0x10},{0x12, 1},{0x12, 2},{0x12, 8},{0xA, 1},
        {0x30, 2},{0x30, 1},{0x38, 0x10},{0x10, 2},{0x10, 1},{0x38, 4},{0x2A, 1},{0x32, 0x10},{0x10, 4},{0x30, 4},{0x28, 0x10}
    };
    
    return psr260->lcdc.cgram[mapping[segment].offset] & mapping[segment].mask;
}

Then we have this logical mapping:

VOICE_SCREEN = 0
STYLE_SCREEN = 1
SONG_SCREEN = 2
STYLE_MODE = 3
SONG_MODE = 4

OVERALL_1 = 5
OVERALL_2 = 6
OVERALL_3 = 7
OVERALL_4 = 8
OVERALL_5 = 9
OVERALL_6 = 10
OVERALL_7 = 11

// The large 3-digit display denoting voice number, etc
SEVEN_SEGEMENT_1_TOP = 12
SEVEN_SEGEMENT_1_TOP_RIGHT = 13
SEVEN_SEGEMENT_1_BOTTOM_RIGHT = 14
SEVEN_SEGEMENT_1_BOTTOM = 15
SEVEN_SEGEMENT_1_BOTTOM_LEFT = 16
SEVEN_SEGEMENT_1_BOTTOM_TOP = 17
SEVEN_SEGEMENT_1_CENTER = 18

SEVEN_SEGEMENT_2_TOP = 19
SEVEN_SEGEMENT_2_TOP_RIGHT = 20
SEVEN_SEGEMENT_2_BOTTOM_RIGHT = 21
SEVEN_SEGEMENT_2_BOTTOM = 22
SEVEN_SEGEMENT_2_BOTTOM_LEFT = 23
SEVEN_SEGEMENT_2_BOTTOM_TOP = 24
SEVEN_SEGEMENT_2_CENTER = 25

SEVEN_SEGEMENT_3_TOP = 26
SEVEN_SEGEMENT_3_TOP_RIGHT = 27
SEVEN_SEGEMENT_3_BOTTOM_RIGHT = 28
SEVEN_SEGEMENT_3_BOTTOM = 29
SEVEN_SEGEMENT_3_BOTTOM_LEFT = 30
SEVEN_SEGEMENT_3_BOTTOM_TOP = 31
SEVEN_SEGEMENT_3_CENTER = 32

// The small 3-digit display denoting tempo, etc
SEVEN_SEGEMENT_MINI_1_TOP = 33
SEVEN_SEGEMENT_MINI_1_TOP_RIGHT = 34
SEVEN_SEGEMENT_MINI_1_BOTTOM_RIGHT = 35
SEVEN_SEGEMENT_MINI_1_BOTTOM = 36
SEVEN_SEGEMENT_MINI_1_BOTTOM_LEFT = 37
SEVEN_SEGEMENT_MINI_1_BOTTOM_TOP = 38
SEVEN_SEGEMENT_MINI_1_CENTER = 39

SEVEN_SEGEMENT_MINI_2_TOP = 40
SEVEN_SEGEMENT_MINI_2_TOP_RIGHT = 41
SEVEN_SEGEMENT_MINI_2_BOTTOM_RIGHT = 42
SEVEN_SEGEMENT_MINI_2_BOTTOM = 43
SEVEN_SEGEMENT_MINI_2_BOTTOM_LEFT = 44
SEVEN_SEGEMENT_MINI_2_BOTTOM_TOP = 45
SEVEN_SEGEMENT_MINI_2_CENTER = 46

SEVEN_SEGEMENT_MINI_3_TOP = 47
SEVEN_SEGEMENT_MINI_3_TOP_RIGHT = 48
SEVEN_SEGEMENT_MINI_3_BOTTOM_RIGHT = 49
SEVEN_SEGEMENT_MINI_3_BOTTOM = 50
SEVEN_SEGEMENT_MINI_3_BOTTOM_LEFT = 51
SEVEN_SEGEMENT_MINI_3_BOTTOM_TOP = 52
SEVEN_SEGEMENT_MINI_3_CENTER = 53

ACCOMP = 54
MEASURE = 55
TEMPO = 56
HAND_OPEN = 57
UNKNOWN = 58 // Seems to be used, but doesn't map to any LCD segment?
HAND_CLOSED = 59
BEAT = 60

// Chord letter
CHORD_LETTER_TOP_LEFT_ARC = 61
CHORD_LETTER_TOP_RIGHT = 62
CHORD_LETTER_MIDDLE_NOTCH = 63
CHORD_LETTER_BOTTOM_RIGHT = 64
CHORD_LETTER_BOTTOM = 65
CHORD_LETTER_MIDDLE_LEFT = 66
CHORD_LETTER_MIDDLE_RIGHT = 67

// Chord modifiers, didn't map them all yet
CHORD_MODIFIER_MAJOR = 70
CHORD_MODIFIER_MINOR = 71
CHORD_MODIFIER_7 = 72

/* The static keyboard shape. Since this entire group is either
   completely on (normal operation) or completely off (test mode),
   I'm not entirely sure how it is split */
KEYBOARD_SHAPE = 79 - 103

// Keyboard buttons
KEYBOARD_BUTTON_C1 = 110
// ... Chromatically continues toward C6
KEYBOARD_BUTTON_C6 = 170

LOW_8VA = 171
HIGH_8VA = 172

// Notes

NOTE_C2 = 175
// ... Continues toward C5
NOTE_C5 = 196

/* Also missing: Flat and sharp signs */

@rb6502
Copy link
Contributor

rb6502 commented Jun 12, 2024

Nice, thanks. That's very useful!

@devinacker are you interested? I would like to see all this implemented but I'm in the middle of additional Mac untangling right now :-)

@devinacker
Copy link
Contributor Author

Possibly, but I'm also focused on other stuff at the moment, myself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants