--- uae/src/audio.c 2018/04/24 16:58:09 1.1.1.7 +++ uae/src/audio.c 2018/04/24 17:05:23 1.1.1.11 @@ -21,6 +21,7 @@ #include "sounddep/sound.h" #include "events.h" #include "audio.h" +#include "savestate.h" struct audio_channel_data audio_channel[6]; int sound_available = 0; @@ -621,7 +622,7 @@ static void audio_handler (int nr) switch (cdp->state) { case 0: - fprintf(stderr, "Bug in sound code\n"); + write_log ("Bug in sound code\n"); break; case 1: @@ -631,8 +632,8 @@ static void audio_handler (int nr) cdp->state = 5; INTREQ(0x8000 | (0x80 << nr)); if (cdp->wlen != 1) - cdp->wlen--; - cdp->nextdat = chipmem_bank.wget(cdp->pt); + cdp->wlen = (cdp->wlen - 1) & 0xFFFF; + cdp->nextdat = chipmem_wget (cdp->pt); cdp->pt += 2; break; @@ -671,7 +672,7 @@ static void audio_handler (int nr) /* Period attachment? */ if (adkcon & (0x10 << nr)) { if (cdp->intreq2 && cdp->dmaen) - INTREQ(0x8000 | (0x80 << nr)); + INTREQ (0x8000 | (0x80 << nr)); cdp->intreq2 = 0; cdp->dat = cdp->nextdat; @@ -741,52 +742,46 @@ static void ahi_handler (int nr) struct audio_channel_data *cdp = audio_channel + nr; switch (cdp->state) { - case 0: - fprintf(stderr, "Bug in sound code\n"); + case 0: + write_log ("Bug in sound code\n"); break; - case 1: - /* We come here at the first hsync after DMA was turned on. */ - cdp->evtime = maxhpos * CYCLE_UNIT; + case 1: + cdp->evtime = cdp->per; + cdp->intreq2 = 0; - cdp->state = 5; - INTREQ (0xA000); + cdp->state = 2; if (cdp->wlen != 1) - cdp->wlen--; - cdp->nextdat = chipmem_bank.wget(cdp->pt); + cdp->wlen = (cdp->wlen - 1) & 0xFFFF; + cdp->dat = chipmem_wget (cdp->pt); cdp->pt += 2; break; - case 5: - /* We come here at the second hsync after DMA was turned on. */ - if (currprefs.produce_sound == 0) - cdp->per = PERIOD_MAX; - - cdp->evtime = cdp->per; - cdp->dat = cdp->nextdat; - - cdp->state = 2; - cdp->data_written = 2; - break; - - case 2: + case 2: if (currprefs.produce_sound == 0) cdp->per = PERIOD_MAX; - cdp->evtime = cdp->per; - - if (cdp->intreq2 && cdp->dmaen) - INTREQ(0xA000); + /* Only do interrupts for one of the AHI channels. */ + if (nr == 4 && cdp->intreq2) + /* Reuse AUD0 interrupt for AHI purposes. This is easier than + fixing the driver source to use AddIntServer instead of SetIntVector + for the EXTER interrupt. */ + INTREQ (0x8080); cdp->intreq2 = 0; + cdp->evtime = cdp->per; - cdp->dat = cdp->nextdat; - - if (cdp->dmaen) - cdp->data_written = 2; + cdp->dat = chipmem_wget (cdp->pt); + cdp->pt += 2; + if (cdp->wlen == 1) { + cdp->pt = cdp->lc; + cdp->wlen = cdp->len; + cdp->intreq2 = 1; + } else + cdp->wlen = (cdp->wlen - 1) & 0xFFFF; break; - default: + default: cdp->state = 0; break; } @@ -809,24 +804,60 @@ void aud3_handler (void) audio_handler (3); } +void audio_channel_enable_dma (struct audio_channel_data *cdp) +{ + if (cdp->state == 0) { + cdp->state = 1; + cdp->pt = cdp->lc; + cdp->wper = cdp->per; + cdp->wlen = cdp->len; + cdp->data_written = 2; + cdp->evtime = eventtab[ev_hsync].evtime - get_cycles (); + } +} + +void audio_channel_disable_dma (struct audio_channel_data *cdp) +{ + if (cdp->state == 1 || cdp->state == 5) { + cdp->state = 0; + cdp->last_sample = 0; + cdp->current_sample = 0; + } +} + void audio_reset (void) { - memset (audio_channel, 0, sizeof audio_channel); - audio_channel[0].per = PERIOD_MAX; - audio_channel[1].per = PERIOD_MAX; - audio_channel[2].per = PERIOD_MAX; - audio_channel[3].per = PERIOD_MAX; + int i; + if (savestate_state != STATE_RESTORE) { + memset (audio_channel, 0, 4 * sizeof *audio_channel); + audio_channel[0].per = PERIOD_MAX; + audio_channel[1].per = PERIOD_MAX; + audio_channel[2].per = PERIOD_MAX; + audio_channel[3].per = PERIOD_MAX; + audio_channel[0].voltbl = sound_table[0]; + audio_channel[1].voltbl = sound_table[0]; + audio_channel[2].voltbl = sound_table[0]; + audio_channel[3].voltbl = sound_table[0]; + } else + for (i = 0; i < 4; i++) + audio_channel[i].dmaen = (dmacon & 0x200) && (dmacon & (1 << i)); + + + memset (audio_channel + 4, 0, 2 * sizeof *audio_channel); audio_channel[4].per = PERIOD_MAX; audio_channel[5].per = PERIOD_MAX; - audio_channel[0].voltbl = sound_table[0]; - audio_channel[1].voltbl = sound_table[0]; - audio_channel[2].voltbl = sound_table[0]; - audio_channel[3].voltbl = sound_table[0]; + +#ifndef MULTIPLICATION_PROFITABLE + for (i = 0; i < 6; i++) + audio_channel[nr].voltbl = sound_table[audio_channel[nr].vol]; +#endif last_cycles = 0; ahi_enabled = 0; ahi_interrupt_state = 0; next_sample_evtime = scaled_sample_evtime; + + schedule_audio (); } STATIC_INLINE int sound_prefs_changed (void) @@ -834,6 +865,7 @@ STATIC_INLINE int sound_prefs_changed (v return (changed_prefs.produce_sound != currprefs.produce_sound || changed_prefs.stereo != currprefs.stereo || changed_prefs.mixed_stereo != currprefs.mixed_stereo + || changed_prefs.sound_maxbsiz != currprefs.sound_maxbsiz || changed_prefs.sound_freq != currprefs.sound_freq || changed_prefs.sound_bits != currprefs.sound_bits); } @@ -848,16 +880,16 @@ void check_prefs_changed_audio (void) currprefs.mixed_stereo = changed_prefs.mixed_stereo; currprefs.sound_bits = changed_prefs.sound_bits; currprefs.sound_freq = changed_prefs.sound_freq; - + currprefs.sound_maxbsiz = changed_prefs.sound_maxbsiz; if (currprefs.produce_sound >= 2) { if (init_audio ()) { last_cycles = get_cycles () - 1; next_sample_evtime = scaled_sample_evtime; } else if (! sound_available) { - fprintf (stderr, "Sound is not supported.\n"); + write_log ("Sound is not supported.\n"); } else { - fprintf (stderr, "Sorry, can't initialize sound.\n"); + write_log ("Sorry, can't initialize sound.\n"); currprefs.produce_sound = 0; /* So we don't do this every frame */ changed_prefs.produce_sound = 0; @@ -878,7 +910,6 @@ void check_prefs_changed_audio (void) : currprefs.sound_interpol == 1 ? sample16si_rh_handler : sample16si_crux_handler); if (currprefs.produce_sound == 0) { - memset (audio_channel, 0, sizeof audio_channel); eventtab[ev_audio].active = 0; events_schedule (); } @@ -888,25 +919,24 @@ void update_audio (void) { unsigned long int n_cycles; - if (currprefs.produce_sound == 0) + if (currprefs.produce_sound == 0 || savestate_state == STATE_RESTORE) return; n_cycles = get_cycles () - last_cycles; for (;;) { - int best = -1; unsigned long int best_evtime = n_cycles + 1; if (audio_channel[0].state != 0 && best_evtime > audio_channel[0].evtime) - best = 0, best_evtime = audio_channel[0].evtime; + best_evtime = audio_channel[0].evtime; if (audio_channel[1].state != 0 && best_evtime > audio_channel[1].evtime) - best = 1, best_evtime = audio_channel[1].evtime; + best_evtime = audio_channel[1].evtime; if (audio_channel[2].state != 0 && best_evtime > audio_channel[2].evtime) - best = 2, best_evtime = audio_channel[2].evtime; + best_evtime = audio_channel[2].evtime; if (audio_channel[3].state != 0 && best_evtime > audio_channel[3].evtime) - best = 3, best_evtime = audio_channel[3].evtime; + best_evtime = audio_channel[3].evtime; if (audio_channel[4].state != 0 && best_evtime > audio_channel[4].evtime) - best = 4, best_evtime = audio_channel[4].evtime; + best_evtime = audio_channel[4].evtime; if (audio_channel[5].state != 0 && best_evtime > audio_channel[5].evtime) - best = 5, best_evtime = audio_channel[5].evtime; + best_evtime = audio_channel[5].evtime; if (currprefs.produce_sound > 1 && best_evtime > next_sample_evtime) best_evtime = next_sample_evtime; @@ -995,13 +1025,21 @@ void AUDxPER (int nr, uae_u16 v) if (per < maxhpos * CYCLE_UNIT / 2 && currprefs.produce_sound < 3) per = maxhpos * CYCLE_UNIT / 2; + if (audio_channel[nr].per == PERIOD_MAX + && per != PERIOD_MAX) + { + audio_channel[nr].evtime = CYCLE_UNIT; + if (currprefs.produce_sound > 0) { + schedule_audio (); + events_schedule (); + } + } audio_channel[nr].per = per; } void AUDxLEN (int nr, uae_u16 v) { update_audio (); - audio_channel[nr].len = v; } @@ -1031,6 +1069,10 @@ int init_audio (void) static uae_u32 ahi_entry (void) { + struct audio_channel_data *cdp; + int opcode = m68k_dreg (regs, 0); + int was_enabled; + /* D0 contains the function code: 0: get current output sample rate. 1: get scaled divider for that frequency @@ -1038,42 +1080,111 @@ static uae_u32 ahi_entry (void) 3: set right AHI channel info (LC in A0, period in D1, length D2, dmaen D3). 4: set left AHI channel info (LC in A0, period in D1, length D2, dmaen D3). 5: return interrupt state. */ - switch (m68k_dreg (regs, 0)) { + switch (opcode) { case 0: return currprefs.sound_freq; case 1: return scaled_sample_evtime; case 2: - ahi_enabled = m68k_dreg (regs, 1); + was_enabled = ahi_enabled; + ahi_enabled = !!m68k_dreg (regs, 1); + if (ahi_enabled > was_enabled) { + audio_channel[4].evtime = CYCLE_UNIT; + audio_channel[5].evtime = CYCLE_UNIT; + if (currprefs.produce_sound > 0) { + schedule_audio (); + events_schedule (); + } + } return 0; case 3: + case 4: /* Must set a frequency lower than sample rate, i.e. using a higher divider. */ - if (m68k_dreg (regs, 1) < scaled_sample_evtime) + if (m68k_dreg (regs, 3) && m68k_dreg (regs, 1) < scaled_sample_evtime) return 0; - audio_channel[4].lc = m68k_areg (regs, 0); - audio_channel[4].per = m68k_dreg (regs, 1); - audio_channel[4].len = m68k_dreg (regs, 2); - audio_channel[4].dmaen = m68k_dreg (regs, 3); - return 1; - case 4: - if (m68k_dreg (regs, 1) < scaled_sample_evtime) - return 0; - audio_channel[5].lc = m68k_areg (regs, 0); - audio_channel[5].per = m68k_dreg (regs, 1); - audio_channel[5].len = m68k_dreg (regs, 2); - audio_channel[5].dmaen = m68k_dreg (regs, 3); + cdp = audio_channel + opcode + 1; + was_enabled = cdp->dmaen; + cdp->lc = m68k_areg (regs, 0); + cdp->per = m68k_dreg (regs, 1); + cdp->len = m68k_dreg (regs, 2); + cdp->dmaen = m68k_dreg (regs, 3); + if (currprefs.produce_sound > 0) { + if (cdp->dmaen && ! was_enabled) { + cdp->state = 0; + audio_channel_enable_dma (cdp); + } else if (cdp->dmaen == 0) { + cdp->state = 0; + cdp->dat = 0; + } + schedule_audio (); + events_schedule (); + } return 1; case 5: return ahi_interrupt_state; + default: + return 0; } } void ahi_install (void) { uaecptr a = here (); - org (0xF0FFB0); + org (RTAREA_BASE + 0xFFB0); calltrap (deftrap (ahi_entry)); dw (RTS); org (a); } + +/* audio save/restore code FIXME: not working correctly */ +/* help needed */ + +uae_u8 *restore_audio (uae_u8 *src, int i) +{ + struct audio_channel_data *acd; + uae_u16 p; + + acd = audio_channel + i; + acd->state = restore_u8 (); + acd->vol = restore_u8 (); + acd->intreq2 = restore_u8 (); + acd->data_written = restore_u8 (); + acd->len = restore_u16 (); + acd->wlen = restore_u16 (); + p = restore_u16 (); + acd->per = p ? p * CYCLE_UNIT : PERIOD_MAX; + p = restore_u16 (); + acd->wper = p ? p * CYCLE_UNIT : PERIOD_MAX; + acd->lc = restore_u32 (); + acd->pt = restore_u32 (); + acd->evtime = restore_u32 (); + + return src; +} + + +uae_u8 *save_audio (int *len, int i) +{ + struct audio_channel_data *acd; + uae_u8 *dst = malloc (100); + uae_u8 *dstbak = dst; + uae_u16 p; + + acd = audio_channel + i; + save_u8 ((uae_u8)acd->state); + save_u8 (acd->vol); + save_u8 (acd->intreq2); + save_u8 (acd->data_written); + save_u16 (acd->len); + save_u16 (acd->wlen); + p = acd->per == PERIOD_MAX ? 0 : acd->per / CYCLE_UNIT; + save_u16 (p); + p = acd->per == PERIOD_MAX ? 0 : acd->wper / CYCLE_UNIT; + save_u16 (p); + save_u32 (acd->lc); + save_u32 (acd->pt); + save_u32 (acd->evtime); + *len = dst - dstbak; + return dstbak; +}