Sound Blaster improvements :

- Implemented audio recording on SB16 & AWE32 (MIDI only)
- Mixer improvements
- SB 2.0 CD mixer can be emulated
- DSP tweaks, mainly for SB Pro. Implemented speaker on/off and limited high speed DMA to SB 2.0 & Pro
- A few other things
Patch from JosepMa.
This commit is contained in:
SarahW 2017-09-09 14:58:07 +01:00
commit 4ca6d67f87
5 changed files with 1188 additions and 297 deletions

View file

@ -16,10 +16,22 @@
#include "sound_sb_dsp.h"
#include "timer.h"
/*The recording safety margin is intended for uneven "len" calls to the get_buffer mixer calls on sound_sb*/
#define SB_DSP_REC_SAFEFTY_MARGIN 4096
void pollsb(void *p);
void sb_poll_i(void *p);
//#define SB_DSP_RECORD_DEBUG
//#define SB_TEST_RECORDING_SAW
#ifdef SB_DSP_RECORD_DEBUG
FILE* soundf = 0/*NULL*/;
#endif
#ifdef SB_TEST_RECORDING_SAW
int counttest;
#endif
static int sbe2dat[4][9] = {
{ 0x01, -0x02, -0x04, 0x08, -0x10, 0x20, 0x40, -0x80, -106 },
@ -133,9 +145,20 @@ void sb_dsp_reset(sb_dsp_t *dsp)
dsp->sbreset = 0;
dsp->sbenable = dsp->sb_enable_i = dsp->sb_count_i = 0;
dsp->record_pos_read=0;
dsp->record_pos_write=SB_DSP_REC_SAFEFTY_MARGIN;
picintc(1 << dsp->sb_irqnum);
dsp->asp_data_len = 0;
#ifdef SB_DSP_RECORD_DEBUG
if (soundf != 0)
{
fclose(soundf);
soundf=0;
}
#endif
}
void sb_doreset(sb_dsp_t *dsp)
@ -205,8 +228,8 @@ void sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len
if (dsp->sb_8_enable && dsp->sb_8_output) dsp->sb_8_enable = 0;
dsp->sb_16_output = 1;
timer_process();
dsp->sbenable = dsp->sb_16_enable;
timer_update_outstanding();
dsp->sbenable = dsp->sb_16_enable;
timer_update_outstanding();
// pclog("Start 16-bit DMA addr %06X len %04X\n",dma16.ac[1]+(dma16.page[1]<<16),len);
}
}
@ -215,6 +238,13 @@ void sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int l
{
if (dma8)
{
#ifdef SB_TEST_RECORDING_SAW
switch (dsp->sb_8_format)
{
case 00:case 20:counttest=0x80;break;
case 10:case 30:counttest=0;break;
}
#endif
dsp->sb_8_length = len;
dsp->sb_8_format = format;
dsp->sb_8_autoinit = autoinit;
@ -229,6 +259,13 @@ void sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int l
}
else
{
#ifdef SB_TEST_RECORDING_SAW
switch (dsp->sb_16_format)
{
case 00:case 20:counttest=0x8000;break;
case 10:case 30:counttest=0;break;
}
#endif
dsp->sb_16_length = len;
dsp->sb_16_format = format;
dsp->sb_16_autoinit = autoinit;
@ -241,6 +278,16 @@ void sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int l
timer_update_outstanding();
// pclog("Start 16-bit input DMA addr %06X len %04X\n",dma.ac[1]+(dma.page[1]<<16),len);
}
memset(dsp->record_buffer,0,sizeof(dsp->record_buffer));
#ifdef SB_DSP_RECORD_DEBUG
if (soundf != 0)
{
fclose(soundf);
soundf=0;
}
#endif
}
int sb_8_read_dma(sb_dsp_t *dsp)
@ -250,14 +297,23 @@ int sb_8_read_dma(sb_dsp_t *dsp)
void sb_8_write_dma(sb_dsp_t *dsp, uint8_t val)
{
dma_channel_write(dsp->sb_8_dmanum, val);
#ifdef SB_DSP_RECORD_DEBUG
if (!soundf) soundf=fopen("sound_dsp.pcm","wb");
fwrite(&val,1,1,soundf);
#endif
}
uint16_t sb_16_read_dma(sb_dsp_t *dsp)
{
return dma_channel_read(5);
return dma_channel_read(dsp->sb_16_dmanum);
}
void sb_16_write_dma(sb_dsp_t *dsp, uint16_t val)
int sb_16_write_dma(sb_dsp_t *dsp, uint16_t val)
{
dma_channel_write(5, val);
int ret = dma_channel_write(dsp->sb_16_dmanum, val);
#ifdef SB_DSP_RECORD_DEBUG
if (!soundf) soundf=fopen("sound_dsp.pcm","wb");
fwrite(&val,2,1,soundf);
#endif
return (ret == DMA_NODATA);
}
void sb_dsp_setirq(sb_dsp_t *dsp, int irq)
@ -270,6 +326,10 @@ void sb_dsp_setdma8(sb_dsp_t *dsp, int dma)
dsp->sb_8_dmanum = dma;
}
void sb_dsp_setdma16(sb_dsp_t *dsp, int dma)
{
dsp->sb_16_dmanum = dma;
}
void sb_exec_command(sb_dsp_t *dsp)
{
int temp,c;
@ -310,7 +370,19 @@ void sb_exec_command(sb_dsp_t *dsp)
dsp->sb_8_length--;
break;
case 0x20: /*8-bit direct input*/
sb_add_data(dsp, 0);
sb_add_data(dsp, (dsp->record_buffer[dsp->record_pos_read]>>8) ^0x80);
/*Due to the current implementation, I need to emulate a samplerate, even if this
* mode does not imply such samplerate. Position is increased in sb_poll_i*/
if (dsp->sb_enable_i==0)
{
dsp->sb_timei = 256 - 22;
dsp->sblatchi = TIMER_USEC * 22;
temp = 1000000 / 22;
dsp->sb_freq = temp;
timer_process();
dsp->sb_enable_i = 1;
timer_update_outstanding();
}
break;
case 0x24: /*8-bit single cycle DMA input*/
sb_start_dma_i(dsp, 1, 0, 0, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
@ -373,28 +445,30 @@ void sb_exec_command(sb_dsp_t *dsp)
case 0x80: /*Pause DAC*/
dsp->sb_pausetime = dsp->sb_data[0] + (dsp->sb_data[1] << 8);
// pclog("SB pause %04X\n",sb_pausetime);
timer_process();
timer_process();
dsp->sbenable = 1;
timer_update_outstanding();
break;
case 0x90: /*High speed 8-bit autoinit DMA output*/
if (dsp->sb_type < SB2) break;
if (dsp->sb_type < SB2 || dsp->sb_type > SBPRO2) break;
sb_start_dma(dsp, 1, 1, 0, dsp->sb_8_autolen);
break;
case 0x91: /*High speed 8-bit single cycle DMA output*/
if (dsp->sb_type < SB2) break;
if (dsp->sb_type < SB2 || dsp->sb_type > SBPRO2) break;
sb_start_dma(dsp, 1, 0, 0, dsp->sb_8_autolen);
break;
case 0x98: /*High speed 8-bit autoinit DMA input*/
if (dsp->sb_type < SB2) break;
if (dsp->sb_type < SB2 || dsp->sb_type > SBPRO2) break;
sb_start_dma_i(dsp, 1, 1, 0, dsp->sb_8_autolen);
break;
case 0x99: /*High speed 8-bit single cycle DMA input*/
if (dsp->sb_type < SB2) break;
if (dsp->sb_type < SB2 || dsp->sb_type > SBPRO2) break;
sb_start_dma_i(dsp, 1, 0, 0, dsp->sb_8_autolen);
break;
case 0xA0: /*Set input mode to mono*/
case 0xA8: /*Set input mode to stereo*/
if (dsp->sb_type < SB2 || dsp->sb_type > SBPRO2) break;
//TODO: Implement. 3.xx-only command.
break;
case 0xB0: case 0xB1: case 0xB2: case 0xB3:
case 0xB4: case 0xB5: case 0xB6: case 0xB7: /*16-bit DMA output*/
@ -424,9 +498,17 @@ void sb_exec_command(sb_dsp_t *dsp)
dsp->sb_8_pause = 1;
break;
case 0xD1: /*Speaker on*/
if (dsp->sb_type < SB15 )
dsp->sb_8_pause = 1;
else if ( dsp->sb_type < SB16 )
dsp->muted = 0;
dsp->sb_speaker = 1;
break;
case 0xD3: /*Speaker off*/
if (dsp->sb_type < SB15 )
dsp->sb_8_pause = 1;
else if ( dsp->sb_type < SB16 )
dsp->muted = 1;
dsp->sb_speaker = 0;
break;
case 0xD4: /*Continue 8-bit DMA*/
@ -481,6 +563,10 @@ void sb_exec_command(sb_dsp_t *dsp)
// pclog("Trigger IRQ\n");
sb_irq(dsp, 1);
break;
case 0xF3: /*Trigger 16-bit IRQ*/
// pclog("Trigger IRQ\n");
sb_irq(dsp, 0);
break;
case 0xE7: /*???*/
case 0xFA: /*???*/
break;
@ -517,6 +603,22 @@ void sb_exec_command(sb_dsp_t *dsp)
break;
// default:
// fatal("Exec bad SB command %02X\n",sb_command);
/*TODO: Some more data about the DSP registeres
* http://the.earth.li/~tfm/oldpage/sb_dsp.html
* http://www.synchrondata.com/pheaven/www/area19.htm
* http://www.dcee.net/Files/Programm/Sound/
0E3h DSP Copyright SBPro2???
0F0h Sine Generator SB
0F1h DSP Auxiliary Status (Obsolete) SB-Pro2
0F2h IRQ Request, 8-bit SB
0F3h IRQ Request, 16-bit SB16
0FBh DSP Status SB16
0FCh DSP Auxiliary Status SB16
0FDh DSP Command Status SB16
*/
}
}
@ -570,7 +672,6 @@ void sb_write(uint16_t a, uint8_t v, void *priv)
uint8_t sb_read(uint16_t a, void *priv)
{
sb_dsp_t *dsp = (sb_dsp_t *)priv;
// if (a==0x224) output=1;
// pclog("sb_read : Read soundblaster %04X %04X:%04X\n",a,CS,pc);
switch (a & 0xf)
{
@ -613,8 +714,10 @@ void sb_dsp_init(sb_dsp_t *dsp, int type)
{
dsp->sb_type = type;
// Default values. Use sb_dsp_setxxx() methods to change.
dsp->sb_irqnum = 7;
dsp->sb_8_dmanum = 1;
dsp->sb_16_dmanum = 5;
sb_doreset(dsp);
@ -670,7 +773,7 @@ void pollsb(void *p)
dsp->sbleftright = !dsp->sbleftright;
}
else
dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
dsp->sb_8_length--;
break;
case 0x10: /*Mono signed*/
@ -685,7 +788,7 @@ void pollsb(void *p)
dsp->sbleftright = !dsp->sbleftright;
}
else
dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
dsp->sb_8_length--;
break;
case 0x20: /*Stereo unsigned*/
@ -737,7 +840,7 @@ void pollsb(void *p)
dsp->sbleftright = !dsp->sbleftright;
}
else
dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
break;
case ADPCM_26:
@ -771,7 +874,7 @@ void pollsb(void *p)
dsp->sbleftright = !dsp->sbleftright;
}
else
dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
break;
case ADPCM_2:
@ -801,7 +904,7 @@ void pollsb(void *p)
dsp->sbleftright = !dsp->sbleftright;
}
else
dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
break;
// default:
@ -839,7 +942,6 @@ void pollsb(void *p)
dsp->sbdatr = sb_16_read_dma(dsp);
dsp->sb_16_length -= 2;
break;
// default:
// fatal("Unrecognised SB 16-bit format %02X\n",sb_16_format);
}
@ -867,32 +969,57 @@ void pollsb(void *p)
void sb_poll_i(void *p)
{
sb_dsp_t *dsp = (sb_dsp_t *)p;
int processed=0;
dsp->sb_count_i += dsp->sblatchi;
// pclog("PollSBi %i %i %i %i\n",sb_8_enable,sb_8_pause,sb_pausetime,sb_8_output);
if (dsp->sb_8_enable && !dsp->sb_8_pause && dsp->sb_pausetime < 0 && !dsp->sb_8_output)
{
switch (dsp->sb_8_format)
{
case 0x00: /*Mono unsigned*/
sb_8_write_dma(dsp, 0x80);
#ifdef SB_TEST_RECORDING_SAW
case 0x00: /*Unsigned mono. As the manual says, only the left channel is recorded*/
case 0x10: /*Signed mono. As the manual says, only the left channel is recorded*/
sb_8_write_dma(dsp, counttest);
counttest+=0x10;
counttest&=0xFF;
dsp->sb_8_length--;
break;
case 0x10: /*Mono signed*/
sb_8_write_dma(dsp, 0x00);
case 0x20: /*Unsigned stereo*/
case 0x30: /*Signed stereo*/
sb_8_write_dma(dsp, counttest);
sb_8_write_dma(dsp, counttest);
counttest+=0x10;
counttest&=0xFF;
dsp->sb_8_length -= 2;
break;
#else
case 0x00: /*Mono unsigned As the manual says, only the left channel is recorded*/
sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read]>>8) ^0x80);
dsp->sb_8_length--;
dsp->record_pos_read+=2;
dsp->record_pos_read&=0xFFFF;
break;
case 0x10: /*Mono signed As the manual says, only the left channel is recorded*/
sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read]>>8));
dsp->sb_8_length--;
dsp->record_pos_read+=2;
dsp->record_pos_read&=0xFFFF;
break;
case 0x20: /*Stereo unsigned*/
sb_8_write_dma(dsp, 0x80);
sb_8_write_dma(dsp, 0x80);
sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read]>>8)^0x80);
sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read+1]>>8)^0x80);
dsp->sb_8_length -= 2;
dsp->record_pos_read+=2;
dsp->record_pos_read&=0xFFFF;
break;
case 0x30: /*Stereo signed*/
sb_8_write_dma(dsp, 0x00);
sb_8_write_dma(dsp, 0x00);
sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read]>>8));
sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read+1]>>8));
dsp->sb_8_length -= 2;
dsp->record_pos_read+=2;
dsp->record_pos_read&=0xFFFF;
break;
#endif
// default:
// fatal("Unrecognised SB 8-bit input format %02X\n",sb_8_format);
}
@ -901,33 +1028,65 @@ void sb_poll_i(void *p)
{
// pclog("Input DMA over %i\n",sb_8_autoinit);
if (dsp->sb_8_autoinit) dsp->sb_8_length = dsp->sb_8_autolen;
else dsp->sb_8_enable = dsp->sbenable = 0;
else dsp->sb_8_enable = dsp->sb_enable_i = 0;
sb_irq(dsp, 1);
}
processed=1;
}
if (dsp->sb_16_enable && !dsp->sb_16_pause && dsp->sb_pausetime < 0 && !dsp->sb_16_output)
{
switch (dsp->sb_16_format)
{
case 0x00: /*Unsigned mono*/
sb_16_write_dma(dsp, 0x8000);
dsp->sb_16_length--;
break;
case 0x10: /*Signed mono*/
sb_16_write_dma(dsp, 0);
#ifdef SB_TEST_RECORDING_SAW
case 0x00: /*Unsigned mono. As the manual says, only the left channel is recorded*/
case 0x10: /*Signed mono. As the manual says, only the left channel is recorded*/
if (sb_16_write_dma(dsp, counttest))
return;
counttest+=0x1000;
counttest&=0xFFFF;
dsp->sb_16_length--;
break;
case 0x20: /*Unsigned stereo*/
sb_16_write_dma(dsp, 0x8000);
sb_16_write_dma(dsp, 0x8000);
case 0x30: /*Signed stereo*/
if (sb_16_write_dma(dsp, counttest))
return;
sb_16_write_dma(dsp, counttest);
counttest+=0x1000;
counttest&=0xFFFF;
dsp->sb_16_length -= 2;
break;
#else
case 0x00: /*Unsigned mono. As the manual says, only the left channel is recorded*/
if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read]^0x8000))
return;
dsp->sb_16_length--;
dsp->record_pos_read+=2;
dsp->record_pos_read&=0xFFFF;
break;
case 0x10: /*Signed mono. As the manual says, only the left channel is recorded*/
if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read]))
return;
dsp->sb_16_length--;
dsp->record_pos_read+=2;
dsp->record_pos_read&=0xFFFF;
break;
case 0x20: /*Unsigned stereo*/
if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read]^0x8000))
return;
sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read+1]^0x8000);
dsp->sb_16_length -= 2;
dsp->record_pos_read+=2;
dsp->record_pos_read&=0xFFFF;
break;
case 0x30: /*Signed stereo*/
sb_16_write_dma(dsp, 0);
sb_16_write_dma(dsp, 0);
if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read]))
return;
sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read+1]);
dsp->sb_16_length -= 2;
dsp->record_pos_read+=2;
dsp->record_pos_read&=0xFFFF;
break;
#endif
// default:
// fatal("Unrecognised SB 16-bit input format %02X\n",sb_16_format);
}
@ -936,20 +1095,43 @@ void sb_poll_i(void *p)
{
// pclog("16iDMA over %i\n",sb_16_autoinit);
if (dsp->sb_16_autoinit) dsp->sb_16_length = dsp->sb_16_autolen;
else dsp->sb_16_enable = dsp->sbenable = 0;
else dsp->sb_16_enable = dsp->sb_enable_i = 0;
sb_irq(dsp, 0);
}
processed=1;
}
//Assume this is direct mode
if (!processed)
{
dsp->record_pos_read+=2;
dsp->record_pos_read&=0xFFFF;
}
}
void sb_dsp_update(sb_dsp_t *dsp)
{
/*this "if" implements two things: speaker on/off and buffer cleanup after stopping audio. */
if (!dsp->sbenable || dsp->muted)
{
dsp->sbdatl=0;
dsp->sbdatr=0;
}
for (; dsp->pos < sound_pos_global; dsp->pos++)
{
dsp->buffer[dsp->pos*2] = dsp->sbdatl;
dsp->buffer[dsp->pos*2 + 1] = dsp->sbdatr;
}
}
void sb_dsp_close(sb_dsp_t *dsp)
{
#ifdef SB_DSP_RECORD_DEBUG
if (soundf != 0)
{
fclose(soundf);
soundf=0;
}
#endif
}
void sb_dsp_add_status_info(char *s, int max_len, sb_dsp_t *dsp)
{