I'd like to verify the details of the experimental PCM support
Quietust added to his NSF player and used by the experimental rips of
Battletoads and
Battletoads & Double Dragon. What follows is my best understanding of the changes and what seems to work in my NSF player.
- The init routine can now run indefinitely (never return)
- The play routine is now invoked at the playback rate, regardless of whether init routine has returned yet.
- The NSF player must save the CPU registers (PC, A, X, Y, P) before invoking the play routine, and restore them after it returns.
- The first call to the play routine should be delayed by four frames, to allow time for a normal init routine to return.
Did I leave anything out? Are there any problems with this scheme?
blargg wrote:
What follows is my best understanding of the changes and what seems to work in my NSF player.
- The init routine can now run indefinitely (never return)
Quote:
- The play routine is now invoked at the playback rate, regardless of whether init routine has returned yet.
- The NSF player must save the CPU registers (PC, A, X, Y, P) before invoking the play routine, and restore them after it returns.
Which would correspond to a 'pha : txa : pha : tya : pha : jsr play_address : pla : tay : pla : tax : pla : rti' in the hypothetical hardware NSF player's NMI routine, right?
Quote:
- The first call to the play routine should be delayed by four frames, to allow time for a normal init routine to return.
And to allow the DAC enough time to reach a stable level so that init codes that write to $4011 don't produce a click.
Actually, my player only preserves the registers it modifies on its way to the "JSR play_address" - the PHA/TXA/PHA/TYA/PHA must be done within the NSF itself, ideally as a stub around the play routine -
Code:
stub_play:
PHA
TXA
PHA
TYA
PHA
JSR real_play_address
PLA
TAY
PLA
TAX
PLA
RTS
...
NSF_PLAY:
JSR nsf_play_address
RTI
In my NSF_PLAY routine, I had to preserve X (and, therefore, A) because I'm using IRQs rather than NMIs, and because my IRQs can have multiple meanings (init, play, or stop) which I must read from a register (as well as to acknowledge the IRQ). In addition, I had to execute a CLI before jumping off to the INIT routine (so the PLAY IRQ could make it through).
The reason I chose 4 frames for the init code is simply arbitrary - I wanted to make sure I gave it plenty of time to initialize, and I decided that 1 frame might not have been sufficient. In most cases, 1 frame would probably be plenty.
Should an expansion byte be used to denote that the play routine should be called even if the init routine doesn't return, ensuring that no current rips are broken, or should an extra memory-mapped register be added to the spec(allowing for mixed behavior in different songs)?
Quietust wrote:
Actually, my player only preserves the registers it modifies on its way to the "JSR play_address" - the PHA/TXA/PHA/TYA/PHA must be done within the NSF itself, ideally as a stub around the play routine -
One of the Battletoads rips didn't work when I didn't save the registers around the play routine invocation. It'd be good to make clear whether an NSF player must save registers around it. The lazy part of me doesn't want to (even though it's just a couple of lines of code), but the consistent part wants to, since the play routine should be thought of something that is called from the game's IRQ handler, which would usually save and restore registers itself.
So let's decide one way or the other, otherwise we'll have compatibility problems. Forcing the NSF player to save/restore registers around the play call makes it simplest for NSF authors, which outnumber NSF player authors.
Quote:
Should an expansion byte be used to denote that the play routine should be called even if the init routine doesn't return, ensuring that no current rips are broken
I or Quietust could scan Kevin Horton's archive with an emulator that notes any NSF init routines which take longer than four frames to return. It's unlikely any take this long, so these extensions should be backwards-compatible.
Quietust wrote:
I wanted to make sure I gave it plenty of time to initialize, and I decided that 1 frame might not have been sufficient. In most cases, 1 frame would probably be plenty.
When I first wrote my NSF player a while back, I found a few NSF init routines that took a bit longer than a frame to return.
Will you also scan the large number of NSFs from commercial games that are not in Kevin Horton's archive, and also the hundreds of homebrewn NSFs, many of which are scattered across Japanese message boards?
Calling the play routine before the init routine has returned will also cause sound distortion in experimental-ish NSFs that rely on tight CPU timing(like those raw PCM playback demos), although they could be written to use a play routine that never returns instead...
So do we try to find something backwards-compatible or add a second NSF playback environment that is enabled by a new flag? What advantages does backwards-compatibility have over adding a new flag, that outweigh the potential problems due to unexpected behavior of NSF files that work with the current spec? Admittedly, the current spec doesn't really say what happens if the init routine never returns, or the play routine takes more than one frame to return, so anything which does this is already outside well-defined behavior. From the NSF spec:
Initalizing a tune: [...] Once the song # and optional PAL/NTSC standard are loaded, simply call the INIT address. Once init is done, it should perform an RTS.
Playing a tune: Once the tune has been initalized, it can now be played. To do this, simply call the play address several times a second.
I suppose this does allow for an init routine that never returns, in which case play is never called. Following the spec carefully, this PCM hack could work by instead rewriting the Battletoads NSFs so that the play routine never return (which isn't covered in the NSF spec). In this case the first call of the play routine would run the PCM code, and subsequent calls would clock the music and return normally. This method would likely not break any current NSF files since it's unlikely that their play routine takes longer than a frame before returning (or if it does, it's already using this idea).
The PCM playback definitely shouldn't be in the play routine.
I advocate a third approach:
Instead of using a watchdog timer that gets triggered if the init routine takes more than four frames, let the init routine signal to the player that it's never going to return by writing to a special memory address (i.e. 3FF5 or something like that). No existing NSF writes to that memory address, so for them, the player will behave as usual, that is, not calling play until init returns.
NSFs that never return from init because of PCM playback will write to that memory address as soon as they're ready to have "play" called.
I like this approach because it seems that this is already what some players are already doing; for example, the 3FF5 address is used by FCEUltra's NSF bios for the very purpose of signaling that Play should be called (my Battletoads & Double Dragon PCM still doesn't work in FCE Ultra because it doesn't properly return from the Play routine).
Another way would be to add a fourth code pointer to the header: in addition to load, init and play, there would be an address for an "idle" routine where the PCM playback code sits.
This method has the advantage that older players, who do not recognize the "idle" field, will still be able to play those files since the init routine still returns, minus the PCM drums since the idle routine is never called. Newer players would check this field and if it is != 0, call the routine after init, never expecting it to return.
Quote:
let the init routine signal to the player that it's never going to return by writing to a special memory address (i.e. 3FF5 or something like that).
How about writing $80 to $2000? :)
Quote:
[...] in addition to load, init and play, there would be an address for an "idle" routine where the PCM playback code sits. This method has the advantage that older players, who do not recognize the "idle" field, will still be able to play those files [...]
I like this approach a lot, especially the way the
Battletoads soundtracks would revert to how the PCM-less rips have always sounded. Too bad that would halve the number of unused bytes in the NSF header. This approach has the advantage of being workable on NES hardware (well, as workable as current NSFs are).
Let's finish this up, shall we. In order to better allow for raw PCM playback while retaining compatibility with existing players, the NSF specification shall be updated as follows:
Header bytes 0x7C-0x7D: (lo/hi) address of an "idle" routine. If zero, player behaves as usual, waiting for init/play to return before calling play again. If nonzero, player calls it after "init" has finished and doesn't expect it to return, calling "play" at the normal interval and properly returning to "idle" afterwards.
Yea or Nay?
Does the player save and restore A, X, Y, and P around the call to play from the idle routine? I think it should. Either way, it sounds good to me. I'd like to get the response of a few other people whom this affects (Quietust, Disch, perhaps the author of the original NSF spec).
I see the response is overwhelming...
The real question has been answered: people apparently don't care much about PCM support in NSF music. If Quietust implements this proposed idle routine (with automatic register saving when the play routine interrupts), I'll add it to my NSF library, which means it'll go into a
bunch of players on the next release.
It'll take me a little while to implement it, since I'll have to make some significant changes to both my NSF player BIOS (code changes, as well as adding a pair of registers to read the 'idle' address from) and to my emulator (update the emu to load said 'idle' address and update the mapper interface to store it).
Before I do so, however, I'd like to have the NSF spec officially updated.
Fine by me. It'll take a while before my implementation gets out anyway. I'll try writing something up tomorrow if I have time.
As long as it works alright on a real system, it should be fine. Sounds ok to me so far, except there's no way to escape from the idle routine. I almost would've recommended a CPU-cycle based interrupt to call it, but I suppose that's getting kinda far from how the actual games work even if it is escapeable. No big deal, the system can just get reset.
What would it do on tracks that don't use the idle routine (like Skate or Die 2 except the title), just return from idle maybe?
Memblers wrote:
What would it do on tracks that don't use the idle routine (like Skate or Die 2 except the title)?
Good point - it would probably be best to just have it loop forever.
I'd just like to say that I do care, and I'm glad people are finally finding ways of implementing what I wanted in the NSF spec from the very beginning. It just seemed that the people involved got the issue sorted out by themselves.
But like Memblers, I would like an interrupt-based method as well... the lack of an interrupt doesn't seem to hurt the audio quality in Battletoads nor Skate or Die 2, but one only uses drums and the other has a 4-bit (=heavily quantized) sample, so none are a good test for how noticable glitches you get from interrupting the sample playback with the sound code. Then again, I haven't done any actual tests myself.
I think the best way to provide an interrupt-based method like this would simply be to use the playback rate in the NSF header. The ripper could then set it to say 22kHz, play a sample on each to the playback routine call and at the same time increment a counter and invoke it's music engine routine if 1/50 (or 1/60) seconds have passed since the last call to it. An actual NSF playback cart would then need to include a pretty sophisticated hardware timer, but maybe that's not a big issue since you'd have to add extra circuitry for the 4kB bankswitcing in any case.
Though an obvious problem with this approach is that it would still be theoretically possible to make programs that can't be converted to NSF format, by invoking the sample playback with an IRQ and the playback with the NMI. If the IRQ sample playback routine really uses up all cycles to the limit, it might not be possible to add the extra code that unifies the 1/50 and the xx kHz routines.
In Bananmos' scheme, the play routine is re-entrant. If that's the case, then it also supports idle-style $4011 samples; the NSF play routine would route the first call to its private idle routine that never returns, and the rest would clock the player normally. This is a nice scheme in that it's just a clarification of the current format without addition of anything.
The whole point is to allow for raw PCM playback without having to go to great lengths to adapt the existing game code for NSF playback (like it had to be done with the Skate or Die 2 NSF); the idle routine concept serves that purpose nicely.
A 22 kHz interrupt would require a lot of effort to adapt existing game code, since no real NES hardware uses that, and you gain absolutely nothing by that.
I also don't see why there's no way to escape from the idle routine. The idle routine is not continously called, but just ONCE after "init", and if the particular song uses PCM, it never returns, if it doesn't use PCM, it just returns via RTS and is never called again for the remaining duration of the song.
If you think that this violates the meaning of the word "idle", well, I'm not married to that word, just to the concept.
It's been a long time since my last NSF emu experience... but, what do you mean by "idle"? An infinity loop, like JMP $pc_addr (same of waiting for an NMI)?
Three routines in one of proposed NSF additions:
Init - sets up for playback, returns once done. Until it returns no other routines are invoked.
Idle - called just after init returns. Can run endlessly and never return, or return normally.
Play - called periodically once init returns. Can interrupt idle routine.
The other proposal from Bananamos doesn't require the addition of a new routine:
Init - same as above
Play - called periodically once init returns. Called even if previous call to play hasn't returned yet. Could play high-speed samples by setting playback rate in header to an extremely high value (as compared to normal).
This allows the play routine to keep a global flag "first call made" and invoke its own version of the idle routine, without the player having to know about it:
Code:
play:
lda first_call
beq idle_loop
... normal play code
idle_loop:
lda #1
sta first_call ; cause normal play code to execute on future calls
loop:
... play pcm when play routine sets flag
jmp loop ; keep looping here and let play routine invocations interrupt
I like this proposal better because it doesn't add anything new. It is trivial to code the above first call/later call branch, and gives all the functionality of the proposed idle routine.
I like your final(?) decision on this matter, since IMO we shouldn't put any unnecessary restrictions on what kind of music code can be converted into NSF format.
Speaking of which, do you know a way to fit your neat "sawtooth emulation on DPCM" music code in the NSF specification? If not, shouldn't we also try to resolve this issue as well?
Quote:
I like your final(?)
Haha. What can I say, yours was mostly superior, so my opinion changed. :)
Quote:
since IMO we shouldn't put any unnecessary restrictions on what kind of music code can be converted into NSF format.
Well if you really want no restrictions then make a NES ROM for use in an emulator. I'm with NewRisingSun on the basic purpose of NSF: capture music from games with minimal changes to the driver code. I want nothing to do with people who make NSF music with every custom chip enabled simultaneously or use things like MIDINES with a custom processor running 10x the speed of the NES, a parasite using the NES only for its sound chip. If you want sampled instruments, the NES is not the thing for it. Sorry, rant over.
Quote:
Speaking of which, do you know a way to fit your neat "sawtooth emulation on DPCM" music code in the NSF specification?
According to Disch, an NSF player should support IRQ, which means it should work, but he also says that several NSF files enable IRQ but leave trash in the IRQ vector. I'm still undecided as to whether I think my DMC-based saw wave would really be practical in a game for background music, and thus legitimate for use in an NSF. If you're only using it in an NSF, you might as well just write code that spends all the CPU time outputting complex waveforms via $4011.
It would be nice for the IRQ to work in an NSF. The resolution would be to fix current NSF rips which carelessly enable IRQ.
I'm with you as far as the expansion chip junkies on 2a03.org are concerned. Anything you don't plan on building in hardware is utterly pointless to use NES emulation for. And nothing is more appaling than the "use every known Famicom expansion chip simultaneously"-competitions.
As for the rest of your rant, I can only agree to disagree. Even is a certain sound-generating method is unlikely to be discovered/invented, it's not wise to put restrictions that disallow this.
Keep in mind that the reason you had to put this effort of making raw PCM work in the first place is that Kevin Horton had the exact same attitude about the NSF format when he created it - that since raw PCM was so uncommon, the samples could be converted to DPCM for the few games that used it, in order to keep the format as simple as possible. And thus, the Skate or Die 2 title song could not be NSF:ed, until we started discussing a (controversial) solution that fits the format.
It's fortunate that it could be solved, but it had been way better if the specifications had taken raw PCM in mind from the very beginning, so we wouldn't have rips and players that conflict with this. I'd like to see some detailed NSF guidelines for rippers and player authors, and I sure hope suggestions that open up for possibilities won't be answered with a "no games ever did that" remark.
In order to understand the true meaning, it is useful to explore the extremes:
Bananmos wrote:
I'm with you as far as the expansion chip junkies on 2a03.org are concerned. Anything you don't plan on building in hardware is utterly pointless to use NES emulation for.
Now watch someone answer your criticism by wiring up a cart with a UART on $5000-$5007 and a line-in jack. This could be connected to the 60-pin sound input or (in the case of 72-pin systems) to an ADC on the board that outputs DMC. And here's how the 2A03 junkies might use it: Clock the UART at 31.25 kHz and plug in a high-end MIDI synthesizer, and you have a mapper built in very expensive hardware. Clock the UART slower and plug in an old CD player's wired remote control, and you have another mapper built in hardware with music capability comparable to the PC Engine (TurboGrafx) CD system. Both techniques
have been used in actual consumer video games, though not actual NES games.
Heh tepples, here's a much simpler way to have the NES control a keyboard: drop it on the keys. OK OK serious. At some point one has departed from the "NES experience" when adding hardware to essentially replace its core functions. I'd say what makes the NES the NES is not so much its CPU, but the PPU and APU, particularly their limitations and quirks. Add replacement graphics hardware or sound chips and you've replaced its essence. But not to get side-tracked on my opinions...
Quote:
'd like to see some detailed NSF guidelines for rippers and player authors, and I sure hope suggestions that open up for possibilities won't be answered with a "no games ever did that" remark.
That is merely a general direction I have in mind for NSF, rather than a hard technical rule. I don't mean to be dismissive about technical proposals, just of working on extensive support in a direction that has little to do with the NES experience. Your idea is simply taking the playback tempo to the extreme and clarifies that the play routine is re-entrant, which is about as minimal an update as possible. If an NSF player works when you set the playback tempo to 1.32 million beats per minute, great, you can do 22 kHz samples. I almost want to code this up and see how it works.
I really don't know why I'm being criticized for wanting to use extra sound hardware, since I never ever mentioned anything in that direction. However, contrary to what you're saying, I DO feel that sampled instruments fit the NES experience. Truthfully, Skate or Die 2 never was part of my NES experience back in the days. But nowadays it is, since it's a really cool example of playing a kick-ass sampled instrument (the 4-bit quantization even fits the electric guitar sound nicely) on the NES sound hardware, and my NES experience is not static but ever evolving. ;)
When the NSF format was being developed, I argued that the ability to play sampled instruments thru raw PCM should be included, but it was not considered to fit the NES experience. I was asked to show a NES program that did something like this, and I knew none. I only knew it was possible to do. Later I discovered Skate Or Die 2, but then it was already too late. And even then, I wouldn't have liked a solution which only mimicked the code in the original game, since a less quantized sample could sound distorted when not played thru an interrupt. It's funny how I never considered the idea of changing the playback rate and having a re-entrant routine. I just kind of viewed the playback rate as fixed.
Going further on this matter, the NSF format is indeed a very good environment for playing samples, due to it's 4kB bank-switching, Just have 1 page fixed to a volume-multiplied-by-samplevalue-table, and a few other pages set by the music engine to the particular samples to be played on each software channel. Then, for each channel, just do something like this for each channel in the irq routine: (self-modifying code implied)
lda #sampleLo
adc #sample_incr_lo
sta sampleLo
lda #sampleHi
adc #sample_incr_hi
sta sampleHi
tax
ldy SamplePtr,X
lda volume_by_sample,Y
adc #OldSamples
sta OldSamples_next_channel
...
sta $4011
...and you should have a workable 2-3 channels sampled instruments player. This puts certain restrictions on the maximum sampling frequency and playback speed, but I figure it should be decent. If not, sacrifice one channel and add some more elaborate code in the loop. The reason I haven't tried something like this yet is lack of time/laziness on my part. :)
Bananmos wrote:
When the NSF format was being developed, I argued that the ability to play sampled instruments thru raw PCM should be included, but it was not considered to fit the NES experience. I was asked to show a NES program that did something like this, and I knew none. I only knew it was possible to do. Later I discovered Skate Or Die 2, but then it was already too late.
Had I been in the scene at the time, I would have mentioned
Big Bird's Hide and Speak.
-- How many words can you make before the sun goes down? Ready... go!
-- N, S, F, I don't think that's a word. Go again!
And even a kick*** $4011 based music player that doesn't work during the game could still be useful during cutscenes.
SoD 2 is the minimal example of this, but imagine if the
Ninja Gaiden or
Vice: Project Doom cut scenes had some approximation of Amiga style music.
There seems to be
some interest in reviving and cleaning up this thread.
EDIT 2: Removed unnecessary question.
Hey guys I was just listening to the nsf file of Superstar Pro Wrestling and after all the songs comes Static like noises for some tracks and then sound effects.Well I found out on Zophar's Domain that the rip has 18 songs,11 voices,and 30 sfx.I was listening to it on NotsoFatso but then I read here that Quiestust has a nsf player that supports Raw PCM (which is mostly used for voices and guitar riffs in games)and once i downloaded it and tried out Superstar Pro Wrestling on Nintendulator I heard all the voices samples lovely.Now what I'm suggesting is it possible for the Author of NotSoFatso (a great nsf(e) player I might add) to upgrade the plugin for winamp so that this way it CAN now support raw pcm samples?
P.S.I read on wikipedia that the title screen for WCW Wrestling (a.k.a.the American version of Superstar Pro Wrestling)has Paul E.Dangerously's voice on it saying "World Championship Wrestling" isnt that cool!!
It is highly doubtful that the Superstar Pro Wrestling NSF makes use of the raw PCM method I use in my player, since I introduced that method back in February and said NSF was ripped over 4 years ago.
My mistake I can hear it now on NotSo.It's that I had "Don't wait for play return" checked on Notso.Once I uncheceked it I heard the voice samples on Superstar Pro Wrestling NSF.Still though wouldn't it be nice if the author of NotSo had the ability to play raw pcm samples like your player does?
By the way, since three people (possibly all the same person) have now emailed asking for a "rip" of Punch-Out Arcade with "all the voice samples":
The Arcade version uses raw PCM for the crowd noises (so it can modify the samples to change their volume). Until the raw PCM thing is finalized in the spec, I'm not going to waste time on this. The speech voice samples are produced by the VLM5030 chip, which the NES doesn't have, and thus a NSF can't include them either.
I'm sorry I was one of the people who request the Punch-Out arcade voice samples.Hey is it possible to add that VLM5030 soundchip as an Expansion chip for the nsf spec?And does that mean that someone is currently working on the NSF spec to add Raw PCM support into it?
No and no. Just let go, okay?
No!lol.Sorry so does that mean nobody right now is working on the NSF Spec on adding raw pcm support on it? And If not, how come?
Argh, I had revisited the issue of PCM support today and thought I had my mind made up, but then re-read this thread and became undecided again. The three approaches that remain viable in my mind:
1.Init routine never returns. NSF player interrupts it periodically to call play routine, then resumes where it left off.
2.Init routine returns normally. If new idle routine is provided, NSF player calls it and interrupts it periodically to call play routine, then resumes where it left off (as above). Idle routine should be allowed to return on tracks that don't need PCM, in order to reduce CPU usage of NSF player (on mine, a non-returning init routine makes things around 10x slower).
3.Same as 1, but interruption of a non-returning init routine is only enabled after init routine writes to special enable address.
Approach 1 requires a delay before the play routine starts being called, since many normal NSFs don't tolerate having play called before init returns. I had a program scan about 1400 NSFs from one of the recent archives posted here. The following files took a frame or longer to return from the init routine (- = never returned):
Code:
- 1991 Du Ma Racing
- Baltron
2.0 Bard's Tale 2
- Battleship
1.1 Bill Elliott's NASCA Challenge
- Bugs Bunny Crazy Castle
1.4 Deep Dungeon 4
- Dorae Bomb
- Ferrari Grand Prix Challenge
- Great Wall
3.8 Musashi no Bouken
27.4 Years Behind (varied greatly, this was the highest)
All of these seemed to play fine if the play routine was called four frames after the init routine was invoked, even for those whose init routine hadn't returned. This pretty much settles the compatibility issue for me, which was what approaches 2 and 3 were to work around. So I like approach 1 for its simplicity, but if there are still concerns of breaking current NSF rips, approach 3 seems cleaner than 2, partly because it doesn't require any changes to the file format. The special address in approach 2 should be in already-existing memory, to allow for easier implementation on a NES.
For any of the approaches, we need to decide whether the NSF player saves and restores registers around invocation of the play routine (i.e. PHP, PHA, TXA, PHA, TYA, PHA, JSR PLAY, PLA, TAY, PLA, TAX, PLA, PLP). If it does, this makes NSF ripping easier. I could write a test NSF that verified that a player was saving and restoring registers properly, which would go a long way to ensuring proper support.
I'd just like to ask why you ditched the solution of allowing the play routine to be re-entrant? You definitely seemed to favor it the last time this was discussed, and it still seems to be the cleanest alternative in my opinion.
Argh, I should have included my first draft of the posting, since I covered some other approaches and why I didn't consider them viable. Here are the relevant bits:
4.Init routine returns normally. NSF player then invokes play routine regularly, even if previous call hasn't returned. On first call to play, NSF itself would enter its "idle loop" and never return. On calls after this, NSF would do the real play routine and return normally:
Code:
Init ----
|
<----
Play ----
| (doesn't return)
|
|
Play ---- (first call is interrupted with second one)
|
<---- (this call returns normally)
|
|
|
Play ----
|
<----
.
:
5.Introduce a new status flag that can be polled to find out when 1/60 second has passed and have init routine poll this and never return.
6.Allow the play rate to be increased dramatically, i.e. thousands of times a second.
Approach 4 won't work as at least one legitimate NSF's play routine takes longer than 1/60 second before returning and can't handle being interrupted (mitokou.nsf). Even if it didn't break compatibility, it is considerably more complex for the NSF ripper to code.
Approaches 5 and 6 are radical and would probably require even more changes to the NSF sound code.
I've re-implemented approach 1 (init routine never returns) a second time today and strongly support its simplicity.
The compatibility argument makes sense. So then, the most proper way to do raw PCM in NSFs would be this:
* Have an init routine that never returns. This routine will actually contain the music playback code that would normally be found in the "play" routine. This routine will wait for the play routine to set a variable, indicating to the init routine that a frame has passed and another call to the playback code should be made, after which the init routine will reset this flag and once again enter it's wait state
* Have a play routine that is called a few thousand times a second. This routine will output a sample (or possibly a few of them) as well as decrease an internal counter. When this counter is zero, it will set the abovementioned flag so that the idle init routine will perform another call to the playback code.
Another possibility is to let the init routine do the actual raw pcm, and allow it to be interrupted by the playback routine. This is actually how Battletoads and Skate or Die 2 handle their sample playback, and it works ok. However, this method has the serious drawback of pausing the played raw pcm waveform for several scanlines, most likely causing an annoying buzz in the waveform, which you may wish to avoid in other cases.
However, I am a bit confused by this comment of yours:
Quote:
6.Allow the play rate to be increased dramatically, i.e. thousands of times a second.
...
Approaches 5 and 6 are radical and would probably require even more changes to the NSF sound code.
As this is perfectly allowed, and was actually a requirement for the "re-entrant play routine" solution considered earlier. To quote the NSF spec:
Quote:
Once the tune has been initalized, it can now be played. To do this,
simply call the play address several times a second. How many times
per second is determined by offsets 006eh and 006fh in the file.
These bytes denote the speed of playback in 1/1000000ths of a second.
For the "usual" 60Hz playback rate, set this to 411ah.
To generate a differing playback rate, use this formula:
1000000
PBRATE= ---------
speed
Where PBRATE is the value you stick into 006e/006fh in the file, and
speed is the desired speed in hertz.
For example, to get a 15.625 kHz playback (one samples/scanline on a PAL NES), PBRATE would equal 64. The only problem would be that the sample rate wouldn't be very easily adjustable due to the the non-linear 1/x formulae, but I suppose we can live with that.
Bananmos wrote:
Another possibility is to let the init routine do the actual raw pcm, and allow it to be interrupted by the playback routine. This is actually how Battletoads and Skate or Die 2 handle their sample playback, and it works ok. However, this method has the serious drawback of pausing the played raw pcm waveform for several scanlines, most likely causing an annoying buzz in the waveform, which you may wish to avoid in other cases.
This is exactly how I handled it in the Battletoads+PCM NSF I released (linked from the beginning of this thread) - the init code loops forever on a variable from the sound engine which says "play a RAW PCM sample", and whenever the play routine runs and returns, the init code responds by playing the sample.
As for the "annoying buzz", the exact same thing happens in the real game, because that's how RAW PCM actually works.
What Quietust discusses is the main reason I like the "init never returns, play routine interrupts" approach: it matches how most games would play PCM. The main loop would wait for a signal to start a PCM sample, then output it while being periodically interrupted by the NMI code. The main loop would probably not be polling an "NMI occurred" variable, which is why I don't like that approach (#5 above).
So does this mean you found a way to add raw pcm support on the nsf spec?Also if this is true would this mean for nsfs that are already ripped with the dmc channel sound, would the tune of the dmc change differently if raw pcm was supported? Is there a count listed for how many Nsf's with DMC/PCM samples have been ripped?
Once you see a new NSF spec released, then support has been added. Until then, it's a slowly-moving issue.
Hayamaru3000 wrote:
Also if this is true would this mean for nsfs that are already ripped with the dmc channel sound, would the tune of the dmc change differently if raw pcm was supported? Is there a count listed for how many Nsf's with DMC/PCM samples have been ripped?
the only nsfs that support PCM samples are the demos posted at the beginning of this thread to my knowledge. changing support for PCM will not change the sound of all the DMC-enabled NSFs out there. it will merely more closely emulate what a NES can do. games like Skate or Die 2 that used raw PCM will have to be re-ripped to actually hear the samples.