Skip to content

Commit

Permalink
AudiogeneratorMOD: use full 16bit with slightly better accuracy (#479)
Browse files Browse the repository at this point in the history
* Fix a tricky bug that causes delays in playback

... this bug was already present in stellaplayer. Obviously "effectNumber" must be checked, because "effectParameterX" just holds the "upper nibble" of effectParameter

* fix for issue #474

- do channel mixing in 32bit, to avoid noise from over/underflow
- write audio output as "signed int" (not unsigned)
- add int16 saturation check ("clipping")

* avoid crash when MOD file has to many CHANNELS

- avoid guru meditation, by logging an error and refusing playback in case MOD.numberOfChannels > CHANNELS
- correct small typo

* internal DAC: avoid possible overflow in conversion int16 -> uint16

* update constants

.. we can do 16bit and 8 channels, so let's do it ;-)

This adds some debugging code to better understand what is going on inside the mixer code (AudioGeneratorMOD::GetSample). And it also prints out some usefull information from the MOD file.

Only active when do_MIXER_DEBUG is defined.

* MOD generator: 12 bits of "real" resolution (instead of 10)

With this change, we gain 2 additional bits of "real resolution" from the sample interpolation step.
Still need some testing to be sure that nothing get "lost" at the same time.

* limit to 4 channels on ESP8266

As discussed in #479 (comment)
  • Loading branch information
softhack007 authored Jan 9, 2022
1 parent c55d240 commit 7a82ba2
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 8 deletions.
23 changes: 17 additions & 6 deletions src/AudioGeneratorMOD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ bool AudioGeneratorMOD::stop()
free(FatBuffer.channels[i]);
FatBuffer.channels[i] = NULL;
}

if(running && (file != NULL) && (file->isOpen() == true)) {
output->flush(); //flush I2S output buffer, if the player was actually running before.
}

if (file) file->close();
running = false;
output->stop();
Expand Down Expand Up @@ -803,14 +808,20 @@ void AudioGeneratorMOD::GetSample(int16_t sample[2])

current = FatBuffer.channels[channel][(samplePointer - FatBuffer.samplePointer[channel]) /*& (FATBUFFERSIZE - 1)*/];
next = FatBuffer.channels[channel][(samplePointer + 1 - FatBuffer.samplePointer[channel]) /*& (FATBUFFERSIZE - 1)*/];

out = current;

// preserve a few more bits from sample interpolation, by upscaling input values.
// This does (slightly) reduce quantization noise in higher frequencies, typically above 8kHz.
// Actually we could could even gain more bits, I was just not sure if more bits would cause overflows in other conputations.
int16_t current16 = (int16_t) current << 2;
int16_t next16 = (int16_t) next << 2;

out = current16;

// Integer linear interpolation - only works correctly in 16bit
out += (next - current) * (Mixer.channelSampleOffset[channel] & ((1 << FIXED_DIVIDER) - 1)) >> FIXED_DIVIDER;
out += (next16 - current16) * (Mixer.channelSampleOffset[channel] & ((1 << FIXED_DIVIDER) - 1)) >> FIXED_DIVIDER;

// Upscale to BITDEPTH
out32 = (int32_t)out << (BITDEPTH - 8);
// Upscale to BITDEPTH, considering the we already gained two bits in the previous step
out32 = (int32_t)out << (BITDEPTH - 10);

// Channel volume
out32 = out32 * Mixer.channelVolume[channel] >> 6;
Expand All @@ -819,7 +830,7 @@ void AudioGeneratorMOD::GetSample(int16_t sample[2])
sumL += out32 * min(128 - Mixer.channelPanning[channel], 64) >> 6;
sumR += out32 * min(Mixer.channelPanning[channel], 64) >> 6;
}

// Downscale to BITDEPTH - a bit faster because the compiler can replaced division by constants with proper "right shift" + correct handling of sign bit
if (Mod.numberOfChannels <= 4) {
// up to 4 channels
Expand Down
10 changes: 8 additions & 2 deletions src/AudioGeneratorMOD.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class AudioGeneratorMOD : public AudioGenerator

protected:
int mixerTick;
enum {BITDEPTH = 15};
enum {BITDEPTH = 16};
int sampleRate;
int fatBufferSize; //(6*1024) // File system buffers per-CHANNEL (i.e. total mem required is 4 * FATBUFFERSIZE)
enum {FIXED_DIVIDER = 10}; // Fixed-point mantissa used for integer arithmetic
Expand All @@ -64,8 +64,14 @@ class AudioGeneratorMOD : public AudioGenerator
// Hz = 7159091 / (amigaPeriod * 2) for NTSC
int AMIGA;
void UpdateAmiga() { AMIGA = ((usePAL?7159091:7093789) / 2 / sampleRate << FIXED_DIVIDER); }


#ifdef ESP8266 // Not sure if C3/C2 have RAM constraints, maybe add them here?
// support max 4 channels
enum {ROWS = 64, SAMPLES = 31, CHANNELS = 4, NONOTE = 0xFFFF, NONOTE8 = 0xff };
#else
// support max 8 channels
enum {ROWS = 64, SAMPLES = 31, CHANNELS = 8, NONOTE = 0xFFFF, NONOTE8 = 0xff };
#endif

typedef struct Sample {
uint16_t length;
Expand Down

0 comments on commit 7a82ba2

Please sign in to comment.