16-bit stereo 32 kHz streaming success

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.
User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

16-bit stereo 32 kHz streaming success

Post by blargg » Fri Mar 05, 2010 5:43 pm

Today I got 16-bit stereo 32 kHz uncompressed streaming to the SPC-700 working. It sets the echo buffer size to one sample, and disables echo write. This results in the DSP reading the stereo samples every 32 clocks and playing them. Then some timed S-SMP code copies the samples from the S-CPU to the echo buffer every 32 clocks.

The core S-SMP code uses word moves from the IO registers, and has the echo buffer at address 0 so it can use word writes as well. The lack of indexed writing is what allows this to stream 128 K/sec, well over the apparent maximum transfer rate detailed in another post.

Code: Select all

      ; Stream samples from S-CPU
-     movw  ya,CPUIO0   ; 5 left
      movw  0,ya        ; 5
      mov   CPUIO3,#$80 ; 5 acknowledge
      movw  ya,CPUIO2   ; 5 right
      mov   CPUIO3,#0   ; 5 clear acknowledge
      movw  2,ya        ; 5
      
      movw  ya,CPUIO0   ; 5 left
      movw  0,ya        ; 5
      mov   CPUIO3,#$80 ; 5 acknowledge
      movw  ya,CPUIO2   ; 5 right
      mov   CPUIO3,#0   ; 5 clear acknowledge
      movw  2,ya        ; 5
      bra   -           ; 4
Even though there seem to be no spare cycles, a couple can be squeezed out. You'll note that the acknowledgements back to the S-CPU change twice per sample. This simplifies the S-CPU synchronization code.

On the S-CPU side, writing samples is very easy:

Code: Select all

; A,X,Y all 16 bits wide
loop:
      ; Wait for S-SMP to be ready for more samples
:     bit APUIO2
      bpl :-
      
      ; Write left and right samples. There's time
      ; to do some table lookups instead of having
      ; them pre-calculated in x and y.
      nop
      stx APUIO0
      sty APUIO2
      
      ; Calculate next samples.
      ; 486 master clocks available to do so.
      ...
      
      bra loop
Note how the synchronization merely waits for the small window in which the fourth I/O register is set to $80. I used this instead of a comparison with a counter, because that would have required more cycles on the S-CPU side. Better to make the S-SMP do a bit more work (write two acknowledgement values each sample) rather than the S-CPU.

I've been able to write a simple mono real-time FM synthesizer, but it doesn't have volume control as there's not enough time. I figured I'd post this in case anyone did have some actual samples worth outputting. The above code works perfectly, as I've tested it carefully with things like sine waves and various samples. I can put together a complete demo if anyone's interested.

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

Post by tepples » Fri Mar 05, 2010 5:48 pm

So you found how to emulate NES $4011 :-)

I wonder how suitable this would be for speech synthesis. For this, you won't really need more than 8-bit mono at 8.0 or 10.7 kHz, so filling an entire page of the echo buffer might simplify the S-CPU side of it and allow game logic to run at the same time.

byuu
Posts: 1540
Joined: Mon Mar 27, 2006 5:23 pm
Contact:

Post by byuu » Fri Mar 05, 2010 8:08 pm

Even generating music will be tough. 128K/s is a lot of bandwidth. The entirety of SNES work RAM.

But if this can be dropped to a lower bit rate, say 16KHz 8-bit mono or stereo, then it could lead to something really impressive if an SA-1 coprocessor were used as a custom audio mixer. The quality gain would be in avoiding 4-bit ADPCM.

Or perhaps someone could make some really awesome synth music in the 486 clock window ...

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Fri Mar 05, 2010 8:36 pm

The main challenge here was to reach the full rate of the SPC-700's DAC, to see whether it could even be done. Lower rates allow plenty of time for indexed addressing on the SMP side, and thus buffering, as tepples mentioned. Buffering is more efficient on the S-CPU side too, and allows other tasks like graphics at the same time.

User avatar
Bregalad
Posts: 7781
Joined: Fri Nov 12, 2004 2:49 pm
Location: Chexbres, VD, Switzerland

Post by Bregalad » Sat Mar 06, 2010 1:46 pm

Well this is clever, as always, Blarg. Congratulations on finding this method. I always wondered if the echo buffer could be used for streaming, it seems it does ! Echo can definitely turn into a 9th channel for the SPC somehow.
Life is complex: it has both real and imaginary components.

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Sat Mar 06, 2010 3:08 pm

You can't do anything else, though, since it ties up the S-SMP. It's more of a curiosity, unless someone sticks an MPEG decoder in a cartridge and uses the SPC-700 as a roundabout DAC. Or maybe someone wants to stick a really high quality, short sample in his homebrew game and play it at the title screen. This just establishes that it can be done...

byuu
Posts: 1540
Joined: Mon Mar 27, 2006 5:23 pm
Contact:

Post by byuu » Sat Mar 06, 2010 9:53 pm

Using the audio mixing stereo pins on the cartridge connector would mix directly into the DAC, completely bypassing the need for S-CPU and S-SMP code. You could in fact end up using the S-SMP as a dedicated logic coprocessor.

But yes, showing that it's possible is truly awesome. A tech demo playing a song at 32KHz stereo with no ADPCM compression would be most cool. Just need a really great 48 second song. And then we can put to rest the arguments that the Genesis had a better sound chip once and for all :)

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Sun Mar 07, 2010 8:18 am

Haha, I like the idea of something to outdo the Genesis, as the latter only has something like an 8-bit DAC. Maybe someone can make a song that uses a few looped samples. It's definitely possible to layer a few. It needs to be something with some quiet passages, to show the dynamic range possible. It also must have some complex things that couldn't be synthesized with the DSP normally. Maybe something classical? Heh, it could start out with that FM synthesis I worked on, sounding like the Genesis, then meld into the sample, showing that it can do both.

I've got the perfect piece: Mozart's Eine kleine Nachtmusik.

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Wed Mar 10, 2010 8:51 am

Finally, I finished a one-minute near-CD-quality demo for the SNES (2.9 MB download): blargg_near_cd_quality.7z (mirror).

Thanks to byuu for some assistance with the ROM access and testing. We found that it only plays properly on bsnes (or a SNES of course); the current SNES9x plays it much lower quality than it should. Put on headphones if possible, otherwise you can't really tell that it's 16-bit stereo.

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

Post by tepples » Wed Mar 10, 2010 10:04 am

Dynamic range is possible even with the existing ADPCM because the "A" is for Adaptive. This means the bitstream encodes the volume somehow, either explicitly (number of shits in BRR on Super NES) or implicitly (10% decay per sample in IMA on Nintendo DS). I'm more interested in the real-time synthesis aspects.

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Wed Mar 10, 2010 11:38 am

Heh, that's exactly what I did here. I compressed the original 16-bit samples to 8-bit samples, with a shift count stored for each group of 15 samples. There's barely enough time to decompress it in real-time on the S-CPU. I only did this to allow double the time in a 4MB ROM. For real-time synthesis, it'd make more sense to do a buffered approach, which would be entirely different than this (and much lower than 128KB/sec transfer to the SPC-700). That would run at a lower rate, probably 16 kHz, 8-bit mono or something.

orwannon
Posts: 40
Joined: Fri Feb 20, 2009 10:07 am

Post by orwannon » Wed Mar 10, 2010 11:51 am

blargg wrote:Finally, I finished a one-minute near-CD-quality demo for the SNES (2.9 MB download): blargg_near_cd_quality.7z (mirror).
Just tried this on the PowerPak. WOW! :shock: Simply amazing. Great work, blargg. :D

byuu
Posts: 1540
Joined: Mon Mar 27, 2006 5:23 pm
Contact:

Post by byuu » Fri Mar 12, 2010 8:23 am

Would anyone mind testing this on the Windows Snes9X v1.52 release under Windows XP? It seems to crackle and pop for me, but not for the Windows port author.

If it works cleanly there, then there's no reason not to use Snes9X instead for the lower system requirements.

mic_
Posts: 917
Joined: Thu Oct 05, 2006 6:29 am

Post by mic_ » Fri Mar 12, 2010 9:03 am

Sounds very noisy for me with Snes9x 1.51 on XP.

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Fri Mar 12, 2010 11:10 am

I noticed that the DSP configuration I used wasn't as loud as it could be, and thus wasn't using the full DAC precision. Here's an updated one: blargg_near_cd_quality2.7z (mirror). I've also updated the link in the original message.

Before I had a single FIR constant of $3F (49%) and evol of $7F (99%). I had used the $3F because I was getting what sounded like overdriving when I was first developing the technique. I tried again yesterday and couldn't hear any of that, so I changed the FIR constant to $80 (-100%) and evol to $80 (-100%). Combined these give exactly 100%, i.e. no change in volume. There's no way to get +100%, so I used the two negatives to cancel each other out. This still technically only gives 15 bits per sample, due to the DSP always clearing the low bit during echo calculation, but there's no way around that.

Post Reply