NESDev and Strangulation Records messageboards
Forum Index | FAQ | New User | Login | Search

Previous ThreadView All ThreadsNext Thread*Show in Threaded Mode


SubjectNESAPU dll new  
Posted byDisch
Posted on11/8/03 4:39 PM
From IP66.127.105.177  



A little while ago someone on the board thought it would be cool if emus used the sound core I've been working on. Then last night on IRC someone asked if I could make a DLL that emulated the NES's sound.

And I got to thinking... why not? I'm already kind of redoing a lot of things anyway... this would be a nice addition.

So I guess I'm asking for what other people think. Does this sound like a good idea? Would anyone even use it? I know a lot of people put off emulating the sound when they're starting out their emus. It seems like it would be nice if they had something they could just drop into their emu. I've also noticed that a lot of emulators use other emu/NSF player's sound core in their emu (nester is a good example... from what I understand he used Nofrendo's sound core).

I guess I'm looking for motivation more than anything else. Are there any NES emu authors out there who would find something like this useful?




SubjectRe: NESAPU dll new  
Posted byASMGuy
Posted on11/8/03 4:50 PM



Absolutely!




SubjectRe: NESAPU dll new  
Posted byRoboNes
Posted on11/8/03 10:57 PM
From IP81.77.202.150  



i would guess this would be helpful to many people out there, emu authors and otherwise, and i guess being that sound not used as heavily used as graphics there would not be a performance hit (you suffer this with graphics in a dll)




SubjectRe: NESAPU dll new  
Posted byDisch
Posted on11/10/03 02:20 AM
From IP66.127.105.177  



Here's the layout I'm thinking:


APU_Initialize() will be called when the emu first loads. This will allocate memory and prepare some stuff.

APU_Reset() will reset the APU... it'll also specify the extra sound used by the mapper (if any)... like VRC6 etc.

APU_RegWrite() and APU_RegRead() will be called for apu register read/writes. You'll have to specify a CPU cycle timestamp... the apu will run for the given cycles, then perform the register read/write.

APU_RunToCycle() will run the APU for so many cycles without performing register writes or anything. Like when you need the APU to 'catch up' (like before outputting sound)

APU_GetTimestamp() gives you the current APU timestamp/cycle count. Just in case your emu needs to check it for some reason (don't know why)

APU_SetTimestamp() changes the internal APU timestamp (without doing any emulation). To keep it in sync with the emu or something. Like if the frameskip thing is on and you're moving faster than real time or something.

APU_IRQPending() will let you know if an IRQ is pending (by either the frame counter or the DMC). It will pretty much always be true =P.

APU_GetSamples() gives you a pointer to the generated samples. I'll leave it up to the emu to stream them, since there are several ways of doing it... and relaying all the output options through the emu would be cumbersome.

APU_Destroy() should be called on shutdown... to clean up memory and stuff.



There will also be miscellaneous functions to specify user options and whatnot.

A few notes:

- any time the apu runs for cycles (APU_RunToCycle, APU_RegWrite, APU_RegRead), it runs to the given timestamp PLUS however many cycles were 'stolen' by the DMC. The function will also return the number of cycles stolen by the DMC so that the emu can have the CPU skip those cycles. I debated having the number of stolen cycles tracked seperately... but then you'd get multiple function calls during register writes or anytime the apu is clocked... so for speed's sake I figured this was the best way to do it. To be most accurate... the emu should update the APU prior to any speed-sensitive register writes (like PPU writes) to account for those stolen cycles. It's not necessary for APU writes, since the apu functions do that internally.

- you'll have to supply a pointer to ROM addressing space for the DMC to access. I believe the smallest PRG swapping any mapper does is 4k, so the emu will have to supply 8 pointers to 4k blocks of memory and keep those pointers updated as banks are swapped out.

- APU_GetSamples gives you a pointer to a buffer containing the generated samples as well as the size of that buffer. The number of generated samples depends on how many cycles have been run.

- right now the APU assumes that there are 1789772.7272 cycles per second. To my knowledge this is accurate. Does PAL handle that differently? Are there fewer/more cycles per second on PAL? And if so does it affect sound pitch? Or would it just affect the number of output samples every second?

- I'm trying to do this without any platform-specific code... but truth be told I've never coded for anything but Windows so I might not know everything to look out for. I'm keeping an eye out for Big/little endian differences... but I'm worried about sound output formats. On Windows, 16-bit output is signed, 8-bit is unsigned... in stereo the low word is left channel and the high is right. Is that the way it works on other platforms?

- It's being done in C++, but could probably be converted to C without too much trouble if necessary (even though I don't really like straight C >_>). It will be open source. It'd probably be faster to drop the code into the emu than it would be to work with a DLL... but the DLL will be available so that non-C coded apps can use it (Delphi, VB, etc)



Any points I'm missing? Would this be easy enough to incorporate into an emulator?




SubjectRe: NESAPU dll new  
Posted byblargg
Posted on11/10/03 2:09 PM
From IP199.170.89.142  



APU_SetTimestamp() changes the internal APU timestamp (without doing any emulation). To keep it in sync with the emu or something. Like if the frameskip thing is on and you're moving faster than real time or something.

Surely the CPU cycle counter in an emulator must be rolled over periodiocally (or kept with more than 32 bits). This function seems like it'd essential for renormalizing the APU's time every frame. i.e. the APU and CPU are at time 0. Run the CPU for at most the number of cycles in a frame. Have APU run to that number of cycles. Read samples from APU. Reset APU and CPU's clocks back to 0 for the next frame.


you'll have to supply a pointer to ROM addressing space for the DMC to access. I believe the smallest PRG swapping any mapper does is 4k, so the emu will have to supply 8 pointers to 4kblocks of memory and keep those pointers updated as banks are swapped out.


By my calculations, the PCM can be playing at most a little over 4000 bytes/sec on its own. Would a pointer to a memory read function be sufficient? That way user can use any memory mapping scheme they like. Unless performance dictates otherwise, simpler seems better.

right now the APU assumes that there are 1789772.7272 cycles per second. To my knowledge this is accurate. Does PAL handle that differently? Are there fewer/more cycles per second on PAL? And if so does it affect sound pitch? Or would it just affect the number of output samples every second?

Could the APU be given a ratio of clock rate to sampling rate, so that it doesn't need to know either? All it does is scale from clock time to sampling rate. For example, if the emulator was outputting at a 44.1kHz sampling rate and running the APU at 1.79Mhz, the APU could be told to scale its output by a factor of 44100/1790000. Otherwise all timings in the APU are in clock cycle counts.

I'm trying to do this without any platform-specific code... but truth be told I've never coded for anything but Windows so I might not know everything to look out for.

I compiled the core of NotSoFatso on a big-endian machine and the only endian-related problems I remember finding were where it read the load/init/play addresses from an NSF file and where it used them to set up the invocation code in the 6502 (the emulated 6502 got the right address since it's also little-endian, but the address as seen by the native CPU inbetween was swapped!).

Except where performance matters, I just assemble multi-byte entities using shifts and masks. i.e. load_addr = (nsf.load_addr [1] << 8) | nsf.load_addr [0]

I'm keeping an eye out for Big/little endian differences... but I'm worried about sound output formats. On Windows, 16-bit output is signed, 8-bit is unsigned... in stereo the low word is left channel and the high is right. Is that the way it works on other platforms?

I'd just output 16-bit signed samples and let the user convert them to something else if they have special needs. As for stereo, I've always seen it represented as interleaved samples, with left first, i.e. l0, r0, l1, r1, etc. If a left-right frame is loaded into a single variable, then the machine characteristics come into the picture. The least significant bits will hold the left sample on a little-endian machine, the right sample on a big-endian machine. The particular layout only matters if the sample pair is being operated on while loaded into a single variable, but there's not that much you can do to the pair without having the low sample "spill over" to the high sample. I usually deal with each channel individually. Is efficiency the motivation for multiple output formats?





SubjectRe: NESAPU dll new  
Posted byDisch
Posted on11/10/03 5:47 PM
From IP66.127.105.177  



Surely the CPU cycle counter in an emulator must be rolled over periodiocally (or kept with more than 32 bits)

I just assumed emus would have their counters roll over, so the APU's counter will rollover if not reset. If the emu resets it's own counter then yeah.. that function would be necessary for operating the APU.

Would a pointer to a memory read function be sufficient?

Well... the problem is not all emus will use the same read memory function format. And in the end, all emus will boil down to having an array of bytes containing ROM data (there really is no other practical way to do it). Not to mention having direct access to the ROM through pointers is a lot faster than calling a function everytime a byte needs to be retreived.

Could the APU be given a ratio of clock rate to sampling rate, so that it doesn't need to know either?

Yeah, that's probably a good idea.

My concern is changing the clock rate will directly affect the pitch... if it's too far away from 1.79MHz the sound will be horribly off key... unless PAL games write to the registers expecting the output frequency to be different (which is pretty likely, now that I think of it).

the only endian-related problems I remember finding were where it read the load/init/play addresses from an NSF file and where it used them to set up the invocation code in the 6502

That's good to hear... I actually didn't pay any mind at all to cross platforming when I made it >_>. Shame on me, I know. (But it was a Winamp plugin... so I figured how portable could it be =P)

Except where performance matters, I just assemble multi-byte entities using shifts and masks

I'm going the other route and using unions.

union TWIN
{
u16 W;
#ifdef BIG_ENDIAN
struct { u8 h; u8 l; } B;
#else
struct { u8 l; u8 h; } B;
#endif
};

that way I can set high/low and front/rear bytes at will without having to do any bitshifting or endian worries.


As for stereo, I've always seen it represented as interleaved samples, with left first, i.e. l0, r0, l1, r1, etc

Yeah... that's what I meant before but it came out all wrong. I was wondering if the right channel came first on any platforms.

But I set up a series of #defines which cover all these bases... so if a someone on another platform needs to make that minor adjustment it will be as easy as commenting/uncommenting a single line of code.


Thanks for the feedback and ideas... so far it's coming along nicely




SubjectRe: NESAPU dll new  
Posted byblargg
Posted on11/10/03 10:03 PM
From IP199.170.89.27  



I just assumed emus would have their counters roll over, so the APU's counter will rollover if not reset. If the emu resets it's own counter then yeah.. that function would be necessary for operating the APU.

Wouldn't this cause problems with ordered comparisons? I'd imagine in your APU you'd have some kind of loop with a condition of timestamp < final_timestamp. Now that I think about it, I suppose you could make the types unsigned and find the difference, which would be immune to rollover due to unsigned's modulo characteristics (1u - 0xFFFFFFFFu == 2u). Anyway, your inclusion of a SetTimestamp() function allows prevention of rollover if it would otherwise be a problem.

My concern is changing the clock rate will directly affect the pitch... if it's too far away from 1.79MHz the sound will be horribly off key... unless PAL games write to the registers expecting the output frequency to be different (which is pretty likely, now that I think of it).

Say your output sampling rate is 44.1kHz. If the APU is clocked at 1.79MHz, then 40.59 APU cycles corresponds to the duration of one output sample. Presumably the library user would choose a ratio which kept the APU at 1.79MHz in relation to their output sampling rate. Having a single scaling factor allows the user a selection of APU clock speed, output sampling rate, and an overall speed (including pitch) adjust, without the APU library having to know about these details.





SubjectRe: NESAPU dll new  
Posted byDisch
Posted on11/10/03 10:51 PM
From IP66.127.105.177  



I suppose you could make the types unsigned and find the difference

Yeah, that's what I was thinking. But yeah... SetTimestamp should definatly be there for emus that want to dodge the rollover.

Say your output sampling rate is 44.1kHz. If the APU is clocked at 1.79MHz, then 40.59 APU cycles corresponds to the duration of one output sample.

For some reason I thought that wouldn't be enough info for generating samples, but I guess it would work. I don't know why but for some reason it just seemed like I needed to know the samplerate (which I don't, really).

The only other issue then is the Frame Counter... which gets clocked every 7457.5 cycles (for near 240Hz at 1.79MHz). Does that behave the same way on PAL systems? Or does the timing change?




SubjectUpdate new  
Posted byDisch
Posted on11/17/03 6:23 PM
From IP66.127.105.177  



Quick update on the DLL status for those of you interested.

All 5 native sound channels are functioning... VRC6 chip support is added, as well as MMC5 (including MMC5's PCM playback channel... although I haven't been able to test it since I don't know of any games that use it). Still working on N106 support... and other sound chips will come later.

I've been striving to make everything as accurate as I could while keeping it fast and adding the nifty little extras (all extras are optional).

Quick list of features and whatnot:

- The 'volume' side-effect of the DMC is functional (thanks Xod)
- The sound output is fully downsampled to the output frequency. The opening of Kid Icarus plays without alaising and everything sounds real nice and smooth.
- It's fast. Using under 1% of my CPU (1GHz). If I had to guesstimate... I'd say 0.4% or somewhere around there. And that's with a lot of unnecessary function calls made by the app (got lazy with the app I used to test the DLL's code).
- It's pretty small... right now the exe compiles to 84 Kb... I would assume the DLL would be even smaller.
- Sound generated IRQs are tracked and sent back to the parent emu... there's also a funtion to get the timestamp of the next IRQ to be generated.
- DMC steals a cpu cycle every time it fetches a byte... that stolen cycle gets sent back to the emu so that it can be accounted for.
- NTSC and PAL modes are supported... although I haven't really tested PAL >_>

extras (all optional):

- Stereo! I love stereo. Panning control of each individual channel (including each channel on external chips).
- A dmc pop reducer... cleans up some of the pops made by sloppy 4011 writes. It's not as accurate, but I personally feel it really helps the sound... especially in tunes like TMNT2
- Individual channel volume control, as well as channel disabling.


Planning to put in a filter Xod gave me, but that'll come later. Once I get Namco-106 support, I'll try and put it in, touch up everything, then release. Should be in a few days.




SubjectRe: NESAPU dll new  
Posted byAnonymous
Posted on11/18/03 06:40 AM
From IP68.184.225.105  



Hey, this'd be useful for more than an emu. How about a NES sequencer or something? Then the player code (CPU cycles notwithstanding) can mimick its NES counterpart with less effort and more accuracy.

I've looked into modifying NoseFart/Festalon to this effect before, but it seemed like more trouble than it was worth. I was going for realtime control (from midi or keyboard) and such, and multiple APUs (one for playback, one for UI/editing stuff, etc).

Also, I'll admit it, I want to hear at /least/ a simulated NES orchestra. :)
At any rate, would something like this be possible?

-- Draci






SubjectRe: NESAPU dll new  
Posted bytepples
Posted on11/18/03 12:49 PM
From IP68.53.188.31  



It would be a good idea to allow APU plug-ins to be used for sequencers targeting NES sound chips. It would at least make cloning NT2 on Win32 a lot easier.




SubjectRe: NESAPU dll new  
Posted bykoitsu
Posted on11/18/03 6:56 PM
From IP64.81.51.192  



Why bother?

www.skale.org

-- jdc


SubjectRe: NESAPU dll new  
Posted bykoitsu
Posted on11/18/03 6:56 PM
From IP64.81.51.192  



Ah, I love how you can't edit messages on here..

www.skale.org

-- jdc


SubjectRe: NESAPU dll new  
Posted bytepples
Posted on11/18/03 10:33 PM
From IP68.53.188.31  



Not everybody who wants to get into NES music has the knowledge to set up a separate partition for FreeDOS. Not everybody who wants to get into NES music has a sound card that's register-compatible with an original Sound Blaster. Not everybody who wants to get into NES music can get NT2 to work with VDMSound; can you?




SubjectRe: NESAPU dll new  
Posted bytepples
Posted on11/18/03 10:42 PM
From IP68.53.188.31  



What do you mean by "Why bother?" Can SKALE Tracker output in a format suitable for use in NES replay code? Or do you say "Why bother?" to the whole NSF scene?




SubjectRe: NESAPU dll new  
Posted byASMGuy
Posted on11/18/03 10:52 PM



I would hope someone who wants to touch the NES
can set up a partition...




SubjectDo I have to defend... new  
Posted bytepples
Posted on11/18/03 11:41 PM
From IP68.53.188.31  



But should buying a motherboard with an extra slot for a legacy Sound Blaster card be a prerequisite for making NES music?




SubjectRe: Do I have to defend... new  
Posted byASMGuy
Posted on11/18/03 11:46 PM



I agree with everything you said except the part about partitions.




SubjectRe: Do I have to defend... new  
Posted byXodnizel
Posted on11/18/03 11:57 PM
From IP68.86.61.158  



Rebooting just to use one program is incredibly annoying.

Where exactly is this thread going, anyway?




SubjectRe: NESAPU dll new  
Posted bykoitsu
Posted on11/19/03 01:41 AM
From IP64.81.51.192  



Why bother moving NT2 to Win32 when there's already decent sequencers out there which could be used for exactly what people need...

-- jdc


SubjectRe: Do I have to defend... new  
Posted byRoboNes
Posted on11/19/03 12:21 PM
From IP212.219.143.105  



if push comes to shove, use a dos emu, in windows (sound blaster emulation aswell) like the command prompt only actually trying to provide compatibilty that should help with any old dos progs, i know of 2, dosbox & booch (or something like that) the later is full pc emu, so highly compatible




SubjectAlternatives? new  
Posted bytepples
Posted on11/19/03 12:50 PM
From IP68.53.188.31  



What decent Win32 sequencers designed for the NES playback paradigm exist? Remember that the NES has four tone generators, each of which is limited in the waveforms it can generate, and one sample channel that is very limited in playback frequencies. Which sequencer are you talking about that follows these constraints and runs natively on Win32? There's MML/MCK, but some people like to work in a graphical environment.




SubjectRe: Alternatives? new  
Posted byMemblers
Posted on11/19/03 8:13 PM
From IP68.58.99.218  



I think it'd be possible to use something like Skale Tracker. Some music programs support plugins for sound generation. I think Skale does (but I'm not sure), I know Buzz does because I used it a while back (and it's freeware, also). http://directory.google.com/Top/Computers/Multimedia/Music_and_Audio/Software/Buzz/

But there's no NES generator plugin that I know of, and no NES replay code (with a format converter for it, too). It should be possible, but it would require a good amount of work to get it going.




SubjectRe: Alternatives? new  
Posted bykoitsu
Posted on11/20/03 06:02 AM
From IP64.81.51.192  



It's simple in concept; I believe Memblers is already doing something like this (in concept, but not as a project). My recommendation is that people use a pre-existing Win32 tracker that supports a sequenced format (i.e. MOD, S3M, IT, XM, SKM) and simply map appropriate instruments inside of the tracker (we already use pre-generated formulaes for emulators, so having instruments of this sort won't be hard -- they should be extremely small files as well, something like 32-64 bytes).

Save one's work using one of the aforementioned file formats, and have someone write a conversion/export utility for converting one of those formats into sequencer data. I don't know what NT2's NES playback code looks like (I've never touched NES audio, much to everyone's chagrin), but I'm sure someone could write something to support more of the common effects used by the above formats. Arpeggio, vibrato, tremelo, volume, etc. etc...

The advantage to this idea is that extended audio chips (re: N106, VRC series, etc.) can be appropriately controlled using effect numbers.

Have no idea how to handle DMC, but I'm absolutely positive it can be worked in without too much pain.

If trackers don't suffice, there's always the option of going a step-sequencer route (16 steps per pattern, pattern count maximum of whatever the software supports). This is just literal pure sequence data, but I don't know what file formats are out there which do this.

Sadly, I'd refer you to www.maz-sound.com, but it looks as if they just redid their site and none of the old links they had presently exist. Bummer, as there were a lot of Win32 tracker/sequencer applications which could be used exactly for this. I'm sure Maz will update his site in time with those older links...

Anyways, my point is that composers should be writing their stuff using present-day tools that already exist for music sequencing/composition in the real world (i.e. tracking). Most of those tools use file formats that are already documented. NT2 was cool for it's time, but DOS is dead regardless of those who argue it's not -- it seems much more efficient in general to have someone write a converter between one of those formats, work with a NES music author (maybe Bananamos) to create a fantastic 6502-based music playback routine for the NES, and the rest just falls into place. What I'm trying to say is that I don't see the need for people creating new UIs and new "NES audio music tracker" applications when we can rely on pre-existing file formats and pre-existing Win32 tools which give us great UIs and environments. Skale is a good example.

-- jdc


SubjectNice idea, but... new  
Posted bytepples
Posted on11/20/03 9:11 PM
From IP68.53.188.31  



Discussion of this issue continues in the NES Music forum:
http://nesdev.parodius.com/cgi-bin/wwwthreads/showpost.pl?Board=music&Number=565&page=0&view=collapsed&sb=5




SubjectIt's done. new  
Posted byDisch
Posted on11/23/03 01:31 AM
From IP66.127.105.177  



Uploaded quick archives to my angelfire account... too lazy to upload it to my real page:


http://www.angelfire.com/sd2/disch/NESAPU.rar
http://www.angelfire.com/sd2/disch/NESAPU.zip

both archives are the same, but the rar is smaller! (what a suprise!). I put the zip up for the poor souls without WinRAR.

Source is included, along with binaries. Didn't want to make a zillion downloads... so I put it all in the same archive (it's only like 100K for it all anyway).

Lemme know what you think, guys ^_^. I'm quite happy with its performance so far... it's really fast even when used as a DLL.




SubjectRe: It's done. new  
Posted byblargg
Posted on11/23/03 06:34 AM
From IP199.170.89.172  



The sound quality is quite good. The beginning of Kid Icarus was handled well. I didn't have any problems compiling it on a big-endian machine. About the only issue was a #include <memory.h>, which my system didn't have (I changed it to #include <string.h>).

I found it easy to plug into my NSF player framework as the APU module (with a minor interface wrapper). I ran a performance profile and found that when synthesizing an NSF to a sound file, 92% of the time the CPU was in APU_RunToCycle() (what a surprise! not :) I didn't see any significant hot spots within the (mega) function itself. I have found a few areas for optimization with maybe a 20% improvement (mainly the elimination of costly divisions and some multiplications).

I'm also interested in how well it integrates with others' NES emulation project designs.





SubjectRe: It's done.  
Posted by<_Hyde_>
Posted on11/23/03 07:37 AM
From IP66.53.49.226  



Buen trabajo, senor Disch. Ahora tengo que cambiar la porqueria de mi emulador para ver que es lo que pasa...

Translate this on google and see what you get :)

hydesprojects.cjb.net


SubjectRe: It's done. new  
Posted byDisch
Posted on11/23/03 10:46 PM
From IP66.127.105.177  



>About the only issue was a #include <memory.h>

I think the only thing I used out of there was memset() anyway. Seems kind of strange that it would be defined in string.h. Ah well.

> I have found a few areas for optimization with maybe a 20% improvement (mainly
> the elimination of costly divisions and some multiplications)

I think I'm going to take your advise on that thing you talked about earlier (to avoid the divide by 40.5 every sample). Plus I found a way I might be able to dodge some division on the Namco 106 frequency calculations.

I also found an error in there, but it only would surface if the library runs out of buffer space to write to. Turns out I accidentally left a section of code in there that called DoTicks() again if there's not enough buffer space to generate a new sample (it was in there before because I was playing with the idea of clocking the channels as the sample was being generated, which didn't take). Apparently I forgot to take it out.. or forgot that it needed taking out.

Tossing 8-bit output is probably the best thing to do. Having support for 8 and 16 just slows things down... and I don't know why anyone would need 8-bit output anyway.

Cool to hear that it was working out =). I'm rather interested in how it'll work when dropped into an emulator as well.




SubjectRe: It's done. new  
Posted byDisch
Posted on11/23/03 10:47 PM
From IP66.127.105.177  



hahaa... google didn't know what "porqueria" was.

Anwyay... coo coo thanks ^_^. Hope it works out for ya.




Previous ThreadView All ThreadsNext Thread*Show in Threaded Mode
Jump to

Memblers' homepage             Contact Me

Forums powered by WWWThreads Demo