+static int ulaw_to_linear(unsigned char ulawbyte)
+{
+ static int exp_lut[8] = { 0, 132, 396, 924, 1980, 4092, 8316, 16764 };
+ int sign, exponent, mantissa, sample;
+
+ ulawbyte = ~ ulawbyte;
+ sign = ( ulawbyte & 0x80 );
+ exponent = ( ulawbyte >> 4 ) & 0x07;
+ mantissa = ulawbyte & 0x0F;
+ sample = exp_lut[exponent] + ( mantissa << ( exponent + 3 ) );
+ if (sign != 0)
+ sample = -sample;
+
+ return(sample);
+}
+#endif /* AUDIO_UNIX_NATIVE && !AUDIO_STREAMING_DSP */
+
+
+/* THE STUFF ABOVE IS ONLY USED BY THE SOUND SERVER CHILD PROCESS */
+/* ========================================================================= */
+/* THE STUFF BELOW IS ONLY USED BY THE MAIN PROCESS */
+
+#define CHUNK_ID_LEN 4 /* IFF style chunk id length */
+#define WAV_HEADER_SIZE 16 /* size of WAV file header */
+
+static void *Load_WAV(char *filename)
+{
+ SoundInfo *snd_info;
+#if defined(AUDIO_UNIX_NATIVE)
+ struct SoundHeader_WAV header;
+#if 0
+ byte sound_header_buffer[WAV_HEADER_SIZE];
+ int i;
+#endif
+ char chunk_name[CHUNK_ID_LEN + 1];
+ int chunk_size;
+ int data_byte_len;
+ FILE *file;
+#endif
+
+ if (!audio.sound_available)
+ return NULL;
+
+ snd_info = checked_calloc(sizeof(SoundInfo));
+
+#if defined(TARGET_SDL)
+
+ if ((snd_info->data_ptr = Mix_LoadWAV(filename)) == NULL)
+ {
+ Error(ERR_WARN, "cannot read sound file '%s'", filename);
+ free(snd_info);
+ return NULL;
+ }
+
+ snd_info->data_len = ((Mix_Chunk *)snd_info->data_ptr)->alen;
+
+#elif defined(TARGET_ALLEGRO)
+
+ if ((snd_info->data_ptr = load_sample(filename)) == NULL)
+ {
+ Error(ERR_WARN, "cannot read sound file '%s'", filename);
+ free(snd_info);
+ return NULL;
+ }
+
+ snd_info->data_len = ((SAMPLE *)snd_info->data_ptr)->len;
+
+#else /* AUDIO_UNIX_NATIVE */
+
+ clear_mem(&header, sizeof(struct SoundHeader_WAV)); /* to make gcc happy */
+
+ if ((file = fopen(filename, MODE_READ)) == NULL)
+ {
+ Error(ERR_WARN, "cannot open sound file '%s'", filename);
+ free(snd_info);
+ return NULL;
+ }
+
+ /* read chunk id "RIFF" */
+ getFileChunkLE(file, chunk_name, &chunk_size);
+ if (!strEqual(chunk_name, "RIFF"))
+ {
+ Error(ERR_WARN, "missing 'RIFF' chunk of sound file '%s'", filename);
+ fclose(file);
+ free(snd_info);
+ return NULL;
+ }
+
+ /* read "RIFF" type id "WAVE" */
+ getFileChunkLE(file, chunk_name, NULL);
+ if (!strEqual(chunk_name, "WAVE"))
+ {
+ Error(ERR_WARN, "missing 'WAVE' type ID of sound file '%s'", filename);
+ fclose(file);
+ free(snd_info);
+ return NULL;
+ }
+
+ while (getFileChunkLE(file, chunk_name, &chunk_size))
+ {
+ if (strEqual(chunk_name, "fmt "))
+ {
+ if (chunk_size < WAV_HEADER_SIZE)
+ {
+ Error(ERR_WARN, "sound file '%s': chunk 'fmt ' too short", filename);
+ fclose(file);
+ free(snd_info);
+ return NULL;
+ }
+
+ header.compression_code = getFile16BitLE(file);
+ header.num_channels = getFile16BitLE(file);
+ header.sample_rate = getFile32BitLE(file);
+ header.bytes_per_second = getFile32BitLE(file);
+ header.block_align = getFile16BitLE(file);
+ header.bits_per_sample = getFile16BitLE(file);
+
+ if (chunk_size > WAV_HEADER_SIZE)
+ ReadUnusedBytesFromFile(file, chunk_size - WAV_HEADER_SIZE);
+
+ if (header.compression_code != 1)
+ {
+ Error(ERR_WARN, "sound file '%s': compression code %d not supported",
+ filename, header.compression_code);
+ fclose(file);
+ free(snd_info);
+ return NULL;
+ }
+
+ if (header.num_channels != 1 &&
+ header.num_channels != 2)
+ {
+ Error(ERR_WARN, "sound file '%s': number of %d channels not supported",
+ filename, header.num_channels);
+ fclose(file);
+ free(snd_info);
+ return NULL;
+ }
+
+ if (header.bits_per_sample != 8 &&
+ header.bits_per_sample != 16)
+ {
+ Error(ERR_WARN, "sound file '%s': %d bits per sample not supported",
+ filename, header.bits_per_sample);
+ fclose(file);
+ free(snd_info);
+ return NULL;
+ }
+
+ /* warn, but accept wrong sample rate (may be only slightly different) */
+ if (header.sample_rate != DEFAULT_AUDIO_SAMPLE_RATE)
+ Error(ERR_WARN, "sound file '%s': wrong sample rate %d instead of %d",
+ filename, header.sample_rate, DEFAULT_AUDIO_SAMPLE_RATE);
+
+#if 0
+ printf("WAV file: '%s'\n", filename);
+ printf(" Compression code: %d'\n", header.compression_code);
+ printf(" Number of channels: %d'\n", header.num_channels);
+ printf(" Sample rate: %d'\n", header.sample_rate);
+ printf(" Average bytes per second: %d'\n", header.bytes_per_second);
+ printf(" Block align: %d'\n", header.block_align);
+ printf(" Significant bits per sample: %d'\n", header.bits_per_sample);
+#endif
+ }
+ else if (strEqual(chunk_name, "data"))
+ {
+ data_byte_len = chunk_size;
+
+ snd_info->data_len = data_byte_len;
+ snd_info->data_ptr = checked_malloc(snd_info->data_len);
+
+ /* read sound data */
+ if (fread(snd_info->data_ptr, 1, snd_info->data_len, file) !=
+ snd_info->data_len)
+ {
+ Error(ERR_WARN,"cannot read 'data' chunk of sound file '%s'",filename);
+ fclose(file);
+ free(snd_info->data_ptr);
+ free(snd_info);
+ return NULL;
+ }
+
+ /* check for odd number of data bytes (data chunk is word aligned) */
+ if ((data_byte_len % 2) == 1)
+ ReadUnusedBytesFromFile(file, 1);
+ }
+ else /* unknown chunk -- ignore */
+ ReadUnusedBytesFromFile(file, chunk_size);
+ }
+
+ fclose(file);
+
+ if (snd_info->data_ptr == NULL)
+ {
+ Error(ERR_WARN, "missing 'data' chunk of sound file '%s'", filename);
+ free(snd_info);
+ return NULL;
+ }
+
+ if (header.bits_per_sample == 8)
+ snd_info->format = AUDIO_FORMAT_U8;
+ else /* header.bits_per_sample == 16 */
+ {
+ snd_info->format = AUDIO_FORMAT_S16;
+ snd_info->data_len /= 2; /* correct number of samples */
+ }
+
+ snd_info->num_channels = header.num_channels;
+ if (header.num_channels == 2)
+ snd_info->data_len /= 2; /* correct number of samples */
+
+#if 0
+ if (header.num_channels == 1) /* convert mono sound to stereo */
+ {
+ void *buffer_ptr = checked_malloc(data_byte_len * 2);
+ void *sample_ptr = snd_info->data_ptr;
+ int sample_size = snd_info->data_len;
+ int i;
+
+ if (snd_ctrl->format == AUDIO_FORMAT_U8)
+ for (i = 0; i < sample_size; i++)
+ *buffer_ptr++ =
+ ((short)(((byte *)sample_ptr)[i] ^ 0x80)) << 8;
+ else /* AUDIO_FORMAT_S16 */
+ for (i = 0; i < sample_size; i++)
+ *buffer_ptr++ =
+ ((short *)sample_ptr)[i];
+ }
+#endif
+
+#endif /* AUDIO_UNIX_NATIVE */
+
+ snd_info->type = SND_TYPE_WAV;
+ snd_info->source_filename = getStringCopy(filename);
+
+ return snd_info;
+}
+
+static void *Load_MOD(char *filename)
+{
+#if defined(TARGET_SDL)
+ MusicInfo *mod_info;
+
+ if (!audio.sound_available)
+ return NULL;
+
+ mod_info = checked_calloc(sizeof(MusicInfo));
+
+ if ((mod_info->data_ptr = Mix_LoadMUS(filename)) == NULL)
+ {
+ Error(ERR_WARN, "cannot read music file '%s'", filename);
+ free(mod_info);
+ return NULL;
+ }
+
+ mod_info->type = MUS_TYPE_MOD;
+ mod_info->source_filename = getStringCopy(filename);
+
+ return mod_info;
+#else
+ return NULL;
+#endif
+}
+
+static void *Load_WAV_or_MOD(char *filename)
+{
+#if 1
+ if (FileIsMusic(filename))
+ return Load_MOD(filename);
+ else if (FileIsSound(filename))
+ return Load_WAV(filename);
+ else
+ return NULL;
+#else
+ if (FileIsSound(filename))
+ return Load_WAV(filename);
+ else if (FileIsMusic(filename))
+ return Load_MOD(filename);
+ else
+ return NULL;
+#endif
+}
+
+#if 1
+
+void LoadCustomMusic_NoConf(void)
+{
+ static boolean draw_init_text = TRUE; /* only draw at startup */
+ static char *last_music_directory = NULL;
+ char *music_directory = getCustomMusicDirectory();
+ Directory *dir;
+ DirectoryEntry *dir_entry;
+ int num_music = getMusicListSize();
+
+ if (!audio.sound_available)
+ return;
+
+ if (last_music_directory != NULL &&
+ strEqual(last_music_directory, music_directory))
+ return; /* old and new music directory are the same */
+
+ if (last_music_directory != NULL)
+ free(last_music_directory);
+ last_music_directory = getStringCopy(music_directory);
+
+ FreeAllMusic_NoConf();
+
+ if ((dir = openDirectory(music_directory)) == NULL)
+ {
+ Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
+
+ audio.music_available = FALSE;
+
+ return;
+ }
+
+ if (draw_init_text)
+ DrawInitText("Loading music", 120, FC_GREEN);
+
+ while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
+ {
+ char *basename = dir_entry->basename;
+ char *filename = NULL;
+ MusicInfo *mus_info = NULL;
+ boolean music_already_used = FALSE;
+ int i;
+
+ /* skip all music files that are configured in music config file */
+ for (i = 0; i < num_music; i++)
+ {
+ struct FileInfo *music = getMusicListEntry(i);
+
+ if (strEqual(basename, music->filename))
+ {
+ music_already_used = TRUE;
+ break;
+ }
+ }
+
+ if (music_already_used)
+ continue;
+
+ if (draw_init_text)
+ DrawInitText(basename, 150, FC_YELLOW);
+
+ filename = getPath2(music_directory, basename);
+
+ if (FileIsMusic(basename))
+ mus_info = Load_WAV_or_MOD(filename);
+
+ free(filename);
+
+ if (mus_info)
+ {
+ num_music_noconf++;
+ Music_NoConf = checked_realloc(Music_NoConf,
+ num_music_noconf * sizeof(MusicInfo *));
+ Music_NoConf[num_music_noconf - 1] = mus_info;
+ }
+ }
+
+ closeDirectory(dir);
+
+ draw_init_text = FALSE;
+}
+
+#else
+
+void LoadCustomMusic_NoConf(void)
+{
+ static boolean draw_init_text = TRUE; /* only draw at startup */
+ static char *last_music_directory = NULL;
+ char *music_directory = getCustomMusicDirectory();
+ DIR *dir;
+ struct dirent *dir_entry;
+ int num_music = getMusicListSize();
+
+ if (!audio.sound_available)
+ return;
+
+ if (last_music_directory != NULL &&
+ strEqual(last_music_directory, music_directory))
+ return; /* old and new music directory are the same */
+
+ if (last_music_directory != NULL)
+ free(last_music_directory);
+ last_music_directory = getStringCopy(music_directory);
+
+ FreeAllMusic_NoConf();
+
+ if ((dir = opendir(music_directory)) == NULL)
+ {
+ Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
+
+ audio.music_available = FALSE;
+
+ return;
+ }
+
+ if (draw_init_text)
+ DrawInitText("Loading music", 120, FC_GREEN);
+
+ while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
+ {
+ char *basename = dir_entry->d_name;
+ char *filename = NULL;
+ MusicInfo *mus_info = NULL;
+ boolean music_already_used = FALSE;
+ int i;
+
+ /* skip all music files that are configured in music config file */
+ for (i = 0; i < num_music; i++)
+ {
+ struct FileInfo *music = getMusicListEntry(i);
+
+ if (strEqual(basename, music->filename))
+ {
+ music_already_used = TRUE;
+ break;
+ }
+ }
+
+ if (music_already_used)
+ continue;
+
+ if (draw_init_text)
+ DrawInitText(basename, 150, FC_YELLOW);
+
+ filename = getPath2(music_directory, basename);
+
+ if (FileIsMusic(basename))
+ mus_info = Load_WAV_or_MOD(filename);
+
+ free(filename);
+
+ if (mus_info)
+ {
+ num_music_noconf++;
+ Music_NoConf = checked_realloc(Music_NoConf,
+ num_music_noconf * sizeof(MusicInfo *));
+ Music_NoConf[num_music_noconf - 1] = mus_info;
+ }
+ }
+
+ closedir(dir);
+
+ draw_init_text = FALSE;
+}
+
+#endif
+
+int getSoundListSize()
+{
+ return (sound_info->num_file_list_entries +
+ sound_info->num_dynamic_file_list_entries);
+}
+
+int getMusicListSize()
+{
+ return (music_info->num_file_list_entries +
+ music_info->num_dynamic_file_list_entries);
+}
+
+struct FileInfo *getSoundListEntry(int pos)
+{
+ int num_list_entries = sound_info->num_file_list_entries;
+ int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
+
+ return (pos < num_list_entries ? &sound_info->file_list[list_pos] :
+ &sound_info->dynamic_file_list[list_pos]);
+}
+
+struct FileInfo *getMusicListEntry(int pos)
+{
+ int num_list_entries = music_info->num_file_list_entries;
+ int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
+
+ return (pos < num_list_entries ? &music_info->file_list[list_pos] :
+ &music_info->dynamic_file_list[list_pos]);
+}
+
+static SoundInfo *getSoundInfoEntryFromSoundID(int pos)
+{
+ int num_list_entries = sound_info->num_file_list_entries;
+ int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
+ SoundInfo **snd_info =
+ (SoundInfo **)(pos < num_list_entries ? sound_info->artwork_list :
+ sound_info->dynamic_artwork_list);
+
+ return snd_info[list_pos];
+}
+
+static MusicInfo *getMusicInfoEntryFromMusicID(int pos)