manuel, can you describe that a bit more detailed. Did the change that broke Manuel's demo also make the sound less good in other cases? If so did you figure out what you did?
There have been loads and loads of sound changes since openMSX 0.6.2 got released. So I have no idea which one made the sound quality change so much. In 0.6.2 the sound is a bit harsh and has high volume peaks. In 0.6.3 it is softer and smoother, like the real MSX.
Here is the code. The sample data needs to be interleaved the following way:
32 bytes ch1
32 bytes ch2
32 bytes ch3
32 bytes ch4
32 bytes ch1
...
One thing I haven't tested yet is if writing only one byte in the period
register resets the phase. This code assumes that writing the high
byte resets the phase.
Its very easy to change to write the low byte and fairly easy to write
the whole period if needed. Tests on real SCC will tell...
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
fname "sccplay.rom"
;output "sccplay.rom"
org 4000h
dw 4241h,START,0,0,0,0,0,0
Bank1: equ 05000h
Bank2: equ 07000h
Bank3: equ 09000h
Bank4: equ 0B000h
;; 7648Hz in 60Hz mode
Period: equ 1873
;-------------------------------------
; Entry point
;-------------------------------------
START:
call powerup
xor a
call 005Fh
di
ld a,3Fh
LD (Bank3),A
call SccInit
call ReplayerInit
call InstallIntHanlder
ei
.halt: jp .halt
ret
InstallIntHanlder:
di
ld a,195
ld hl,HandleInt
ld ($FD9A),a
ld ($FD9B),hl
ret
;-------------------------------------
;-------------------------------------
HandleInt:
push af
push bc
push de
push hl
call ReplayerUpdate
pop hl
pop de
pop bc
pop af
ret
;-------------------------------------
; Initialize the scc
;-------------------------------------
SccInit:
ld a,00100000b ; Reset phase when freq is written
ld (98E0h),a
ld a,15
ld (988Ah),a ; volume ch1
ld (988Bh),a ; volume ch2
ld (988Ch),a ; volume ch3
ld (988Dh),a ; volume ch4
xor a
ld (988Eh),a ; volume ch5
ld a,00011111b ; all channels active
ld (988Fh),a
ld hl,Period
ld (9880h),hl
ld (9882h),hl
ld (9884h),hl
ld (9886h),hl
ret
;-------------------------------------
; Initialize replayer
;-------------------------------------
ReplayerInit:
ld hl,6000h
ld (SamplePos),hl
ld a,1
ld (SamplePage),a
ld (Bank2),a
ret
;-------------------------------------
; Moves sample pointer to next page
;-------------------------------------
ReplayerNextPage:
ld hl,6000h
push af
ld a,(SamplePage)
cp (SAMPLE_END - SAMPLE_START + 1FFFh)/2000h
jr nz,.norewind
xor a
.norewind:
inc a
ld (SamplePage),a
ld (Bank2),a
pop af
ret
;-------------------------------------
; Updates the SCC wave table.
;-------------------------------------
ReplayerUpdate:
ld hl,(SamplePos)
ld a,7
;468
ld de,9800h ; 11
ld bc,18 ; 11
ldir ; 23*18-5
ld bc,14 ; 11
add hl,bc ; 12
ld (9881h),a ; 14
ld de,9820h ; 11
ld bc,18 ; 11
ldir ; 23*18-5
ld bc,14 ; 11
add hl,bc ; 12
ld (9883h),a ; 14
ld de,9840h ; 11
ld bc,18 ; 11
ldir ; 23*18-5
ld bc,14 ; 11
add hl,bc ; 12
ld (9885h),a ; 14
ld de,9860h ; 11
ld bc,18 ; 11
ldir ; 23*18-5
ld bc,14 ; 11
add hl,bc ; 12
ld (9887h),a ; 14
; Start over with rest of samples
ld bc,10000h-128+16
add hl,bc
ld bc,16
ld de,9810h
ldir
ld bc,16
add hl,bc
ld de,9830h
ldir
ld bc,16
add hl,bc
ld de,9850h
ldir
ld bc,16
add hl,bc
ld de,9870h
ldir
bit 7,h
call nz,ReplayerNextPage
ld (SamplePos),hl
ret
;-------------------------------------
; Powerup routine for non-Z180 code.
; set pages and subslot
;-------------------------------------
powerup:
call 0x138
rrca
rrca
and 0x03
ld c,a
ld b,0
ld hl,0xfcc1
add hl,bc
or (hl)
ld b,a
inc hl
inc hl
inc hl
inc hl
ld a,(hl)
and 0x0c
or b
ld h,0x80
call 0x24
ret
;-------------------------------------
; To make rom guesseres happy
;-------------------------------------
dummy:
xor a
ld (Bank1),a
ld (Bank3),a
ld (Bank1),a
ld (Bank3),a
ret
;-------------------------------------
; Padding for rom player
;-------------------------------------
ds $6000 - $
;-------------------------------------
; Sample data
;-------------------------------------
SAMPLE_START:
incbin "fth01.bin",0,(56+64+128+256)*1024
SAMPLE_END:
;-------------------------------------
; Padding, align rom image to a power of two.
;-------------------------------------
SAMPLE_LENGTH: equ SAMPLE_END - SAMPLE_START
IF (SAMPLE_LENGTH <= 6000h)
DS (6000h - SAMPLE_LENGTH)
ELSE
IF (SAMPLE_LENGTH <= 10000h-2000h)
DS (E000h - SAMPLE_LENGTH)
ELSE
IF (SAMPLE_LENGTH <= 1E000h)
DS (1E000h - SAMPLE_LENGTH)
ELSE
IF (SAMPLE_LENGTH <= 3E000h)
DS (3E000h - SAMPLE_LENGTH)
ELSE
IF (SAMPLE_LENGTH <= 7E000h)
DS (7E000h - SAMPLE_LENGTH)
ELSE
DS (FE000h - SAMPLE_LENGTH)
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
FINISH:
;---------------------------------------------------------
; Variables
;---------------------------------------------------------
VarBase: equ $e000
SamplePos: equ VarBase + 0
SamplePage: equ VarBase + 2
I tested on a real MSX and the result is ok but not great. There are some clicks and its clearly some phase problems. Perhaps the two are related.
I'll see if setting the period differently may help.
What is actually better/different than that fifth.rom? That one sounded really good on a real MSX... (it sounded a bit distorted on openMSX, but we haven't found out why yet.)
fifth.rom uses one channel for playing samples and one rotated channel for synchronization. That one is also fully syncrhonized to avoid access to the SCC while its playing some samples that cause bus conflicts or something.
This new one has a very nice algorithm that requires updates only in VBLANK to play 7.6kHz samples. But its using all SCC channels with phase shift do make the sample frequency 4x the actual replayed frequency.
From a gaming perspective the last version is a lot better because its not using much cpu at all, less than 4% of the cpu (!)
fifth.rom replays 22kHz samples and uses around 25% cpu and it requires more complicated synchronization but with much better result.
it sounded a bit distorted on openMSX
In 0.6.2 it sounds very funny. Is it better in 0.6.3?
Questions:
why 7648Hz and not 4*60*32 ?
Could this be the problem ?
If the freqs in SCC and in the VDP does not match exactly you could miss some samples or hear jitter on samples at each VINT
do you mean with this that the VDP framerate is a bit lower than 60Hz ?
Why all the push/pop in HandleInt ?
At that point bios have already saved all the registers,
maybe it has already done something that jitter the timing...
One point
using lineint you can double or more the Fs....
The frequency isn't exactly 60Hz ( its 3579545/(262*228) ). But I thought that was the problem too so I did an adaptive method that calculates the correct period using rotation mode and the result is still the same. I also tried to make my own int handler and still not any better results.
The pops and pushes are because its saving all registers, its mainly if you have your own int handler but thats not an issue either.
I also thought about the line int. One line int would give quite good sample frequency and still using less then 8% of the CPU. Its too bad that that SCC isn't as good as it should be....
Daniel,
In your code you update 32+2 samples per channel per frame
I think I understand the reasons, and I think what you do is correct:
the problem is that the SCC goes on with the play while you're updating
tables, and when you end to the second pass of updates, SCC has moved
on of 2 samples in the new data
but in this way, in the end, you pass to the scc 34*4 samples per frame, so
the actual FS should be 34*4*59,9227434 = 8149,49Hz and not less!!
moreover, I see that you reset the SCC at the end of the first update loop (that lasts one sample)
ld de,9800h ; 11
ld bc,18 ; 11
ldir ; 23*18-5
ld bc,14 ; 11
add hl,bc ; 12
ld (9881h),a ; 14 <-----
why not updating one byte only, reset the scc, continue updating fot the last of one sample, and than move to the rest ?
ld de,9800h ; 11
ldi
ld (9881h),a ; 14 <-----
ld bc,new_count ; 11
ldir
this allows the SCC to start playing the new sample immediately and in theory to not play the old sample at position #0
moreover, just and idea, you can interleave the channel data in rom in order to avoid
all the adjustments :
ld bc,14 ; 11
add hl,bc ; 12
As you need to preprocess the input in any case, this whouldn't be a problem.
it sounded a bit distorted on openMSX
In 0.6.2 it sounds very funny. Is it better in 0.6.3?
It has the same distortions. It's just that the sound is less harsh in 0.6.3, similar to the other improvement I mentioned.
