tron@2186: /* $Id$ */ tron@2186: belugas@6916: /** @file sound.cpp */ belugas@6916: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" maedhros@6949: #include "landscape.h" tron@1496: #include "mixer.h" rubidium@8653: #include "sound_func.h" truelight@0: #include "fileio.h" peter1138@4656: #include "newgrf_sound.h" rubidium@8301: #include "fios.h" rubidium@8602: #include "window_gui.h" rubidium@8626: #include "core/alloc_func.hpp" rubidium@8635: #include "map_func.h" rubidium@8640: #include "vehicle_base.h" peter1138@8935: #include "debug.h" truelight@0: tron@1496: static uint _file_count; belugas@4171: static FileEntry *_files; rubidium@8653: MusicFileSettings msf; truelight@0: peter1138@2594: // Number of levels of panning per side peter1138@2594: #define PANNING_LEVELS 16 truelight@0: truelight@0: tron@1496: static void OpenBankFile(const char *filename) truelight@0: { tron@2371: uint i; truelight@0: truelight@0: FioOpenFile(SOUND_SLOT, filename); truelight@8087: uint pos = FioGetPos(); peter1138@8935: uint count = FioReadDword() / 8; peter1138@8935: peter1138@8935: /* Simple check for the correct number of original sounds. */ peter1138@8935: if (count != 73) { peter1138@8935: DEBUG(misc, 6, "Incorrect number of sounds in '%s', ignoring.", filename); peter1138@8935: _file_count = 0; peter1138@8935: _files = NULL; peter1138@8935: return; peter1138@8935: } peter1138@8935: KUDr@5860: FileEntry *fe = CallocT(count); tron@2371: tron@2371: if (fe == NULL) { tron@2371: _file_count = 0; tron@2371: _files = NULL; tron@2372: return; tron@2371: } tron@2371: tron@2371: _file_count = count; tron@2371: _files = fe; truelight@0: truelight@8087: FioSeekTo(pos, SEEK_SET); truelight@0: tron@1496: for (i = 0; i != count; i++) { truelight@8066: fe[i].file_slot = SOUND_SLOT; truelight@8087: fe[i].file_offset = FioReadDword() + pos; tron@1496: fe[i].file_size = FioReadDword(); truelight@0: } truelight@193: tron@337: for (i = 0; i != count; i++, fe++) { tron@337: char name[255]; truelight@0: tron@337: FioSeekTo(fe->file_offset, SEEK_SET); tron@337: belugas@6916: /* Check for special case, see else case */ tron@337: FioReadBlock(name, FioReadByte()); // Read the name of the sound tron@337: if (strcmp(name, "Corrupt sound") != 0) { tron@337: FioSeekTo(12, SEEK_CUR); // Skip past RIFF header tron@337: belugas@6916: /* Read riff tags */ tron@337: for (;;) { tron@2371: uint32 tag = FioReadDword(); tron@2371: uint32 size = FioReadDword(); tron@337: tron@337: if (tag == ' tmf') { tron@337: FioReadWord(); // wFormatTag tron@337: fe->channels = FioReadWord(); // wChannels tron@337: FioReadDword(); // samples per second tron@337: fe->rate = 11025; // seems like all samples should be played at this rate. tron@337: FioReadDword(); // avg bytes per second tron@337: FioReadWord(); // alignment tron@337: fe->bits_per_sample = FioReadByte(); // bits per sample tron@337: FioSeekTo(size - (2 + 2 + 4 + 4 + 2 + 1), SEEK_CUR); tron@337: } else if (tag == 'atad') { tron@337: fe->file_size = size; truelight@8066: fe->file_slot = SOUND_SLOT; truelight@8066: fe->file_offset = FioGetPos(); tron@337: break; tron@337: } else { tron@337: fe->file_size = 0; tron@337: break; tron@337: } truelight@0: } tron@337: } else { tron@337: /* tron@337: * Special case for the jackhammer sound tron@337: * (name in sample.cat is "Corrupt sound") tron@337: * It's no RIFF file, but raw PCM data tron@337: */ tron@337: fe->channels = 1; tron@337: fe->rate = 11025; tron@337: fe->bits_per_sample = 8; truelight@8066: fe->file_slot = SOUND_SLOT; truelight@8066: fe->file_offset = FioGetPos(); truelight@0: } truelight@0: } truelight@0: } truelight@0: rubidium@6573: uint GetNumOriginalSounds() peter1138@4656: { peter1138@4656: return _file_count; peter1138@4656: } peter1138@4656: peter1138@8935: static bool SetBankSource(MixerChannel *mc, const FileEntry *fe) truelight@0: { peter1138@8935: assert(fe != NULL); truelight@193: tron@2371: if (fe->file_size == 0) return false; tron@2371: KUDr@5860: int8 *mem = MallocT(fe->file_size); tron@2371: if (mem == NULL) return false; tron@2371: truelight@8066: FioSeekToFile(fe->file_slot, fe->file_offset); truelight@0: FioReadBlock(mem, fe->file_size); truelight@0: peter1138@8935: for (uint i = 0; i != fe->file_size; i++) { darkvater@1136: mem[i] += -128; // Convert unsigned sound data to signed peter1138@8935: } truelight@193: truelight@0: assert(fe->bits_per_sample == 8 && fe->channels == 1 && fe->file_size != 0 && fe->rate != 0); truelight@0: tron@1496: MxSetChannelRawSrc(mc, mem, fe->file_size, fe->rate, MX_AUTOFREE); truelight@0: truelight@0: return true; truelight@0: } truelight@0: tron@1496: bool SoundInitialize(const char *filename) truelight@0: { tron@1496: OpenBankFile(filename); truelight@0: return true; truelight@0: } truelight@0: belugas@6916: /* Low level sound player */ peter1138@2594: static void StartSound(uint sound, int panning, uint volume) truelight@0: { peter1138@8935: if (volume == 0) return; tron@2371: peter1138@8935: const FileEntry *fe = GetSound(sound); peter1138@8935: if (fe == NULL) return; peter1138@8935: peter1138@8935: MixerChannel *mc = MxAllocateChannel(); tron@2371: if (mc == NULL) return; peter1138@8935: peter1138@8935: if (!SetBankSource(mc, fe)) return; peter1138@8935: peter1138@8935: /* Apply the sound effect's own volume. */ peter1138@8935: volume = (fe->volume * volume) / 128; peter1138@2594: skidd13@8418: panning = Clamp(panning, -PANNING_LEVELS, PANNING_LEVELS); peter1138@8935: uint left_vol = (volume * PANNING_LEVELS) - (volume * panning); peter1138@8935: uint right_vol = (volume * PANNING_LEVELS) + (volume * panning); peter1138@2594: MxSetChannelVolume(mc, left_vol * 128 / PANNING_LEVELS, right_vol * 128 / PANNING_LEVELS); tron@2371: MxActivateChannel(mc); truelight@0: } truelight@0: truelight@0: truelight@7349: static const byte _vol_factor_by_zoom[] = {255, 190, 134, 87}; smatz@8591: assert_compile(lengthof(_vol_factor_by_zoom) == ZOOM_LVL_END - ZOOM_LVL_BEGIN); truelight@0: truelight@0: static const byte _sound_base_vol[] = { truelight@0: 128, 90, 128, 128, 128, 128, 128, 128, truelight@0: 128, 90, 90, 128, 128, 128, 128, 128, truelight@0: 128, 128, 128, 80, 128, 128, 128, 128, truelight@0: 128, 128, 128, 128, 128, 128, 128, 128, truelight@0: 128, 128, 90, 90, 90, 128, 90, 128, truelight@0: 128, 90, 128, 128, 128, 90, 128, 128, truelight@0: 128, 128, 128, 128, 90, 128, 128, 128, truelight@0: 128, 90, 128, 128, 128, 128, 128, 128, truelight@0: 128, 128, 90, 90, 90, 128, 128, 128, truelight@0: 90, truelight@0: }; truelight@0: truelight@0: static const byte _sound_idx[] = { tron@2371: 2, 3, 4, 5, 6, 7, 8, 9, truelight@0: 10, 11, 12, 13, 14, 15, 16, 17, truelight@0: 18, 19, 20, 21, 22, 23, 24, 25, truelight@0: 26, 27, 28, 29, 30, 31, 32, 33, tron@2371: 34, 35, 36, 37, 38, 39, 40, 0, tron@2371: 1, 41, 42, 43, 44, 45, 46, 47, truelight@0: 48, 49, 50, 51, 52, 53, 54, 55, truelight@0: 56, 57, 58, 59, 60, 61, 62, 63, truelight@0: 64, 65, 66, 67, 68, 69, 70, 71, truelight@0: 72, truelight@0: }; truelight@0: rubidium@6573: void SndCopyToPool() peter1138@4656: { peter1138@4656: uint i; peter1138@4656: peter1138@4656: for (i = 0; i < _file_count; i++) { peter1138@4656: FileEntry *orig = &_files[_sound_idx[i]]; peter1138@4656: FileEntry *fe = AllocateFileEntry(); peter1138@4656: tron@5024: *fe = *orig; peter1138@4656: fe->volume = _sound_base_vol[i]; peter1138@4656: fe->priority = 0; peter1138@4656: } peter1138@4656: } peter1138@4656: smatz@9084: static void SndPlayScreenCoordFx(SoundFx sound, int left, int right, int top, int bottom) truelight@0: { Darkvater@5137: Window* const *wz; truelight@0: tron@2371: if (msf.effect_vol == 0) return; truelight@0: Darkvater@5124: FOR_ALL_WINDOWS(wz) { Darkvater@5124: const ViewPort *vp = (*wz)->viewport; tron@2371: tron@2371: if (vp != NULL && smatz@9084: left < vp->virtual_left + vp->virtual_width && right > vp->virtual_left && smatz@9084: top < vp->virtual_top + vp->virtual_height && bottom > vp->virtual_top) { smatz@9084: int screen_x = (left + right) / 2 - vp->virtual_left; smatz@9091: int width = (vp->virtual_width == 0 ? 1 : vp->virtual_width); smatz@9091: int panning = (screen_x * PANNING_LEVELS * 2) / width - PANNING_LEVELS; truelight@193: truelight@0: StartSound( peter1138@4656: sound, smatz@9091: panning, peter1138@8935: (msf.effect_vol * _vol_factor_by_zoom[vp->zoom - ZOOM_LVL_BEGIN]) / 256 truelight@0: ); truelight@0: return; truelight@0: } truelight@0: } truelight@0: truelight@0: } truelight@0: tron@337: void SndPlayTileFx(SoundFx sound, TileIndex tile) truelight@0: { tron@3645: /* emits sound from center of the tile */ tron@3645: int x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2; smatz@9084: int y = TileY(tile) * TILE_SIZE - TILE_SIZE / 2; smatz@9100: uint z = (y < 0 ? 0 : GetSlopeZ(x, y)); smatz@9100: Point pt = RemapCoords(x, y, z); smatz@9084: y += 2 * TILE_SIZE; smatz@9084: Point pt2 = RemapCoords(x, y, GetSlopeZ(x, y)); smatz@9084: SndPlayScreenCoordFx(sound, pt.x, pt2.x, pt.y, pt2.y); truelight@0: } truelight@0: tron@337: void SndPlayVehicleFx(SoundFx sound, const Vehicle *v) truelight@0: { truelight@0: SndPlayScreenCoordFx(sound, smatz@9084: v->left_coord, v->right_coord, smatz@9084: v->top_coord, v->top_coord truelight@0: ); truelight@0: } truelight@0: tron@337: void SndPlayFx(SoundFx sound) truelight@0: { peter1138@8935: StartSound(sound, 0, msf.effect_vol); truelight@0: }