Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
/*
2
 * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>.
3
 * It is copyright by its individual contributors, as recorded in the
4
 * project's Git history.  See COPYING.txt at the top level for license
5
 * terms and a link to the Git history.
6
 */
7
/*
8
 *
9
 * SDL CD Audio functions
10
 *
11
 *
12
 */
13
 
14
#include <algorithm>
15
#include <stdio.h>
16
#include <stdlib.h>
17
 
18
#include <SDL.h>
19
 
20
#ifdef __linux__
21
#include <sys/ioctl.h>
22
#include <linux/cdrom.h>
23
#endif
24
 
25
#include "pstypes.h"
26
#include "dxxerror.h"
27
#include "args.h"
28
#include "rbaudio.h"
29
#include "console.h"
30
#include "timer.h"
31
#include "partial_range.h"
32
#include "compiler-range_for.h"
33
 
34
#define DXX_CHECK_CD_INDRIVE_0(S)       \
35
        static_assert(!CD_INDRIVE(S), #S)
36
#define DXX_CHECK_CD_INDRIVE_1(S)       \
37
        static_assert(CD_INDRIVE(S), #S)
38
#undef CD_INDRIVE
39
#define CD_INDRIVE(s)   (static_cast<int>(s) > 0)
40
DXX_CHECK_CD_INDRIVE_0(CD_ERROR);
41
DXX_CHECK_CD_INDRIVE_0(CD_TRAYEMPTY);
42
DXX_CHECK_CD_INDRIVE_1(CD_STOPPED);
43
DXX_CHECK_CD_INDRIVE_1(CD_PLAYING);
44
DXX_CHECK_CD_INDRIVE_1(CD_PAUSED);
45
 
46
namespace dcx {
47
 
48
#define REDBOOK_VOLUME_SCALE 255
49
 
50
static SDL_CD *s_cd = NULL;
51
static int initialised = 0;
52
 
53
void RBAExit()
54
{
55
        if (s_cd)
56
        {
57
                SDL_CDStop(s_cd);
58
                SDL_CDClose(s_cd);
59
        }
60
}
61
 
62
void RBAInit()
63
{
64
        int num_cds;
65
        if (initialised) return;
66
 
67
        if (SDL_Init(SDL_INIT_CDROM) < 0)
68
        {
69
                Warning("RBAudio: SDL library initialisation failed: %s.",SDL_GetError());
70
                return;
71
        }
72
 
73
        num_cds = SDL_CDNumDrives();
74
        if (num_cds < 1)
75
        {
76
                con_puts(CON_NORMAL, "RBAudio: No cdrom drives found!");
77
#if defined(__APPLE__) || defined(macintosh)
78
                SDL_QuitSubSystem(SDL_INIT_CDROM);      // necessary for rescanning CDROMs
79
#endif
80
                return;
81
        }
82
 
83
        for (int i = 0; i < num_cds; i++)
84
        {
85
                if (s_cd)
86
                        SDL_CDClose(s_cd);
87
                s_cd = SDL_CDOpen(i);
88
 
89
                if (s_cd && CD_INDRIVE(SDL_CDStatus(s_cd)))
90
                {
91
                        const auto &&r = partial_const_range(s_cd->track, static_cast<unsigned>(s_cd->numtracks));
92
                        if (std::find_if(r.begin(), r.end(), [](const SDL_CDtrack &t){ return t.type == SDL_AUDIO_TRACK; }) != r.end())
93
                        {
94
                                initialised = 1;
95
                                RBAList();
96
                                return; // we've found an audio CD
97
                        }
98
                }
99
                else if (s_cd == NULL)
100
                        Warning("RBAudio: Could not open cdrom %i for redbook audio:%s\n", i, SDL_GetError());
101
        }
102
 
103
        {
104
                con_puts(CON_NORMAL, "RBAudio: No audio CDs found");
105
                if (s_cd)       // if there's no audio CD, say that there's no redbook and hence play MIDI instead
106
                {
107
                        SDL_CDClose(s_cd);
108
                        s_cd = NULL;
109
                }
110
#if defined(__APPLE__) || defined(macintosh)
111
                SDL_QuitSubSystem(SDL_INIT_CDROM);      // necessary for rescanning CDROMs
112
#endif
113
                return;
114
        }
115
}
116
 
117
int RBAEnabled()
118
{
119
        return initialised;
120
}
121
 
122
int RBAPlayTrack(int a)
123
{
124
        if (!s_cd) return -1;
125
 
126
        if (CD_INDRIVE(SDL_CDStatus(s_cd)) ) {
127
                if (SDL_CDPlayTracks(s_cd, a-1, 0, 0, 0) == 0)
128
                {
129
                        con_printf(CON_VERBOSE, "RBAudio: Playing track %i", a);
130
                        return a;
131
                }
132
        }
133
        return -1;
134
}
135
 
136
static void (*redbook_finished_hook)() = NULL;
137
 
138
void RBAStop()
139
{
140
        if (!s_cd) return;
141
        SDL_CDStop(s_cd);
142
        redbook_finished_hook = NULL;   // no calling the finished hook - stopped means stopped here
143
        con_puts(CON_VERBOSE, "RBAudio: Playback stopped");
144
}
145
 
146
void RBAEjectDisk()
147
{
148
        if (!s_cd) return;
149
        SDL_CDEject(s_cd);      // play nothing until it tries to load a song
150
#if defined(__APPLE__) || defined(macintosh)
151
        SDL_QuitSubSystem(SDL_INIT_CDROM);      // necessary for rescanning CDROMs
152
#endif
153
        initialised = 0;
154
}
155
 
156
#ifdef __linux__
157
void RBASetVolume(int volume)
158
{
159
        int cdfile, level;
160
        struct cdrom_volctrl volctrl;
161
 
162
        if (!s_cd) return;
163
 
164
        cdfile = s_cd->id;
165
        level = volume*REDBOOK_VOLUME_SCALE/8;
166
 
167
        if ((level<0) || (level>REDBOOK_VOLUME_SCALE)) {
168
                con_printf(CON_CRITICAL, "RBAudio: illegal volume value (allowed values 0-%i)",REDBOOK_VOLUME_SCALE);
169
                return;
170
        }
171
 
172
        volctrl.channel0
173
                = volctrl.channel1
174
                = volctrl.channel2
175
                = volctrl.channel3
176
                = level;
177
        if ( ioctl(cdfile, CDROMVOLCTRL, &volctrl) == -1 ) {
178
                con_puts(CON_CRITICAL, "RBAudio: CDROMVOLCTRL ioctl failed");
179
                return;
180
        }
181
}
182
#endif
183
 
184
void RBAPause()
185
{
186
        if (!s_cd) return;
187
        SDL_CDPause(s_cd);
188
        con_puts(CON_VERBOSE, "RBAudio: Playback paused");
189
}
190
 
191
int RBAResume()
192
{
193
        if (!s_cd) return -1;
194
        SDL_CDResume(s_cd);
195
        con_puts(CON_VERBOSE, "RBAudio: Playback resumed");
196
        return 1;
197
}
198
 
199
int RBAPauseResume()
200
{
201
        if (!s_cd) return 0;
202
 
203
        if (SDL_CDStatus(s_cd) == CD_PLAYING)
204
        {
205
                SDL_CDPause(s_cd);
206
                con_puts(CON_VERBOSE, "RBAudio: Toggle Playback pause");
207
        }
208
        else if (SDL_CDStatus(s_cd) == CD_PAUSED)
209
        {
210
                SDL_CDResume(s_cd);
211
                con_puts(CON_VERBOSE, "RBAudio: Toggle Playback resume");
212
        }
213
        else
214
                return 0;
215
 
216
        return 1;
217
}
218
 
219
int RBAGetNumberOfTracks()
220
{
221
        if (!s_cd) return -1;
222
        SDL_CDStatus(s_cd);
223
        con_printf(CON_VERBOSE, "RBAudio: Found %i tracks on CD", s_cd->numtracks);
224
        return s_cd->numtracks;
225
}
226
 
227
// check if we need to call the 'finished' hook
228
// needs to go in all event loops
229
// a real hook would be ideal, but is currently unsupported by SDL Audio CD
230
void RBACheckFinishedHook()
231
{
232
        static fix64 last_check_time = 0;
233
 
234
        if (!s_cd) return;
235
 
236
        if ((timer_query() - last_check_time) >= F2_0)
237
        {
238
                if ((SDL_CDStatus(s_cd) == CD_STOPPED) && redbook_finished_hook)
239
                {
240
                        con_puts(CON_VERBOSE, "RBAudio: Playback done, calling finished-hook");
241
                        redbook_finished_hook();
242
                }
243
                last_check_time = timer_query();
244
        }
245
}
246
 
247
// plays tracks first through last, inclusive
248
int RBAPlayTracks(int first, int last, void (*hook_finished)(void))
249
{
250
        if (!s_cd)
251
                return 0;
252
 
253
        if (CD_INDRIVE(SDL_CDStatus(s_cd)))
254
        {
255
                redbook_finished_hook = hook_finished;
256
                con_printf(CON_VERBOSE, "RBAudio: Playing tracks %i to %i", first, last);
257
                return SDL_CDPlayTracks(s_cd, first - 1, 0, last - first + 1, 0) == 0;
258
        }
259
        return 0;
260
}
261
 
262
// return the track number currently playing.  Useful if RBAPlayTracks()
263
// is called.  Returns 0 if no track playing, else track number
264
int RBAGetTrackNum()
265
{
266
        if (!s_cd)
267
                return 0;
268
 
269
        if (SDL_CDStatus(s_cd) != CD_PLAYING)
270
                return 0;
271
 
272
        return s_cd->cur_track + 1;
273
}
274
 
275
int RBAPeekPlayStatus()
276
{
277
        if (!s_cd)
278
                return 0;
279
 
280
        if (SDL_CDStatus(s_cd) == CD_PLAYING)
281
                return 1;
282
        else if (SDL_CDStatus(s_cd) == CD_PAUSED)       // hack so it doesn't keep restarting paused music
283
                return -1;
284
 
285
        return 0;
286
}
287
 
288
static int cddb_sum(int n)
289
{
290
        int ret;
291
 
292
        /* For backward compatibility this algorithm must not change */
293
 
294
        ret = 0;
295
 
296
        while (n > 0) {
297
                ret = ret + (n % 10);
298
                n = n / 10;
299
        }
300
 
301
        return (ret);
302
}
303
 
304
 
305
unsigned long RBAGetDiscID()
306
{
307
        int i, t = 0, n = 0;
308
 
309
        if (!s_cd)
310
                return 0;
311
 
312
        /* For backward compatibility this algorithm must not change */
313
 
314
        i = 0;
315
 
316
        while (i < s_cd->numtracks) {
317
                n += cddb_sum(s_cd->track[i].offset / CD_FPS);
318
                i++;
319
        }
320
 
321
        t = (s_cd->track[s_cd->numtracks].offset / CD_FPS) -
322
            (s_cd->track[0].offset / CD_FPS);
323
 
324
        return ((n % 0xff) << 24 | t << 8 | s_cd->numtracks);
325
}
326
 
327
void RBAList(void)
328
{
329
 
330
        if (!s_cd)
331
                return;
332
 
333
        range_for (auto &i, partial_const_range(s_cd->track, static_cast<unsigned>(s_cd->numtracks)))
334
                con_printf(CON_VERBOSE, "RBAudio: CD track %d, type %s, length %d, offset %d", i.id, (i.type == SDL_AUDIO_TRACK) ? "audio" : "data", i.length, i.offset);
335
}
336
 
337
}