Hello.
Yesterday I have looked into the sound code of dosemu and found it in not
as miserable state as I expected after so many complains in this list.
Now I already made some programs to work with sound: pv (mp3 player),
iplay (stm player) some other utilities and some games.
The fact that I don't have a sound card appears to be not a big problem: I have
pc-speaker, and that just required some more hacks.
The attached patch does not solve all of the sound problems in dosemu, but
it is probably still worth to test, though it is a bit hackish yet.
Mostly it must fix DMA Auto-Init modes, but also improves direct DSP writes a
little.
And, as I can't test it very well myself without a sound card, report the
results, please.
The diffs are against dosemu-1.1.2, but should work with 1.0.2 either.
--- src/include/dma.h Sat Nov 17 05:40:34 2001
+++ src/include/dma.h Sun Nov 18 00:15:35 2001
@@ -31,6 +31,7 @@
void dma_drop_DACK(int channel);
void dma_assert_DREQ(int channel);
void dma_assert_DACK(int channel);
+int dma_get_block_size (int channel);
#define DMA_HANDLER_READ 1
--- src/include/sound.h Mon Oct 29 23:01:37 2001
+++ src/include/sound.h Sun Nov 18 01:13:06 2001
@@ -94,9 +94,9 @@
__u8 data; /* Data is available */
__u8 write_size_mode; /* Are we writing the upper or lower byte */
__u8 last_write; /* First part of a multi-byte instruction */
- __u16 length; /* Length of the DMA transfer */
- __u16 blocksize; /* **CRISK** Block size of transfer */
- __u16 bytes_left; /* No. of bytes left in current blk */
+ int length; /* Length of the DMA transfer */
+ int blocksize; /* **CRISK** Block size of transfer */
+ int bytes_left; /* No. of bytes left in current blk */
/*
* This is the maximum number of bytes transferred via DMA. If you turn
* it too low, the dosemu-overhead gets too big, so the transfer is
@@ -105,7 +105,7 @@
* jump in too high steps
*/
#define MAX_DMA_TRANSFERSIZE 512
- __u16 dma_transfer_size;
+ int dma_transfer_size;
__u8 empty_state; /* what we have to do when transfer ends */
#define DREQ_AT_EMPTY 1
#define IRQ_AT_EMPTY 2
@@ -154,7 +154,7 @@
void (* DMA_stop)(void);
int (* DMA_complete_test)(void);
void (* DMA_complete)(void);
- void (* DMA_set_blocksize)(__u16); /* **CRISK** */
+ void (* DMA_set_blocksize)(int, int); /* **CRISK** */
/*
* Miscellaneous Functions
@@ -267,6 +267,7 @@
*/
#define DSP_QUEUE_SIZE 64
+#define DSP_CMD_QUEUE_SIZE 50000
EXTERN struct SB_queue_t {
__u8 output[DSP_QUEUE_SIZE]; /* Output Queue */
--- src/base/dev/dma/dma.c Mon Oct 29 23:01:37 2001
+++ src/base/dev/dma/dma.c Sun Nov 18 00:22:16 2001
@@ -180,7 +180,7 @@
dma[controller].status |= mask;
- if(!dma[controller].i[ch].mask)
+// if(!dma[controller].i[ch].mask)
activate_channel (controller, ch);
}
@@ -602,8 +602,21 @@
h_printf ("DMA: Wrote 0x%x into Channel %d Length (Byte %d)\n", value,
(dma_c *4) + channel, dma[dma_c].ff);
+ if (dma[dma_c].ff)
+ h_printf("DMA: Transfer size set to %d\n",
+ dma_get_block_size((dma_c * 4) + channel) + 1);
dma_toggle_ff (dma_c);
+}
+
+int dma_get_block_size (int channel)
+{
+ int controller, ch;
+
+ controller = (channel & 4) >> 2;
+ ch = channel & DMA_CH_SELECT;
+ return (dma[controller].length[ch].data[0] +
+ (dma[controller].length[ch].data[1] << 8));
}
inline void dma_write_mask (int dma_c, Bit8u value)
--- src/arch/linux/dosext/sound/linux_sound.c Mon Oct 29 23:01:37 2001
+++ src/arch/linux/dosext/sound/linux_sound.c Sun Nov 18 01:55:05 2001
@@ -61,8 +61,10 @@
/* New fragment control - AM */
static int sound_frag_size = 0x9; /* ie MAX_DMA_TRANSFERSIZE (== 512) */
+static int sb_frag_size = 512;
static int num_sound_frag = MAX_NUM_FRAGMENTS;
static int extra_buffer = 8000*BUFFER_MSECS/1000;
+static int bits_per_samp = 0;
/* MPU static vars */
static int mpu_fd = -1; /* -1 = closed */
@@ -70,9 +72,11 @@
extern void sb_set_speed (void); /* From sound.c */
-void linux_sb_dma_set_blocksize(__u16 sb_blocksize)
+void linux_sb_dma_set_blocksize(int sb_blocksize, int dma_blocksize)
{
- __u16 blockbits, oss_blocksize, oss_blocknum;
+ int blockbits, oss_blocksize, oss_blocknum;
+
+ sb_frag_size = sb_blocksize;
/*
* The blocksize we are passed is the actual number of bytes to include in a
@@ -93,10 +97,10 @@
* starts.
*/
- if(sb_blocksize > MAX_DMA_TRANSFERSIZE) {
+ if(dma_blocksize > MAX_DMA_TRANSFERSIZE) {
oss_blocksize = MAX_DMA_TRANSFERSIZE;
} else {
- oss_blocksize = sb_blocksize;
+ oss_blocksize = dma_blocksize;
}
block_size = oss_blocksize;
@@ -110,11 +114,11 @@
/* This is intentionally here, because fragments should not be greater
than the SB block size */
- sb_blocksize += extra_buffer;
+ dma_blocksize += extra_buffer;
- oss_blocknum = sb_blocksize / (1 << sound_frag_size);
+ oss_blocknum = dma_blocksize / (1 << sound_frag_size);
- if (sb_blocksize - ( oss_blocknum * (1 << sound_frag_size)) > 0 ) {
+ if (dma_blocksize - ( oss_blocknum * (1 << sound_frag_size)) > 0 ) {
num_sound_frag = oss_blocknum + 1;
} else {
num_sound_frag = oss_blocknum;
@@ -124,7 +128,7 @@
num_sound_frag = MAX_NUM_FRAGMENTS;
}
- S_printf ("SB:[Linux] DMA blocksize set to %u (%u,%u)\n", sb_blocksize,
+ S_printf ("SB:[Linux] DMA blocksize set to %u (%u,%u)\n", dma_blocksize,
sound_frag_size, num_sound_frag);
}
@@ -235,7 +239,6 @@
static void linux_sb_DAC_write (int bits, __u8 value)
{
- static int last_bits = 0;
static int sound_frag = 0x0200007;
static __u8 buffer[128];
static __u8 buffer_count = 0;
@@ -245,14 +248,13 @@
if (buffer_count == 128)
{
- if (bits != last_bits)
+ if (bits != bits_per_samp)
{
S_printf ("SB:[Linux] Intialising Direct DAC write (%u bits)\n", bits);
- last_bits = bits;
- ioctl (dsp_fd, SNDCTL_DSP_SAMPLESIZE, &last_bits);
+ bits_per_samp = bits;
+ ioctl (dsp_fd, SNDCTL_DSP_SAMPLESIZE, &bits);
ioctl (dsp_fd, SNDCTL_DSP_SETFRAGMENT, &sound_frag);
- SB_dsp.time_constant = 193; /* Approx 16kHz */
- sb_set_speed();
+ linux_sb_set_speed(6500, 0);
}
write (dsp_fd, buffer, buffer_count);
@@ -484,7 +486,7 @@
int linux_sb_dma_complete_test(void)
{
- int free_fragments = 0, total_fragments = 0;
+ int extra_fragments, free_fragments = 0, total_fragments = 0;
int result = DMA_HANDLER_OK;
result = linux_sb_get_free_fragments(&total_fragments, &free_fragments);
@@ -496,8 +498,14 @@
* filled as soon as possible. - MK
*/
+ extra_fragments = sb_frag_size / ((1 << sound_frag_size) * 2);
+ if (extra_fragments < 2)
+ extra_fragments = 2;
+ if (extra_fragments > total_fragments / 2)
+ extra_fragments = total_fragments / 2;
+
if (result == DMA_HANDLER_OK &&
- free_fragments >= (total_fragments - 2)) {
+ free_fragments >= (total_fragments - extra_fragments)) {
return DMA_HANDLER_OK;
}
@@ -655,7 +663,7 @@
void SB_driver_reset() {
S_printf ("SB:[Linux] SB Driver Reset Called\n");
-
+ bits_per_samp = 0; /* to reinitialize on direct DAC writes */
}
void FM_driver_reset() {
--- src/dosext/sound/sound.c Sun Nov 4 19:50:13 2001
+++ src/dosext/sound/sound.c Sun Nov 18 02:07:22 2001
@@ -96,6 +96,8 @@
inline __u8 dsp_read_output(void);
static void start_dsp_dma(void);
+static void handle_dsp_commands(Bit8u value);
+static void process_dsp_queue(void);
static void restart_dsp_dma(void);
static void pause_dsp_dma(void);
@@ -151,7 +153,7 @@
void sb_do_reset (Bit8u value);
-int sb_set_length(Bit16u *variable); /* LSB, MSB */
+int sb_set_length(int *variable); /* LSB, MSB */
int sb_set_rate(Bit16u *variable); /* MSB, LSB */
@@ -168,6 +170,9 @@
/* Not in the array as it fixes a "bug" in the SB programming */
static int sb_uses_broken_dma = 0;
+static Bit8u DSP_cmd_queue[DSP_CMD_QUEUE_SIZE];
+static int DSP_queued = 0;
+
/*
************************************************************
* DSP Queue functions : Used to allow multi-byte responses *
@@ -311,6 +316,7 @@
/* DSP 8-bit IRQ Ack - SB */
S_printf("SB: Ready?/8-bit IRQ Ack: %x\n", SB_dsp.data);
SB_info.irq.active &= ~SB_IRQ_8BIT; /* may mean it never triggers! */
+ process_dsp_queue();
if(SB_dsp.empty_state & DREQ_AT_EOI)
{
dma_assert_DREQ(config.sb_dma);
@@ -325,6 +331,7 @@
SB_info.irq.active &= ~SB_IRQ_16BIT; /* may mean it never triggers! */
+ process_dsp_queue();
if(SB_dsp.empty_state & DREQ_AT_EOI)
{
dma_assert_DREQ(config.sb_dma);
@@ -844,6 +851,30 @@
void sb_dsp_write ( Bit8u value )
{
+ S_printf("SB: value 0x%x written to DSP...\n", value);
+ if (DSP_queued < DSP_CMD_QUEUE_SIZE)
+ DSP_cmd_queue[DSP_queued++] = value;
+ if (DSP_queued >= DSP_CMD_QUEUE_SIZE - 2) {
+ S_printf("Warning: DSP queue overflowed!!!\n");
+ SB_dsp.ready = 0xff;
+ }
+ sb_check_complete();
+ if (DSP_queued)
+ S_printf("SB: %d bytes in DSP queue\n", DSP_queued);
+}
+
+void process_dsp_queue (void)
+{
+int i;
+ if(DSP_queued > 1)
+ S_printf("SB: Processing DSP command queue, %d bytes\n", DSP_queued);
+ for(i = 0; i < DSP_queued; i++)
+ handle_dsp_commands(DSP_cmd_queue[i]);
+ DSP_queued = 0;
+}
+
+void handle_dsp_commands ( Bit8u value )
+{
/*
* ALL commands set SB_dsp.command to SB_NO_DSP_COMMAND when they
* complete
@@ -855,10 +886,13 @@
SB_dsp.parameter = 0;
SB_dsp.write_size_mode = 0;
SB_dsp.have_parameter = SB_PARAMETER_EMPTY;
+ S_printf("SB: DSP command 0x%x accepted\n", value);
}
else {
SB_dsp.parameter = value;
SB_dsp.have_parameter = SB_PARAMETER_FULL;
+ S_printf("SB: DSP parameter 0x%x accepted for command 0x%x\n",
+ value, SB_dsp.command);
}
switch (SB_dsp.command) {
@@ -1327,6 +1361,8 @@
* Allow drop through, since this is probably the speaker
* (or something!)
*/
+ if (!SB_dsp.sample_rate)
+ return;
break;
}
@@ -1884,7 +1920,7 @@
* Returns 1 when both bytes are set, and 0 otherwise.
*/
-int sb_set_length(Bit16u *variable)
+int sb_set_length(int *variable)
{
if (SB_dsp.have_parameter != SB_PARAMETER_EMPTY) {
if (!SB_dsp.write_size_mode) {
@@ -1943,7 +1979,7 @@
case 0x76:
case 0x77:
/* Multi-parameter DMA commands - Supported (!) */
- if (! sb_set_length(&SB_dsp.length)) {
+ if (! set_dma_blocksize()) {
/* Not complete yet */
return;
}
@@ -2102,10 +2138,12 @@
};
/* Set up the housekeeping for the DMA transfer */
- SB_dsp.bytes_left = SB_dsp.blocksize;
+ SB_dsp.bytes_left = SB_dsp.length;
SB_dsp.dma_transfer_size = (SB_dsp.blocksize > MAX_DMA_TRANSFERSIZE)
? MAX_DMA_TRANSFERSIZE : SB_dsp.blocksize;
+ sb_set_speed(); /* set again, it could be destroyed by direct DAC writes */
+
if (SB_driver.DMA_start_complete == NULL) {
S_printf ("SB: Required function 'DMA_start_complete' not provided.\n");
}
@@ -2149,20 +2187,22 @@
int set_dma_blocksize(void)
{
if (SB_info.version >= SB_20) {
- if (sb_set_length (&SB_dsp.blocksize))
+ if (sb_set_length (&SB_dsp.length))
{
+ SB_dsp.length++;
/* DMA system transfers 1 extra byte, so ensure we know about it */
- SB_dsp.blocksize++;
- if (SB_driver.DMA_set_blocksize != NULL)
- (*SB_driver.DMA_set_blocksize)(SB_dsp.blocksize);
- S_printf("SB: DMA blocksize set to %u.\n",
- SB_dsp.blocksize);
- SB_dsp.command = SB_NO_DSP_COMMAND; /* **CRISK** multibyte fix */
+ SB_dsp.blocksize = dma_get_block_size(config.sb_dma) + 1;
+ S_printf("SB: DSP transfer length is set to %d\n", SB_dsp.length);
+ S_printf("SB: DMA block size is set to %d\n", SB_dsp.blocksize);
+ if (SB_driver.DMA_set_blocksize != NULL)
+ (*SB_driver.DMA_set_blocksize)(SB_dsp.length, SB_dsp.blocksize);
+ SB_dsp.command = SB_NO_DSP_COMMAND; /* **CRISK** multibyte fix */
return 1;
}
}
+ S_printf("SB: length LSB=%d\n", SB_dsp.length);
return 0;
}
@@ -2198,7 +2238,7 @@
result = DMA_HANDLER_OK;
if (d.sound >= 2) {
- S_printf ("SB: In DMA Handler\n");
+ S_printf ("SB: In DMA Handler: status=%d amount=%d\n", status, amount);
}
if (!SB_info.speaker) {
@@ -2213,11 +2253,15 @@
if(!amount)
return DMA_HANDLER_OK; /* I'm getting a zero length call when DMA
auto-reinits. Ignore it! */
- if (d.sound >= 2) {
- S_printf ("SB: Outputted %d bytes\n",amount);
- }
+
+ sb_is_running |= DSP_OUTPUT_RUN;
SB_dsp.bytes_left -= amount;
+
+ if (d.sound >= 2) {
+ S_printf ("SB: Outputted %d bytes, %d bytes left\n",
+ amount, SB_dsp.bytes_left);
+ }
/* For to not reach impossible transfer speeds and thus overfilling
the oss-driver-buffer, we interrupt data-flow till we see enough
@@ -2236,19 +2280,18 @@
dma_transfer_size(config.sb_dma,SB_dsp.dma_transfer_size);
}
SB_dsp.empty_state = DREQ_AT_EMPTY;
- sb_is_running |= DSP_OUTPUT_RUN;
}
else
{
S_printf("SB: Done block, triggering IRQ\n");
/* Generate IRQ when DMA complete */
SB_dsp.empty_state = IRQ_AT_EMPTY;
- sb_is_running |= DSP_OUTPUT_RUN;
/* We are at the end of an block */
+ dma_drop_DREQ(config.sb_dma);
if(SB_dsp.dma_mode & DMA_AUTO_INIT)
{
/* Reset block size */
- SB_dsp.bytes_left = SB_dsp.blocksize;
+ SB_dsp.bytes_left = SB_dsp.length;
/* Reset DMA transfer size */
if(SB_dsp.blocksize > MAX_DMA_TRANSFERSIZE)
SB_dsp.dma_transfer_size = MAX_DMA_TRANSFERSIZE;
@@ -2309,8 +2352,14 @@
dma_assert_DREQ(config.sb_dma);
if(SB_dsp.empty_state & IRQ_AT_EMPTY)
sb_activate_irq(SB_IRQ_8BIT);
+ else
+ process_dsp_queue();
SB_dsp.empty_state &= ~(DREQ_AT_EMPTY | IRQ_AT_EMPTY);
+ SB_dsp.ready = 0;
}
+
+ if (DSP_queued)
+ sb_is_running |= DSP_OUTPUT_RUN;
}
/*
@@ -2494,6 +2543,7 @@
sb_disable_speaker();
dsp_clear_output ();
+ SB_dsp.ready = 0;
SB_dsp.dma_mode &= ~DMA_AUTO_INIT;
dma_drop_DREQ(config.sb_dma);
@@ -2508,7 +2558,9 @@
SB_dsp.sb16_playmode = 0xFF;
SB_dsp.blocksize = 0;
+ SB_dsp.length = 0;
sb_uses_broken_dma = 0;
+ DSP_queued = 0;
SB_driver_reset();
}
@@ -2544,6 +2596,7 @@
else {
(*SB_driver.DAC_write)(bits, value);
}
+ sb_is_running |= DSP_OUTPUT_RUN;
}