File: sound.c

package info (click to toggle)
crossfire-client 1.70.0-1
  • links: PTS
  • area: main
  • in suites: wheezy
  • size: 6,604 kB
  • sloc: ansic: 22,190; sh: 3,641; makefile: 304; perl: 54
file content (283 lines) | stat: -rw-r--r-- 9,497 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
const char * const rcsid_gtk2_sound_c =
    "$Id: sound.c 13974 2010-10-08 02:26:52Z kbulgrien $";

/*
    CrossFire, A Multiplayer game for X-windows

    Copyright (C) 2005,2010 Mark Wedel & Crossfire Development Team

    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., 675 Mass Ave, Cambridge, MA 02139, USA.

    The author can be reached via e-mail to crossfire@metalforge.org
*/

/**
 * @file gtk-v2/src/sound.c
 * This file contains the sound support for the GTK V2 client.  It does not
 * actually play sounds, but rather tries to run cfsndserve, which is
 * responsible for playing sounds.
 */

#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <client-types.h>
#include "client.h"

FILE *sound_pipe=NULL;
ChildProcess* sound_process;

/**
 * Opens the audio device, and reads relevant configuration files.
 *
 * @return
 * Returns 0 on success.  On failure, the calling function will likely disable
 * sound support/requests from the server.
 */
int init_sounds(void)
{
#ifndef WIN32
    char sound_path[MAX_BUF];

    /*
     * Easy trick - global nosound is set in the arg processing - if set, just
     * return -1 - this way, the calling function only needs to check the value
     * of init_sounds, and not worry about checking nosound.
     */
    if (!want_config[CONFIG_SOUND]) return -1;

    if (sound_server[0] == '\0') {
        LOG(LOG_ERROR,"init_sounds:", "sound-server variable not set to anything");
        return -1;
    }
    /*
     * If an absolute path is given, we use it unadorned.  Otherwise, we use
     * the path in the BINDIR.
     */
    if (sound_server[0] == '/')
        strcpy(sound_path, sound_server);
    else
        snprintf(sound_path, sizeof(sound_path),"%s/%s", BINDIR, sound_server);

    if (access(sound_path, X_OK)<0) {
        fprintf(stderr,"Unable to access %s sound server process\n", sound_path);
        return -1;
    }

    sound_process=raiseChild(sound_path,CHILD_STDIN|CHILD_STDOUT|CHILD_STDERR);
    logChildPipe(sound_process, LOG_INFO, CHILD_STDOUT|CHILD_STDERR);

    if (fcntl(sound_process->tube[0], F_SETFL, O_NONBLOCK)<0) {
        /*
         * Setting non-blocking isn't 100% critical, but a good thing if
         * possible.
         */
        perror("init_sounds: Warning - unable to set non blocking on sound pipe\n");
    }
    sound_pipe=fdopen(sound_process->tube[0],"w");
    return 0;
#else
    return -1;
#endif
}

/**
 * Initiates playing of a sound effect, specified by name and type, to
 * cfsndserv via a pipe.
 *
 * @param x      Offset of the sound relative to the player.
 * @param y      Offset of the sound relative to the player.
 * @param dir    The direction the sound is moving toward, where north = 1,
 *               northeast = 2, and so on.  0 indicates a stationary source.
 * @param vol    A value from 0 through 100 inclusive that suggests the
 *               relative loudness of the sound effect.
 * @param type   See server doc/Developers/sound for details.  1 is a sound
 *               related to living things.  2 is a spell-related sound.  3 is
 *               is made by an item.  4 is created by the environment.  5 is a
 *               sound of an attack.  6 is a sound of a incoming hit.  This is
 *               list may grow over time.
 * @param sound  A descriptive name for the sound effect to play.  It usually
 *               describes the sound itself, and may be combined with the type
 *               and source name to select the file to play.
 * @param source The name of the sound emitter.  It is used in combination
 *               with type and sound to determine which file to play.
 */
void play_sound_effect(sint8 x, sint8 y, uint8 dir, uint8 vol, uint8 type,
                       const char *sound, const char *source) {
#ifndef WIN32
    /**
     * cfsndserv recognizes sound commands by seeing the numeric parameters at
     * the beginning of the command line.
     */
    char format[] = "%4x %4x %4x %4x %4x \"%s\" \"%s\"\n";

    if (! use_config[CONFIG_SOUND])
        return;

    /*
     * Pass the sound command on to the player.
     *
     * NOTE: Sound and source are reversed with respect to how the server sent
     * data to the client.  This is intentional, so that the sound/music name
     * is always the last quoted string on the command sent to cfsndserv.
     */
    if ((fprintf(sound_pipe, format, x, y, dir, vol, type, source, sound) <= 0)
    ||  (fflush(sound_pipe) != 0)) {
        LOG(LOG_ERROR,
            "gtk-v2::play_sound_effect", "Cannot write sound pipe: %d", errno);
        use_config[CONFIG_SOUND] = 0;
        fclose(sound_pipe);
        sound_process = NULL;
        return;
    }
#if 1
    else
        LOG(LOG_INFO, "gtk-v2::play_sound_effect",
            format, x, y, dir, vol, type, sound, source);
#endif
#endif
}

/**
 * Parse the data contained by a sound2 command coming from the server and
 * handle playing the specified sound.  See server doc/Developers/sound for
 * details.
 *
 * @param data Data provided following the sound2 command from the server.
 * @param len  Length of the sound2 command data.
 */
void Sound2Cmd(unsigned char *data, int len) {
#ifndef WIN32
    sint8 x, y;
    uint8 dir, vol, type, len_sound, len_source;
    char* sound = NULL;
    char* source = NULL;

    /**
     * Format of the sound2 command recieved in data:
     *
     * <pre>
     * sound2 {x}{y}{dir}{volume}{type}{len_sound}{sound}{len_source}{source}
     *         b  b  b    b       b     b          str    b           str
     * </pre>
     */
    if (len < 8) {
        LOG(LOG_WARNING,
            "gtk-v2::Sound2Cmd", "Sound command too short: %d\n bytes", len);
        return;
    }

    x = data[0];
    y = data[1];
    dir = data[2];
    vol = data[3];
    type = data[4];
    len_sound = data[5];
    /*
     * The minimum size of data is 1 for each byte in the command (7) plus the
     * size of the sound string.  If we do not have that, the data is bogus.
     */
    if (6 + len_sound + 1 > len) {
        LOG(LOG_WARNING,
            "gtk-v2::Sound2Cmd", "sound length check: %i len: %i\n",
                len_sound, len);
        return;
    }

    len_source = data[6 + len_sound];
    if (len_sound != 0) {
        sound = (char*) data + 6;
        data[6 + len_sound] = '\0';
    }
    /*
     * The minimum size of data is 1 for each byte in the command (7) plus the
     * size of the sound string, and the size of the source string.
     */
    if (6 + len_sound + 1 + len_source > len) {
        LOG(LOG_WARNING,
            "gtk-v2::Sound2Cmd", "source length check: %i len: %i\n",
                len_source, len);
        return;
    }
    /*
     * Though it looks like there is potential for writing a null off the end
     * of the buffer, there is always room for a null (see do_client()).
     */
    if (len_source != 0) {
        source = (char*) data + 6 + len_sound + 1;
        data[6 + len_sound + 1 + len_source] = '\0';
    }

#if 1
    LOG(LOG_INFO, "gtk-v2::Sound2Cmd", "Playing sound2 x=%hhd y=%hhd dir=%hhd volume=%hhd type=%hhd",
        x, y, dir, vol, type);
    LOG(LOG_INFO, "gtk-v2::Sound2Cmd", "               len_sound=%hhd sound=%s", len_sound, sound);
    LOG(LOG_INFO, "gtk-v2::Sound2Cmd", "               len_source=%hhd source=%s", len_source, source);
#endif

    play_sound_effect(x, y, dir, vol, type, sound, source);
#endif
}

/**
 * Parse the data contained by a music command coming from the server and
 * pass the name along to cfsndserv as a quoted string.
 *
 * @param data Data provided following the music command from the server
 *             that hints what kind of music should play.  NONE is an
 *             indication that music should stop playing.
 * @param len  Length of the string describing the music to play.
 */
void MusicCmd(const char *data, int len) {
#ifndef WIN32
    /**
     * Format of the music command received in data:
     *
     * <pre>
     * music {string}
     * </pre>
     */
    if (! use_config[CONFIG_SOUND])
        return;
    /*
     * The client puts a null character at the end of the data.  If one is not
     * there, ignore the command.
     */
    if (data[len]) {
        LOG(LOG_ERROR,
            "gtk-v2::MusicCmd", "Music command buffer not null-terminated.");
        return;
    }
    /**
     * cfsndserv recognizes music commands by seeing the quoted string as the
     * first item on the command line.
     */
    if ((fprintf(sound_pipe, "\"%s\"\n", data) <= 0)
    ||  (fflush(sound_pipe) != 0)) {
        LOG(LOG_ERROR,
            "gtk-v2::MusicCmd", "Cannot write sound pipe: %d", errno);
        use_config[CONFIG_SOUND] = 0;
        fclose(sound_pipe);
        sound_process = NULL;
        return;
    }
#if 1
    else
        LOG(LOG_INFO, "gtk-v2::MusicCmd", "\"%s\"", data);
#endif
#endif
}