Schrijver
| PCM player using SCC
|
dvik msx master Berichten: 1339 | Geplaatst: 24 Oktober 2007, 08:01   |
But maybe just adding one read in the beginning of the interrupt to see if we're in the area to avoid is ok. Depending on how the bug works, it could be that its only really an issue if the read is done more or less at the same point as the samples are beginning to play. If so, it may be possible to sometimes read in the problematic interval without having any sound defect.
The code will become a lot more complicated and also dependent on the sample frequency but we could do something like this:
a = read roation register
switch (a) {
case 0: update waves but not during sample 15 and 16
case 1: update waves but not during sample 15 and 16
...
}
The different values of a will require different implementations based on when the delay needs to be done. But I think this may be almost noise free since we're only doing one read access that may interfere with the buggy playback.
EDIT: Since this method will be very dependent on the sample frequency, what sample frequency should I pick if I'll go ahead and try this method?
|
|
ARTRAG msx master Berichten: 1737 | Geplaatst: 24 Oktober 2007, 08:22   |
Daniel,
I think that you can read ch4 when you like,
all you need to avoid is writing ch1 while sample 15 & 16 are played
Moreover I have a BIG suggestion to improve the speed of wave update in your code
you can skip this part in the inner loop
bit 7,h
call nz,ReplayerNextPage
all you need to do is to test if HL > endpage-32 OUTSIDE the loop and than
duplicate the last 32 byte at the end of page n-1 on the top of page n
In this way, if HL > endpage-32 you can set hl = hl - startpage and call nextpage
avoiding the two instructions above 32 times !
PROS
avoid 32 times testing h
CONS
add 32 duplicated extra bytes for each page after the first
add one single test after the loop
PROS wins!
|
|
dvik msx master Berichten: 1339 | Geplaatst: 24 Oktober 2007, 08:26   |
Yes thats a good idea. We can also duplicate the code so run the existing one if the condition is met and do the optimized one otherwise (it will only execute once not too often).
I think reading also affect the samples. At least I tried to move the synchronization point in the original version and always got a noisy result. I can redo the tests but I think all access to the SCC while playing these two samples cause noise.
In the version always updating 32 bytes, the test and conditional call can be moved outside the loop:
|
|
ARTRAG msx master Berichten: 1737 | Geplaatst: 24 Oktober 2007, 08:28   |
Any access? also reading bytes different than 15&16 ?

I'd not use two versions of the code as the upper part
of the player needs to know exactly how long the inner
loop takes to write 32 bytes.
Only knowing this in # of samples allows you to decide if
start the update or not.
You cannot start the update if you know that while updating
the SCC will play that samples
So you need to know how many samples the inner loop takes
and decide if you can or cannot start the update |
|
dvik msx master Berichten: 1339 | Geplaatst: 24 Oktober 2007, 08:29   |
Yes any access regardless of location it seems like and its when these two samples are played (I should double check this but I first assumed the same as you and either I made some errors while testing or it is this bad)
|
|
dvik msx master Berichten: 1339 | Geplaatst: 24 Oktober 2007, 08:38   |
One solution to the page problem is to use a temporary RAM buffer that covers the page break, e.g. 32 bytes of the end of one page and 32 bytes of the next. This buffer will need to be updated after each page but it can be done outside the timing sensitive part of the code. Then the timing sensitive code will use ROM in most cases and the RAM buffer if its close to the end of a page, both will use the same number of cpu cycles so no need for special timing handling.
|
|
ARTRAG msx master Berichten: 1737 | Geplaatst: 24 Oktober 2007, 08:48   |
Yes, in this case you call "nextpage" after the inner loop when HL > endpage
and you can modify that function in order to take care of the border
effects in ram (copy 32 bytes from the top of page n+2 in ram at the bottom of page n+1)
this solution, has an higher CPU load, but you do not need preprocessing the data
mine has no extra CPU but costs more room in rom and need preprocessing the data
both are valid
|
|
ARTRAG msx master Berichten: 1737 | Geplaatst: 24 Oktober 2007, 08:57   |
about the outer code
my idea is this:
say Tu the time needed by the inner loop to update the wavetable (T is fixed and known)
say Fs the sample frequency
say Nu=Tu/Fs the number of samples that the SCC passes while updating
the idea:
0) dummy actions (e.g. the main of a game)
1) read ch4
2) say P the current scc position (You cannot avoid reading while scc is at 15&16, but nothing is perfect)
3)
if (P+Nu) mod 31 < 15
call the update method
else
{
wait the time corresponding to [(P+Nu) mod 31 -16]/Fs
call the update method
}
4) goto 0
do you like in this way?
|
|
dvik msx master Berichten: 1339 | Geplaatst: 24 Oktober 2007, 09:10   |
Its too much waiting I think but it is an easy to implement solution. I think something that wastes less cpu is needed, thats why I think that case thing is better and have individual implementations depending on the current position (step 2 in your code). This may work:
L = position of last updated sample
P = current scc position
If P > x and P <= 16 // x depends on how long it takes to update 32 samples (to be safe)
N = safe number of samples to update based on value of P
if N > L
copy L samples
else
copy N samples
wait 2 samples
copy (P-L-N and 31) samples
something like this. this only adds at most a 2 sample wait and the math to get N and x shouldn't be too hard to do.
|
|
dvik msx master Berichten: 1339 | Geplaatst: 24 Oktober 2007, 09:27   |
Actually this is a much easier way (assuming that the the paging is already taken care of):
ReplayerUpdate2:
ld de,(WavePos)
ld hl,(SamplePos)
ld a,(9800h+32*3+1)
cp e
jp z,.end
.loop:
sub 14 ; Start of problematic region
cp 3 ; Size of problematic region
call c,Delay ; Delay if in the region
ldi
res 5,e
ld a,(9800h+32*3+1)
cp e
jp nz,.loop
.end:
ld (SamplePos),hl
ld (WavePos),de
ret
The Delay should delay for three or so samples to be on the safe side. I know this adds some cpu cycles to the inner loop but I think its worth it. doing it outside the loop may in fact be worse because it would require more branches and special handling. |
|
ARTRAG msx master Berichten: 1737 | Geplaatst: 24 Oktober 2007, 09:52   |
Sorry I spotted a bug in my idea, here the correction :
say Tu the time needed by the inner loop to update the wavetable (T is fixed and known)
say Fs the sample frequency
say Nu=Tu/Fs the number of samples that the SCC passes while updating
the loop:
0) dummy actions (e.g. the main of a game)
1) read ch4
2) say P the current scc position (You cannot avoid reading while scc is at 15&16, but nothing is perfect)
3)
if (P+Nu) mod 31 < 15
call the update method
else
{
wait the time corresponding to [(P-16) mod 31]/Fs
call the update method
}
4) goto 0
in this way the wait time is just as much as needed to avoid writing while scc is playing sample 15&16
I'll look at your new posts asap |
|
ARTRAG msx master Berichten: 1737 | Geplaatst: 24 Oktober 2007, 10:17   |
Quote:
| Actually this is a much easier way (assuming that the the paging is already taken care of):
ReplayerUpdate2:
ld de,(WavePos)
ld hl,(SamplePos)
ld a,(9800h+32*3+1)
cp e
jp z,.end
.loop:
sub 14 ; Start of problematic region
cp 3 ; Size of problematic region
call c,Delay ; Delay if in the region
ldi
res 5,e
ld a,(9800h+32*3+1)
cp e
jp nz,.loop
.end:
ld (SamplePos),hl
ld (WavePos),de
ret
The Delay should delay for three or so samples to be on the safe side. I know this adds some cpu cycles to the inner loop but I think its worth it. doing it outside the loop may in fact be worse because it would require more branches and special handling.
|
!! You master!!
apart from changing the
jp z,.end
in
ret z
I think that your idea is FAR better than mine!
as you actually wait only 2 samples!
it can work and you do not need any other sync in the outer loop!!!
No Case, no IF, nothing else !!!
Well done!!
Delay routine should wait 2/Fs and is the sole part
that depends on Fs, so it can be parametric !!!GOOD!!
|
|
dvik msx master Berichten: 1339 | Geplaatst: 24 Oktober 2007, 10:32   |
Unfortunately I think that the reads that the occasionally reads while playing sample 15 and 16 will add too much noise. I did some simulation with bluemsx and it doesn't sound very good. Same happens on a real SCC. I'm not sure the quality is not good enough to motivate moving forward with this idea and I don't think there is any good workaround either.
Problem is that at least one read is needed to determine where the current wave ptr is. The best solution is to do a more synchronized version like the last one posted.
Quote:
|
apart from changing the
jp z,.end
in
ret z
|
I actually did this to get the same timing from either reading the roatation register to when the desicion is made. Not that I think it matters much though. |
|
ARTRAG msx master Berichten: 1737 | Geplaatst: 24 Oktober 2007, 10:46   |
I do not know, do you think my latest proposal reads less often the ch4 then yours?
|
|
dvik msx master Berichten: 1339 | Geplaatst: 24 Oktober 2007, 10:54   |
probably not. If you place the code in an int handler you'll get some drift between the line interrupts and the scc sample played. So if you place a line int every 28 or so samples, you'll get reads in the wrong place no matter what. I don't have any really good ideas on how to avoid it.
Best is perhaps to use the fixed 32 sample version after all and then use well sized program blocks between each scc update (that are big enough to avoid the problematic region)
|
|
|
|
|