Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[servers/soundblaster] Support 16-bit stereo #148

Open
perlun opened this issue Jul 5, 2019 · 1 comment
Open

[servers/soundblaster] Support 16-bit stereo #148

perlun opened this issue Jul 5, 2019 · 1 comment

Comments

@perlun
Copy link
Contributor

perlun commented Jul 5, 2019

The SB16 code we have currently only supports 8-bit monaural playback, up to 44 kHz. This is quite inferior to 16-bit stereo playback. Even though the .mod files don't always have 16-bit samples, it would still make sense to try and get 16-bit stereo playback working for some potential improvements in the audio quality. Should't be more than just a matter of sending the right commands to the card, I think - the VirtualBox emulates a SB16 card anyway.

@perlun
Copy link
Contributor Author

perlun commented Jul 22, 2019

Hmm... I played around with this, but whenever I try to use the 0xB6 command, I can get no sound - the audio never starts playing and I probably don't get any interrupts (or perhaps only one).

With the 0xC6 code, for playing 8-bit audio, things are working nicely, which is rather annoying. 🙁 I looked at the VirtualBox SB16 emulation code and it should work with 16-bit autoinitialized transfers as well. For reference: https://www.virtualbox.org/browser/vbox/trunk/src/VBox/Devices/Audio/DevSB16.cpp

One notable difference I can tell is that I need to read from base_port + 0x0F instead of base_port + 0x0E in the IRQ handler to acknowledge the interrupt to the card. But, even after changing this, it still doesn't work.

My next strategy: change our existing 8-bit playing code to use 0xC6 at least, i.e. use those parts of this PR. That way, we are closer to 16-bit audio but not quite there yet. (but we can probably fix 8-bit stereo output very easily then, which is always nice, still a step in the right direction... i.e. 16-bit stereo in the end.)

For reference, here is my diff so far:

diff --git a/servers/sound/soundblaster/soundblaster.c b/servers/sound/soundblaster/soundblaster.c
index bf18d82..56fea1c 100644
--- a/servers/sound/soundblaster/soundblaster.c
+++ b/servers/sound/soundblaster/soundblaster.c
@@ -54,14 +54,14 @@ static tag_type empty_tag =
 
 static void dsp_write(uint8_t data)
 {
-    while ((system_port_in_uint8_t (DSP_DATA_WRITE) & 0x80) != 0);
-    system_port_out_uint8_t (DSP_DATA_WRITE, (data));
+    while ((system_port_in_uint8_t(DSP_DATA_WRITE) & 0x80) != 0);
+    system_port_out_uint8_t(DSP_DATA_WRITE, (data));
 }
 
 static uint8_t dsp_read(void)
 {
-    while ((system_port_in_uint8_t (DSP_DATA_AVAILABLE) & 0x80) == 0);
-    return system_port_in_uint8_t (DSP_DATA_READ);
+    while ((system_port_in_uint8_t(DSP_DATA_AVAILABLE_8BIT) & 0x80) == 0);
+    return system_port_in_uint8_t(DSP_DATA_READ);
 }
 
 static void dsp_mixer_write(uint8_t which_register, uint8_t data)
@@ -77,15 +77,15 @@ static bool detect_sb(void)
     system_call_port_range_register(base_port, 16, "Sound Blaster");
 
     // Reset the DSP.
-    system_port_out_uint8_t (DSP_RESET, 0x01);
+    system_port_out_uint8_t(DSP_RESET, 0x01);
     system_sleep(4);
-    system_port_out_uint8_t (DSP_RESET, 0x00);
+    system_port_out_uint8_t(DSP_RESET, 0x00);
 
     // FIXME: Should only wait for a maximum of 100 us.
-    while ((system_port_in_uint8_t (DSP_DATA_AVAILABLE) & (1 << 7)) == 0);
+    while ((system_port_in_uint8_t(DSP_DATA_AVAILABLE_8BIT) & (1 << 7)) == 0);
 
     // Check if the DSP was reset successfully.
-    if (system_port_in_uint8_t (DSP_DATA_READ) == 0xAA)
+    if (system_port_in_uint8_t(DSP_DATA_READ) == 0xAA)
     {
         // Let's check which kind of SB this is.
         dsp_write(DSP_VERSION);
@@ -177,17 +177,16 @@ int main(void)
         }
     }
 
-
-    // Because we only support soundblaster 2.0-functionality, treat every card as a sb2.0 whatever
-    // card is installed
+    // TODO: Consider supporting other cards than SB16, or at least log
+    // an error when they are being used.
     soundblaster_device.irq = irq;
     soundblaster_device.base_port = base_port;
     soundblaster_device.dma_channel = dma_channel;
-    soundblaster_device.max_frequency_output = 22050;
+    soundblaster_device.max_frequency_output = 44100;
     soundblaster_device.supports_8bit_output = TRUE;
-    soundblaster_device.supports_16bit_output = FALSE;
+    soundblaster_device.supports_16bit_output = TRUE;
     soundblaster_device.supports_autoinit_dma = TRUE;
-    soundblaster_device.device_name = "Sound Blaster 2.0";
+    soundblaster_device.device_name = "Sound Blaster 16";
 
     // Register the DMA channel.
     if (system_call_dma_register(soundblaster_device.dma_channel,
@@ -254,7 +253,7 @@ void irq_handler(irq_handler_data_type *irq_handler_data)
         log_print(&log_structure, LOG_URGENCY_DEBUG, "Received hardware interrupt");
 
         // Acknowledge the SB interrupt.
-        system_port_in_uint8_t(DSP_DATA_AVAILABLE);
+        system_port_in_uint8_t(DSP_DATA_AVAILABLE_16BIT);
 
         // Send an acknowledgement message to the client program.
         message_parameter.protocol = IPC_PROTOCOL_NONE;
@@ -375,7 +374,7 @@ void handle_connection(mailbox_id_type reply_mailbox_id)
                 // transferred)
 
                 length = sound_message->length-1;
-                dsp_write(DSP_MODE_DMA_8BIT_DAC);
+                dsp_write(DSP_MODE_DMA_16BIT_DAC); // TODO: fix
                 dsp_write((uint8_t) length);
                 dsp_write((uint8_t)(length >> 8));
 
@@ -441,13 +440,13 @@ void handle_connection(mailbox_id_type reply_mailbox_id)
                         // Program Sound Blaster buffer length (triggers an interrupt after 'length'
                         // bytes transferred).
                         length = sound_message->length - 1;
-                        dsp_write(DSP_SET_DMA_BLOCK_SIZE);
-                        dsp_write((uint8_t) length);
-                        dsp_write((uint8_t)(length >> 8));
 
                         // Enable 8-bit auto-initializing DMA-based playing. See sblaster.doc for
-                        // more details (the 01Ch command)
-                        dsp_write(DSP_MODE_DMA_8BIT_AUTOINIT_DAC);
+                        // more details (0Bxh/0Cxh  Generic DAC/ADC DMA)
+                        dsp_write(DSP_MODE_DMA_16BIT_AUTOINIT_DAC);
+                        dsp_write(0); // Bit 5 = stereo, bit 4 = signed. 0 means "mono, unsigned"
+                        dsp_write((uint8_t) length);
+                        dsp_write((uint8_t)(length >> 8));
 
                         // Now the sample is hopefully being played, so set some variables.
                         soundblaster_event.is_playing = TRUE;
diff --git a/servers/sound/soundblaster/soundblaster.h b/servers/sound/soundblaster/soundblaster.h
index 0ffa067..e5169be 100644
--- a/servers/sound/soundblaster/soundblaster.h
+++ b/servers/sound/soundblaster/soundblaster.h
@@ -15,7 +15,8 @@
 #define DSP_RESET                       (base_port + 0x06)
 #define DSP_DATA_READ                   (base_port + 0x0A)
 #define DSP_DATA_WRITE                  (base_port + 0x0C)
-#define DSP_DATA_AVAILABLE              (base_port + 0x0E)
+#define DSP_DATA_AVAILABLE_8BIT         (base_port + 0x0E)
+#define DSP_DATA_AVAILABLE_16BIT        (base_port + 0x0F)
 
 // Commands (sent to DSP_DATA_WRITE).
 #define DSP_VERSION                     (0xE1)
@@ -29,6 +30,9 @@
 #define DSP_SET_TIME_CONSTANT           (0x40)
 #define DSP_SET_DMA_BLOCK_SIZE          (0x48)
 
+#define DSP_MODE_DMA_16BIT_DAC          (0xB0)
+#define DSP_MODE_DMA_16BIT_AUTOINIT_DAC (0xB6) // FIXME: should be 0xB6
+
 typedef struct
 {
     unsigned int irq;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant