USF player library by haspor at 5:36 PM EDT on October 2, 2013
I'm looking for a library to play USF/MINIUSF files on Unix/Linux. Anyone know a proper candidate for it? It would be for an Android application i'm developing (free app).
USF is unlikely to be playable on ARM, as the existing USF player/emulator library, lazyusf, makes extensive use of x86 32 bit recompilation for both R4300i and RSP emulation. Each core's C interpreter version is an order of magnitude of four times as slow as its recompiling version, making a full recompiler 16 times as slow on x86.
Under my own testing, the full interpreter mode barely pulls real time on my old Athlon 64 3200+ machine. Just imagine how slow that will be running on ARM.
You can always try, but there's no guarantee that it will work properly.
The emulator loops could probably use some major idle detection to improve their playback speed.
Still, you have to remember, N64 games rendered their music in software using the processors available. The RSP did support vectorized math (add/subtract/multiply/divide/etc operations on four floating point numbers in a single opcode) which the recompiler turns into SSE math opcodes. You'd have to look into converting that to something that will work on Android platforms.
The API is quite simple. You couple it with a PSF loader, such as my psflib C library.
Basically, with psflib, you draft up file a file access system. I advise looking in here, specifically the parts involving usf_loader_state.
You would use psf_load with no loader functions, or possibly just the tag loader function for metadata, and a type of 0, and it will return either -1 on failure, or the type of the PSF requested. When you wish to load a specific PSF type, I suggest passing that type to the function so it verifies the types of all files opened.
You instantiate the loader state structure, and assign a newly allocated usf emulator library to it, and call the psf_load with both the loader and loader metadata callbacks. The loader uploads the reserved sections to the emulator in the correct order, and the info function retrieves the two supported emulation behavior override tags and sets the correct members of the loader structure.
Once loading has completed properly, you use those two tag property variables in the structure to call two setter functions on the lazyusf instance.
Then you call usf_render to produce actual sample data, and optionally retrieve the last DAC sample rate. If your player needs to know the sample rate at startup, it's safe to call this function once, store the sample rate, and save the first block of samples for later. With many USF sets, loading the emulator up and calling for a single block of samples is quite rapid. For a number of others, like Animal Forest or Neon Genesis Evangelion, it's a bit slow, so maybe advisable to only bother when starting playback.
Before disposing of the allocated memory block, you need to call usf_shutdown on it, to free the mmap'd block of memory used by the emulator core.
I would like to try to make it so that all memory is allocated against the initial state structure, but alas, that doesn't seem as easy as I thought. When I tried this, certain structures would overlap other parts of the structure.
I also use an offset at the head of the base memory block, initialized by usf_clear, which automatically aligns the internal lazyusf state structure on a page boundary. Paragraph alignment is all it really needs, for the RSP register and temporary structures, for aligned access by SSE2 opcodes, if you enable them on x86/x86_64 platforms. It may help a bit with auto vectorization on non-x86 platforms, though.
As with the stock Iconoclast RSP, defining ARCH_MIN_SSE2 for just rsp.c will enable use of SSE2 intrinsic functions, while defining ARCH_MIN_SSSE3 will use both. Do not use both preprocessor definitions at once, because there are several places in the code in which the SSE2 macro takes precedence over the SSSE3 macro. Neither apply to non-x86 architectures, unless there's some compiler which can produce ARM or MIPS compatible SIMD code from Intel intrinsic functions.
EDIT: I chose to use reserved upload function, like Neill's libraries, instead of bundling PSF loading into every player. Since PSF parsing is so simple, I figured a simple loader library would benefit any PSF player. Check out that linked Obj-C++ source code for examples of loading every xSF format currently in existence, and within my Github or Bitbucket accounts, all of the libraries necessary to interface as the player cores for those formats.
edited 8:05 PM EST February 15, 2014
Okay, the API is in usf.h. usf_internal.h is the internal structure required by the entire library, which I didn't see fit to split up into components because I am lazy.
All of the public functions in usf.h are fairly self-explanatory, except for the upload function's return value. It's like most POSIX library functions, returning -1 on error and 0 on success. The only case where it really fails is either if it can't allocate the memory, or if the data is invalid.
Hmm, I need to add more error checking, since the AllocateMemory internal function from the original code can return error states on failure.
It also needs some better means of allocating the majority of the core memory, since it uses mmap, and would probably need a generic memory allocation and mapping function set so it can be reused on Windows, for instance. Something based on VirtualAlloc/VirtualFree, like the original Win32 code it's based on.
I suggest you pull my latest version as-is and not try to make any further modifications. If you still need any modifications, let me know.
I've eliminated all of the internal headers from the public interface header. That should fix all odd warnings or errors importing it into other projects.
As for missing usf_state_t in any C file, that should not happen. If it does happen, it means that particular C file is missing an include reference for usf_internal.h below all of its other imports.