Hah, was just about to post my revision to that.Can we adjust the interface a bit, for efficiency/simplicity? Basically, I'm thinking you don't need "samples_pending", just a way to read as many samples as are available into the caller's buffer.
Code: Select all
class Resampler { public: // Sets output sample rate and removes all samples from buffer void set_rate( int sample_rate ); // Writes pair of samples, each 16-bit signed void write( int left, int right ); // Reads at most n sample pairs to *out and returns number actually written int read( uint32_t* out, int n ); };
I came up with:
Code: Select all
class resampler {
public:
//if buffer is too full, it can 'drop' samples in the extreme case
void sample(uint16_t left, uint16_t right);
//audio driver will ask for the samples needed to complete one block
//resampler can return NULL in extreme cases
uint32_t* read(unsigned samples);
//setup routine, also used to clear buffer (eg for system reset)
void init(unsigned frequency, optional unsigned latency_in_ms, optional unsigned samples_per_block);
};
I don't know if it'd help much to tell the resampler that it'll be working with blocks of n samples or not. Maybe, maybe not.
But for the read() function, it would need to get the same number of samples it asks for (excepting for extreme cases where audio will fail anyway, of course.)
For both DirectSound and OpenAL at least, I basically query the play cursor to determine the current block. There are three blocks, one for playback, one for writing new samples, and one to make sure that playback doesn't cross over into the write buffer. What I do is once one block has been completely played, I write it with the new block. Right now, I just simply lock the emulation once I have one whole block and wait for the play cursor to complete a block before returning control. Each block is basically 1/3rd of the audio latency total, so with the average being ~60ms, you're looking at me requesting ~20ms worth of audio at a time, but it'd be a consistent value every time, whereas the input samples will vary by as much as ~5-10ms each time I ask for output (I can run some tests at home to be more specific on the variance, if needed.) Overall, of course, you'll be getting almost exactly 32040 samples per second worth of input. They'd just be coming in at sporadic intervals.
So ideally, the resampler should be able to compensate for the largest possible difference in samples, obviously increasing in range as requested latency rises. Given the insane overhead of bsnes, not even 4-tap hermite phases it in the slightest, so the resampling algorithm can be as intense as it needs to be.
Also, bsnes currently works by setting the audio card frequency to the speed of emulation, eg 2x will set playback to 64khz. Ideally, it'd probably be best if we could just give it a constant, user-specified frequency for all emulation speed settings ... but the frequency change works perfectly fine for now, anyway.