struct audio_channel_data2
{
int current_sample, last_sample;
+ uae_u8 new_sample;
int sample_accum, sample_accum_time;
int sinc_output_state;
sinc_queue_t sinc_queue[SINC_QUEUE_LENGTH];
int sinc_queue_time;
int sinc_queue_head;
- int vol;
+ int audvol;
+ int mixvol;
unsigned int adk_mask;
};
struct audio_stream_data
void *cb_data;
};
+#define FIR_WIDTH 512
+#define VOLCNT_BUFFER_SIZE 4096
+union sIntFlt { uae_u32 U32; float F32; };
+static float firmem[2 * FIR_WIDTH + 1];
+
struct audio_channel_data
{
unsigned int evtime;
int state;
int per;
int len, wlen;
+ int volcnt;
uae_u16 dat, dat2;
struct audio_channel_data2 data;
#if TEST_AUDIO > 0
bool ptx_written;
bool ptx_tofetch;
int dmaofftime_active;
+ int volcntbufcnt;
+ float volcntbuf[VOLCNT_BUFFER_SIZE];
};
static int audio_extra_streams[AUDIO_CHANNEL_STREAMS];
static unsigned long last_cycles;
static float next_sample_evtime;
+static int previous_volcnt_update;
typedef uae_s8 sample8_t;
-#define DO_CHANNEL_1(v, c) do { (v) *= audio_channel[c].data.vol; } while (0)
+#define DO_CHANNEL_1(v, c) do { (v) *= audio_channel[c].data.mixvol; } while (0)
#define SBASEVAL16(logn) ((logn) == 1 ? SOUND16_BASE_VAL >> 1 : SOUND16_BASE_VAL)
STATIC_INLINE int FINISH_DATA (int data, int bits, int ch)
static int mixed_on, mixed_stereo_size, mixed_mul1, mixed_mul2;
static int led_filter_forced, sound_use_filter, sound_use_filter_sinc, led_filter_on;
+#define PAULARATE 3740000
+static float Sinc(float x)
+{
+ return x ? sinf(x) / x : 1;
+}
+static float Hamming(float x)
+{
+ float pi = 4 * atanf(1);
+ float v;
+ if (x > -1 && x < 1)
+ v = cosf(x * pi / 2);
+ else
+ v = 0;
+ return v * v;
+}
+static void makefir(void)
+{
+ float pi = 4 * atanf(1);
+ float *FIRTable = firmem + FIR_WIDTH;
+ float yscale = float(currprefs.sound_freq) / float(PAULARATE);
+ float xscale = pi * yscale;
+ for (int i = -FIR_WIDTH; i <= FIR_WIDTH; i++)
+ FIRTable[i] = yscale * Sinc(float(i) * xscale) * Hamming(float(i) / float(FIR_WIDTH - 1));
+}
+
/* denormals are very small floating point numbers that force FPUs into slow
mode. All lowpass filters using floats are suspectible to denormals unless
a small offset is added to avoid very small floating point numbers. */
/* Handle accumulator antialiasiation */
for (i = 0; audio_data[i]; i++) {
acd = audio_data[i];
- output = (acd->current_sample * acd->vol) & acd->adk_mask;
+ output = (acd->current_sample * acd->mixvol) & acd->adk_mask;
acd->sample_accum += output * best_evtime;
acd->sample_accum_time += best_evtime;
}
for (i = 0; i < AUDIO_CHANNELS_PAULA; i++) {
acd = audio_data[i];
- int vol = acd->vol;
+ int vol = acd->mixvol;
output = (acd->current_sample * vol) & acd->adk_mask;
/* if output state changes, record the state change and also
cdp->intreq2 = 0;
cdp->dmaenstore = false;
cdp->dmaofftime_active = 0;
+ cdp->volcnt = 0;
+ cdp->volcntbufcnt = 0;
+ memset(cdp->volcntbuf, 0, sizeof(cdp->volcntbuf));
#if TEST_AUDIO > 0
cdp->have_dat = false;
#endif
v &= 127;
if (v > 64)
v = 64;
- cdp->data.vol = v;
+ cdp->data.audvol = v;
+ if (!currprefs.sound_volcnt)
+ cdp->data.mixvol = v;
}
uae_u16 audio_dmal (void)
#endif
if (!(audio_channel_mask & (1 << nr)))
sample = 0;
- cdp->data.last_sample = cdp->data.current_sample;
- cdp->data.current_sample = sample;
+ if (currprefs.sound_volcnt) {
+ cdp->data.new_sample = sample;
+ } else {
+ cdp->data.last_sample = cdp->data.current_sample;
+ cdp->data.current_sample = sample;
+ }
}
STATIC_INLINE void setdr (int nr)
loaddat (nr, false);
}
-STATIC_INLINE void loadper (int nr)
+static void loadper (int nr)
{
struct audio_channel_data *cdp = audio_channel + nr;
loadper (nr);
cdp->pbufldl = true;
cdp->intreq2 = 0;
+ cdp->volcnt = 0;
audio_state_channel2 (nr, false);
break;
case 2:
cdp = &audio_channel[i];
memset (cdp, 0, sizeof *audio_channel);
cdp->per = PERIOD_MAX - 1;
- cdp->data.vol = 0;
+ cdp->data.mixvol = 0;
cdp->evtime = MAX_EV;
}
for (i = 0; i < AUDIO_CHANNEL_STREAMS; i++) {
|| changed_prefs.sound_stereo != currprefs.sound_stereo
|| changed_prefs.sound_maxbsiz != currprefs.sound_maxbsiz
|| changed_prefs.sound_freq != currprefs.sound_freq
+ || changed_prefs.sound_volcnt != currprefs.sound_volcnt
|| changed_prefs.sound_auto != currprefs.sound_auto)
return 1;
}
}
-
void set_audio (void)
{
int old_mixed_size = mixed_stereo_size;
currprefs.sound_auto = changed_prefs.sound_auto;
currprefs.sound_freq = changed_prefs.sound_freq;
currprefs.sound_maxbsiz = changed_prefs.sound_maxbsiz;
+ currprefs.sound_volcnt = changed_prefs.sound_volcnt;
currprefs.sound_stereo_separation = changed_prefs.sound_stereo_separation;
currprefs.sound_mixed_stereo_delay = changed_prefs.sound_mixed_stereo_delay;
memset (sound_filter_state, 0, sizeof sound_filter_state);
led_filter_audio ();
+ makefir();
+
/* Select the right interpolation method. */
if (sample_handler == sample16_handler
|| sample_handler == sample16i_crux_handler
sample_prehandler = anti_prehandler;
}
for (int i = 0; i < AUDIO_CHANNELS_PAULA; i++) {
- audio_data[i] = &audio_channel[i].data;
+ struct audio_channel_data *cdp = audio_channel + i;
+ audio_data[i] = &cdp->data;
+ if (currprefs.sound_volcnt) {
+ cdp->data.mixvol = 1;
+ } else {
+ cdp->data.mixvol = cdp->data.audvol;
+ }
}
audio_total_extra_streams = 0;
for (int i = 0; i < AUDIO_CHANNEL_STREAMS; i++) {
cd_audio_mode_changed = true;
}
+static void update_audio_volcnt(int cycles, float evtime, bool nextsmp)
+{
+ if (cycles) {
+ for (int i = 0; i < AUDIO_CHANNELS_PAULA; i++) {
+ struct audio_channel_data *cdp = audio_channel + i;
+ sIntFlt v;
+ v.U32 = ((cdp->data.new_sample ^ 0x80) << 15) | 0x40000000;
+ v.F32 -= 3.0;
+ int cycs = cycles;
+ while (cycs > 0) {
+ if (cdp->volcnt < cdp->data.audvol) {
+ cdp->volcntbuf[cdp->volcntbufcnt] = v.F32;
+ } else {
+ cdp->volcntbuf[cdp->volcntbufcnt] = 0;
+ }
+ cdp->volcntbufcnt++;
+ cdp->volcntbufcnt &= (VOLCNT_BUFFER_SIZE - 1);
+ cdp->volcnt++;
+ cdp->volcnt &= 63;
+ cycs -= CYCLE_UNIT;
+ }
+ }
+ }
+ if (!nextsmp)
+ return;
+ float frac = evtime - (int)evtime;
+ for (int i = 0; i < AUDIO_CHANNELS_PAULA; i++) {
+ struct audio_channel_data *cdp = audio_channel + i;
+ float out0 = 0, out1 = 0;
+ int offs = (cdp->volcntbufcnt - FIR_WIDTH - 1) & (VOLCNT_BUFFER_SIZE - 1);
+ float v = cdp->volcntbuf[offs];
+ for (int j = 1; j < 2 * FIR_WIDTH - 1; j++) {
+ float w = firmem[j];
+ out0 += v * w;
+ offs++;
+ offs &= (VOLCNT_BUFFER_SIZE - 1);
+ v = cdp->volcntbuf[offs];
+ out1 += v * w;
+ }
+ float out = out0 + frac * (out1 - out0);
+ out *= 8192;
+
+ cdp->data.last_sample = cdp->data.current_sample;
+ cdp->data.current_sample = out;
+ }
+ (*sample_handler) ();
+}
+
void update_audio (void)
{
unsigned long int n_cycles = 0;
/* next_sample_evtime >= 0 so floor() behaves as expected */
rounded = floorf (next_sample_evtime);
+ float nevtime = next_sample_evtime;
if ((next_sample_evtime - rounded) >= 0.5)
rounded++;
n_cycles -= best_evtime;
if (currprefs.produce_sound > 1) {
- /* Test if new sample needs to be outputted */
- if (rounded == best_evtime) {
- /* Before the following addition, next_sample_evtime is in range [-0.5, 0.5) */
- next_sample_evtime += scaled_sample_evtime;
+ if (currprefs.sound_volcnt) {
+ bool nextsmp = false;
+ if (rounded == best_evtime) {
+ next_sample_evtime += scaled_sample_evtime;
+ nextsmp = true;
+ }
+ update_audio_volcnt(best_evtime, nevtime, nextsmp);
+ } else {
+ /* Test if new sample needs to be outputted */
+ if (rounded == best_evtime) {
+ /* Before the following addition, next_sample_evtime is in range [-0.5, 0.5) */
+ next_sample_evtime += scaled_sample_evtime;
#if SOUNDSTUFF > 1
- next_sample_evtime -= extrasamples * 15;
- doublesample = 0;
- if (--samplecounter <= 0) {
- samplecounter = currprefs.sound_freq / 1000;
- if (extrasamples > 0) {
- outputsample = 1;
- doublesample = 1;
- extrasamples--;
- } else if (extrasamples < 0) {
- outputsample = 0;
- doublesample = 0;
- extrasamples++;
+ next_sample_evtime -= extrasamples * 15;
+ doublesample = 0;
+ if (--samplecounter <= 0) {
+ samplecounter = currprefs.sound_freq / 1000;
+ if (extrasamples > 0) {
+ outputsample = 1;
+ doublesample = 1;
+ extrasamples--;
+ } else if (extrasamples < 0) {
+ outputsample = 0;
+ doublesample = 0;
+ extrasamples++;
+ }
}
- }
#endif
- (*sample_handler) ();
+ (*sample_handler) ();
#if SOUNDSTUFF > 1
- if (outputsample == 0)
- outputsample = -1;
- else if (outputsample < 0)
- outputsample = 1;
+ if (outputsample == 0)
+ outputsample = -1;
+ else if (outputsample < 0)
+ outputsample = 1;
#endif
+ }
}
}
audio_deactivate ();
}
update_audio ();
+ previous_volcnt_update = 0;
}
void AUDxDAT (int nr, uae_u16 v, uaecptr addr)
zerostate (nr);
acd->state = restore_u8 ();
- acd->data.vol = restore_u8 ();
+ acd->data.audvol = restore_u8 ();
acd->intreq2 = restore_u8 () ? true : false;
uae_u8 flags = restore_u8 ();
acd->dr = acd->dsr = false;
else
acd->drhpos = 1;
acd->dmaenstore = (dmacon & DMA_MASTER) && (dmacon & (1 << nr));
+ if (currprefs.sound_volcnt)
+ acd->data.mixvol = 1;
+ else
+ acd->data.mixvol = acd->data.audvol;
return src;
}
else
dstbak = dst = xmalloc (uae_u8, 100);
save_u8 (acd->state);
- save_u8 (acd->data.vol);
+ save_u8 (acd->data.audvol);
save_u8 (acd->intreq2);
save_u8 ((acd->dr ? 1 : 0) | (acd->dsr ? 2 : 0) | 0x80);
save_u16 (acd->len);
for (int i = 0; i < ch; i++) {
struct audio_channel_data2 *acd = &asd->data[i];
acd->adk_mask = 0xffffffff;
- acd->vol = 1;
+ acd->mixvol = 1;
}
audio_activate();
}