Jump to content
  • entries
    62
  • comments
    464
  • views
    87,139

BTP2 Music Specs (as seen in Stella's Stocking)


In case anyone is curious about the exact specs for the music in Stella's Stocking (BTP2 music driver):

 

  • 4 independent voices; two are output to each sound channel. The voices are otherwise isomorphic.
  • Two wave tables, which are used for "loud" and "soft". Normally most voices are shaped to start soft and get loud, or start loud and get soft, or something. In the "Echo Carol" (last tune at fireplace) there's a spot where loud and soft can be heard for a solid stretch.
  • Five octave range--61 note chromatic from 65.5Hz to 2096Hz (C3 to C8). Notes in the C diatonic scale are well in tune. Some others not so well, but all pitches are recognizable.
  • Subharmonic distortion only exists at pitches one or two octaves down from the target pitch, not at other pitches (e.g. the C8 has some distortion at 1048Hz, but since that's also a C, it's not particularly objectionable).
  • All music processing is done by the 6507 processor; it takes 46 cycles every 76 (just over 60%).
  • No exotic hardware required for the music, though exotic banking may improve the graphics that I can perform while playing the music.
  • If I get Magic Zeepy banking or some similar scheme working, the EBTBTP2 driver will allow for up to 24 or 32 wave tables instead of two, which should allow for much more varied dynamics and instrumentation. That might be considered 'hardware support' for music, but the CPU would still be doing all the real work.

10 Comments


Recommended Comments

It's definitely sounds impressive. Can you post more details about how it is actually done, e.g.:

  1. I presume you are manipulating the sound registers on every scanline, to give something like 60Hz * 262 = 15720 (15KHz) sound?
  2. Does it play actual sound samples and if so, where did these come from?
  3. Would it be possible to do AtariVox-style speech using your method?

 

Chris

  1. I presume you are manipulating the sound registers on every scanline, to give something like 60Hz * 262 = 15720 (15KHz) sound?
  2. Does it play actual sound samples and if so, where did these come from?
  3. Would it be possible to do AtariVox-style speech using your method?

 

Both sound registers are written every scan line; two voices are routed to each. Sound is played from wave tables. The system uses 12 'loud' wave tables (one for each pitch of a chromatic scale) and twelve 'soft' wave tables. There are also 12 'modulus' tables. Each set of 12 tables takes about three pages of ROM.

 

The wave tables vary from 60 to 32 bytes long, for pitches from middle C to the B above. The pointer may advance once byte every fourth line, once every other, once every, twice every, or four times every. For C, it may advance eight times every. Because the 'wrap' is only handled every fourth line, the wave tables must be padded out with an extra 16 bytes each; the 'C' table must be padded out with an extra 32 bytes.

 

If Magic Zeepy banking works, it should be possible to use a lot more than two tables. It may even be possible to do crude vocalizations. Otherwise, I don't know that it will be possible to improve things too much beyond what's there.

I'd be curious to see the RAM/ROM usage detailed a bit. How big is a wave table and how much data is required for a song?

 

For raw playback (just outputting four continuous tones), the RAM usage is 40 bytes for pointers; the ROM usage is about nine pages for tables. Wave generation uses cycles 6 to 51 of each scan line (since it starts on cycle 6, one can do an STA followed by a JSR).

 

If one wishes to play tunes, one will need additional RAM to keep track of tune pointers, and additional ROM to hold the tunes themselves. I really don't like the way I stored music in Stella's Stocking. Despite being rather bloated, it's not as versatile as I'd like. Given the time pressure, though, I didn't think it worthwhile to do something more sophisticated.

I'm glad you did this; I had been working on and off over the years on a multi voice synth for the 2600 and always knew it was technically possible. As I was working on it I was inspired by Bruce Tomlin's very cool Red Box (DTMF dialer + phone hacking) and his remarks that you couldn't do a general player (although I'm sure he would probably qualify them with the fact that you are hampered in what you can display whilst doing this music routine and specifically he was talking about using the techniques in Red Box). I also liked Andy Muchos driver that used PWM. Which brings me to the question. Generalizing, what kinds of display kernels can you have while playing your four voice music?

 

I never really put the effort in to finish a working model but I also appreciate that your genius in solving this!

I'm glad you did this; I had been working on and off over the years on a multi voice synth for the 2600 and always knew it was technically possible. As I was working on it I was inspired by Bruce Tomlin's very cool Red Box (DTMF dialer + phone hacking) and his remarks that you couldn't do a general player (although I'm sure he would probably qualify them with the fact that you are hampered in what you can display whilst doing this music routine and specifically he was talking about using the techniques in Red Box). I also liked Andy Muchos driver that used PWM. Which brings me to the question. Generalizing, what kinds of display kernels can you have while playing your four voice music?

 

I never really put the effort in to finish a working model but I also appreciate that your genius in solving this!

My new ARM board should fix that ;)

 

In practice, it will allow for three-voice Pitfall II-quality music, and Supercat pointed out that it can be done in 5 cycles per scanline, or in theory, BTP-style 4-voice in 10 cycles, and all the programmer needs to do is place INC AUDV0 and possibly an INC AUDV1 in the kernel.

 

Of course the programmer would still need to place those instructions about once per scanline outside the kernel as well, which might be a little challenging. But there may be a way around that. There has also been talk about making the ARM automatically send VSYNCs and run the kernel by feeding BRKs at proper times. I am thinking that this could be expanded to allow for automatic feeding of the audio outside the kernel. The board could feed a BRK/INC AUDVx/RTI every 76 (-ish) cycles during this time. Granted, this would use roughly 1/4 to 1/3 of your cycles, but it would be relatively transparent to the programmer.

The board could feed a BRK/INC AUDVx/RTI every 76 (-ish) cycles during this time. Granted, this would use roughly 1/4 to 1/3 of your cycles, but it would be relatively transparent to the programmer.

 

Wouldn't it be more like "brk/inc AUDV0/inc AUDV1/pla/sta temp1/pla/sta temp2/pla/sec/sbc #2/pha/lda temp2/sbc #0/pha/lda temp1/pha/rti"?

 

Better might be to inject "php/inc AUDV0/inc AUDV1/plp/jmp [to original address]".

The board could feed a BRK/INC AUDVx/RTI every 76 (-ish) cycles during this time. Granted, this would use roughly 1/4 to 1/3 of your cycles, but it would be relatively transparent to the programmer.

 

Wouldn't it be more like "brk/inc AUDV0/inc AUDV1/pla/sta temp1/pla/sta temp2/pla/sec/sbc #2/pha/lda temp2/sbc #0/pha/lda temp1/pha/rti"?

 

Better might be to inject "php/inc AUDV0/inc AUDV1/plp/jmp [to original address]".

EDIT: You are right, as RTI wouldn't return to the right place ;)

 

However, php/plp/jmp saves 3 cycles, so that might be better if the original address is not difficult to store.

 

EDIT2: I think it would be better to do php/inc/inc/bne/plp. The BNE would always branch if bit 5 of the data bus were always driven high and bit 4 low. It would occasionally take an additional cycle over the JMP, but some programs rely on the upper 3 address bits for bankswitching purposes, and it would be hard to recover them from the running program to feed into the JMP.

EDIT: You are right, as RTI wouldn't return to the right place ;)

 

For simulating interrupts for vertical sync and the end of vblank, the stack behavior of BRK would be a nuisance but not a problem; there should be plenty of cycles within the vertical sync or display kernel to fix the stack. But for a per-line interrupt, it would be a killer.

 

EDIT2: I think it would be better to do php/inc/inc/bne/plp. The BNE would always branch if bit 5 of the data bus were always driven high and bit 4 low. It would occasionally take an additional cycle over the JMP, but some programs rely on the upper 3 address bits for bankswitching purposes, and it would be hard to recover them from the running program to feed into the JMP.

 

Fair point in favor of BNE. Actually, if any bit is clear, the INC will clear the Z flag. Alternatively, one could use LSR (putting the data on bits 1-4 and setting bit 0 to a known state). Execution time either way would be 20-21 cycles every 76.

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...