+#if defined(PLATFORM_UNIX) && !defined(TARGET_SDL)
+
+static void CopySampleToMixingBuffer(struct SoundControl *snd_ctrl,
+ int sample_pos, int sample_size,
+ short *buffer_ptr)
+{
+ void *sample_ptr = snd_ctrl->data_ptr;
+ int i;
+
+ if (snd_ctrl->format == AUDIO_FORMAT_U8)
+ for (i=0; i<sample_size; i++)
+ *buffer_ptr++ =
+ ((short)(((byte *)sample_ptr)[sample_pos + i] ^ 0x80)) << 8;
+ else /* AUDIO_FORMAT_S16 */
+ for (i=0; i<sample_size; i++)
+ *buffer_ptr++ =
+ ((short *)sample_ptr)[sample_pos + i];
+}
+
+#if defined(AUDIO_STREAMING_DSP)
+static void SoundServer_Mixer()
+{
+ static int stereo_volume[PSND_MAX_LEFT2RIGHT + 1];
+ static boolean stereo_volume_calculated = FALSE;
+ static short premix_first_buffer[SND_BLOCKSIZE];
+ static short premix_left_buffer[SND_BLOCKSIZE];
+ static short premix_right_buffer[SND_BLOCKSIZE];
+ static long premix_last_buffer[SND_BLOCKSIZE];
+ static byte playing_buffer[SND_BLOCKSIZE];
+ boolean stereo;
+ int fragment_size;
+ int sample_bytes;
+ int max_sample_size;
+ int i, j;
+
+ if (!stereo_volume_calculated)
+ {
+ for(i=0; i<=PSND_MAX_LEFT2RIGHT; i++)
+ stereo_volume[i] =
+ (int)sqrt((float)(PSND_MAX_LEFT2RIGHT * PSND_MAX_LEFT2RIGHT - i * i));
+
+ stereo_volume_calculated = TRUE;
+ }
+
+ if (!playing_sounds)
+ return;
+
+ if (audio.device_fd < 0)
+ {
+ if ((audio.device_fd = OpenAudioDevice(audio.device_name)) < 0)
+ return;
+
+ InitAudioDevice(&afmt);
+ }
+
+ stereo = afmt.stereo;
+ fragment_size = afmt.fragment_size;
+ sample_bytes = (afmt.format & AUDIO_FORMAT_U8 ? 1 : 2);
+ max_sample_size = fragment_size / ((stereo ? 2 : 1) * sample_bytes);
+
+ /* first clear the last premixing buffer */
+ memset(premix_last_buffer, 0,
+ max_sample_size * (stereo ? 2 : 1) * sizeof(long));
+
+ for(i=0; i<audio.num_channels; i++)
+ {
+ void *sample_ptr;
+ int sample_len;
+ int sample_pos;
+ int sample_size;
+
+ if (!playlist[i].active)
+ continue;
+
+ /* pointer, lenght and actual playing position of sound sample */
+ sample_ptr = playlist[i].data_ptr;
+ sample_len = playlist[i].data_len;
+ sample_pos = playlist[i].playingpos;
+ sample_size = MIN(max_sample_size, sample_len - sample_pos);
+ playlist[i].playingpos += sample_size;
+
+ /* copy original sample to first mixing buffer */
+ CopySampleToMixingBuffer(&playlist[i], sample_pos, sample_size,
+ premix_first_buffer);
+
+ /* are we about to restart a looping sound? */
+ if (IS_LOOP(playlist[i]) && sample_size < max_sample_size)
+ {
+ while (sample_size < max_sample_size)
+ {
+ int restarted_sample_size =
+ MIN(max_sample_size - sample_size, sample_len);
+
+ if (playlist[i].format == AUDIO_FORMAT_U8)
+ for (j=0; j<restarted_sample_size; j++)
+ premix_first_buffer[sample_size + j] =
+ ((short)(((byte *)sample_ptr)[j] ^ 0x80)) << 8;
+ else
+ for (j=0; j<restarted_sample_size; j++)
+ premix_first_buffer[sample_size + j] =
+ ((short *)sample_ptr)[j];
+
+ playlist[i].playingpos = restarted_sample_size;
+ sample_size += restarted_sample_size;
+ }
+ }
+
+ /* decrease volume if sound is fading out */
+ if (IS_FADING(playlist[i]) &&
+ playlist[i].volume >= SOUND_FADING_VOLUME_THRESHOLD)
+ playlist[i].volume -= SOUND_FADING_VOLUME_STEP;
+
+ /* adjust volume of actual sound sample */
+ if (playlist[i].volume != PSND_MAX_VOLUME)
+ for(j=0; j<sample_size; j++)
+ premix_first_buffer[j] =
+ (playlist[i].volume * (long)premix_first_buffer[j])
+ >> PSND_MAX_VOLUME_BITS;
+
+ /* fill the last mixing buffer with stereo or mono sound */
+ if (stereo)
+ {
+ int middle_pos = PSND_MAX_LEFT2RIGHT / 2;
+ int left_volume = stereo_volume[middle_pos + playlist[i].stereo];
+ int right_volume= stereo_volume[middle_pos - playlist[i].stereo];
+
+ for(j=0; j<sample_size; j++)
+ {
+ premix_left_buffer[j] =
+ (left_volume * premix_first_buffer[j])
+ >> PSND_MAX_LEFT2RIGHT_BITS;
+ premix_right_buffer[j] =
+ (right_volume * premix_first_buffer[j])
+ >> PSND_MAX_LEFT2RIGHT_BITS;
+
+ premix_last_buffer[2 * j + 0] += premix_left_buffer[j];
+ premix_last_buffer[2 * j + 1] += premix_right_buffer[j];
+ }
+ }
+ else
+ {
+ for(j=0; j<sample_size; j++)
+ premix_last_buffer[j] += premix_first_buffer[j];
+ }
+
+ /* delete completed sound entries from the playlist */
+ if (playlist[i].playingpos >= playlist[i].data_len)
+ {
+ if (IS_LOOP(playlist[i]))
+ playlist[i].playingpos = 0;
+ else
+ PlaylistRemoveSound(i);
+ }
+ else if (playlist[i].volume <= SOUND_FADING_VOLUME_THRESHOLD)
+ PlaylistRemoveSound(i);
+ }
+
+ /* prepare final playing buffer according to system audio format */
+ for(i=0; i<max_sample_size * (stereo ? 2 : 1); i++)
+ {
+ /* cut off at 17 bit value */
+ if (premix_last_buffer[i] < -65535)
+ premix_last_buffer[i] = -65535;
+ else if (premix_last_buffer[i] > 65535)
+ premix_last_buffer[i] = 65535;
+
+ /* shift to 16 bit value */
+ premix_last_buffer[i] >>= 1;
+
+ if (afmt.format & AUDIO_FORMAT_U8)
+ {
+ playing_buffer[i] = (premix_last_buffer[i] >> 8) ^ 0x80;
+ }
+ else if (afmt.format & AUDIO_FORMAT_LE) /* 16 bit */
+ {
+ playing_buffer[2 * i + 0] = premix_last_buffer[i] & 0xff;
+ playing_buffer[2 * i + 1] = premix_last_buffer[i] >> 8;
+ }
+ else /* big endian */
+ {
+ playing_buffer[2 * i + 0] = premix_last_buffer[i] >> 8;
+ playing_buffer[2 * i + 1] = premix_last_buffer[i] & 0xff;
+ }
+ }
+
+ /* finally play the sound fragment */
+ write(audio.device_fd, playing_buffer, fragment_size);
+
+ if (!playing_sounds)
+ CloseAudioDevice(&audio.device_fd);
+}
+
+#else /* !AUDIO_STREAMING_DSP */
+
+static int SoundServer_SimpleAudio(struct SoundControl snd_ctrl)
+{
+ static short premix_first_buffer[SND_BLOCKSIZE];
+ static byte playing_buffer[SND_BLOCKSIZE];
+ int max_sample_size = SND_BLOCKSIZE;
+ void *sample_ptr;
+ int sample_len;
+ int sample_pos;
+ int sample_size;
+ int i, j;
+
+ i = 1;
+
+ /* pointer, lenght and actual playing position of sound sample */
+ sample_ptr = playlist[i].data_ptr;
+ sample_len = playlist[i].data_len;
+ sample_pos = playlist[i].playingpos;
+ sample_size = MIN(max_sample_size, sample_len - sample_pos);
+ playlist[i].playingpos += sample_size;
+
+ /* copy original sample to first mixing buffer */
+ CopySampleToMixingBuffer(&playlist[i], sample_pos, sample_size,
+ premix_first_buffer);
+
+ /* adjust volume of actual sound sample */
+ if (playlist[i].volume != PSND_MAX_VOLUME)
+ for(j=0; j<sample_size; j++)
+ premix_first_buffer[j] =
+ (playlist[i].volume * (long)premix_first_buffer[j])
+ >> PSND_MAX_VOLUME_BITS;
+
+ /* might be needed for u-law /dev/audio */
+#if 0
+ for(j=0; j<sample_size; j++)
+ playing_buffer[j] =
+ linear_to_ulaw(premix_first_buffer[j]);
+#endif
+
+ /* delete completed sound entries from the playlist */
+ if (playlist[i].playingpos >= playlist[i].data_len)
+ PlaylistRemoveSound(i);
+
+ for(i=0; i<sample_size; i++)
+ playing_buffer[i] = (premix_first_buffer[i] >> 8) ^ 0x80;
+
+ /* finally play the sound fragment */
+ write(audio.device_fd, playing_buffer, sample_size);
+
+ return sample_size;
+}
+#endif /* !AUDIO_STREAMING_DSP */
+
+void SoundServer()
+{
+ struct SoundControl snd_ctrl;
+ fd_set sound_fdset;
+
+ close(audio.soundserver_pipe[1]); /* no writing into pipe needed */
+
+ InitPlaylist();
+
+#if defined(PLATFORM_HPUX)
+ InitAudioDevice(&afmt);
+#endif
+
+ FD_ZERO(&sound_fdset);
+ FD_SET(audio.soundserver_pipe[0], &sound_fdset);
+
+ while(1) /* wait for sound playing commands from client */
+ {
+ struct timeval delay = { 0, 0 };
+
+ FD_SET(audio.soundserver_pipe[0], &sound_fdset);
+ select(audio.soundserver_pipe[0] + 1, &sound_fdset, NULL, NULL, NULL);
+ if (!FD_ISSET(audio.soundserver_pipe[0], &sound_fdset))
+ continue;
+ if (read(audio.soundserver_pipe[0], &snd_ctrl, sizeof(snd_ctrl))
+ != sizeof(snd_ctrl))
+ Error(ERR_EXIT_SOUND_SERVER, "broken pipe -- no sounds");
+
+ HandleSoundRequest(snd_ctrl);
+
+#if defined(AUDIO_STREAMING_DSP)
+
+ while (playing_sounds && select(audio.soundserver_pipe[0] + 1,
+ &sound_fdset, NULL, NULL, &delay) < 1)
+ {
+ FD_SET(audio.soundserver_pipe[0], &sound_fdset);
+
+ SoundServer_Mixer();
+ }
+
+#else /* !AUDIO_STREAMING_DSP */
+
+ if (!snd_ctrl.active || IS_LOOP(snd_ctrl) ||
+ (audio.device_fd = OpenAudioDevice(audio.device_name)) < 0)
+ continue;
+
+ InitAudioDevice(&afmt);
+
+ delay.tv_sec = 0;
+ delay.tv_usec = 0;
+
+ while (playing_sounds && select(audio.soundserver_pipe[0] + 1,
+ &sound_fdset, NULL, NULL, &delay) < 1)
+ {
+ int wait_percent = 90; /* wait 90% of the real playing time */
+ int sample_size;
+
+ FD_SET(audio.soundserver_pipe[0], &sound_fdset);
+
+ sample_size = SoundServer_SimpleAudio(snd_ctrl);
+
+ delay.tv_sec = 0;
+ delay.tv_usec =
+ ((sample_size * 10 * wait_percent) / afmt.sample_rate) * 1000;
+ }
+
+ CloseAudioDevice(&audio.device_fd);
+
+ /* delete all sounds from playlist */
+ InitPlaylist();
+
+#endif /* !AUDIO_STREAMING_DSP */
+ }
+}
+#endif /* PLATFORM_UNIX */
+
+#if defined(PLATFORM_MSDOS)
+static void sound_handler(struct SoundControl snd_ctrl)
+{
+ int i;
+
+ if (snd_ctrl.fade_sound)
+ {
+ if (!playing_sounds)
+ return;
+
+ for (i=0; i<audio.num_channels; i++)
+ if ((snd_ctrl.stop_all_sounds ||
+ (i != audio.music_channel && playlist[i].nr == snd_ctrl.nr) ||
+ (i == audio.music_channel && snd_ctrl.music)) &&
+ !playlist[i].fade_sound)
+ {
+ playlist[i].fade_sound = TRUE;
+ if (voice_check(playlist[i].voice))
+ voice_ramp_volume(playlist[i].voice, 1000, 0);
+ playlist[i].state &= ~SND_CTRL_IS_LOOP;
+ }
+ }
+ else if (snd_ctrl.stop_all_sounds)
+ {
+ if (!playing_sounds)
+ return;
+ SoundServer_StopAllSounds();
+ }
+ else if (snd_ctrl.stop_sound)
+ {
+ if (!playing_sounds)
+ return;
+ SoundServer_StopSound(snd_ctrl);
+ }
+
+ for (i=0; i<audio.num_channels; i++)
+ {
+ if (!playlist[i].active || playlist[i].loop)
+ continue;
+
+ playlist[i].playingpos = voice_get_position(playlist[i].voice);
+ playlist[i].volume = voice_get_volume(playlist[i].voice);
+ if (playlist[i].playingpos == -1 || !playlist[i].volume)
+ {
+ deallocate_voice(playlist[i].voice);
+ PlaylistRemoveSound(i);
+ }
+ }
+
+ if (snd_ctrl.active)
+ SoundServer_InsertNewSound(snd_ctrl);
+}
+#endif /* PLATFORM_MSDOS */
+
+#if 0
+#if defined(TARGET_SDL)
+static void sound_handler_SDL(struct SoundControl snd_ctrl)
+{
+ /* copy sound_handler() here ... */
+}
+#endif /* TARGET_SDL */
+#endif
+
+#if !defined(PLATFORM_WIN32)
+static void SoundServer_InsertNewSound(struct SoundControl snd_ctrl)
+{
+ PlaylistInsertSound(snd_ctrl);
+}
+#endif /* !PLATFORM_WIN32 */
+
+/*
+void SoundServer_FadeSound(int nr)
+{
+ int i;
+
+ if (!playing_sounds)
+ return;
+
+ for(i=0;i<audio.num_channels;i++)
+ if (snd_ctrl.stop_all_sounds || playlist[i].nr == snd_ctrl.nr)
+ playlist[i].fade_sound = TRUE;
+}
+*/
+
+#if !defined(PLATFORM_WIN32)
+#if defined(PLATFORM_MSDOS)
+static void SoundServer_StopSound(struct SoundControl snd_ctrl)
+{
+ int nr = snd_ctrl.nr;
+ int i;
+
+ if (!playing_sounds)
+ return;
+
+ for(i=0; i<audio.num_channels; i++)
+ {
+ if ((i == audio.music_channel && snd_ctrl.music) ||
+ (i != audio.music_channel && playlist[i].nr == nr))
+ {
+#if defined(PLATFORM_MSDOS)
+ voice_set_volume(playlist[i].voice, 0);
+ deallocate_voice(playlist[i].voice);
+#endif
+ PlaylistRemoveSound(i);
+ }
+ }
+
+#if !defined(PLATFORM_MSDOS)
+ if (!playing_sounds)
+ close(audio.device_fd);
+#endif
+}
+
+static void SoundServer_StopAllSounds()
+{
+ int i;
+
+ for(i=0; i<audio.num_channels; i++)
+ {
+#if defined(PLATFORM_MSDOS)
+ voice_set_volume(playlist[i].voice, 0);
+ deallocate_voice(playlist[i].voice);
+#endif
+ PlaylistRemoveSound(i);
+ }
+ playing_sounds = 0;
+
+#if !defined(PLATFORM_MSDOS)
+ close(audio.device_fd);
+#endif
+}
+#endif /* PLATFORM_MSDOS */
+#endif /* !PLATFORM_WIN32 */
+
+
+/* ------------------------------------------------------------------------- */
+/* platform dependant audio initialization code */
+/* ------------------------------------------------------------------------- */
+
+#if defined(AUDIO_LINUX_IOCTL)
+static void InitAudioDevice_Linux(struct AudioFormatInfo *afmt)
+{
+ /* "ioctl()" expects pointer to 'int' value for stereo flag
+ (boolean is defined as 'char', which will not work here) */
+ unsigned int fragment_spec = 0;
+ int fragment_size_query;
+ int stereo = TRUE;
+ struct
+ {
+ int format_ioctl;
+ int format_result;
+ }
+ formats[] =
+ {
+ /* supported audio format in preferred order */
+ { AFMT_S16_LE, AUDIO_FORMAT_S16 | AUDIO_FORMAT_LE },
+ { AFMT_S16_BE, AUDIO_FORMAT_S16 | AUDIO_FORMAT_BE },
+ { AFMT_U8, AUDIO_FORMAT_U8 },
+ { -1, -1 }
+ };
+ int i;
+
+ /* determine logarithm (log2) of the fragment size */
+ while ((1 << fragment_spec) < afmt->fragment_size)
+ fragment_spec++;
+
+ /* use two fragments (play one fragment, prepare the other);
+ one fragment would result in interrupted audio output, more
+ than two fragments would raise audio output latency to much */
+ fragment_spec |= 0x00020000;
+
+ /* Example for fragment specification:
+ - 2 buffers / 512 bytes (giving 1/16 second resolution for 8 kHz)
+ - (with stereo the effective buffer size will shrink to 256)
+ => fragment_size = 0x00020009 */
+
+ if (ioctl(audio.device_fd, SNDCTL_DSP_SETFRAGMENT, &fragment_spec) < 0)
+ Error(ERR_EXIT_SOUND_SERVER,
+ "cannot set fragment size of /dev/dsp -- no sounds");
+
+ i = 0;
+ afmt->format = 0;
+ while (formats[i].format_result != -1)
+ {
+ unsigned int audio_format = formats[i].format_ioctl;
+ if (ioctl(audio.device_fd, SNDCTL_DSP_SETFMT, &audio_format) == 0)
+ {
+ afmt->format = formats[i].format_result;
+ break;
+ }
+ }
+
+ if (afmt->format == 0) /* no supported audio format found */
+ Error(ERR_EXIT_SOUND_SERVER,
+ "cannot set audio format of /dev/dsp -- no sounds");
+
+ /* try if we can use stereo sound */
+ afmt->stereo = TRUE;
+ if (ioctl(audio.device_fd, SNDCTL_DSP_STEREO, &stereo) < 0)
+ afmt->stereo = FALSE;
+
+ if (ioctl(audio.device_fd, SNDCTL_DSP_SPEED, &afmt->sample_rate) < 0)
+ Error(ERR_EXIT_SOUND_SERVER,
+ "cannot set sample rate of /dev/dsp -- no sounds");
+
+ /* get the real fragmentation size; this should return 512 */
+ if (ioctl(audio.device_fd, SNDCTL_DSP_GETBLKSIZE, &fragment_size_query) < 0)
+ Error(ERR_EXIT_SOUND_SERVER,
+ "cannot get fragment size of /dev/dsp -- no sounds");
+ if (fragment_size_query != afmt->fragment_size)
+ Error(ERR_EXIT_SOUND_SERVER,
+ "cannot set fragment size of /dev/dsp -- no sounds");
+}
+#endif /* AUDIO_LINUX_IOCTL */
+
+#if defined(PLATFORM_NETBSD)
+static void InitAudioDevice_NetBSD(struct AudioFormatInfo *afmt)
+{
+ audio_info_t a_info;
+ boolean stereo = TRUE;
+
+ AUDIO_INITINFO(&a_info);
+ a_info.play.encoding = AUDIO_ENCODING_LINEAR8;
+ a_info.play.precision = 8;
+ a_info.play.channels = 2;
+ a_info.play.sample_rate = sample_rate;
+ a_info.blocksize = fragment_size;
+
+ afmt->format = AUDIO_FORMAT_U8;
+ afmt->stereo = TRUE;
+
+ if (ioctl(audio.device_fd, AUDIO_SETINFO, &a_info) < 0)
+ {
+ /* try to disable stereo */
+ a_info.play.channels = 1;
+
+ afmt->stereo = FALSE;
+
+ if (ioctl(audio.device_fd, AUDIO_SETINFO, &a_info) < 0)
+ Error(ERR_EXIT_SOUND_SERVER,
+ "cannot set sample rate of /dev/audio -- no sounds");
+ }
+}
+#endif /* PLATFORM_NETBSD */
+
+#if defined(PLATFORM_HPUX)
+static void InitAudioDevice_HPUX(struct AudioFormatInfo *afmt)
+{
+ struct audio_describe ainfo;
+ int audio_ctl;
+
+ audio_ctl = open("/dev/audioCtl", O_WRONLY | O_NDELAY);
+ if (audio_ctl == -1)
+ Error(ERR_EXIT_SOUND_SERVER, "cannot open /dev/audioCtl -- no sounds");
+
+ if (ioctl(audio_ctl, AUDIO_DESCRIBE, &ainfo) == -1)
+ Error(ERR_EXIT_SOUND_SERVER, "no audio info -- no sounds");