From de7c8bb84489f24d360e06c38a6a88f123c77a45 Mon Sep 17 00:00:00 2001 From: crazii Date: Wed, 25 Jan 2023 18:06:16 +0800 Subject: [PATCH] use soundcard interrupt instead of timer, music plays more stable. --- MAIN.C | 84 ++++++++++++++++++++++++------------- MPXPLAY/AU_CARDS/AU_CARDS.C | 9 +++- MPXPLAY/AU_CARDS/AU_CARDS.H | 3 ++ MPXPLAY/AU_CARDS/PCIBIOS.H | 4 +- MPXPLAY/AU_CARDS/SC_ICH.C | 29 +++++++++++-- SBEMU/SBEMU.C | 12 ++++-- SBEMU/SBEMU.H | 2 +- 7 files changed, 103 insertions(+), 40 deletions(-) diff --git a/MAIN.C b/MAIN.C index b125398..cc44e00 100644 --- a/MAIN.C +++ b/MAIN.C @@ -22,13 +22,21 @@ extern int16_t* TEST_Sample; extern unsigned long TEST_SampleLen; mpxplay_audioout_info_s aui = {0}; - -#define MAIN_USE_INT70 0 +#define MAIN_USE_TIMER 0 +#if MAIN_USE_TIMER +#define MAIN_USE_INT70 1 #define MAIN_INT70_FREQ 1024 //1024 max -#define MAIN_PCM_SAMPLESIZE 32768 #define MAIN_FREQ (MAIN_USE_INT70 ? MAIN_INT70_FREQ : 18) //because some programs will assume default frequency on running/uninstalling its own timer, so we don't change the default #define MAIN_SAMPLES (SBEMU_SAMPLERATE / MAIN_FREQ) +#define MAIN_PCM_SAMPLESIZE 32768 static const int MAIN_PCM_COUNT = (MAIN_PCM_SAMPLESIZE / (MAIN_SAMPLES*SBEMU_CHANNELS) * MAIN_SAMPLES*SBEMU_CHANNELS); +static int16_t MAIN_OPLPCM[MAIN_SAMPLES*8+256]; +static int MAIN_PCMEndTag = MAIN_PCM_COUNT; +#else +#define MAIN_USE_INT70 0 +#define MAIN_PCM_SAMPLESIZE 8192 +static int16_t MAIN_OPLPCM[MAIN_PCM_SAMPLESIZE+256]; +#endif static DPMI_ISR_HANDLE MAIN_TimerIntHandlePM; static DPMI_REG MAIN_TimerPMReg = {0}; @@ -39,14 +47,13 @@ static uint32_t MAIN_DMA_Size = 0; static uint32_t MAIN_DMA_MappedAddr = 0; static uint32_t MAIN_SBBytes = 0; static int16_t MAIN_PCM[MAIN_PCM_SAMPLESIZE+256]; -static int16_t MAIN_OPLPCM[MAIN_SAMPLES*8+256]; + static int MAIN_PCMStart; static int MAIN_PCMEnd; -static int MAIN_PCMEndTag = MAIN_PCM_COUNT; -static void MAIN_TimerInterrupt(); +static void MAIN_Interrupt(); //need capture both RM & PM context to issue virq -static void MAIN_TimerInterruptPM(); -static void MAIN_TimerInterruptRM(); +static void MAIN_InterruptPM(); +static void MAIN_InterruptRM(); static void MAIN_EnableInt70(); static uint32_t MAIN_OPL3_388(uint32_t port, uint32_t val, uint32_t out) @@ -322,13 +329,14 @@ int main(int argc, char* argv[]) //TestSound(FALSE); } - SBEMU_Init(MAIN_Options[OPT_IRQ].value, MAIN_Options[OPT_DMA].value); + SBEMU_Init(MAIN_Options[OPT_IRQ].value, MAIN_Options[OPT_DMA].value, &MAIN_Interrupt); for(int i = 0; i < countof(MAIN_SB_IODT); ++i) MAIN_SB_IODT[i].port += MAIN_Options[OPT_ADDR].value; QEMM_IODT* SB_Iodt = MAIN_Options[OPT_OPL].value ? MAIN_SB_IODT : MAIN_SB_IODT+4; int SB_IodtCount = MAIN_Options[OPT_OPL].value ? countof(MAIN_SB_IODT) : countof(MAIN_SB_IODT)-4; //MAIN_SB_IODT[countof(MAIN_SB_IODT)-1].port = DPMI_LoadW(DPMI_SEGOFF2L(0x40,0x63)) + 6; - + + /* BOOL QEMMInstalledVDMA = QEMM_Install_IOPortTrap(MAIN_VDMA_IODT, countof(MAIN_VDMA_IODT), &MAIN_VDMA_IOPT); #if MAIN_USE_INT70 //will crash with VIRQ installed, do it temporarily. TODO: figure out why BOOL QEMMInstalledVIRQ = TRUE; @@ -343,16 +351,21 @@ int main(int argc, char* argv[]) BOOL HDPMIInstalledVIRQ1 = HDPMIPT_Install_IOPortTrap(0x20, 0x21, MAIN_VIRQ_IODT, 2, &MAIN_VIRQ_IOPT_PM1); BOOL HDPMIInstalledVIRQ2 = HDPMIPT_Install_IOPortTrap(0xA0, 0xA1, MAIN_VIRQ_IODT+2, 2, &MAIN_VIRQ_IOPT_PM2); BOOL HDPMIInstalledSB = HDPMIPT_Install_IOPortTrap(MAIN_Options[OPT_ADDR].value, MAIN_Options[OPT_ADDR].value+0x0F, SB_Iodt, SB_IodtCount, &MAIN_SB_IOPT_PM); - - BOOL PM_ISR = DPMI_InstallISR(MAIN_USE_INT70 ? 0x70 : 0x08, MAIN_TimerInterruptPM, &MAIN_TimerIntHandlePM) == 0; + */ + #if MAIN_USE_TIMER + BOOL PM_ISR = DPMI_InstallISR(MAIN_USE_INT70 ? 0x70 : 0x08, MAIN_InterruptPM, &MAIN_TimerIntHandlePM) == 0; #if MAIN_USE_INT70 MAIN_EnableInt70(); #endif + #else + BOOL PM_ISR = DPMI_InstallISR(PIC_IRQ2VEC(aui.card_irq), MAIN_InterruptPM, &MAIN_TimerIntHandlePM) == 0; + PIC_UnmaskIRQ(aui.card_irq); + #endif BOOL TSR = TRUE; if(!PM_ISR - || !QEMMInstalledVDMA || !QEMMInstalledVIRQ || !QEMMInstalledSB - || !HDPMIInstalledVDMA1 || !HDPMIInstalledVDMA2 || !HDPMIInstalledVDMA3 || !HDPMIInstalledVIRQ1 || !HDPMIInstalledVIRQ2 || !HDPMIInstalledSB + //|| !QEMMInstalledVDMA || !QEMMInstalledVIRQ || !QEMMInstalledSB + //|| !HDPMIInstalledVDMA1 || !HDPMIInstalledVDMA2 || !HDPMIInstalledVDMA3 || !HDPMIInstalledVIRQ1 || !HDPMIInstalledVIRQ2 || !HDPMIInstalledSB || !(TSR=DPMI_TSR())) { if(MAIN_Options[OPT_OPL].value) @@ -360,7 +373,7 @@ int main(int argc, char* argv[]) QEMM_Uninstall_IOPortTrap(&OPL3IOPT); HDPMIPT_Uninstall_IOPortTrap(&OPL3IOPT_PM); } - + /* if(!QEMMInstalledVDMA || !QEMMInstalledVIRQ || !QEMMInstalledSB) printf("Error: Failed installing IO port trap for QEMM.\n"); if(QEMMInstalledVDMA) QEMM_Uninstall_IOPortTrap(&MAIN_VDMA_IOPT); @@ -377,7 +390,7 @@ int main(int argc, char* argv[]) if(HDPMIInstalledVIRQ1) HDPMIPT_Uninstall_IOPortTrap(&MAIN_VIRQ_IOPT_PM1); if(HDPMIInstalledVIRQ2) HDPMIPT_Uninstall_IOPortTrap(&MAIN_VIRQ_IOPT_PM2); if(HDPMIInstalledSB) HDPMIPT_Uninstall_IOPortTrap(&MAIN_SB_IOPT_PM); - + */ if(!PM_ISR) printf("Error: Failed installing timer.\n"); if(PM_ISR) DPMI_UninstallISR(&MAIN_TimerIntHandlePM); @@ -388,9 +401,8 @@ int main(int argc, char* argv[]) return 1; } -static void MAIN_TimerInterruptPM() +static void MAIN_InterruptPM() { - CLIS(); #if MAIN_USE_INT70 if(++MAIN_Int70Counter >= (1024/MAIN_INT70_FREQ)) #endif @@ -418,7 +430,7 @@ static void MAIN_TimerInterruptPM() DBG_DumpREG(&MAIN_TimerPMReg);//while(1); #endif - MAIN_TimerInterrupt(); + MAIN_Interrupt(); } DPMI_CallOldISR(&MAIN_TimerIntHandlePM); @@ -426,9 +438,12 @@ static void MAIN_TimerInterruptPM() #if MAIN_USE_INT70 MAIN_EnableInt70(); #endif + #if !MAIN_USE_TIMER + PIC_UnmaskIRQ(aui.card_irq); + #endif } -static void MAIN_TimerInterrupt() +static void MAIN_Interrupt() { #if 0 const int samples = MAIN_SAMPLES*2; @@ -439,7 +454,17 @@ static void MAIN_TimerInterrupt() cur += aui.samplenum; AU_writedata(&aui); #else + #if MAIN_USE_TIMER int samples = MAIN_SAMPLES; + #else + if(aui.card_handler->irq_routine) aui.card_handler->irq_routine(&aui); + aui.card_outbytes = aui.card_dmasize; + int samples = AU_cardbuf_space(&aui) / sizeof(int16_t) / 2; //16 bit, 2 channels + if(samples == 0) + return; + #endif + + #if MAIN_USE_TIMER if((MAIN_PCMEnd - MAIN_PCMStart + MAIN_PCMEndTag)%MAIN_PCMEndTag >= MAIN_PCMEndTag-MAIN_SAMPLES*4) //buffer full { aui.samplenum = MAIN_PCMEnd >= MAIN_PCMStart ? MAIN_PCMEnd - MAIN_PCMStart : MAIN_PCMEndTag - MAIN_PCMStart; @@ -454,6 +479,7 @@ static void MAIN_TimerInterrupt() } return; } + #endif /*if(MAIN_PCMEnd >= MAIN_PCMStart && MAIN_PCMEnd - MAIN_PCMStart < MAIN_SAMPLES*4) //(almost) empty samples += samples/2; //extra samples to avoid underrun due to timer interrupt precision //_LOG("PTR: %d, %d, %d\n", MAIN_PCMEnd, MAIN_PCMStart, MAIN_PCMEnd - MAIN_PCMStart);*/ @@ -529,12 +555,6 @@ static void MAIN_TimerInterrupt() } } while(DMA_GetAuto() && (pos < samples) && SBEMU_HasStarted()); samples = pos; - /*while(pos < samples) - { - MAIN_PCM[MAIN_PCMEnd+pos*2] = 0; - MAIN_PCM[MAIN_PCMEnd+pos*2+1] = 0; - ++pos; - }*/ //_LOG("digital end\n"); } else if(!MAIN_Options[OPT_OPL].value) @@ -562,14 +582,14 @@ static void MAIN_TimerInterrupt() } } samples *= 2; //to stereo - MAIN_PCMEnd += samples; + #if MAIN_USE_TIMER + MAIN_PCMEnd += samples; if(MAIN_PCMEnd >= MAIN_PCM_COUNT - MAIN_SAMPLES*4) { MAIN_PCMEndTag = MAIN_PCMEnd; MAIN_PCMEnd = 0; } - aui.samplenum = MAIN_PCMEnd >= MAIN_PCMStart ? MAIN_PCMEnd - MAIN_PCMStart : MAIN_PCMEndTag - MAIN_PCMStart; aui.pcm_sample = MAIN_PCM + MAIN_PCMStart; MAIN_PCMStart += aui.samplenum - AU_writedata(&aui); @@ -580,15 +600,21 @@ static void MAIN_TimerInterrupt() aui.pcm_sample = MAIN_PCM + MAIN_PCMStart; MAIN_PCMStart += aui.samplenum - AU_writedata(&aui); } + #else + aui.samplenum = samples; + aui.pcm_sample = MAIN_PCM + MAIN_PCMStart; + AU_writedata(&aui); + #endif + //_LOG("TIMEREND\n"); #endif } + static void MAIN_EnableInt70() { CLIS(); PIC_UnmaskIRQ(8); - outp(0x70, 0x0A); int freq = inp(0x71); if(freq != 0x26) //1024 diff --git a/MPXPLAY/AU_CARDS/AU_CARDS.C b/MPXPLAY/AU_CARDS/AU_CARDS.C index c031ac5..ba805d0 100644 --- a/MPXPLAY/AU_CARDS/AU_CARDS.C +++ b/MPXPLAY/AU_CARDS/AU_CARDS.C @@ -33,7 +33,9 @@ typedef int (*aucards_writedata_t)(struct mpxplay_audioout_info_s *aui,unsigned static unsigned int cardinit(struct mpxplay_audioout_info_s *aui); static unsigned int carddetect(struct mpxplay_audioout_info_s *aui, unsigned int retry); +#ifndef SBEMU static unsigned int AU_cardbuf_space(struct mpxplay_audioout_info_s *aui); +#endif static int aucards_writedata_normal(struct mpxplay_audioout_info_s *aui,unsigned long outbytes_left); static int aucards_writedata_intsound(struct mpxplay_audioout_info_s *aui,unsigned long outbytes_left); #ifdef SBEMU @@ -878,7 +880,10 @@ void AU_setmixer_all(struct mpxplay_audioout_info_s *aui) //------------------------------------------------------------------------- #define SOUNDCARD_BUFFER_PROTECTION 32 // in bytes (requried for PCI cards) -static unsigned int AU_cardbuf_space(struct mpxplay_audioout_info_s *aui) +#ifndef SBEMU +static +#endif +unsigned int AU_cardbuf_space(struct mpxplay_audioout_info_s *aui) { unsigned long buffer_protection; @@ -910,7 +915,7 @@ static unsigned int AU_cardbuf_space(struct mpxplay_audioout_info_s *aui) if(bufpos>=aui->card_outbytes) aui->card_dmalastput=bufpos-aui->card_outbytes; else - aui->card_dmalastput=aui->card_dmasize+bufpos-aui->card_outbytes; + aui->card_dmalastput=aui->card_dmasize+bufpos-aui->card_outbytes+aui->card_bytespersign; funcbit_smp_disable(aui->card_infobits,AUINFOS_CARDINFOBIT_DMAUNDERRUN); } }else{ diff --git a/MPXPLAY/AU_CARDS/AU_CARDS.H b/MPXPLAY/AU_CARDS/AU_CARDS.H index 57421bd..cb76977 100644 --- a/MPXPLAY/AU_CARDS/AU_CARDS.H +++ b/MPXPLAY/AU_CARDS/AU_CARDS.H @@ -281,6 +281,9 @@ extern void AU_setmixer_outs(struct mpxplay_audioout_info_s *aui,unsigned int se extern void AU_setmixer_all(struct mpxplay_audioout_info_s *); extern void AU_clearbuffs(struct mpxplay_audioout_info_s *); extern void AU_pause_process(struct mpxplay_audioout_info_s *); +#ifdef SBEMU +extern unsigned int AU_cardbuf_space(struct mpxplay_audioout_info_s *aui); +#endif extern int AU_writedata(struct mpxplay_audioout_info_s *); #ifdef __cplusplus diff --git a/MPXPLAY/AU_CARDS/PCIBIOS.H b/MPXPLAY/AU_CARDS/PCIBIOS.H index 44a5453..3e67d1d 100644 --- a/MPXPLAY/AU_CARDS/PCIBIOS.H +++ b/MPXPLAY/AU_CARDS/PCIBIOS.H @@ -80,8 +80,8 @@ typedef unsigned long uint32_t; #define PCIR_RID 0x08 // chiprev #define PCIR_CCODE 0x0A #define PCIR_HEADT 0x0E // head type -#define PCIR_NAMBAR 0x10 // io baseport -#define PCIR_NABMBAR 0x14 // busmaster io baseport +#define PCIR_NAMBAR 0x10 // Native Audio Mixer Base Address +#define PCIR_NABMBAR 0x14 // Native Audio Bus Mastering Base Address #define PCIR_SSVID 0x2C // subsystem id + vendor id (serial) #define PCIR_SSID 0x2E // subsystem id (model) #define PCIR_INTR_LN 0x3C // irq number diff --git a/MPXPLAY/AU_CARDS/SC_ICH.C b/MPXPLAY/AU_CARDS/SC_ICH.C index 0c5f884..b50ed62 100644 --- a/MPXPLAY/AU_CARDS/SC_ICH.C +++ b/MPXPLAY/AU_CARDS/SC_ICH.C @@ -31,9 +31,13 @@ #define ICH_PO_CR_REG 0x1b // PCM out Control Register #define ICH_PO_CR_START 0x01 // start codec #define ICH_PO_CR_RESET 0x02 // reset codec +#define ICH_PO_CR_LVBIE 0x04 // last valid buffer interrupt enable +#define ICH_PO_CR_IOCE 0x10 // IOC enable #define ICH_PO_SR_REG 0x16 // PCM out Status register #define ICH_PO_SR_DCH 0x01 // DMA controller halted +#define ICH_PO_SR_LVBCI 0x04 // last valid buffer completion interrupt +#define ICH_PO_SR_BCIS 0x08 // buffer completion interrupt status (IOC) #define ICH_GLOB_CNT_REG 0x2c // Global control register #define ICH_GLOB_CNT_ACLINKOFF 0x00000008 // turn off ac97 link @@ -64,6 +68,8 @@ #define ICH_DEFAULT_RETRY 1000 +#define ICH_BD_IOC 0x8000 + typedef struct intel_card_s { unsigned long baseport_bm; // busmaster baseport @@ -208,6 +214,7 @@ static void snd_intel_chip_init(struct intel_card_s *card) snd_intel_write_8(card,ICH_PO_CR_REG,ICH_PO_CR_RESET); // reset channels #ifdef SBEMU pds_mdelay(50); + snd_intel_write_8(card,ICH_PO_CR_REG,/*ICH_PO_CR_LVBIE*/ICH_PO_CR_IOCE); #endif mpxplay_debugf(ICH_DEBUG_OUTPUT,"chip init end"); @@ -293,7 +300,7 @@ static void snd_intel_prepare_playback(struct intel_card_s *card,struct mpxplay_ period_size_samples=card->period_size_bytes/(aui->bits_card>>3); for(i=0; idm,(char *)card->pcmout_buffer+(i*card->period_size_bytes)); - table_base[i*2+1]=period_size_samples; + table_base[i*2+1]=period_size_samples | (ICH_BD_IOC<<16); } snd_intel_write_32(card,ICH_PO_BDBAR_REG,(uint32_t)pds_cardmem_physicalptr(card->dm,table_base)); @@ -367,8 +374,8 @@ static int INTELICH_adetect(struct mpxplay_audioout_info_s *aui) card->baseport_codec = pcibios_ReadConfig_Dword(card->pci_dev, PCIR_NAMBAR)&0xfff0; if(!card->baseport_codec) goto err_adetect; - card->irq = pcibios_ReadConfig_Byte(card->pci_dev, PCIR_INTR_LN); - + aui->card_irq = card->irq = pcibios_ReadConfig_Byte(card->pci_dev, PCIR_INTR_LN); + card->device_type=card->pci_dev->device_type; mpxplay_debugf(ICH_DEBUG_OUTPUT,"vend_id:%4.4X dev_id:%4.4X devtype:%s bmport:%4.4X mixport:%4.4X irq:%d", @@ -572,6 +579,18 @@ static unsigned long INTELICH_readMIXER(struct mpxplay_audioout_info_s *aui,unsi return snd_intel_codec_read(card,reg); } +#ifdef SBEMU +static void INTELICH_IRQRoutine(mpxplay_audioout_info_s* aui) +{ + intel_card_s *card=aui->card_private_data; + if(snd_intel_read_8(card,ICH_PO_SR_REG)&ICH_PO_SR_LVBCI) + snd_intel_write_8(card, ICH_PO_SR_REG, ICH_PO_SR_LVBCI); + + if(snd_intel_read_8(card,ICH_PO_SR_REG)&ICH_PO_SR_BCIS) + snd_intel_write_8(card, ICH_PO_SR_REG, ICH_PO_SR_BCIS); +} +#endif + one_sndcard_info ICH_sndcard_info={ "ICH AC97", SNDCARD_LOWLEVELHAND|SNDCARD_INT08_ALLOWED, @@ -589,7 +608,11 @@ one_sndcard_info ICH_sndcard_info={ &INTELICH_getbufpos, &MDma_clearbuf, NULL, // ICH doesn't need dma-monitor (LVI handles it) + #ifdef SBEMU + &INTELICH_IRQRoutine, + #else NULL, + #endif &INTELICH_writeMIXER, &INTELICH_readMIXER, diff --git a/SBEMU/SBEMU.C b/SBEMU/SBEMU.C index 91ed757..8dd50de 100644 --- a/SBEMU/SBEMU.C +++ b/SBEMU/SBEMU.C @@ -6,6 +6,7 @@ #define SBEMU_RESET_START 0 #define SBEMU_RESET_END 1 #define SBEMU_RESET_POLL 2 +void(*SBEMU_StartCB)(void); static int SBEMU_ResetState = SBEMU_RESET_END; static int SBEMU_Started = 0; static int SBEMU_IRQ = 5; @@ -90,6 +91,7 @@ void SBEMU_DSP_Write(uint16_t port, uint8_t value) _LOG("SBEMU: DSP write %02x, original: %02x\n", value, SBEMU_DSPCMD); if(SBEMU_HighSpeed) return; + int OldStarted = SBEMU_Started; if(SBEMU_DSPCMD == 0) { SBEMU_DSPCMD = value; @@ -109,7 +111,6 @@ void SBEMU_DSP_Write(uint16_t port, uint8_t value) } else { - int OldStarted = SBEMU_Started; switch(SBEMU_DSPCMD) { case SBEMU_CMD_SET_TIMECONST: @@ -210,7 +211,11 @@ void SBEMU_DSP_Write(uint16_t port, uint8_t value) } if(SBEMU_DSPCMD_Subindex >= 2) SBEMU_DSPCMD = 0; - /*if(SBEMU_Started && !OldStarted && SBEMU_Samples < 16)//handle driver detection + } + if(SBEMU_Started && !OldStarted)//handle driver detection + { + if(SBEMU_StartCB) SBEMU_StartCB(); + /*if(SBEMU_Samples < 16) { SBEMU_Started = 0; SBEMU_MixerRegs[SBEMU_MIXERREG_INT_STS] = (SBEMU_Bits==8) ? 0x01 : 0x02; @@ -286,10 +291,11 @@ uint8_t SBEMU_DSP_INT16ACK(uint16_t port) return 0xFF; } -void SBEMU_Init(int irq, int dma) +void SBEMU_Init(int irq, int dma, void(*startCB)(void)) { SBEMU_IRQ = irq; SBEMU_DMA = dma; + SBEMU_StartCB = startCB; } uint8_t SBEMU_GetIRQ() diff --git a/SBEMU/SBEMU.H b/SBEMU/SBEMU.H index 975d7b6..e4a3d9c 100644 --- a/SBEMU/SBEMU.H +++ b/SBEMU/SBEMU.H @@ -88,7 +88,7 @@ uint8_t SBEMU_DSP_ReadStatus(uint16_t port); //read buffer status. uint8_t SBEMU_DSP_INT16ACK(uint16_t port); //used by emulations -void SBEMU_Init(int irq, int dma); +void SBEMU_Init(int irq, int dma, void(*startCB)(void)); uint8_t SBEMU_GetIRQ(); uint8_t SBEMU_GetDMA(); int SBEMU_HasStarted();