Jump to content

Photo

Using Pulse Density Modulation for 8-bit PCM


202 replies to this topic

#1 kool kitty89 OFFLINE  

kool kitty89

    River Patroller

  • 2,449 posts
  • Location:San Jose, CA

Posted Sun Oct 25, 2015 6:57 PM

I'm not sure if this technique is ever actually used in existing games or demos, but using 2 POKEY channels and two timers, it should be possible to get 8-bit linear PCM output through POKEY. Set one timer to the desired sample rate and set one 4-bit volume level for the upper 4 bits of a sample while the lower 4 bits go to the other channel. The second channel gets its volume then cut again to 1/16 by using the second timer to set the duration of the pulse to 1/16 of a sample period. Yes you'd get noise at low sample rates (more and more audible below 11 kHz) but that's true to some extent for harmonics of low sample rate PCM in general. (and not the same sort of squeal and noise as 1-bit PDM/PWM on a PC speaker or beeper or whatnot)

 

Using cycle timed loops instead of interrupts (or polling) for the timing would also be possible.

 

I also know using 4-bit samples in general is often more practical on the A8 due to memory constraints (and SNR often favoring 4 bit over 8 bit at low bitrates) and that PCM playback is also CPU intensive (from previous talks with malducci, I believe 50 cycles per interrupt for decently tight code) and this method would be more CPU intensive than straight 4-bit playback, but it still seems interesting at least. (8-bit output is better for software mixing too, but that's only good if you have free cycles to dedicate to mixing, and I know multiplexing 4-bit samples on a single channel is also an option there)

 

 

I may also be misusing the term PDM, pulse-width modulation might be more correct here, but I think PDM applies. (there isn't really modulation involved either, just fixed pulse width of the lower channel ... you COULD modulate a single channel to get those added 16 volume levels, but that still takes 2 timers -or careful coding- and I don't think would sound as good)



#2 tebe OFFLINE  

tebe

    Dragonstomper

  • 742 posts
  • Location:Poland

Posted Sat May 5, 2018 6:23 AM



#3 Xuel ONLINE  

Xuel

    Dragonstomper

  • 722 posts
  • Location:US

Posted Sun May 6, 2018 10:49 PM

Amazing idea!

I implemented my take on your idea here:

github.com/lybrown/hifi

Example:

Attached File  aha.car.zip   1.38MB   143 downloads

Longer example:

thecart.car.zip (21MB, 10 minutes)

I implemented 44270Hz but there's headroom to go up to 65520Hz if I take out all of the NOPs.

In Altirra you can press Ctrl+Alt+1 to toggle POKEY channel 1 where the lower 4-bits are being played, so you can hear what it sounds like with just the upper 4-bits being played as normal PCM on channel 2. You can also press Ctrl+Alt+2 to toggle channel 2 so you can hear what the lower 4 bits sound like. (Hint: it basically sounds like noise. :))

I ran the 1/16 signal as fast as it can go so it's running at 111Khz (1.77Mhz / 16). At that frequency you don't really have to synchronize the signal with the PCM signal. You can just apply the 4 lower bits to the 111Khz signal and roughly three pulses will be played at that volume.

Curious if anyone with a The!Cart can try this on real hardware.



#4 Xuel ONLINE  

Xuel

    Dragonstomper

  • 722 posts
  • Location:US

Posted Sun May 6, 2018 11:41 PM

Correction: The 1/16 signal is running at 55.4KHz. I forgot that you have to divide by 2!   :dunce:  All the more amazing to me that this works as well as it does then, since you're only getting about 1.25 pulses per sample. I guess that's enough.Maybe I'll try pegging to as close to 1 pulse per sample as possible and see how that sounds...

 

Note: For best quality in Altirra, I've found it's best to disable non-linear mixing. This could bode poorly for the quality on real hardware. Maybe if we massage the waveform to fit the 256 voltage-levels observed on real hardware we can improve things.



#5 NRV OFFLINE  

NRV

    Moonsweeper

  • 386 posts

Posted Mon May 7, 2018 12:44 AM

Nice!

 

Two questions.. would doing:

 

lda hi,x

ldy lo,x

sta audc2

sty audc1

 

instead of:

 

lda hi,x

sta audc2

lda lo,x

sta audc1

 

..improve anything?

 

And do you think it still would sound good at 15.7 KHz?

 



#6 Rybags OFFLINE  

Rybags

    Quadrunner

  • 15,759 posts
  • Location:Australia

Posted Mon May 7, 2018 1:02 AM

That sounds pretty good, the notice definitely noticable.

 

Strange request though... even without the low bits it sounds way better than most A8 sampling due to the high rate.

How about a test at lower bitrates like 8, 16 KHz?



#7 Sheddy OFFLINE  

Sheddy

    Dragonstomper

  • 735 posts
  • Location:UK

Posted Mon May 7, 2018 1:40 AM

Note: For best quality in Altirra, I've found it's best to disable non-linear mixing. This could bode poorly for the quality on real hardware. Maybe if we massage the waveform to fit the 256 voltage-levels observed on real hardware we can improve things.


Presumably the real voltage levels are very close together at higher volumes, losing effective bits in the same way as PCM(?)

BTW this is all very awesome!

#8 R0ger OFFLINE  

R0ger

    Chopper Commander

  • 233 posts
  • Location:Olomouc, Czech Republic

Posted Mon May 7, 2018 5:55 AM

Damn that's smooth. And such a simple idea ! Great job !



#9 R0ger OFFLINE  

R0ger

    Chopper Commander

  • 233 posts
  • Location:Olomouc, Czech Republic

Posted Mon May 7, 2018 6:23 AM

Correction: The 1/16 signal is running at 55.4KHz. I forgot that you have to divide by 2!   :dunce:  All the more amazing to me that this works as well as it does then, since you're only getting about 1.25 pulses per sample. I guess that's enough.Maybe I'll try pegging to as close to 1 pulse per sample as possible and see how that sounds...

 

Note: For best quality in Altirra, I've found it's best to disable non-linear mixing. This could bode poorly for the quality on real hardware. Maybe if we massage the waveform to fit the 256 voltage-levels observed on real hardware we can improve things.

 

That's actually great. I mean the fact the 1/16 signal doesn't have to run at sample frequency at all. You can the 8kHz sample rate without the 1/16 signal being audible. Simply run the 1/16 signal as high as real hardware can reliably mix it.



#10 xxl OFFLINE  

xxl

    Stargunner

  • 1,103 posts
  • Location:Rabka-Zdrój /Poland

Posted Mon May 7, 2018 6:38 AM

another barrier was broken.



#11 gorgh OFFLINE  

gorgh

    Chopper Commander

  • 146 posts
  • Location:warsaw,poland

Posted Mon May 7, 2018 7:52 AM

amazing quality!



#12 mono OFFLINE  

mono

    Star Raider

  • 89 posts

Posted Mon May 7, 2018 8:31 AM

You are the star! 4.4 samples :)



#13 antrykot OFFLINE  

antrykot

    Space Invader

  • 28 posts

Posted Mon May 7, 2018 8:35 AM

Correction: The 1/16 signal is running at 55.4KHz. I forgot that you have to divide by 2!

Its frequency really is 110840 Hz (PAL), there is no divide by two when hi-pass filter is enabled.


Edited by antrykot, Mon May 7, 2018 8:35 AM.


#14 Stephen OFFLINE  

Stephen

    Quadrunner

  • 7,136 posts
  • A8 Gear Head
  • Location:No longer in Crakron, Ohio

Posted Mon May 7, 2018 8:36 AM

Wow - sound is just incredible!



#15 flashjazzcat OFFLINE  

flashjazzcat

    Quadrunner

  • 13,767 posts
  • Location:United Kingdom

Posted Mon May 7, 2018 8:40 AM

This is bloody remarkable. Pity there isn't time to issue IDE sector read requests, otherwise we could play huge files direct from FAT partitions on one of the currently available hard disk solutions. Really astounding quality, though.

#16 Xuel ONLINE  

Xuel

    Dragonstomper

  • 722 posts
  • Location:US

Posted Mon May 7, 2018 9:06 AM

Its frequency really is 110840 Hz (PAL), there is no divide by two when hi-pass filter is enabled.

 

Oh yeah, because the waveforms are XOR'ed so you get two pulses per period. Awesome!

 

BTW, I tried other periods like 1/13, 1/14, 1/15, 1/17 and some of them sound pretty good. Maybe something to tune for real hardware?

 

Nice!

 

Two questions.. would doing:

 

lda hi,x

ldy lo,x

sta audc2

sty audc1

 

instead of:

 

lda hi,x

sta audc2

lda lo,x

sta audc1

 

..improve anything?

 

And do you think it still would sound good at 15.7 KHz?

 

 

I actually had it that way at first (first version), but I was trying to make the cart code simpler so I tried it the other way and it sounded the same to me. Need to break out the test and measurement equipment for a quantitative answer. :)

 

That sounds pretty good, the notice definitely noticable.

 

Strange request though... even without the low bits it sounds way better than most A8 sampling due to the high rate.

How about a test at lower bitrates like 8, 16 KHz?

 

On my todo list. :)

 

Presumably the real voltage levels are very close together at higher volumes, losing effective bits in the same way as PCM(?)

BTW this is all very awesome!

 

That's what I'm guessing as well. I wonder if there are also non-monotonic transitions in POKEY's DACs like there are in GTIA for example between luminances 7 and 8 in 16-luma mode. Maybe if we had an accurate model of POKEY's DACs we could massage the waveform data to compensate.

 

This is bloody remarkable. Pity there isn't time to issue IDE sector read requests, otherwise we could play huge files direct from FAT partitions on one of the currently available hard disk solutions. Really astounding quality, though.

 

I wouldn't say IDE is out of the question. There's quite a bit of CPU left over. :)



#17 flashjazzcat OFFLINE  

flashjazzcat

    Quadrunner

  • 13,767 posts
  • Location:United Kingdom

Posted Mon May 7, 2018 9:16 AM

I wouldn't say IDE is out of the question. There's quite a bit of CPU left over. :)


I ran the AHA tune in Altirra and it looked like the CPU was flat-out at first, but I can see now the NOP and there's also time to update HPOSP0 every few cycles, so yeah. :) The trick will be waiting for the IDE sector buffer to become ready after a read request. Ideally you'd continue playing during that phase, but since we'd be feeding directly off the IDE data register, we'd be stuck for a few milliseconds waiting for the data to become ready. Such a player would be pretty convenient, though.

#18 ivop OFFLINE  

ivop

    Dragonstomper

  • 725 posts
  • Location:The Netherlands

Posted Mon May 7, 2018 9:16 AM

Very nice results!

 

Could you manage to put the main play loop in page zero? If so, you can save two cycles (inc play+1 and inc play+2). They might come in handy :)



#19 flashjazzcat OFFLINE  

flashjazzcat

    Quadrunner

  • 13,767 posts
  • Location:United Kingdom

Posted Mon May 7, 2018 9:22 AM

Just thinking: we don't need a pointer at all if reading from the IDE data register: it's just repeated loads from the same location, plus the overhead of reading the next cluster (assuming we issue multi-sector reads to grab whole clusters at a time).

#20 twh/f2 OFFLINE  

twh/f2

    Dragonstomper

  • 644 posts
  • Location:Fractalus

Posted Mon May 7, 2018 9:48 AM

Holly shit. Really PCM 8bit @ 44khz ?! This sounds amazing. What a great idea. 

 

So can this idea be applied only with Pokey or is that idea convertible to e.g. C64 SID? (I'm asking that I can provocate my C64-fanboy neighbor *gg)



#21 twh/f2 OFFLINE  

twh/f2

    Dragonstomper

  • 644 posts
  • Location:Fractalus

Posted Mon May 7, 2018 9:55 AM

 

 

I wouldn't say IDE is out of the question. There's quite a bit of CPU left over. icon_smile.gif

 

At at the end you guys discover that there is still CPU left for some smart MP3-decoding *ggg

But honestly, how much of an impact to quality would it be with IDE data feed?



#22 Xuel ONLINE  

Xuel

    Dragonstomper

  • 722 posts
  • Location:US

Posted Mon May 7, 2018 10:01 AM

Holly shit. Really PCM 8bit @ 44khz ?! This sounds amazing. What a great idea. 

 

So can this idea be applied only with Pokey or is that idea convertible to e.g. C64 SID? (I'm asking that I can provocate my C64-fanboy neighbor *gg)

 

See here. :)



#23 twh/f2 OFFLINE  

twh/f2

    Dragonstomper

  • 644 posts
  • Location:Fractalus

Posted Mon May 7, 2018 10:05 AM

dammit :)



#24 flashjazzcat OFFLINE  

flashjazzcat

    Quadrunner

  • 13,767 posts
  • Location:United Kingdom

Posted Mon May 7, 2018 10:23 AM

But honestly, how much of an impact to quality would it be with IDE data feed?


Avery's video player issues multi-sector reads (17 sectors) and expects the data to be ready 4ms after issuing the command, and this equates to 47 scan lines (during which he plays buffered audio). Here, we'd be issuing 8 sector or 16 sector reads (for 4K or 8K clusters), with the sector numbers derived from a pre-computed list built when traversing the FAT chain when the file was first opened. So it seems to me some clever method of keeping the player running during that 4ms wait period is required (it may be less than 4ms, and we can get out early by polling the DRQ bit). Perhaps some clever interleave would allow preparation of a cache large enough to keep things moving during down-time.

This is all assuming the current data rate is somehow preserved, ideally resulting in no quality reduction. :)

Edited by flashjazzcat, Mon May 7, 2018 10:23 AM.


#25 emkay OFFLINE  

emkay

    Quadrunner

  • 9,455 posts
  • What's up?
  • Location:Holy Grail ;)

Posted Mon May 7, 2018 11:35 AM

dammit :)


Not sure how they managed it on the C64, but it sounds like pressed through a compander.




0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users