/* * UAE - The Un*x Amiga Emulator * * Paula audio emulation * * Copyright 1995, 1996, 1997 Bernd Schmidt * Copyright 1996 Marcus Sundberg * Copyright 1996 Manfred Thole * Copyright 2006 Toni Wilen * * new filter algorithm and anti&sinc interpolators by Antti S. Lankila * */ #include "sysconfig.h" #include "sysdeps.h" #include "options.h" #include "memory.h" #include "custom.h" #include "newcpu.h" #include "autoconf.h" #include "gensound.h" #include "audio.h" #include "sounddep/sound.h" #include "events.h" #include "savestate.h" #ifdef DRIVESOUND #include "driveclick.h" #endif #include "zfile.h" #include "uae.h" #include "gui.h" #include "xwin.h" #include "debug.h" #include "sndboard.h" #ifdef AVIOUTPUT #include "avioutput.h" #endif #ifdef AHI #include "traps.h" #include "ahidsound.h" #include "ahidsound_new.h" #endif #include "threaddep/thread.h" #include #define DEBUG_AUDIO 0 #define DEBUG_AUDIO2 0 #define DEBUG_AUDIO_HACK 0 #define DEBUG_CHANNEL_MASK 15 #define TEST_AUDIO 0 #define TEST_MISSED_DMA 0 #define TEST_MANUAL_AUDIO 0 #define PERIOD_MIN 1 #define PERIOD_MIN_NONCE 60 #define PERIOD_MIN_LOOP 16 #define PERIOD_LOW 124 int audio_channel_mask = 15; volatile bool cd_audio_mode_changed; STATIC_INLINE bool isaudio (void) { return currprefs.produce_sound != 0; } #if DEBUG_AUDIO > 0 || DEBUG_AUDIO_HACK > 0 || DEBUG_AUDIO2 > 0 static bool debugchannel (int ch) { return ((1 << ch) & DEBUG_CHANNEL_MASK) != 0; } #endif STATIC_INLINE bool usehacks(void) { return !(currprefs.cs_hacks & 8) && (currprefs.cpu_model >= 68020 || currprefs.m68k_speed != 0 || (currprefs.cs_hacks & 4)); } #define SINC_QUEUE_MAX_AGE 2048 /* Queue length 256 implies minimum emulated period of 8. This should be * sufficient for all imaginable purposes. This must be power of two. */ #define SINC_QUEUE_LENGTH 256 #include "sinctable.cpp" typedef struct { int time, output; } sinc_queue_t; 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 audvol; int mixvol; unsigned int adk_mask; }; struct audio_stream_data { bool active; unsigned int evtime; struct audio_channel_data2 data[AUDIO_CHANNEL_MAX_STREAM_CH]; SOUND_STREAM_CALLBACK cb; 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 { uae_u32 evtime; bool dmaenstore; bool intreq2; int irqcheck; bool dr; bool dsr; bool pbufldl; int drhpos; bool dat_written; #if TEST_MISSED_DMA bool dat_loaded; #endif #if TEST_MANUAL_AUDIO bool mdat_loaded; #endif uaecptr lc, pt; int state; int per; int len, wlen; int volcnt; uae_u16 dat, dat2; struct audio_channel_data2 data; #if TEST_AUDIO > 0 bool hisample, losample; bool have_dat; int per_original; #endif /* too fast cpu fixes */ uaecptr ptx; bool ptx_written; bool ptx_tofetch; int dmaofftime_active; int dmaofftime_cpu_cnt; uaecptr dmaofftime_pc; int volcntbufcnt; float volcntbuf[VOLCNT_BUFFER_SIZE]; }; static int audio_extra_streams[AUDIO_CHANNEL_STREAMS]; static int audio_total_extra_streams; static int samplecnt; #if SOUNDSTUFF > 0 static int extrasamples, outputsample, doublesample; #endif int sampleripper_enabled; struct ripped_sample { struct ripped_sample *next; uae_u8 *sample; int len, per, changed; }; static struct ripped_sample *ripped_samples; void write_wavheader (struct zfile *wavfile, size_t size, uae_u32 freq) { uae_u16 tw; size_t tl; int bits = 8, channels = 1; zfile_fseek (wavfile, 0, SEEK_SET); zfile_fwrite ("RIFF", 1, 4, wavfile); tl = 0; if (size) tl = size - 8; zfile_fwrite (&tl, 1, 4, wavfile); zfile_fwrite ("WAVEfmt ", 1, 8, wavfile); tl = 16; zfile_fwrite (&tl, 1, 4, wavfile); tw = 1; zfile_fwrite (&tw, 1, 2, wavfile); tw = channels; zfile_fwrite (&tw, 1, 2, wavfile); tl = freq; zfile_fwrite (&tl, 1, 4, wavfile); tl = freq * channels * bits / 8; zfile_fwrite (&tl, 1, 4, wavfile); tw = channels * bits / 8; zfile_fwrite (&tw, 1, 2, wavfile); tw = bits; zfile_fwrite (&tw, 1, 2, wavfile); zfile_fwrite ("data", 1, 4, wavfile); tl = 0; if (size) tl = size - 44; zfile_fwrite (&tl, 1, 4, wavfile); } static void convertsample (uae_u8 *sample, int len) { int i; for (i = 0; i < len; i++) sample[i] += 0x80; } static void namesplit (TCHAR *s) { int l; l = uaetcslen(s) - 1; while (l >= 0) { if (s[l] == '.') s[l] = 0; if (s[l] == '\\' || s[l] == '/' || s[l] == ':' || s[l] == '?') { l++; break; } l--; } if (l > 0) memmove (s, s + l, (_tcslen (s + l) + 1) * sizeof (TCHAR)); } void audio_sampleripper (int mode) { struct ripped_sample *rs = ripped_samples; int cnt = 1; TCHAR path[MAX_DPATH], name[MAX_DPATH], filename[MAX_DPATH]; TCHAR underline[] = _T("_"); TCHAR extension[4]; struct zfile *wavfile; if (mode < 0) { while (rs) { struct ripped_sample *next = rs->next; xfree(rs); rs = next; } ripped_samples = NULL; return; } while (rs) { if (rs->changed) { int type = -1; rs->changed = 0; fetch_ripperpath (path, sizeof (path) / sizeof (TCHAR)); name[0] = 0; if (currprefs.floppyslots[0].dfxtype >= 0) { _tcscpy(name, currprefs.floppyslots[0].df); type = PATH_FLOPPY; } else if (currprefs.cdslots[0].inuse) { _tcscpy(name, currprefs.cdslots[0].name); type = PATH_CD; } if (!name[0]) underline[0] = 0; if (type >= 0) cfgfile_resolve_path_load(name, sizeof(name) / sizeof(TCHAR), type); namesplit (name); _tcscpy (extension, _T("wav")); _stprintf (filename, _T("%s%s%s%03d.%s"), path, name, underline, cnt, extension); wavfile = zfile_fopen (filename, _T("wb"), 0); if (wavfile) { int freq = rs->per > 0 ? (currprefs.ntscmode ? 3579545 : 3546895 / rs->per) : 8000; write_wavheader (wavfile, 0, 0); convertsample (rs->sample, rs->len); zfile_fwrite (rs->sample, rs->len, 1, wavfile); convertsample (rs->sample, rs->len); write_wavheader (wavfile, zfile_ftell32(wavfile), freq); zfile_fclose (wavfile); write_log (_T("SAMPLERIPPER: %d: %dHz %d bytes\n"), cnt, freq, rs->len); } else { write_log (_T("SAMPLERIPPER: failed to open '%s'\n"), filename); } } cnt++; rs = rs->next; } } static void do_samplerip (struct audio_channel_data *adp) { struct ripped_sample *rs = ripped_samples, *prev; int len = adp->wlen * 2; uae_u8 *smp = chipmem_xlate_indirect (adp->pt); int cnt = 0, i; if (!smp || !chipmem_check_indirect (adp->pt, len)) return; for (i = 0; i < len; i++) { if (smp[i] != 0) break; } if (i == len || len <= 2) return; prev = NULL; while(rs) { if (rs->sample) { if (len == rs->len && !memcmp (rs->sample, smp, len)) break; /* replace old identical but shorter sample */ if (len > rs->len && !memcmp (rs->sample, smp, rs->len)) { xfree (rs->sample); rs->sample = xmalloc (uae_u8, len); memcpy (rs->sample, smp, len); write_log (_T("SAMPLERIPPER: replaced sample %d (%d -> %d)\n"), cnt, rs->len, len); rs->len = len; rs->per = adp->per / CYCLE_UNIT; rs->changed = 1; audio_sampleripper (0); return; } } prev = rs; rs = rs->next; cnt++; } if (rs || cnt > 100) return; rs = xmalloc (struct ripped_sample ,1); if (prev) prev->next = rs; else ripped_samples = rs; rs->len = len; rs->per = adp->per / CYCLE_UNIT; rs->sample = xmalloc (uae_u8, len); memcpy (rs->sample, smp, len); rs->next = NULL; rs->changed = 1; write_log (_T("SAMPLERIPPER: sample added (%06X, %d bytes), total %d samples\n"), adp->pt, len, ++cnt); audio_sampleripper (0); } static struct audio_channel_data audio_channel[AUDIO_CHANNELS_PAULA]; static struct audio_stream_data audio_stream[AUDIO_CHANNEL_STREAMS]; static struct audio_channel_data2 *audio_data[AUDIO_CHANNELS_PAULA + AUDIO_CHANNEL_STREAMS * AUDIO_CHANNEL_MAX_STREAM_CH]; int sound_available = 0; void (*sample_handler) (void); static void(*sample_prehandler) (unsigned long best_evtime); static void(*extra_sample_prehandler) (unsigned long best_evtime); float sample_evtime; float scaled_sample_evtime; int sound_cd_volume[2]; int sound_paula_volume[2]; static evt_t 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.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) { if (bits < 16) { int shift = 16 - bits; data <<= shift; } else { int shift = bits - 16; data >>= shift; } data = data * sound_paula_volume[ch] / 32768; return data; } static uae_u32 right_word_saved[SOUND_MAX_DELAY_BUFFER]; static uae_u32 left_word_saved[SOUND_MAX_DELAY_BUFFER]; static uae_u32 right2_word_saved[SOUND_MAX_DELAY_BUFFER]; static uae_u32 left2_word_saved[SOUND_MAX_DELAY_BUFFER]; static int saved_ptr, saved_ptr2; 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. */ #define DENORMAL_OFFSET (1E-10) static struct filter_state { float rc1, rc2, rc3, rc4, rc5; } sound_filter_state[AUDIO_CHANNELS_PAULA]; static float a500e_filter1_a0; static float a500e_filter2_a0; static float filter_a0; /* a500 and a1200 use the same */ enum { FILTER_NONE = 0, FILTER_MODEL_A500, FILTER_MODEL_A1200 }; /* Amiga has two separate filtering circuits per channel, a static RC filter * on A500 and the LED filter. This code emulates both. * * The Amiga filtering circuitry depends on Amiga model. Older Amigas seem * to have a 6 dB/oct RC filter with cutoff frequency such that the -6 dB * point for filter is reached at 6 kHz, while newer Amigas have no filtering. * * The LED filter is complicated, and we are modelling it with a pair of * RC filters, the other providing a highboost. The LED starts to cut * into signal somewhere around 5-6 kHz, and there's some kind of highboost * in effect above 12 kHz. Better measurements are required. * * The current filtering should be accurate to 2 dB with the filter on, * and to 1 dB with the filter off. */ static int filter (int input, struct filter_state *fs) { int o; float normal_output, led_output; input = (uae_s16)input; switch (sound_use_filter) { case FILTER_MODEL_A500: fs->rc1 = (float)(a500e_filter1_a0 * input + (1.0f - a500e_filter1_a0) * fs->rc1 + DENORMAL_OFFSET); fs->rc2 = a500e_filter2_a0 * fs->rc1 + (1.0f - a500e_filter2_a0) * fs->rc2; normal_output = fs->rc2; fs->rc3 = filter_a0 * normal_output + (1 - filter_a0) * fs->rc3; fs->rc4 = filter_a0 * fs->rc3 + (1 - filter_a0) * fs->rc4; fs->rc5 = filter_a0 * fs->rc4 + (1 - filter_a0) * fs->rc5; led_output = fs->rc5; break; case FILTER_MODEL_A1200: normal_output = (float)input; fs->rc2 = (float)(filter_a0 * normal_output + (1 - filter_a0) * fs->rc2 + DENORMAL_OFFSET); fs->rc3 = filter_a0 * fs->rc2 + (1 - filter_a0) * fs->rc3; fs->rc4 = filter_a0 * fs->rc3 + (1 - filter_a0) * fs->rc4; led_output = fs->rc4; break; case FILTER_NONE: default: return input; } if (led_filter_on) o = (int)led_output; else o = (int)normal_output; if (o > 32767) o = 32767; else if (o < -32768) o = -32768; return o; } /* Always put the right word before the left word. */ static void put_sound_word_right (uae_u32 w) { if (mixed_on) { right_word_saved[saved_ptr] = w; return; } PUT_SOUND_WORD(w); } static void put_sound_word_left (uae_u32 w) { if (mixed_on) { uae_u32 rold, lold, rnew, lnew, tmp; left_word_saved[saved_ptr] = w; lnew = w - SOUND16_BASE_VAL; rnew = right_word_saved[saved_ptr] - SOUND16_BASE_VAL; saved_ptr = (saved_ptr + 1) & mixed_stereo_size; lold = left_word_saved[saved_ptr] - SOUND16_BASE_VAL; tmp = (rnew * mixed_mul2 + lold * mixed_mul1) / MIXED_STEREO_SCALE; tmp += SOUND16_BASE_VAL; rold = right_word_saved[saved_ptr] - SOUND16_BASE_VAL; w = (lnew * mixed_mul2 + rold * mixed_mul1) / MIXED_STEREO_SCALE; PUT_SOUND_WORD(tmp); PUT_SOUND_WORD(w); } else { PUT_SOUND_WORD(w); } } static void put_sound_word_right2 (uae_u32 w) { if (mixed_on) { right2_word_saved[saved_ptr2] = w; return; } PUT_SOUND_WORD(w); } static void put_sound_word_left2 (uae_u32 w) { if (mixed_on) { uae_u32 rold, lold, rnew, lnew, tmp; left2_word_saved[saved_ptr2] = w; lnew = w - SOUND16_BASE_VAL; rnew = right2_word_saved[saved_ptr2] - SOUND16_BASE_VAL; saved_ptr2 = (saved_ptr2 + 1) & mixed_stereo_size; lold = left2_word_saved[saved_ptr2] - SOUND16_BASE_VAL; tmp = (rnew * mixed_mul2 + lold * mixed_mul1) / MIXED_STEREO_SCALE; tmp += SOUND16_BASE_VAL; rold = right2_word_saved[saved_ptr2] - SOUND16_BASE_VAL; w = (lnew * mixed_mul2 + rold * mixed_mul1) / MIXED_STEREO_SCALE; PUT_SOUND_WORD(tmp); PUT_SOUND_WORD(w); } else { PUT_SOUND_WORD(w); } } static void anti_prehandler (unsigned long best_evtime) { int i, output; struct audio_channel_data2 *acd; /* Handle accumulator antialiasiation */ for (i = 0; audio_data[i]; i++) { acd = audio_data[i]; output = (acd->current_sample * acd->mixvol) & acd->adk_mask; acd->sample_accum += output * best_evtime; acd->sample_accum_time += best_evtime; } } static void samplexx_anti_handler (int *datasp, int ch_start, int ch_num) { int i, j; for (i = ch_start, j = 0; j < ch_num; i++, j++) { struct audio_channel_data2 *acd = audio_data[i]; datasp[j] = acd->sample_accum_time ? (acd->sample_accum / acd->sample_accum_time) : 0; acd->sample_accum = 0; acd->sample_accum_time = 0; } } static void sinc_prehandler_paula (unsigned long best_evtime) { int i, output; struct audio_channel_data2 *acd; for (i = 0; i < AUDIO_CHANNELS_PAULA; i++) { acd = audio_data[i]; int vol = acd->mixvol; output = (acd->current_sample * vol) & acd->adk_mask; /* if output state changes, record the state change and also * write data into sinc queue for mixing in the BLEP */ if (acd->sinc_output_state != output) { acd->sinc_queue_head = (acd->sinc_queue_head - 1) & (SINC_QUEUE_LENGTH - 1); acd->sinc_queue[acd->sinc_queue_head].time = acd->sinc_queue_time; acd->sinc_queue[acd->sinc_queue_head].output = output - acd->sinc_output_state; acd->sinc_output_state = output; } acd->sinc_queue_time += best_evtime; } } /* this interpolator performs BLEP mixing (bleps are shaped like integrated sinc * functions) with a type of BLEP that matches the filtering configuration. */ static void samplexx_sinc_handler (int *datasp, int ch_start, int ch_num) { int n, i, k; int const *winsinc; if (sound_use_filter_sinc && ch_start == 0) { n = (sound_use_filter_sinc == FILTER_MODEL_A500) ? 0 : 2; if (led_filter_on) n += 1; } else { n = 4; } winsinc = winsinc_integral[n]; for (i = ch_start, k = 0; k < ch_num; i++, k++) { int j, v; struct audio_channel_data2 *acd = audio_data[i]; /* The sum rings with harmonic components up to infinity... */ int sum = acd->sinc_output_state << 17; /* ...but we cancel them through mixing in BLEPs instead */ int offsetpos = acd->sinc_queue_head & (SINC_QUEUE_LENGTH - 1); for (j = 0; j < SINC_QUEUE_LENGTH; j += 1) { int age = acd->sinc_queue_time - acd->sinc_queue[offsetpos].time; if (age >= SINC_QUEUE_MAX_AGE || age < 0) break; sum -= winsinc[age] * acd->sinc_queue[offsetpos].output; offsetpos = (offsetpos + 1) & (SINC_QUEUE_LENGTH - 1); } v = sum >> 15; if (v > 32767) v = 32767; else if (v < -32768) v = -32768; datasp[k] = v; } } static void do_filter(int *data, int num) { if (currprefs.sound_filter) *data = filter(*data, &sound_filter_state[num]); } static void get_extra_channels(int *data1, int *data2, int sample1, int sample2) { int d1 = *data1 + sample1; int d2 = (data2 ? *data2 : 0) + sample2; if (d1 < -32768) d1 = -32768; if (d1 > 32767) d1 = 32767; if (d2 < -32768) d2 = -32768; if (d2 > 32767) d2 = 32767; int needswap = currprefs.sound_stereo_swap_paula ^ currprefs.sound_stereo_swap_ahi; if (needswap) { *data1 = d2; if (data2) *data2 = d1; } else { *data1 = d1; if (data2) *data2 = d2; } } static void do_extra_channels(int idx, int ch, int *data1, int *data2, int *data3, int *data4, int *data5, int *data6) { idx += AUDIO_CHANNELS_PAULA; if (ch == 2) { int datas[2]; samplexx_anti_handler(datas, idx, 2); get_extra_channels(data1, data2, datas[0], datas[1]); } else if (ch == 1) { int datas[1]; samplexx_anti_handler(datas, idx, 1); int d1 = *data1 + datas[0]; if (d1 < -32768) d1 = -32768; if (d1 > 32767) d1 = 32767; *data1 = d1; if (data2) *data2 = d1; } else if (ch > 2) { int datas[AUDIO_CHANNEL_MAX_STREAM_CH]; samplexx_anti_handler(datas, idx, 6); get_extra_channels(data1, data2, datas[0], datas[1]); if (data3 && data4) get_extra_channels(data3, data4, datas[2], datas[3]); if (data5 && data6) get_extra_channels(data5, data6, datas[4], datas[5]); } } static void get_extra_channels_sample2(int *data1, int *data2, int mode) { if (!audio_total_extra_streams) return; int idx = 0; for (int i = 0; i < AUDIO_CHANNEL_STREAMS; i++) { int ch = audio_extra_streams[i]; if (ch) { do_extra_channels(idx, ch, data1, data2, NULL, NULL, NULL, NULL); idx += ch; } } } static void get_extra_channels_sample6(int *data1, int *data2, int *data3, int *data4, int *data5, int *data6, int mode) { if (!audio_total_extra_streams) return; int idx = 0; for (int i = 0; i < AUDIO_CHANNEL_STREAMS; i++) { int ch = audio_extra_streams[i]; if (ch) { do_extra_channels(idx, ch, data1, data2, data3, data4, data5, data6); idx += ch; } } } static void set_sound_buffers(void) { #if SOUNDSTUFF > 1 paula_sndbufpt_prev = paula_sndbufpt_start; paula_sndbufpt_start = paula_sndbufpt; #endif } static void clear_sound_buffers(void) { memset(paula_sndbuffer, 0, paula_sndbufsize); paula_sndbufpt = paula_sndbuffer; } static void check_sound_buffers(void) { #if SOUNDSTUFF > 1 int len; #endif if (active_sound_stereo == SND_4CH_CLONEDSTEREO) { ((uae_u16 *)paula_sndbufpt)[0] = ((uae_u16 *)paula_sndbufpt)[-2]; ((uae_u16 *)paula_sndbufpt)[1] = ((uae_u16 *)paula_sndbufpt)[-1]; paula_sndbufpt = (uae_u16 *)(((uae_u8 *)paula_sndbufpt) + 2 * 2); } else if (active_sound_stereo == SND_6CH_CLONEDSTEREO) { uae_s16 *p = ((uae_s16 *)paula_sndbufpt); uae_s32 sum; p[2] = p[-2]; p[3] = p[-1]; sum = (uae_s32)(p[-2]) + (uae_s32)(p[-1]) + (uae_s32)(p[2]) + (uae_s32)(p[3]); p[0] = sum / 8; p[1] = sum / 8; paula_sndbufpt = (uae_u16 *)(((uae_u8 *)paula_sndbufpt) + 4 * 2); } else if (active_sound_stereo == SND_8CH_CLONEDSTEREO) { uae_s16 *p = ((uae_s16 *)paula_sndbufpt); uae_s32 sum; p[2] = p[-2]; p[3] = p[-1]; p[4] = p[-2]; p[5] = p[-1]; sum = (uae_s32)(p[-2]) + (uae_s32)(p[-1]) + (uae_s32)(p[2]) + (uae_s32)(p[3]); p[0] = sum / 8; p[1] = sum / 8; paula_sndbufpt = (uae_u16 *)(((uae_u8 *)paula_sndbufpt) + 6 * 2); } #if SOUNDSTUFF > 1 if (outputsample == 0) return; len = paula_sndbufpt - paula_sndbufpt_start; if (outputsample < 0) { int i; uae_s16 *p1 = (uae_s16 *)paula_sndbufpt_prev; uae_s16 *p2 = (uae_s16 *)paula_sndbufpt_start; for (i = 0; i < len; i++) { *p1 = (*p1 + *p2) / 2; } paula_sndbufpt = paula_sndbufpt_start; } #endif if ((uae_u8 *)paula_sndbufpt - (uae_u8 *)paula_sndbuffer >= paula_sndbufsize) { finish_sound_buffer(); } #if SOUNDSTUFF > 1 while (doublesample-- > 0) { memcpy(paula_sndbufpt, paula_sndbufpt_start, len * 2); paula_sndbufpt += len; if ((uae_u8 *)paula_sndbufpt - (uae_u8 *)paula_sndbuffer >= paula_sndbufsize) { finish_sound_buffer(); paula_sndbufpt = paula_sndbuffer; } } #endif } static void sample16i_sinc_handler (void) { int datas[AUDIO_CHANNELS_PAULA], data1; samplexx_sinc_handler (datas, 0, AUDIO_CHANNELS_PAULA); data1 = datas[0] + datas[3] + datas[1] + datas[2]; data1 = FINISH_DATA (data1, 18, 0); do_filter(&data1, 0); get_extra_channels_sample2(&data1, NULL, 2); set_sound_buffers (); PUT_SOUND_WORD_MONO (data1); check_sound_buffers (); } void sample16_handler (void) { int data0 = audio_channel[0].data.current_sample; int data1 = audio_channel[1].data.current_sample; int data2 = audio_channel[2].data.current_sample; int data3 = audio_channel[3].data.current_sample; int data; DO_CHANNEL_1 (data0, 0); DO_CHANNEL_1 (data1, 1); DO_CHANNEL_1 (data2, 2); DO_CHANNEL_1 (data3, 3); data0 &= audio_channel[0].data.adk_mask; data1 &= audio_channel[1].data.adk_mask; data2 &= audio_channel[2].data.adk_mask; data3 &= audio_channel[3].data.adk_mask; data0 += data1; data0 += data2; data0 += data3; data = SBASEVAL16(2) + data0; data = FINISH_DATA (data, 16, 0); do_filter(&data, 0); get_extra_channels_sample2(&data, NULL, 0); set_sound_buffers (); PUT_SOUND_WORD_MONO (data); check_sound_buffers (); } /* This interpolator examines sample points when Paula switches the output * voltage and computes the average of Paula's output */ static void sample16i_anti_handler (void) { int datas[AUDIO_CHANNELS_PAULA], data1; samplexx_anti_handler (datas, 0, AUDIO_CHANNELS_PAULA); data1 = datas[0] + datas[3] + datas[1] + datas[2]; data1 = FINISH_DATA (data1, 16, 0); do_filter(&data1, 0); get_extra_channels_sample2(&data1, NULL, 1); set_sound_buffers (); PUT_SOUND_WORD_MONO (data1); check_sound_buffers (); } static void sample16i_rh_handler (void) { unsigned long delta, ratio; int data0 = audio_channel[0].data.current_sample; int data1 = audio_channel[1].data.current_sample; int data2 = audio_channel[2].data.current_sample; int data3 = audio_channel[3].data.current_sample; int data0p = audio_channel[0].data.last_sample; int data1p = audio_channel[1].data.last_sample; int data2p = audio_channel[2].data.last_sample; int data3p = audio_channel[3].data.last_sample; int data; DO_CHANNEL_1 (data0, 0); DO_CHANNEL_1 (data1, 1); DO_CHANNEL_1 (data2, 2); DO_CHANNEL_1 (data3, 3); DO_CHANNEL_1 (data0p, 0); DO_CHANNEL_1 (data1p, 1); DO_CHANNEL_1 (data2p, 2); DO_CHANNEL_1 (data3p, 3); data0 &= audio_channel[0].data.adk_mask; data0p &= audio_channel[0].data.adk_mask; data1 &= audio_channel[1].data.adk_mask; data1p &= audio_channel[1].data.adk_mask; data2 &= audio_channel[2].data.adk_mask; data2p &= audio_channel[2].data.adk_mask; data3 &= audio_channel[3].data.adk_mask; data3p &= audio_channel[3].data.adk_mask; /* linear interpolation and summing up... */ delta = audio_channel[0].per; ratio = ((audio_channel[0].evtime % delta) << 8) / delta; data0 = (data0 * (256 - ratio) + data0p * ratio) >> 8; delta = audio_channel[1].per; ratio = ((audio_channel[1].evtime % delta) << 8) / delta; data0 += (data1 * (256 - ratio) + data1p * ratio) >> 8; delta = audio_channel[2].per; ratio = ((audio_channel[2].evtime % delta) << 8) / delta; data0 += (data2 * (256 - ratio) + data2p * ratio) >> 8; delta = audio_channel[3].per; ratio = ((audio_channel[3].evtime % delta) << 8) / delta; data0 += (data3 * (256 - ratio) + data3p * ratio) >> 8; data = SBASEVAL16(2) + data0; data = FINISH_DATA (data, 16, 0); do_filter(&data, 0); get_extra_channels_sample2(&data, NULL, 0); set_sound_buffers (); PUT_SOUND_WORD_MONO (data); check_sound_buffers (); } static void sample16i_crux_handler (void) { int data0 = audio_channel[0].data.current_sample; int data1 = audio_channel[1].data.current_sample; int data2 = audio_channel[2].data.current_sample; int data3 = audio_channel[3].data.current_sample; int data0p = audio_channel[0].data.last_sample; int data1p = audio_channel[1].data.last_sample; int data2p = audio_channel[2].data.last_sample; int data3p = audio_channel[3].data.last_sample; int data; DO_CHANNEL_1 (data0, 0); DO_CHANNEL_1 (data1, 1); DO_CHANNEL_1 (data2, 2); DO_CHANNEL_1 (data3, 3); DO_CHANNEL_1 (data0p, 0); DO_CHANNEL_1 (data1p, 1); DO_CHANNEL_1 (data2p, 2); DO_CHANNEL_1 (data3p, 3); data0 &= audio_channel[0].data.adk_mask; data0p &= audio_channel[0].data.adk_mask; data1 &= audio_channel[1].data.adk_mask; data1p &= audio_channel[1].data.adk_mask; data2 &= audio_channel[2].data.adk_mask; data2p &= audio_channel[2].data.adk_mask; data3 &= audio_channel[3].data.adk_mask; data3p &= audio_channel[3].data.adk_mask; { struct audio_channel_data *cdp; unsigned long ratio, ratio1; #define INTERVAL ((int)(scaled_sample_evtime * 3)) cdp = audio_channel + 0; ratio1 = cdp->per - cdp->evtime; ratio = (ratio1 << 12) / INTERVAL; if (cdp->evtime < scaled_sample_evtime || ratio1 >= INTERVAL) ratio = 4096; data0 = (data0 * ratio + data0p * (4096 - ratio)) >> 12; cdp = audio_channel + 1; ratio1 = cdp->per - cdp->evtime; ratio = (ratio1 << 12) / INTERVAL; if (cdp->evtime < scaled_sample_evtime || ratio1 >= INTERVAL) ratio = 4096; data1 = (data1 * ratio + data1p * (4096 - ratio)) >> 12; cdp = audio_channel + 2; ratio1 = cdp->per - cdp->evtime; ratio = (ratio1 << 12) / INTERVAL; if (cdp->evtime < scaled_sample_evtime || ratio1 >= INTERVAL) ratio = 4096; data2 = (data2 * ratio + data2p * (4096 - ratio)) >> 12; cdp = audio_channel + 3; ratio1 = cdp->per - cdp->evtime; ratio = (ratio1 << 12) / INTERVAL; if (cdp->evtime < scaled_sample_evtime || ratio1 >= INTERVAL) ratio = 4096; data3 = (data3 * ratio + data3p * (4096 - ratio)) >> 12; } data1 += data2; data0 += data3; data0 += data1; data = SBASEVAL16(2) + data0; data = FINISH_DATA (data, 16, 0); do_filter(&data, 0); get_extra_channels_sample2(&data, NULL, 0); set_sound_buffers (); PUT_SOUND_WORD_MONO (data); check_sound_buffers (); } #ifdef HAVE_STEREO_SUPPORT STATIC_INLINE void make6ch (uae_s32 d0, uae_s32 d1, uae_s32 d2, uae_s32 d3, uae_s32 *d4, uae_s32 *d5) { uae_s32 sum = d0 + d1 + d2 + d3; sum /= 8; *d4 = sum; *d5 = sum; } void sample16ss_handler (void) { int data0 = audio_channel[0].data.current_sample; int data1 = audio_channel[1].data.current_sample; int data2 = audio_channel[2].data.current_sample; int data3 = audio_channel[3].data.current_sample; int data4, data5; DO_CHANNEL_1 (data0, 0); DO_CHANNEL_1 (data1, 1); DO_CHANNEL_1 (data2, 2); DO_CHANNEL_1 (data3, 3); data0 &= audio_channel[0].data.adk_mask; data1 &= audio_channel[1].data.adk_mask; data2 &= audio_channel[2].data.adk_mask; data3 &= audio_channel[3].data.adk_mask; data0 = FINISH_DATA (data0, 14, 0); data1 = FINISH_DATA (data1, 14, 0); data2 = FINISH_DATA (data2, 14, 1); data3 = FINISH_DATA (data3, 14, 1); do_filter(&data0, 0); do_filter(&data1, 1); do_filter(&data2, 3); do_filter(&data3, 2); if (active_sound_stereo >= SND_6CH) make6ch(data0, data1, data2, data3, &data4, &data5); get_extra_channels_sample6(&data0, &data1, &data3, &data2, &data4, &data5, 0); set_sound_buffers (); put_sound_word_right(data0); put_sound_word_left(data1); if (active_sound_stereo >= SND_6CH) { PUT_SOUND_WORD(data4); PUT_SOUND_WORD(data5); } if (active_sound_stereo >= SND_8CH) { PUT_SOUND_WORD(data4); PUT_SOUND_WORD(data5); } put_sound_word_right2(data3); put_sound_word_left2(data2); check_sound_buffers (); } /* This interpolator examines sample points when Paula switches the output * voltage and computes the average of Paula's output */ static void sample16ss_anti_handler (void) { int data0, data1, data2, data3, data4, data5; int datas[AUDIO_CHANNELS_PAULA]; samplexx_anti_handler (datas, 0, AUDIO_CHANNELS_PAULA); data0 = FINISH_DATA (datas[0], 14, 0); data1 = FINISH_DATA (datas[1], 14, 0); data2 = FINISH_DATA (datas[2], 14, 1); data3 = FINISH_DATA (datas[3], 14, 1); do_filter(&data0, 0); do_filter(&data1, 1); do_filter(&data2, 3); do_filter(&data3, 2); if (active_sound_stereo >= SND_6CH) make6ch(data0, data1, data2, data3, &data4, &data5); get_extra_channels_sample6(&data0, &data1, &data3, &data2, &data4, &data5, 0); set_sound_buffers(); put_sound_word_right(data0); put_sound_word_left(data1); if (active_sound_stereo >= SND_6CH) { PUT_SOUND_WORD(data4); PUT_SOUND_WORD(data5); } if (active_sound_stereo >= SND_8CH) { PUT_SOUND_WORD(data4); PUT_SOUND_WORD(data5); } put_sound_word_right2(data3); put_sound_word_left2(data2); check_sound_buffers(); } static void sample16si_anti_handler(void) { int datas[AUDIO_CHANNELS_PAULA], data1, data2; samplexx_anti_handler(datas, 0, AUDIO_CHANNELS_PAULA); data1 = datas[0] + datas[3]; data2 = datas[1] + datas[2]; data1 = FINISH_DATA(data1, 15, 0); data2 = FINISH_DATA(data2, 15, 1); do_filter(&data1, 0); do_filter(&data2, 1); get_extra_channels_sample2(&data1, &data2, 1); set_sound_buffers(); put_sound_word_right(data1); put_sound_word_left(data2); check_sound_buffers(); } static void sample16ss_sinc_handler(void) { int data0, data1, data2, data3, data4, data5; int datas[AUDIO_CHANNELS_PAULA]; samplexx_sinc_handler(datas, 0, AUDIO_CHANNELS_PAULA); data0 = FINISH_DATA (datas[0], 16, 0); data1 = FINISH_DATA (datas[1], 16, 0); data2 = FINISH_DATA (datas[2], 16, 1); data3 = FINISH_DATA (datas[3], 16, 1); do_filter(&data0, 0); do_filter(&data1, 1); do_filter(&data2, 3); do_filter(&data3, 2); if (active_sound_stereo >= SND_6CH) make6ch(data0, data1, data2, data3, &data4, &data5); get_extra_channels_sample6(&data0, &data1, &data3, &data2, &data4, &data5, 0); set_sound_buffers (); put_sound_word_right(data0); put_sound_word_left (data1); if (active_sound_stereo >= SND_6CH) { PUT_SOUND_WORD(data4); PUT_SOUND_WORD(data5); } if (active_sound_stereo >= SND_8CH) { PUT_SOUND_WORD(data4); PUT_SOUND_WORD(data5); } put_sound_word_right2(data3); put_sound_word_left2 (data2); check_sound_buffers (); } static void sample16si_sinc_handler (void) { int datas[AUDIO_CHANNELS_PAULA], data1, data2; samplexx_sinc_handler (datas, 0, AUDIO_CHANNELS_PAULA); data1 = datas[0] + datas[3]; data2 = datas[1] + datas[2]; data1 = FINISH_DATA (data1, 17, 0); data2 = FINISH_DATA (data2, 17, 1); do_filter(&data1, 0); do_filter(&data2, 1); get_extra_channels_sample2(&data1, &data2, 2); set_sound_buffers (); put_sound_word_right(data1); put_sound_word_left(data2); check_sound_buffers (); } void sample16s_handler (void) { int data0 = audio_channel[0].data.current_sample; int data1 = audio_channel[1].data.current_sample; int data2 = audio_channel[2].data.current_sample; int data3 = audio_channel[3].data.current_sample; DO_CHANNEL_1 (data0, 0); DO_CHANNEL_1 (data1, 1); DO_CHANNEL_1 (data2, 2); DO_CHANNEL_1 (data3, 3); data0 &= audio_channel[0].data.adk_mask; data1 &= audio_channel[1].data.adk_mask; data2 &= audio_channel[2].data.adk_mask; data3 &= audio_channel[3].data.adk_mask; data0 += data3; data1 += data2; data2 = SBASEVAL16(1) + data0; data2 = FINISH_DATA (data2, 15, 0); data3 = SBASEVAL16(1) + data1; data3 = FINISH_DATA (data3, 15, 1); do_filter(&data2, 0); do_filter(&data3, 1); get_extra_channels_sample2(&data2, &data3, 0); set_sound_buffers (); put_sound_word_right(data2); put_sound_word_left(data3); check_sound_buffers (); } static void sample16si_crux_handler (void) { int data0 = audio_channel[0].data.current_sample; int data1 = audio_channel[1].data.current_sample; int data2 = audio_channel[2].data.current_sample; int data3 = audio_channel[3].data.current_sample; int data0p = audio_channel[0].data.last_sample; int data1p = audio_channel[1].data.last_sample; int data2p = audio_channel[2].data.last_sample; int data3p = audio_channel[3].data.last_sample; DO_CHANNEL_1 (data0, 0); DO_CHANNEL_1 (data1, 1); DO_CHANNEL_1 (data2, 2); DO_CHANNEL_1 (data3, 3); DO_CHANNEL_1 (data0p, 0); DO_CHANNEL_1 (data1p, 1); DO_CHANNEL_1 (data2p, 2); DO_CHANNEL_1 (data3p, 3); data0 &= audio_channel[0].data.adk_mask; data0p &= audio_channel[0].data.adk_mask; data1 &= audio_channel[1].data.adk_mask; data1p &= audio_channel[1].data.adk_mask; data2 &= audio_channel[2].data.adk_mask; data2p &= audio_channel[2].data.adk_mask; data3 &= audio_channel[3].data.adk_mask; data3p &= audio_channel[3].data.adk_mask; { struct audio_channel_data *cdp; unsigned long ratio, ratio1; #define INTERVAL ((int)(scaled_sample_evtime * 3)) cdp = audio_channel + 0; ratio1 = cdp->per - cdp->evtime; ratio = (ratio1 << 12) / INTERVAL; if (cdp->evtime < scaled_sample_evtime || ratio1 >= INTERVAL) ratio = 4096; data0 = (data0 * ratio + data0p * (4096 - ratio)) >> 12; cdp = audio_channel + 1; ratio1 = cdp->per - cdp->evtime; ratio = (ratio1 << 12) / INTERVAL; if (cdp->evtime < scaled_sample_evtime || ratio1 >= INTERVAL) ratio = 4096; data1 = (data1 * ratio + data1p * (4096 - ratio)) >> 12; cdp = audio_channel + 2; ratio1 = cdp->per - cdp->evtime; ratio = (ratio1 << 12) / INTERVAL; if (cdp->evtime < scaled_sample_evtime || ratio1 >= INTERVAL) ratio = 4096; data2 = (data2 * ratio + data2p * (4096 - ratio)) >> 12; cdp = audio_channel + 3; ratio1 = cdp->per - cdp->evtime; ratio = (ratio1 << 12) / INTERVAL; if (cdp->evtime < scaled_sample_evtime || ratio1 >= INTERVAL) ratio = 4096; data3 = (data3 * ratio + data3p * (4096 - ratio)) >> 12; } data1 += data2; data0 += data3; data2 = SBASEVAL16(1) + data0; data2 = FINISH_DATA (data2, 15, 0); data3 = SBASEVAL16(1) + data1; data3 = FINISH_DATA (data3, 15, 1); do_filter(&data2, 0); do_filter(&data3, 1); get_extra_channels_sample2(&data2, &data3, 0); set_sound_buffers (); put_sound_word_right(data2); put_sound_word_left (data3); check_sound_buffers (); } static void sample16si_rh_handler (void) { unsigned long delta, ratio; int data0 = audio_channel[0].data.current_sample; int data1 = audio_channel[1].data.current_sample; int data2 = audio_channel[2].data.current_sample; int data3 = audio_channel[3].data.current_sample; int data0p = audio_channel[0].data.last_sample; int data1p = audio_channel[1].data.last_sample; int data2p = audio_channel[2].data.last_sample; int data3p = audio_channel[3].data.last_sample; DO_CHANNEL_1 (data0, 0); DO_CHANNEL_1 (data1, 1); DO_CHANNEL_1 (data2, 2); DO_CHANNEL_1 (data3, 3); DO_CHANNEL_1 (data0p, 0); DO_CHANNEL_1 (data1p, 1); DO_CHANNEL_1 (data2p, 2); DO_CHANNEL_1 (data3p, 3); data0 &= audio_channel[0].data.adk_mask; data0p &= audio_channel[0].data.adk_mask; data1 &= audio_channel[1].data.adk_mask; data1p &= audio_channel[1].data.adk_mask; data2 &= audio_channel[2].data.adk_mask; data2p &= audio_channel[2].data.adk_mask; data3 &= audio_channel[3].data.adk_mask; data3p &= audio_channel[3].data.adk_mask; /* linear interpolation and summing up... */ delta = audio_channel[0].per; ratio = ((audio_channel[0].evtime % delta) << 8) / delta; data0 = (data0 * (256 - ratio) + data0p * ratio) >> 8; delta = audio_channel[1].per; ratio = ((audio_channel[1].evtime % delta) << 8) / delta; data1 = (data1 * (256 - ratio) + data1p * ratio) >> 8; delta = audio_channel[2].per; ratio = ((audio_channel[2].evtime % delta) << 8) / delta; data1 += (data2 * (256 - ratio) + data2p * ratio) >> 8; delta = audio_channel[3].per; ratio = ((audio_channel[3].evtime % delta) << 8) / delta; data0 += (data3 * (256 - ratio) + data3p * ratio) >> 8; data2 = SBASEVAL16(1) + data0; data2 = FINISH_DATA (data2, 15, 0); data3 = SBASEVAL16(1) + data1; data3 = FINISH_DATA (data3, 15, 1); do_filter(&data2, 0); do_filter(&data3, 1); get_extra_channels_sample2(&data2, &data3, 0); set_sound_buffers (); put_sound_word_right(data2); put_sound_word_left (data3); check_sound_buffers (); } #else void sample16s_handler (void) { sample16_handler (); } static void sample16si_crux_handler (void) { sample16i_crux_handler (); } static void sample16si_rh_handler (void) { sample16i_rh_handler (); } #endif static int audio_work_to_do; static void zerostate (int nr) { struct audio_channel_data *cdp = audio_channel + nr; #if DEBUG_AUDIO > 0 if (debugchannel (nr)) write_log (_T("%d: ZEROSTATE\n"), nr); #endif cdp->state = 0; cdp->irqcheck = 0; cdp->evtime = MAX_EV; cdp->intreq2 = false; 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 } static void schedule_audio (void) { unsigned long best = MAX_EV; int i; eventtab[ev_audio].active = 0; eventtab[ev_audio].oldcycles = get_cycles (); for (i = 0; i < AUDIO_CHANNELS_PAULA; i++) { struct audio_channel_data *cdp = audio_channel + i; if (cdp->evtime != MAX_EV) { if (best > cdp->evtime) { best = cdp->evtime; eventtab[ev_audio].active = 1; } } } for (i = 0; i < audio_total_extra_streams; i++) { struct audio_stream_data *cdp = audio_stream + i; if (cdp->evtime != MAX_EV) { if (best > cdp->evtime) { best = cdp->evtime; eventtab[ev_audio].active = 1; } } } eventtab[ev_audio].evtime = get_cycles () + best; } static void audio_event_reset (void) { int i; last_cycles = get_cycles (); next_sample_evtime = scaled_sample_evtime; for (i = 0; i < AUDIO_CHANNELS_PAULA; i++) zerostate (i); for (i = 0; i < audio_total_extra_streams; i++) audio_stream[i].evtime = MAX_EV; schedule_audio (); events_schedule (); samplecnt = 0; extrasamples = 0; outputsample = 1; doublesample = 0; } void audio_deactivate (void) { gui_data.sndbuf_status = 3; gui_data.sndbuf = 0; audio_work_to_do = 0; pause_sound_buffer (); clear_sound_buffers (); audio_event_reset (); } int audio_activate (void) { int ret = 0; if (!audio_work_to_do) { restart_sound_buffer(); ret = 1; if (!isrestore()) { audio_event_reset(); } } audio_work_to_do = 4 * maxvpos_nom * 50; return ret; } STATIC_INLINE int is_audio_active (void) { return audio_work_to_do; } static void update_volume(int nr, uae_u16 v) { struct audio_channel_data *cdp = audio_channel + nr; // 7 bit register in Paula. v &= 127; if (v > 64) v = 64; cdp->data.audvol = v; } uae_u16 audio_dmal(void) { uae_u16 dmal = 0; for (int nr = 0; nr < AUDIO_CHANNELS_PAULA; nr++) { struct audio_channel_data *cdp = audio_channel + nr; if (cdp->dr) dmal |= 1 << (nr * 2 + 1); if (cdp->dsr) dmal |= 1 << (nr * 2 + 0); cdp->dr = cdp->dsr = false; } return dmal; } static int isirq(int nr) { return INTREQR() & (0x80 << nr); } static void setirq(int nr, int which) { #if DEBUG_AUDIO > 0 struct audio_channel_data *cdp = audio_channel + nr; if (debugchannel (nr)) write_log (_T("SETIRQ%d (%d,%d) PC=%08X\n"), nr, which, isirq (nr) ? 1 : 0, M68K_GETPC); #endif // audio interrupts are delayed by 1 CCK INTREQ_INT(nr + 7, CYCLE_UNIT); } static void newsample(int nr, sample8_t sample) { struct audio_channel_data *cdp = audio_channel + nr; #if DEBUG_AUDIO > 0 if (!debugchannel(nr)) sample = 0; #endif #if DEBUG_AUDIO > 2 if (debugchannel(nr)) write_log(_T("SAMPLE%d: %02x\n"), nr, sample & 0xff); #endif if (!(audio_channel_mask & (1 << nr))) sample = 0; if (currprefs.sound_volcnt) { cdp->data.new_sample = sample; } else { cdp->data.last_sample = cdp->data.current_sample; cdp->data.current_sample = sample; } } void event_setdsr(uae_u32 v) { struct audio_channel_data* cdp = audio_channel + v; cdp->dsr = true; } static void setdr(int nr, bool startup) { struct audio_channel_data *cdp = audio_channel + nr; #if TEST_AUDIO > 0 if (debugchannel (nr) && cdp->dr) write_log (_T("%d: DR already active (STATE=%d)\n"), nr, cdp->state); #endif if (dmaen(DMA_MASTER)) { #if DEBUG_AUDIO > 0 if (debugchannel(nr) && cdp->wlen <= 2) write_log(_T("DR%d=%d LEN=%d/%d PT=%08X PC=%08X\n"), nr, cdp->dr, cdp->wlen, cdp->len, cdp->pt, M68K_GETPC); #endif cdp->drhpos = current_hpos(); if (!startup && cdp->wlen == 1) { if (!currprefs.cachesize && (cdp->per < PERIOD_LOW * CYCLE_UNIT || currprefs.cpu_compatible)) { event2_newevent_xx(-1, 1 * CYCLE_UNIT, nr, event_setdsr); } else { event_setdsr(nr); } #if DEBUG_AUDIO > 0 if (debugchannel(nr)) write_log(_T("DSR%d=1 PT=%08X PC=%08X\n"), nr, cdp->pt, M68K_GETPC); #endif } else { cdp->dr = true; } } else { #if DEBUG_AUDIO > 0 if (debugchannel(nr)) write_log(_T("setdr%d ignored, DMA disabled PT=%08X PC=%08X\n"), nr, cdp->pt, M68K_GETPC); #endif } } static void loaddat (int nr, bool modper) { struct audio_channel_data *cdp = audio_channel + nr; int audav = adkcon & (0x01 << nr); int audap = adkcon & (0x10 << nr); if (audav || (modper && audap)) { if (nr >= 3) return; if (modper && audap) { if (cdp->dat == 0) cdp[1].per = 65536 * CYCLE_UNIT; else if (cdp->dat > PERIOD_MIN) cdp[1].per = cdp->dat * CYCLE_UNIT; else cdp[1].per = PERIOD_MIN * CYCLE_UNIT; } else if (audav) { update_volume(nr + 1, cdp->dat); } } else { #if TEST_AUDIO > 0 if (debugchannel (nr)) { if (cdp->hisample || cdp->losample) write_log (_T("%d: high or low sample not used\n"), nr); if (!cdp->have_dat) write_log (_T("%d: dat not updated. STATE=%d 1=%04x 2=%04x\n"), nr, cdp->state, cdp->dat, cdp->dat2); } cdp->hisample = cdp->losample = true; cdp->have_dat = false; #endif #if DEBUG_AUDIO > 2 || DEBUG_AUDIO2 if (debugchannel (nr)) write_log (_T("LOAD%dDAT: New:%04x, Old:%04x\n"), nr, cdp->dat, cdp->dat2); #endif cdp->dat2 = cdp->dat; } #if TEST_MANUAL_AUDIO if (!cdp->mdat_loaded) { write_log("Missed manual AUD%dDAT\n", nr); } cdp->mdat_loaded = false; #endif #if TEST_MISSED_DMA if (!cdp->dat_loaded) { write_log("Missed DMA %d\n", nr); } cdp->dat_loaded = false; #endif } static void loaddat (int nr) { loaddat (nr, false); } static void loadper1(int nr) { struct audio_channel_data *cdp = audio_channel + nr; cdp->evtime = 1 * CYCLE_UNIT; #if DEBUG_AUDIO2 > 0 if (debugchannel(nr)) { write_log(_T("LOADPERP%d: %d\n"), nr, cdp->evtime / CYCLE_UNIT); } #endif } static void loadperm1(int nr) { struct audio_channel_data *cdp = audio_channel + nr; if (cdp->per == CYCLE_UNIT) { cdp->evtime = CYCLE_UNIT; if (isirq(nr)) { cdp->irqcheck = 1; } else { cdp->irqcheck = -1; } } else if (cdp->per > CYCLE_UNIT) { cdp->evtime = cdp->per - 1 * CYCLE_UNIT; cdp->state |= 0x10; } else { cdp->evtime = 65536 * CYCLE_UNIT; cdp->state |= 0x10; } #if DEBUG_AUDIO2 > 0 if (debugchannel(nr)) { write_log(_T("LOADPERM%d: %d\n"), nr, cdp->evtime / CYCLE_UNIT); } #endif } static void loadper (int nr) { struct audio_channel_data *cdp = audio_channel + nr; cdp->evtime = cdp->per; cdp->data.mixvol = cdp->data.audvol; if (cdp->evtime < CYCLE_UNIT) write_log (_T("LOADPER%d bug %d\n"), nr, cdp->evtime); #if DEBUG_AUDIO2 > 0 if (debugchannel(nr)) { write_log(_T("LOADPER%d: %d\n"), nr, cdp->evtime / CYCLE_UNIT); } #endif } static bool audio_state_channel2 (int nr, bool perfin) { struct audio_channel_data *cdp = audio_channel + nr; bool chan_ena = (dmacon & DMA_MASTER) && (dmacon & (1 << nr)); bool old_dma = cdp->dmaenstore; int audav = adkcon & (0x01 << nr); int audap = adkcon & (0x10 << nr); int napnav = (!audav && !audap) || audav; int hpos = current_hpos (); cdp->dmaenstore = chan_ena; if (currprefs.produce_sound == 0) { zerostate (nr); return true; } audio_activate (); if ((cdp->state & 15) == 2 || (cdp->state & 15) == 3) { if (!chan_ena && old_dma) { // DMA switched off, state=2/3 and "too fast CPU": set flag cdp->dmaofftime_active = true; cdp->dmaofftime_cpu_cnt = regs.instruction_cnt; cdp->dmaofftime_pc = M68K_GETPC; } // check if CPU executed at least 60 instructions (if JIT is off), there are stupid code that // disable audio DMA, then set new sample, then re-enable without actually wanting to start // new sample immediately. if (cdp->dmaofftime_active && !old_dma && chan_ena) { static int warned = 100; // We are still in state=2/3 and program is going to re-enable // DMA. Force state to zero to prevent CPU timed DMA wait // routines in common tracker players to lose notes. if (usehacks() && (currprefs.cachesize || (regs.instruction_cnt - cdp->dmaofftime_cpu_cnt) >= 60)) { if (warned >= 0) { warned--; write_log(_T("Audio %d DMA wait hack ENABLED. OFF=%08x, ON=%08x, PER=%d\n"), nr, cdp->dmaofftime_pc, M68K_GETPC, cdp->evtime / CYCLE_UNIT); } #if DEBUG_AUDIO_HACK > 0 if (debugchannel(nr)) write_log(_T("%d: INSTADMAOFF\n"), nr, M68K_GETPC); #endif newsample(nr, (cdp->dat2 >> 0) & 0xff); zerostate(nr); } else { if (warned >= 0) { warned--; write_log(_T("Audio %d DMA wait hack DISABLED. OFF=%08x, ON=%08x, PER=%d\n"), nr, cdp->dmaofftime_pc, M68K_GETPC, cdp->evtime / CYCLE_UNIT); } } cdp->dmaofftime_active = false; } } #if DEBUG_AUDIO > 0 if (debugchannel (nr) && old_dma != chan_ena) { write_log (_T("%d:DMA=%d IRQ=%d PC=%08x\n"), nr, chan_ena, isirq (nr) ? 1 : 0, M68K_GETPC); } #endif switch (cdp->state) { case 0: if (chan_ena) { cdp->evtime = MAX_EV; cdp->state = 1; setdr(nr, true); cdp->wlen = cdp->len; cdp->ptx_written = false; /* Some programs first start short empty sample and then later switch to * real sample, we must not enable the hack in this case */ if (cdp->wlen > 2) cdp->ptx_tofetch = true; cdp->dsr = true; #if TEST_AUDIO > 0 cdp->have_dat = false; #endif #if DEBUG_AUDIO > 0 if (debugchannel(nr)) { write_log(_T("%d:0>1: LEN=%d PC=%08x\n"), nr, cdp->wlen, M68K_GETPC); } #endif } else if (cdp->dat_written && !isirq (nr)) { cdp->state = 2; setirq(nr, 0); loaddat(nr); if (usehacks() && cdp->per < 10 * CYCLE_UNIT) { static int warned = 100; // make sure audio.device AUDxDAT startup returns to idle state before DMA is enabled newsample(nr, (cdp->dat2 >> 0) & 0xff); zerostate(nr); if (warned > 0) { write_log(_T("AUD%d: forced idle state PER=%d PC=%08x\n"), nr, cdp->per, M68K_GETPC); warned--; } } else { cdp->pbufldl = true; audio_state_channel2(nr, false); } } else { zerostate(nr); } break; case 1: cdp->evtime = MAX_EV; if (!chan_ena) { zerostate(nr); return true; } if (!cdp->dat_written) return true; #if TEST_AUDIO > 0 if (debugchannel(nr) && !cdp->have_dat) write_log(_T("%d: state 1 but no have_dat\n"), nr); cdp->have_dat = false; cdp->losample = cdp->hisample = false; #endif setirq(nr, 10); setdr(nr, false); if (cdp->wlen != 1) cdp->wlen = (cdp->wlen - 1) & 0xffff; cdp->state = 5; if (sampleripper_enabled) do_samplerip(cdp); break; case 5: cdp->evtime = MAX_EV; if (!chan_ena) { zerostate(nr); return true; } if (!cdp->dat_written) return true; #if DEBUG_AUDIO > 0 if (debugchannel (nr)) write_log (_T("%d:>5: LEN=%d PT=%08X PC=%08X\n"), nr, cdp->wlen, cdp->pt, M68K_GETPC); #endif if (cdp->ptx_written) { cdp->ptx_written = 0; cdp->lc = cdp->ptx; } loaddat(nr); if (napnav) setdr(nr, false); cdp->state = 2; loadper(nr); cdp->pbufldl = true; cdp->intreq2 = false; cdp->volcnt = 0; audio_state_channel2(nr, false); break; case 2: if (cdp->pbufldl) { #if TEST_AUDIO > 0 if (debugchannel(nr) && cdp->hisample == false) write_log(_T("%d: high sample used twice\n"), nr); cdp->hisample = false; #endif newsample(nr, (cdp->dat2 >> 8) & 0xff); loadper(nr); cdp->pbufldl = false; } if (!perfin) return true; #if DEBUG_AUDIO2 > 0 if (debugchannel(nr)) { write_log(_T("%d_2->3: LEN=%d/%d DSR=%d\n"), nr, cdp->wlen, cdp->len, cdp->dsr); } #endif #if DEBUG_AUDIO > 0 if (debugchannel(nr) && (cdp->wlen <= 2 || cdp->wlen >= cdp->len - 1)) write_log(_T("%d_2->3: LEN=%d/%d DSR=%d\n"), nr, cdp->wlen, cdp->len, cdp->dsr); #endif if (audap) loaddat(nr, true); if (chan_ena) { if (audap) setdr(nr, false); if (cdp->intreq2 && audap) setirq(nr, 21); } else { if (audap) setirq(nr, 22); } cdp->pbufldl = true; cdp->irqcheck = 0; cdp->state = 3; audio_state_channel2(nr, false); break; case 3 + 0x10: // manual audio period==1 cycle if (!perfin) { return true; } cdp->state = 3; loadper1(nr); if (!chan_ena && isirq(nr)) { cdp->irqcheck = 1; } else { cdp->irqcheck = -1; } return false; case 3: if (cdp->pbufldl) { #if TEST_AUDIO > 0 if (debugchannel(nr) && cdp->losample == false) write_log(_T("%d: low sample used twice\n"), nr); cdp->losample = false; #endif newsample(nr, (cdp->dat2 >> 0) & 0xff); if (chan_ena) { loadper(nr); } else { loadperm1(nr); } cdp->pbufldl = false; } if (!perfin) return true; #if DEBUG_AUDIO2 > 0 if (debugchannel(nr)) { write_log(_T("%d_3->2: LEN=%d/%d DSR=%d\n"), nr, cdp->wlen, cdp->len, cdp->dsr); } #endif #if DEBUG_AUDIO > 0 if (debugchannel(nr) && (cdp->wlen <= 2 || cdp->wlen >= cdp->len - 1)) write_log(_T("%d_3->2: LEN=%d/%d DSR=%d\n"), nr, cdp->wlen, cdp->len, cdp->dsr); #endif if (chan_ena) { loaddat (nr); if (napnav) setdr(nr, false); if (cdp->intreq2 && napnav) setirq(nr, 31); } else { if (napnav) setirq(nr, 32); // cycle-accurate period check was not needed, do delayed check if (!cdp->irqcheck) { cdp->irqcheck = isirq(nr); } if (cdp->irqcheck > 0) { #if DEBUG_AUDIO > 0 if (debugchannel (nr)) write_log(_T("%d: IDLE\n"), nr); #endif zerostate(nr); return true; } loaddat(nr); } cdp->intreq2 = false; cdp->pbufldl = true; cdp->state = 2; audio_state_channel2(nr, false); break; } return true; } static void audio_state_channel (int nr, bool perfin) { struct audio_channel_data *cdp = audio_channel + nr; if (nr < AUDIO_CHANNELS_PAULA) { if (audio_state_channel2(nr, perfin)) { cdp->dat_written = false; } } else { bool ok = false; int streamid = nr - AUDIO_CHANNELS_PAULA + 1; struct audio_stream_data *asd = &audio_stream[nr - AUDIO_CHANNELS_PAULA]; if (asd->cb) { ok = asd->cb(streamid, asd->cb_data); } if (!ok) { audio_state_stream_state(streamid, NULL, 0, MAX_EV); } } } void audio_state_machine (void) { update_audio (); for (int nr = 0; nr < AUDIO_CHANNELS_PAULA; nr++) { struct audio_channel_data *cdp = audio_channel + nr; if (audio_state_channel2(nr, false)) { cdp->dat_written = false; } } schedule_audio (); events_schedule (); } void audio_reset (void) { int i; struct audio_channel_data *cdp; #ifdef AHI ahi_close_sound (); free_ahi_v2 (); #endif reset_sound (); memset (sound_filter_state, 0, sizeof sound_filter_state); if (!isrestore ()) { for (i = 0; i < AUDIO_CHANNELS_PAULA; i++) { cdp = &audio_channel[i]; memset (cdp, 0, sizeof *audio_channel); cdp->per = PERIOD_MAX - 1; cdp->data.mixvol = 0; cdp->evtime = MAX_EV; } for (i = 0; i < AUDIO_CHANNEL_STREAMS; i++) { audio_stream[i].evtime = MAX_EV; } } audio_total_extra_streams = 0; for (int i = 0; i < AUDIO_CHANNEL_STREAMS; i++) { audio_extra_streams[i] = 0; } last_cycles = get_cycles (); next_sample_evtime = scaled_sample_evtime; schedule_audio (); events_schedule (); } static int sound_prefs_changed (void) { if (!config_changed) return 0; if (changed_prefs.produce_sound != currprefs.produce_sound || changed_prefs.win32_soundcard != currprefs.win32_soundcard || 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; if (changed_prefs.sound_stereo_separation != currprefs.sound_stereo_separation || changed_prefs.sound_mixed_stereo_delay != currprefs.sound_mixed_stereo_delay || changed_prefs.sound_interpol != currprefs.sound_interpol || changed_prefs.sound_volume_paula != currprefs.sound_volume_paula || changed_prefs.sound_volume_cd != currprefs.sound_volume_cd || changed_prefs.sound_volume_master != currprefs.sound_volume_master || changed_prefs.sound_volume_board != currprefs.sound_volume_board || changed_prefs.sound_volume_midi != currprefs.sound_volume_midi || changed_prefs.sound_stereo_swap_paula != currprefs.sound_stereo_swap_paula || changed_prefs.sound_stereo_swap_ahi != currprefs.sound_stereo_swap_ahi || changed_prefs.sound_filter != currprefs.sound_filter || changed_prefs.sound_filter_type != currprefs.sound_filter_type) return -1; return 0; } double softfloat_tan(double v); /* This computes the 1st order low-pass filter term b0. * The a1 term is 1.0 - b0. The center frequency marks the -3 dB point. */ #ifndef M_PI #define M_PI 3.14159265358979323846f #endif static float rc_calculate_a0 (int sample_rate, int cutoff_freq) { float omega; /* The BLT correction formula below blows up if the cutoff is above nyquist. */ if (cutoff_freq >= sample_rate / 2) return 1.0; omega = 2.0f * M_PI * cutoff_freq / sample_rate; /* Compensate for the bilinear transformation. This allows us to specify the * stop frequency more exactly, but the filter becomes less steep further * from stopband. */ omega = (float)softfloat_tan (omega / 2.0f) * 2.0f; float out = 1.0f / (1.0f + 1.0f / omega); return out; } void check_prefs_changed_audio (void) { int ch; if (sound_available) { ch = sound_prefs_changed (); if (ch > 0) { #ifdef AVIOUTPUT AVIOutput_Restart(true); #endif clear_sound_buffers (); } if (ch) { set_audio (); audio_activate (); } } #ifdef DRIVESOUND driveclick_check_prefs (); #endif } static void set_extra_prehandler(void) { if (audio_total_extra_streams && sample_prehandler != anti_prehandler) { extra_sample_prehandler = anti_prehandler; } else { extra_sample_prehandler = NULL; } } void set_audio (void) { int old_mixed_size = mixed_stereo_size; int sep, delay; int ch; ch = sound_prefs_changed (); if (ch >= 0) close_sound (); currprefs.produce_sound = changed_prefs.produce_sound; currprefs.win32_soundcard = changed_prefs.win32_soundcard; currprefs.sound_stereo = changed_prefs.sound_stereo; active_sound_stereo = currprefs.sound_stereo; 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; currprefs.sound_interpol = changed_prefs.sound_interpol; currprefs.sound_filter = changed_prefs.sound_filter; currprefs.sound_filter_type = changed_prefs.sound_filter_type; currprefs.sound_volume_paula = changed_prefs.sound_volume_paula; currprefs.sound_volume_master = changed_prefs.sound_volume_master; currprefs.sound_volume_board = changed_prefs.sound_volume_board; currprefs.sound_volume_midi = changed_prefs.sound_volume_midi; currprefs.sound_volume_cd = changed_prefs.sound_volume_cd; currprefs.sound_stereo_swap_paula = changed_prefs.sound_stereo_swap_paula; currprefs.sound_stereo_swap_ahi = changed_prefs.sound_stereo_swap_ahi; sound_cd_volume[0] = sound_cd_volume[1] = (100 - (currprefs.sound_volume_cd < 0 ? 0 : currprefs.sound_volume_cd)) * 32768 / 100; sound_paula_volume[0] = sound_paula_volume[1] = (100 - currprefs.sound_volume_paula) * 32768 / 100; sndboard_ext_volume(); if (ch >= 0) { if (currprefs.produce_sound >= 2) { if (!init_audio ()) { if (! sound_available) { write_log (_T("Sound is not supported.\n")); } else { write_log (_T("Sorry, can't initialize sound.\n")); currprefs.produce_sound = 1; /* So we don't do this every frame */ changed_prefs.produce_sound = 1; } } } next_sample_evtime = scaled_sample_evtime; last_cycles = get_cycles (); compute_vsynctime (); } else { sound_volume (0); } sep = (currprefs.sound_stereo_separation = changed_prefs.sound_stereo_separation) * 3 / 2; if (sep >= 15) sep = 16; delay = currprefs.sound_mixed_stereo_delay = changed_prefs.sound_mixed_stereo_delay; mixed_mul1 = MIXED_STEREO_SCALE / 2 - sep; mixed_mul2 = MIXED_STEREO_SCALE / 2 + sep; mixed_stereo_size = delay > 0 ? (1 << delay) - 1 : 0; mixed_on = sep < MIXED_STEREO_MAX || mixed_stereo_size > 0; if (mixed_on && old_mixed_size != mixed_stereo_size) { saved_ptr = 0; memset (right_word_saved, 0, sizeof right_word_saved); } led_filter_forced = -1; // always off sound_use_filter = sound_use_filter_sinc = 0; if (currprefs.sound_filter) { if (currprefs.sound_filter == FILTER_SOUND_ON) led_filter_forced = 1; if (currprefs.sound_filter == FILTER_SOUND_EMUL) led_filter_forced = 0; if (currprefs.sound_filter_type == FILTER_SOUND_TYPE_A500) sound_use_filter = FILTER_MODEL_A500; else if (currprefs.sound_filter_type == FILTER_SOUND_TYPE_A1200) sound_use_filter = FILTER_MODEL_A1200; } a500e_filter1_a0 = rc_calculate_a0 (currprefs.sound_freq, 6200); a500e_filter2_a0 = rc_calculate_a0 (currprefs.sound_freq, 20000); filter_a0 = rc_calculate_a0 (currprefs.sound_freq, 7000); 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_handler == sample16i_rh_handler || sample_handler == sample16i_sinc_handler || sample_handler == sample16i_anti_handler) { sample_handler = (currprefs.sound_interpol == 0 ? sample16_handler : currprefs.sound_interpol == 3 ? sample16i_rh_handler : currprefs.sound_interpol == 4 ? sample16i_crux_handler : currprefs.sound_interpol == 2 ? sample16i_sinc_handler : sample16i_anti_handler); } else if (sample_handler == sample16s_handler || sample_handler == sample16si_crux_handler || sample_handler == sample16si_rh_handler || sample_handler == sample16si_sinc_handler || sample_handler == sample16si_anti_handler) { sample_handler = (currprefs.sound_interpol == 0 ? sample16s_handler : currprefs.sound_interpol == 3 ? sample16si_rh_handler : currprefs.sound_interpol == 4 ? sample16si_crux_handler : currprefs.sound_interpol == 2 ? sample16si_sinc_handler : sample16si_anti_handler); } else if (sample_handler == sample16ss_handler || sample_handler == sample16ss_sinc_handler || sample_handler == sample16ss_anti_handler) { sample_handler = (currprefs.sound_interpol == 0 ? sample16ss_handler : currprefs.sound_interpol == 3 ? sample16ss_handler : currprefs.sound_interpol == 4 ? sample16ss_handler : currprefs.sound_interpol == 2 ? sample16ss_sinc_handler : sample16ss_anti_handler); } sample_prehandler = NULL; if (sample_handler == sample16si_sinc_handler || sample_handler == sample16i_sinc_handler || sample_handler == sample16ss_sinc_handler) { sample_prehandler = sinc_prehandler_paula; sound_use_filter_sinc = sound_use_filter; sound_use_filter = 0; } else if (sample_handler == sample16si_anti_handler || sample_handler == sample16i_anti_handler || sample_handler == sample16ss_anti_handler) { sample_prehandler = anti_prehandler; } for (int i = 0; i < AUDIO_CHANNELS_PAULA; i++) { 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 = 0; } } set_extra_prehandler(); if (currprefs.produce_sound == 0) { eventtab[ev_audio].active = 0; events_schedule (); } else { audio_activate (); schedule_audio (); events_schedule (); } set_config_changed (); 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 = (int)out; } (*sample_handler) (); } void update_audio (void) { int n_cycles = 0; #if SOUNDSTUFF > 1 static int samplecounter; #endif if (!isaudio ()) goto end; if (isrestore ()) goto end; if (!is_audio_active ()) goto end; n_cycles = (int)(get_cycles () - last_cycles); while (n_cycles > 0) { uae_u32 best_evtime = n_cycles + 1; uae_u32 rounded; int i; for (i = 0; i < AUDIO_CHANNELS_PAULA; i++) { if (audio_channel[i].evtime != MAX_EV && best_evtime > audio_channel[i].evtime) best_evtime = audio_channel[i].evtime; } for (i = 0; i < audio_total_extra_streams; i++) { if (audio_stream[i].evtime != MAX_EV && best_evtime > audio_stream[i].evtime) best_evtime= audio_stream[i].evtime; } /* next_sample_evtime >= 0 so floor() behaves as expected */ rounded = (uae_u32)floorf (next_sample_evtime); float nevtime = next_sample_evtime; if ((next_sample_evtime - rounded) >= 0.5) rounded++; if (currprefs.produce_sound > 1 && best_evtime > rounded) best_evtime = rounded; if (best_evtime > n_cycles) best_evtime = n_cycles; /* Decrease time-to-wait counters */ next_sample_evtime -= best_evtime; if (currprefs.produce_sound > 1) { if (sample_prehandler) sample_prehandler (best_evtime / CYCLE_UNIT); if (extra_sample_prehandler) extra_sample_prehandler(best_evtime / CYCLE_UNIT); } for (i = 0; i < AUDIO_CHANNELS_PAULA; i++) { if (audio_channel[i].evtime != MAX_EV) audio_channel[i].evtime -= best_evtime; } for (i = 0; i < audio_total_extra_streams; i++) { if (audio_stream[i].evtime != MAX_EV) audio_stream[i].evtime -= best_evtime; } n_cycles -= best_evtime; if (currprefs.produce_sound > 1) { 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++; } } #endif (*sample_handler) (); #if SOUNDSTUFF > 1 if (outputsample == 0) outputsample = -1; else if (outputsample < 0) outputsample = 1; #endif } } } for (i = 0; i < AUDIO_CHANNELS_PAULA; i++) { if (audio_channel[i].evtime == 0) { audio_state_channel (i, true); } } for (i = 0; i < audio_total_extra_streams; i++) { if (audio_stream[i].evtime == 0) { audio_state_channel(i + AUDIO_CHANNELS_PAULA, true); } } } end: last_cycles = get_cycles () - n_cycles; } void audio_evhandler (void) { update_audio (); schedule_audio (); } void audio_hsync (void) { if (!currprefs.produce_sound) return; if (!isaudio ()) return; if (audio_work_to_do > 0 && currprefs.sound_auto && !audio_total_extra_streams #ifdef AVIOUTPUT && !avioutput_enabled #endif ) { audio_work_to_do--; if (audio_work_to_do == 0) audio_deactivate (); } update_audio (); previous_volcnt_update = 0; } void event_audxdat_func(uae_u32 v) { int nr = v & 3; int chan_ena = (v & 0x80) != 0; struct audio_channel_data *cdp = audio_channel + nr; if ((cdp->state & 15) == 2 || (cdp->state & 15) == 3) { if (chan_ena) { #if DEBUG_AUDIO > 0 if (debugchannel(nr) && (cdp->wlen >= cdp->len - 1 || cdp->wlen <= 2)) write_log(_T("AUD%d near loop, IRQ=%d, LC=%08X LEN=%d/%d DSR=%d\n"), nr, isirq(nr) ? 1 : 0, cdp->pt, cdp->wlen, cdp->len, cdp->dsr); #endif if (cdp->wlen == 1) { cdp->wlen = cdp->len; // if very low period sample repeats, set higher period value to not cause huge performance drop if (cdp->per < PERIOD_MIN_LOOP * CYCLE_UNIT) { cdp->per = PERIOD_MIN_LOOP * CYCLE_UNIT; } cdp->intreq2 = true; if (sampleripper_enabled) do_samplerip(cdp); #if DEBUG_AUDIO > 0 if (debugchannel(nr) && cdp->wlen > 1) write_log(_T("AUD%d looped, IRQ=%d, LC=%08X LEN=%d DSR=%d\n"), nr, isirq(nr) ? 1 : 0, cdp->pt, cdp->wlen, cdp->dsr); #endif } else { cdp->wlen = (cdp->wlen - 1) & 0xffff; } } else { cdp->dat = v >> 8; cdp->dat_written = true; #if TEST_MANUAL_AUDIO if (cdp->mdat_loaded) { write_log("CH%d double load\n", nr); } cdp->mdat_loaded = true; #endif } } else { cdp->dat = v >> 8; cdp->dat_written = true; audio_activate(); update_audio(); audio_state_channel(nr, false); schedule_audio(); events_schedule(); } cdp->dat_written = false; } void AUDxDAT(int nr, uae_u16 v, uaecptr addr) { struct audio_channel_data *cdp = audio_channel + nr; int chan_ena = (dmacon & DMA_MASTER) && (dmacon & (1 << nr)); #if DEBUG_AUDIO2 if (debugchannel(nr)) { write_log(_T("AUD%dDAT: %04X ADDR=%08X LEN=%d/%d %d,%d,%d %06X\n"), nr, v, addr, cdp->wlen, cdp->len, cdp->state, chan_ena, isirq(nr) ? 1 : 0, M68K_GETPC); } #endif #if DEBUG_AUDIO > 0 if (debugchannel (nr) && (DEBUG_AUDIO > 1 || (!chan_ena || addr == 0xffffffff || ((cdp->state & 15) != 2 && (cdp->state & 15) != 3)))) { write_log (_T("AUD%dDAT: %04X ADDR=%08X LEN=%d/%d %d,%d,%d %06X\n"), nr, v, addr, cdp->wlen, cdp->len, cdp->state, chan_ena, isirq (nr) ? 1 : 0, M68K_GETPC); } #endif #if TEST_MISSED_DMA cdp->dat_loaded = true; #endif #if TEST_AUDIO > 0 if (debugchannel (nr) && cdp->have_dat) write_log (_T("%d: audxdat 1=%04x 2=%04x but old dat not yet used\n"), nr, cdp->dat, cdp->dat2); cdp->have_dat = true; #endif if (chan_ena) { cdp->dat = v; cdp->dat_written = true; } uae_u32 vv = nr | (chan_ena ? 0x80 : 0) | (v << 8); if (!currprefs.cachesize && (cdp->per < PERIOD_LOW * CYCLE_UNIT || currprefs.cpu_compatible)) { int cyc = 0; if (chan_ena) { // AUDxLEN is processed after 1 CCK delay cyc = 1 * CYCLE_UNIT; } if (cyc > 0) { event2_newevent_xx(-1, cyc, vv, event_audxdat_func); } else { event_audxdat_func(vv); } } else { event_audxdat_func(vv); } } void AUDxDAT(int nr, uae_u16 v) { AUDxDAT(nr, v, 0xffffffff); } uaecptr audio_getpt(int nr, bool reset) { struct audio_channel_data *cdp = audio_channel + nr; uaecptr p = cdp->pt; cdp->pt += 2; if (reset) cdp->pt = cdp->lc; cdp->ptx_tofetch = false; return p & ~1; } void AUDxLCH(int nr, uae_u16 v) { struct audio_channel_data *cdp = audio_channel + nr; audio_activate(); update_audio(); // someone wants to update PT but DSR has not yet been processed. // too fast CPU and some tracker players: enable DMA, CPU delay, update AUDxPT with loop position if (usehacks() && ((cdp->ptx_tofetch && cdp->state == 1) || cdp->ptx_written)) { static int warned = 100; cdp->ptx = cdp->lc; cdp->ptx_written = true; if (warned > 0) { write_log(_T("AUD%dLCH HACK: %04X %08X (%d) (%d %d %08x)\n"), nr, v, M68K_GETPC, cdp->state, cdp->dsr, cdp->ptx_written, cdp->ptx); warned--; } #if DEBUG_AUDIO_HACK > 0 if (debugchannel (nr)) write_log (_T("AUD%dLCH HACK: %04X %08X (%d) (%d %d %08x)\n"), nr, v, M68K_GETPC, cdp->state, cdp->dsr, cdp->ptx_written, cdp->ptx); #endif } else { cdp->lc = (cdp->lc & 0xffff) | ((uae_u32)v << 16); #if DEBUG_AUDIO > 0 if (debugchannel (nr)) write_log (_T("AUD%dLCH: %04X %08X (%d) (%d %d %08x)\n"), nr, v, M68K_GETPC, cdp->state, cdp->dsr, cdp->ptx_written, cdp->ptx); #endif } } void AUDxLCL(int nr, uae_u16 v) { struct audio_channel_data *cdp = audio_channel + nr; audio_activate(); update_audio(); if (usehacks() && ((cdp->ptx_tofetch && cdp->state == 1) || cdp->ptx_written)) { static int warned = 100; cdp->ptx = cdp->lc; cdp->ptx_written = true; if (warned > 0) { write_log(_T("AUD%dLCL HACK: %04X %08X (%d) (%d %d %08x)\n"), nr, v, M68K_GETPC, cdp->state, cdp->dsr, cdp->ptx_written, cdp->ptx); warned--; } #if DEBUG_AUDIO_HACK > 0 if (debugchannel (nr)) write_log (_T("AUD%dLCL HACK: %04X %08X (%d) (%d %d %08x)\n"), nr, v, M68K_GETPC, cdp->state, cdp->dsr, cdp->ptx_written, cdp->ptx); #endif } else { cdp->lc = (cdp->lc & ~0xffff) | (v & 0xFFFE); #if DEBUG_AUDIO > 0 if (debugchannel (nr)) write_log (_T("AUD%dLCL: %04X %08X (%d) (%d %d %08x)\n"), nr, v, M68K_GETPC, cdp->state, cdp->dsr, cdp->ptx_written, cdp->ptx); #endif } } void AUDxPER (int nr, uae_u16 v) { struct audio_channel_data *cdp = audio_channel + nr; audio_activate (); update_audio (); int per = (v ? v : 65536) * CYCLE_UNIT; if (per < PERIOD_MIN * CYCLE_UNIT) { /* smaller values would cause extremely high cpu usage */ per = PERIOD_MIN * CYCLE_UNIT; } if (per < PERIOD_MIN_NONCE * CYCLE_UNIT && !currprefs.cpu_memory_cycle_exact && cdp->dmaenstore) { /* DMAL emulation and low period can cause very very high cpu usage on slow performance PCs * Only do this hack if audio DMA is active. */ per = PERIOD_MIN_NONCE * CYCLE_UNIT; } if (cdp->per == PERIOD_MAX - 1 && per != PERIOD_MAX - 1) { cdp->evtime = CYCLE_UNIT; if (isaudio ()) { schedule_audio (); events_schedule (); } } #if TEST_AUDIO > 0 cdp->per_original = v; #endif cdp->per = per; #if DEBUG_AUDIO > 0 if (debugchannel (nr)) write_log (_T("AUD%dPER: %d %08X\n"), nr, v, M68K_GETPC); #endif } void AUDxLEN (int nr, uae_u16 v) { struct audio_channel_data *cdp = audio_channel + nr; audio_activate (); update_audio (); cdp->len = v; #if DEBUG_AUDIO > 0 if (debugchannel (nr)) write_log (_T("AUD%dLEN: %d %08X\n"), nr, v, M68K_GETPC); #endif } void AUDxVOL (int nr, uae_u16 v) { struct audio_channel_data *cdp = audio_channel + nr; audio_activate (); update_audio (); update_volume(nr, v); #if DEBUG_AUDIO > 0 if (debugchannel (nr)) write_log (_T("AUD%dVOL: %d %08X\n"), nr, v, M68K_GETPC); #endif } void audio_update_adkmasks (void) { static int prevcon = -1; unsigned long t = adkcon | (adkcon >> 4); audio_channel[0].data.adk_mask = (((t >> 0) & 1) - 1); audio_channel[1].data.adk_mask = (((t >> 1) & 1) - 1); audio_channel[2].data.adk_mask = (((t >> 2) & 1) - 1); audio_channel[3].data.adk_mask = (((t >> 3) & 1) - 1); if ((prevcon & 0xff) != (adkcon & 0xff)) { audio_activate (); #if DEBUG_AUDIO > 0 write_log (_T("ADKCON=%02x %08X\n"), adkcon & 0xff, M68K_GETPC); #endif prevcon = adkcon; } } int init_audio (void) { return init_sound (); } void led_filter_audio (void) { led_filter_on = 0; if (led_filter_forced > 0 || (gui_data.powerled && led_filter_forced >= 0)) led_filter_on = 1; } void audio_vsync (void) { #if 0 #if SOUNDSTUFF > 0 int max, min; int vsync = isvsync (); static int lastdir; if (1 || !vsync) { extrasamples = 0; return; } min = -10 * 10; max = vsync ? 10 * 10 : 20 * 10; extrasamples = 0; if (gui_data.sndbuf < min) { // +1 extrasamples = (min - gui_data.sndbuf) / 10; lastdir = 1; } else if (gui_data.sndbuf > max) { // -1 extrasamples = (max - gui_data.sndbuf) / 10; } else if (gui_data.sndbuf > 1 * 50 && lastdir < 0) { extrasamples--; } else if (gui_data.sndbuf < -1 * 50 && lastdir > 0) { extrasamples++; } else { lastdir = 0; } if (extrasamples > 99) extrasamples = 99; if (extrasamples < -99) extrasamples = -99; #endif #endif } void restore_audio_finish (void) { last_cycles = get_cycles (); schedule_audio (); events_schedule (); } void restore_audio_start(void) { audio_event_reset(); } uae_u8 *restore_audio (int nr, uae_u8 *src) { struct audio_channel_data *acd = audio_channel + nr; zerostate (nr); acd->state = restore_u8 (); acd->data.audvol = restore_u8 (); acd->intreq2 = restore_u8 () ? true : false; uae_u8 flags = restore_u8 (); acd->dr = acd->dsr = false; if (flags & 1) acd->dr = true; if (flags & 2) acd->dsr = true; acd->len = restore_u16 (); acd->wlen = restore_u16 (); uae_u16 p = restore_u16 (); acd->per = p ? p * CYCLE_UNIT : PERIOD_MAX; acd->dat = acd->dat2 = restore_u16 (); acd->lc = restore_u32 (); acd->pt = restore_u32 (); acd->evtime = restore_u32 (); if (flags & 0x80) acd->drhpos = restore_u8 (); 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; } uae_u8 *save_audio (int nr, size_t *len, uae_u8 *dstptr) { struct audio_channel_data *acd = audio_channel + nr; uae_u8 *dst, *dstbak; if (dstptr) dstbak = dst = dstptr; else dstbak = dst = xmalloc (uae_u8, 100); save_u8 (acd->state); save_u8 (acd->data.audvol); save_u8 (acd->intreq2); save_u8 ((acd->dr ? 1 : 0) | (acd->dsr ? 2 : 0) | 0x80); save_u16 (acd->len); save_u16 (acd->wlen); save_u16 (acd->per == PERIOD_MAX ? 0 : acd->per / CYCLE_UNIT); save_u16 (acd->dat); save_u32 (acd->lc); save_u32 (acd->pt); save_u32 (acd->evtime); save_u8 (acd->drhpos); *len = dst - dstbak; return dstbak; } static void audio_set_extra_channels(void) { int index = AUDIO_CHANNELS_PAULA; audio_total_extra_streams = 0; for (int i = 0; i < AUDIO_CHANNEL_STREAMS; i++) { if (audio_extra_streams[i]) audio_total_extra_streams++; for (int j = 0; j < audio_extra_streams[i]; j++) { audio_data[index++] = &audio_stream[i].data[j]; } } set_extra_prehandler(); } int audio_enable_stream(bool enable, int streamid, int ch, SOUND_STREAM_CALLBACK cb, void *cb_data) { if (streamid == 0) return 0; if (!enable) { if (streamid <= 0) return 0; streamid--; struct audio_stream_data *asd = audio_stream + streamid; audio_extra_streams[streamid] = 0; asd->evtime = MAX_EV; } else { if (streamid < 0) { for (int i = 0; i < AUDIO_CHANNEL_STREAMS; i++) { if (!audio_extra_streams[i]) { streamid = i; break; } } if (streamid < 0) return 0; } audio_extra_streams[streamid] = ch; struct audio_stream_data *asd = audio_stream + streamid; asd->cb = cb; asd->cb_data = cb_data; asd->evtime = CYCLE_UNIT; for (int i = 0; i < ch; i++) { struct audio_channel_data2 *acd = &asd->data[i]; acd->adk_mask = 0xffffffff; acd->mixvol = 1; } audio_activate(); } audio_set_extra_channels(); return streamid + 1; } void audio_state_stream_state(int streamid, int *samplep, int highestch, unsigned int evt) { streamid--; struct audio_stream_data *asd = audio_stream + streamid; if (highestch > audio_extra_streams[streamid]) { audio_extra_streams[streamid] = highestch; audio_set_extra_channels(); } for (int i = 0; i < audio_extra_streams[streamid]; i++) { struct audio_channel_data2 *acd = &asd->data[i]; acd->last_sample = acd->current_sample; acd->current_sample = samplep ? samplep[i] : 0; } asd->evtime = evt; } static uae_u32 cda_evt; static uae_s16 dummy_buffer[4] = { 0 }; void update_cda_sound(float clk) { cda_evt = (uae_u32)(clk * CYCLE_UNIT / 44100.0f); } void audio_cda_volume(struct cd_audio_state *cas, int left, int right) { for (int j = 0; j < 2; j++) { cas->cda_volume[j] = j == 0 ? left : right; cas->cda_volume[j] = sound_cd_volume[j] * cas->cda_volume[j] / 32768; if (cas->cda_volume[j]) cas->cda_volume[j]++; if (cas->cda_volume[j] >= 32768) cas->cda_volume[j] = 32768; } } static bool audio_state_cda(int streamid, void *state) { struct cd_audio_state *cas = (struct cd_audio_state*)state; if (cas->cda_bufptr >= dummy_buffer && cas->cda_bufptr <= dummy_buffer + 4) { audio_enable_stream(false, cas->cda_streamid, 0, NULL, NULL); cas->cda_streamid = 0; return false; } if (cas->cda_streamid <= 0) return false; int samples[2]; samples[0] = cas->cda_bufptr[0] * cas->cda_volume[0] / 32768; samples[1] = cas->cda_bufptr[1] * cas->cda_volume[1] / 32768; audio_state_stream_state(streamid, samples, 2, cda_evt); cas->cda_bufptr += 2; cas->cda_length--; if (cas->cda_length <= 0 && cas->cda_next_cd_audio_buffer_callback) { cas->cda_next_cd_audio_buffer_callback(cas->cda_userdata, cas->cb_data); } return true; } void audio_cda_new_buffer(struct cd_audio_state *cas, uae_s16 *buffer, int length, int userdata, CDA_CALLBACK next_cd_audio_buffer_callback, void *cb_data) { if (length < 0 && cas->cda_streamid > 0) { audio_enable_stream(false, cas->cda_streamid, 0, NULL, cas); cas->cda_streamid = 0; return; } if (!buffer) { cas->cda_bufptr = dummy_buffer; cas->cda_length = 0; } else { cas->cda_bufptr = buffer; cas->cda_length = length; cas->cda_userdata = userdata; if (cas->cda_streamid <= 0) cas->cda_streamid = audio_enable_stream(true, -1, 2, audio_state_cda, cas); } cas->cda_next_cd_audio_buffer_callback = next_cd_audio_buffer_callback; cas->cb_data = cb_data; if (cas->cda_streamid > 0) audio_activate(); }