Skip to contentSkip to conversation

sound patch

PATCH Stas Sergeev 2 participants 3 messages Sep 23, 2009 Sound

lore
Share & tools
lore
#0 lore raw

Hello.

I extracted the sound code from my 3 years old local dosemu branch and re-diffed it against the current svn. This took lot of a hand-work, so the mistakes are possible.
A quick test shows that the adlib now works on x86-64. But many progs (non-adlib ones) are still mute because the cpuemu doesn't exit after every port I/O the way vm86() did. The result is that run_irqs() doesn't come in time.

If someone is willing to work on this, here's the patch.

base/dev/sb16/adlib.c +44 -21
@@ -39,6 +39,7 @@
 #define ADLIB_BASE 0x388
 #define OPL3_INTERNAL_FREQ    14400000	// The OPL3 operates at 14.4MHz
 #define OPL3_MAX_BUF 512
+#define ADLIB_CHANNELS 2
 
 #define ADLIB_THRESHOLD 2000000
 #define ADLIB_RUNNING() (adlib_time_cur > 0)
@@ -64,11 +65,15 @@
 
 Bit8u adlib_io_read_base(ioport_t port)
 {
+    Bit8u ret;
 #ifdef HAS_YMF262
-    return YMF262Read(opl3, port);
+    adlib_timer();
+    ret = YMF262Read(opl3, port);
 #else
-    return 0xff;
+    ret = 0xff;
 #endif
+    S_printf("Adlib: Read %hhx from port %x\n", ret, port);
+    return ret;
 }
 
 static Bit8u adlib_io_read(ioport_t port)
@@ -79,8 +84,10 @@
 void adlib_io_write_base(ioport_t port, Bit8u value)
 {
     adlib_time_last = GETusTIME(0);
+    S_printf("Adlib: Write %hhx to port %x\n", value, port);
 #ifdef HAS_YMF262
     YMF262Write(opl3, port, value);
+    adlib_timer();
 #endif
 }
 
@@ -93,8 +100,10 @@
 static void opl3_set_timer(void *param, int num, double interval_Sec)
 {
     long long *timers = param;
-    timers[num] =
-	interval_Sec > 0 ? GETusTIME(0) + interval_Sec * 1000000 : 0;
+    if (interval_Sec > 0)
+	timers[num] += GETusTIME(0) + interval_Sec * 1000000;
+    else
+	timers[num] = 0;
     S_printf("Adlib: timer %i set to %ius\n", num,
 	     (int) (interval_Sec * 1000000));
 }
@@ -105,6 +114,12 @@
 	ADLIB_RUN();
     run_new_sb();
 }
+
+static void opl3_irq(void *param, int irq)
+{
+    S_printf("SB: OPL3 IRQ (%i)\n", irq);
+    /* this IRQ is not wired, nothing to do */
+}
 #endif
 
 void opl3_init(void)
@@ -120,7 +135,7 @@
     io_device.write_portw = NULL;
     io_device.read_portd = NULL;
     io_device.write_portd = NULL;
-    io_device.handler_name = "Adlib (+ Advanced) Emulation";
+    io_device.handler_name = "OPL3";
     io_device.start_addr = ADLIB_BASE;
     io_device.end_addr = ADLIB_BASE + 3;
     io_device.irq = EMU_NO_IRQ;
@@ -130,14 +145,15 @@
     }
 #ifdef HAS_YMF262
     opl3 = YMF262Init(OPL3_INTERNAL_FREQ, opl3_rate);
-    YMF262SetTimerHandler(opl3, opl3_set_timer, &opl3_timers);
+    YMF262SetTimerHandler(opl3, opl3_set_timer, opl3_timers);
     YMF262SetUpdateHandler(opl3, opl3_update, NULL);
+    YMF262SetIRQHandler(opl3, opl3_irq, NULL);
 #endif
 }
 
 void adlib_init(void)
 {
-    adlib_strm = pcm_allocate_stream(2, "Adlib");
+    adlib_strm = pcm_allocate_stream(ADLIB_CHANNELS, "Adlib");
 }
 
 void adlib_reset(void)
@@ -157,13 +173,24 @@
 }
 
 #ifdef HAS_YMF262
-static void adlib_process_samples(int samps)
+static void adlib_process_samples(int mono_samps)
 {
-    const int chan_map[] = { 0, 1, 0, 1 };
-    int i, j, k;
+    const int chan_map[] =
+#if ADLIB_CHANNELS == 2
+	{ 0, 1, 0, 1 };
+#elif ADLIB_CHANNELS == 1
+	{ 0, 0, 0, 0 };
+#elif ADLIB_CHANNELS == 4
+	{ 0, 1, 2, 3 };
+#else
+#error ADLIB_CHANNELS is wrong
+	{ -1, -1, -1, -1 };
+#endif
+    int i, j, k, samps;
     OPL3SAMPLE *chans[4], buf[4][OPL3_MAX_BUF], buf3;
-    int buf2[OPL3_MAX_BUF][2];
+    int buf2[OPL3_MAX_BUF][ADLIB_CHANNELS];
 
+    samps = mono_samps / ADLIB_CHANNELS;
     if (samps > OPL3_MAX_BUF) {
 	error("Adlib: too many samples requested (%i)\n", samps);
 	samps = OPL3_MAX_BUF;
@@ -174,17 +201,13 @@
     YMF262UpdateOne(opl3, chans, samps);
 
     for (i = 0; i < samps; i++) {
-	for (j = 0; j < 2; j++) {
+	for (j = 0; j < ADLIB_CHANNELS; j++) {
 	    buf2[i][j] = 0;
 	    for (k = 0; k < ARRAY_SIZE(chan_map); k++) {
 		if (chan_map[k] == j)
 		    buf2[i][j] += buf[k][i];
 	    }
-	    if (buf2[i][j] > SHRT_MAX)
-		buf2[i][j] = SHRT_MAX;
-	    if (buf2[i][j] < SHRT_MIN)
-		buf2[i][j] = SHRT_MIN;
-	    buf3 = buf2[i][j];
+	    buf3 = pcm_samp_cutoff(buf2[i][j], opl3_format);
 	    pcm_write_samples(&buf3, pcm_format_size(opl3_format),
 			      opl3_rate, opl3_format, adlib_strm);
 	}
@@ -204,13 +227,13 @@
 	pcm_flush(adlib_strm);
     }
     if (ADLIB_RUNNING()) {
-	period = pcm_samp_period(opl3_rate, 2);
+	period = pcm_samp_period(opl3_rate, ADLIB_CHANNELS);
 	nsamps = (now - adlib_time_cur) / period;
 	if (nsamps > OPL3_MAX_BUF)
 	    nsamps = OPL3_MAX_BUF;
-	nsamps -= nsamps % 2;
+	nsamps -= nsamps % ADLIB_CHANNELS;
 	if (nsamps) {
-	    adlib_process_samples(nsamps / 2);
+	    adlib_process_samples(nsamps);
 	    adlib_time_cur += nsamps * period;
 	    S_printf("SB: processed %i Adlib samples\n", nsamps);
 	}
@@ -219,7 +242,7 @@
     for (i = 0; i < 2; i++) {
 	if (opl3_timers[i] > 0 && now > opl3_timers[i]) {
 	    S_printf("Adlib: timer %i expired\n", i);
-	    opl3_timers[i] = 0;
+	    opl3_timers[i] = now - opl3_timers[i];
 	    YMF262TimerOver(opl3, i);
 	}
     }
base/dev/sb16/dspio.c renamed from base/dev/sb16/dspio.c.orig +7 -21
@@ -36,7 +36,7 @@
 #include "sound/sndpcm.h"
 #include "sound/midi.h"
 #include "adlib.h"
-#include "dma.h"
+#include "dmanew.h"
 #include "sb16.h"
 #include "dspio.h"
 #include <string.h>
@@ -65,22 +65,6 @@
 
 #define DSPIO ((struct dspio_state *)dspio)
 
-static int dma8_get_format(int is_signed)
-{
-    return is_signed ? PCM_FORMAT_S8 : PCM_FORMAT_U8;
-}
-
-static int dma16_get_format(int is_signed)
-{
-    return is_signed ? PCM_FORMAT_S16_LE : PCM_FORMAT_U16_LE;
-}
-
-static int dma_get_format(int is_16, int is_signed)
-{
-    return is_16 ? dma16_get_format(is_signed) :
-	dma8_get_format(is_signed);
-}
-
 static void dma_get_silence(int is_signed, int is16bit, void *ptr)
 {
     if (is16bit) {
@@ -163,9 +147,10 @@
     if (state->output_running)
 	return;
     S_printf("SB: starting output\n");
-    /* We would need real time here, but the HACK is to use stream time instead.
+    /* We would need real time here, but the HACK is to use stream
+     * timestamp instead.
      * That compensates the hack of dspio_process_dma() */
-    state->output_time_cur = pcm_get_stream_time(state->dma_strm);
+    state->output_time_cur = pcm_calc_tstamp(state->dma.rate, state->dma_strm);
     state->output_running = 1;
 }
 
@@ -296,6 +281,7 @@
     if (state->dma.running) {
 	state->dma.stereo = sb_dma_samp_stereo();
 	state->dma.rate = sb_get_dma_sampling_rate();
+	state->dma.samp_signed = sb_dma_samp_signed();
     }
 
     while (state->output_running && (state->output_time_cur <= time_dst ||
@@ -308,13 +294,13 @@
 	    if (state->speaker) {
 		pcm_write_samples(&buf, 1 << state->dma.is16bit,
 				  state->dma.rate,
-				  dma_get_format(state->dma.is16bit,
+				  pcm_get_format(state->dma.is16bit,
 						 state->dma.samp_signed),
 				  state->dma_strm);
 		if (!state->dma.stereo)
 		    pcm_write_samples(&buf, 1 << state->dma.is16bit,
 				      state->dma.rate,
-				      dma_get_format(state->dma.is16bit,
+				      pcm_get_format(state->dma.is16bit,
 						     state->dma.
 						     samp_signed),
 				      state->dma_strm);
base/dev/sb16/sb16.c +3 -2
@@ -636,6 +636,7 @@
     hdma_idx = config.sb_hdma ? 1 << config.sb_hdma : 0;
     sb.mixer_regs[0x80] = irq_idx;
     sb.mixer_regs[0x81] = dma_idx | hdma_idx;
+    sb.mixer_regs[0x82] = SB16_ID82;
 }
 
 static void sb_reset(void)
@@ -920,8 +921,8 @@
     case 0xE1:
 	/* DSP Version - SB */
 	S_printf("SB: Query Version\n");
-	dsp_write_output(SB_16 >> 8);
-	dsp_write_output(SB_16 & 0xFF);
+	dsp_write_output(SB16_ID >> 8);
+	dsp_write_output(SB16_ID & 0xFF);
 	break;
 
     case 0xE2: {
base/dev/sb16/sb16.h +9 -5
@@ -26,11 +26,15 @@
 #include "utilities.h"		// for rng_s
 
 #define SB_NONE  0x000
-#define SB_OLD	 0x105
-#define SB_20	 0x201
-#define SB_PRO	 0x300
-#define SB_16	 0x405
-#define SB_AWE32 0x40C
+#define SB_ID	 0x105
+#define SB20_ID	 0x201
+#define SBPRO_ID 0x300
+#define SB16_ID	 0x405
+#define SBAWE32_ID 0x40C
+
+/* bochs and the old dosemu code disagree on that value.
+ * Of course I trust bochs. :) */
+#define SB16_ID82 (2 << 5)
 
 /*
  * Various Status values
base/dev/dma/dmanew.c +71 -119

283 lines not on this page · raw message

base/dev/dma/dmaregs.h new file +74 -0

74 lines not on this page · raw message

base/sound/nullsnd.c +2 -3
@@ -37,7 +37,6 @@
 static const char *nullsnd_name = "Sound Output: NULL device";
 const int frag_size = 4096;
 static struct player_params params;
-static struct player_callbacks calls;
 static int running, locked;
 static double last_time = 0;
 
@@ -83,7 +82,7 @@
     time = GETusTIME(0);
     while (time - last_time > frag_time) {
 	last_time += frag_time;
-	calls.get_data(NULL, frag_size, &params);
+	pcm_data_get(NULL, frag_size, &params);
     }
 }
 
@@ -99,5 +98,5 @@
     player.unlock = nullsnd_unlock;
     player.timer = nullsnd_timer;
     running = locked = 0;
-    return pcm_register_clocked_player(player, &calls);
+    return pcm_register_clocked_player(player);
 }
base/sound/sndpcm.c +53 -21

150 lines not on this page · raw message

plugin/midimisc/mid_o_tmdty.c +5 -4
@@ -52,7 +51,7 @@
 #define TMDTY_BIN "timidity"
 #define TMDTY_ARGS "-EFreverb=0 -EFchorus=0 -EFresamp=1 -EFvlpf=0 -EFns=0"
 
-static const char *midotmdty_name = "MIDI Output: midid plugin";
+static const char *midotmdty_name = "MIDI Output: TiMidity++ plugin";
 
 static int ctrl_sock_in, ctrl_sock_out, data_sock, pcm_stream;
 static pid_t tmdty_pid = -1;
@@ -71,7 +70,8 @@
     while ((selret = select(data_sock + 1, &rfds, NULL, NULL, &tv)) > 0) {
 	n = RPT_SYSCALL(read(data_sock, buf, sizeof(buf)));
 	if (n > 0) {
-	    pcm_write_samples(buf, n, 44100, PCM_FORMAT_S16_LE,
+	    pcm_write_samples(buf, n, TMDTY_FREQ, pcm_get_format(
+			      TMDTY_8BIT ? 0 : 1, TMDTY_UNS ? 0 : 1),
 			      pcm_stream);
 	} else {
 	    break;
@@ -233,10 +225,10 @@
     if (ret) {
 	const char *ver_str = "Server Version ";
 	char *ptr = strstr(buf, ver_str);
-	int vmin, vmid, vmaj, ver;
 	if (!ptr) {
 	    ret = FALSE;
 	} else {
+	    int vmin, vmid, vmaj, ver;
 	    ptr += strlen(ver_str);
 	    sscanf(ptr, "%d.%d.%d", &vmaj, &vmid, &vmin);
 	    ver = vmaj * 10000 + vmid * 100 + vmin;
@@ -311,7 +303,7 @@
 
     if (TMDTY_CAPT) {
 	add_to_io_select(data_sock, 1, midotmdty_io);
-	pcm_stream = pcm_allocate_stream(2, "MIDI");
+	pcm_stream = pcm_allocate_stream(TMDTY_MONO ? 1 : 2, "MIDI");
     }
 
     return TRUE;
plugin/sdl/snd_o_SDL.c +2 -4
@@ -33,12 +33,11 @@
 #include <SDL.h>
 
 static const char *sdlsnd_name = "Sound Output: SDL device";
-static struct player_callbacks calls;
 static struct player_params params;
 
 static void sdlsnd_callback(void *userdata, Uint8 * stream, int len)
 {
-    calls.get_data(stream, len, &params);
+    pcm_data_get(stream, len, &params);
 }
 
 static void sdlsnd_start(void)
@@ -87,9 +86,8 @@
     player.close = sdlsnd_close;
     player.lock = SDL_LockAudio;
     player.unlock = SDL_UnlockAudio;
-    player.timer = NULL;
 #if 1
-    return pcm_register_clocked_player(player, &calls);
+    return pcm_register_clocked_player(player);
 #else
     return 0;
 #endif
plugin/sndfile/snd_o_wav.c +1 -2
@@ -34,7 +34,7 @@
 #include <stdio.h>
 #include <sndfile.h>
 
-static const char *wavsnd_name = "Sound Output: WAV writer";
+static const char *wavsnd_name = "Sound Output: WAV file writer";
 static SNDFILE *wav;
 
 static int wavsnd_open(struct player_params *par)
@@ -67,7 +67,6 @@
     player.open = wavsnd_open;
     player.close = wavsnd_close;
     player.write = wavsnd_write;
-    player.timer = NULL;
 #if 0
     return pcm_register_unclocked_player(player);
 #else
include/sound/sndpcm.h +4 -2
@@ -30,13 +30,15 @@
 extern void pcm_set_flag(int strm_idx, int flag);
 extern void pcm_set_mode(int strm_idx, int mode);
 extern int pcm_flush(int strm_idx);
+extern int pcm_samp_cutoff(int val, int format);
+extern int pcm_get_format(int is_16, int is_signed);
 extern double pcm_samp_period(double rate, int channels);
 extern double pcm_frag_period(int size, struct player_params *params);
 extern void pcm_write_samples(void *ptr, size_t size,
-	double rate, int format, int strm_idx);
+			      double rate, int format, int strm_idx);
 extern int pcm_format_size(int format);
 extern void pcm_timer(void);
-extern double pcm_get_stream_time(int strm_idx);
+extern double pcm_calc_tstamp(double rate, int strm_idx);
 
 #define PCM_FLAG_RAW 1
 
include/sound/sound.h +5 -9
@@ -20,8 +20,8 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
-#ifndef _SOUND_H
-#define _SOUND_H
+#ifndef __SOUND_H__
+#define __SOUND_H__
 
 /* This is the correct way to run an SB timer tick */
 extern void run_new_sb(void);
@@ -36,9 +36,7 @@
   int channels;
 };
 
-struct player_callbacks {
-  size_t (*get_data)(void *buf, size_t size, struct player_params *params);
-};
+size_t pcm_data_get(void *data, size_t size, struct player_params *params);
 
 struct clocked_player {
   const char *name;
@@ -55,12 +53,10 @@
   const char *name;
   int (*open)(struct player_params *params);
   void (*close)(void);
-  void (*timer)(void);
   size_t (*write)(void *buf, size_t size);
 };
 
-extern int pcm_register_clocked_player(struct clocked_player player,
-    struct player_callbacks *callbacks);
+extern int pcm_register_clocked_player(struct clocked_player player);
 extern int pcm_register_unclocked_player(struct unclocked_player player);
 
 /** PCM sample format */
@@ -84,4 +80,4 @@
 	PCM_FORMAT_IMA_ADPCM,
 };
 
-#endif		/* EMU_SOUND_H */
+#endif
include/dma.h +0 -5
Index: dma.h
===================================================================
@@ -9,12 +9,7 @@
 
 void dma_init(void);
 void dma_reset(void);
-void dma_new_init(void);
-void dma_new_reset(void);
 
-enum { DMA_NO_DACK, DMA_DACK };
-int dma_pulse_DRQ(int ch, Bit8u *buf);
-
 /* 8237 DMA controllers */
 #define IO_DMA1_BASE	0x00	/* 8 bit slave DMA, channels 0..3 */
 #define IO_DMA2_BASE	0xC0	/* 16 bit master DMA, ch 4(=slave input)..7 */
include/dmanew.h new file +33 -0
@@ -0,0 +1,33 @@
+/*
+ *  Copyright (C) 2006 Stas Sergeev <[email protected]>
+ *
+ * The below copyright strings have to be distributed unchanged together
+ * with this file. This prefix can not be modified or separated.
+ */
+
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __DMA_H__
+#define __DMA_H__
+
+void dma_new_init(void);
+void dma_new_reset(void);
+
+enum { DMA_NO_DACK, DMA_DACK };
+int dma_pulse_DRQ(int ch, Bit8u *buf);
+
+#endif
base/dev/dma/dma.c +1 -0
Index: dma.c
===================================================================
@@ -51,6 +51,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include "dma.h"
+#include "dmanew.h"
 #include "pic.h"
 #include "port.h"
 
#1 lore raw
Quoted 6 lines · Stas Sergeev

A quick test shows that the adlib now works on x86-64. But many progs (non-adlib ones) are still mute because the cpuemu doesn't exit after every port I/O the way vm86() did. The result is that run_irqs() doesn't come in time.

Thanks for the patch, I'll have a look. I fixed the cpuemu I/O issue in SVN now so cpuemu generates a GPF for every I/O except for some VGAEMU optimizations and real ports.

Bart

#2 lore raw
Quoted 3 lines · Bart Oldeman

Thanks for the patch, I'll have a look. I fixed the cpuemu I/O issue in SVN now so cpuemu generates a GPF for every I/O except for some VGAEMU optimizations and real ports.

Cool, works for me, but I am still
not getting the SF mails.