1 //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** $Id: snd_audiocd_linux.cpp 3866 2008-11-17 18:58:49Z dj_jl $
11 //**
12 //** Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //** This program is free software; you can redistribute it and/or
15 //** modify it under the terms of the GNU General Public License
16 //** as published by the Free Software Foundation; either version 2
17 //** of the License, or (at your option) any later version.
18 //**
19 //** This program is distributed in the hope that it will be useful,
20 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 //** GNU General Public License for more details.
23 //**
24 //**************************************************************************
25
26 // HEADER FILES ------------------------------------------------------------
27
28 #include <unistd.h>
29 #include <sys/ioctl.h>
30 #include <sys/file.h>
31 #include <sys/types.h>
32 #include <fcntl.h>
33 #include <time.h>
34 #include <errno.h>
35 #include <linux/cdrom.h>
36
37 #include "gamedefs.h"
38 #include "snd_local.h"
39
40 // MACROS ------------------------------------------------------------------
41
42 // TYPES -------------------------------------------------------------------
43
44 class VLinuxCDAudioDevice : public VCDAudioDevice
45 {
46 public:
47 int CDFile;
48
49 void Init();
50 void Update();
51 void Shutdown();
52 void GetInfo();
53 void Play(int, bool);
54 void Pause();
55 void Resume();
56 void Stop();
57 void OpenDoor();
58 void CloseDoor();
59 };
60
61 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
62
63 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
64
65 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
66
67 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
68
69 IMPLEMENT_CD_AUDIO_DEVICE(VLinuxCDAudioDevice, CDDRV_Default, "Default",
70 "Linux CD audio device", NULL);
71
72 // PUBLIC DATA DEFINITIONS -------------------------------------------------
73
74 // PRIVATE DATA DEFINITIONS ------------------------------------------------
75
76 // CODE --------------------------------------------------------------------
77
78 //==========================================================================
79 //
80 // VLinuxCDAudioDevice::Init
81 //
82 //==========================================================================
83
Init()84 void VLinuxCDAudioDevice::Init()
85 {
86 guard(VLinuxCDAudioDevice::Init);
87 int i;
88
89 if (GArgs.CheckParm("-nosound") || GArgs.CheckParm("-nocdaudio"))
90 {
91 return;
92 }
93
94 VStr cd_dev = "/dev/cdrom";
95 const char* p = GArgs.CheckValue("-cddev");
96 if (p)
97 {
98 cd_dev = p;
99 }
100
101 CDFile = open(*cd_dev, O_RDONLY);
102 if (CDFile == -1)
103 {
104 GCon->Logf(NAME_Init, "CD_Init: open of \"%s\" failed (%d)",
105 *cd_dev, errno);
106 CDFile = -1;
107 return;
108 }
109
110 for (i = 0; i < 100; i++)
111 Remap[i] = i;
112 Initialised = true;
113 Enabled = true;
114
115 GetInfo();
116 if (!CDValid)
117 {
118 GCon->Log(NAME_Init, "CD_Init: No CD in player.");
119 }
120
121 GCon->Log(NAME_Init, "CD Audio Initialised");
122 unguard;
123 }
124
125 //==========================================================================
126 //
127 // VLinuxCDAudioDevice::Update
128 //
129 //==========================================================================
130
Update()131 void VLinuxCDAudioDevice::Update()
132 {
133 guard(VLinuxCDAudioDevice::Update);
134 struct cdrom_subchnl subchnl;
135 static time_t lastchk;
136
137 if (!Initialised)
138 return;
139
140 if (!Enabled)
141 return;
142
143 if (Playing && lastchk < time(NULL))
144 {
145 lastchk = time(NULL) + 2; //two seconds between chks
146 subchnl.cdsc_format = CDROM_MSF;
147 if (ioctl(CDFile, CDROMSUBCHNL, &subchnl) == -1 )
148 {
149 GCon->Log(NAME_Dev, "ioctl cdromsubchnl failed");
150 Playing = false;
151 return;
152 }
153 if (subchnl.cdsc_audiostatus != CDROM_AUDIO_PLAY &&
154 subchnl.cdsc_audiostatus != CDROM_AUDIO_PAUSED)
155 {
156 Playing = false;
157 if (PlayLooping)
158 Play(PlayTrack, true);
159 }
160 }
161 unguard;
162 }
163
164 //==========================================================================
165 //
166 // VLinuxCDAudioDevice::Shutdown
167 //
168 //==========================================================================
169
Shutdown()170 void VLinuxCDAudioDevice::Shutdown()
171 {
172 guard(VLinuxCDAudioDevice::Shutdown);
173 if (!Initialised)
174 return;
175
176 Stop();
177 close(CDFile);
178 CDFile = -1;
179 Initialised = false;
180 unguard;
181 }
182
183 //==========================================================================
184 //
185 // VLinuxCDAudioDevice::GetInfo
186 //
187 //==========================================================================
188
GetInfo()189 void VLinuxCDAudioDevice::GetInfo()
190 {
191 guard(VLinuxCDAudioDevice::GetInfo);
192 struct cdrom_tochdr tochdr;
193
194 CDValid = false;
195
196 if (ioctl(CDFile, CDROMREADTOCHDR, &tochdr) == -1)
197 {
198 GCon->Log(NAME_Dev, "ioctl cdromreadtochdr failed");
199 return;
200 }
201
202 if (tochdr.cdth_trk0 < 1)
203 {
204 GCon->Log(NAME_Dev, "CDAudio: no music tracks");
205 return;
206 }
207
208 CDValid = true;
209 MaxTrack = tochdr.cdth_trk1;
210 unguard;
211 }
212
213 //==========================================================================
214 //
215 // VLinuxCDAudioDevice::Play
216 //
217 //==========================================================================
218
Play(int track,bool looping)219 void VLinuxCDAudioDevice::Play(int track, bool looping)
220 {
221 guard(VLinuxCDAudioDevice::Play);
222 struct cdrom_tocentry entry;
223 struct cdrom_ti ti;
224
225 if (!CDValid)
226 {
227 GetInfo();
228 if (!CDValid)
229 return;
230 }
231
232 track = Remap[track];
233
234 if (track < 1 || track > MaxTrack)
235 {
236 GCon->Logf(NAME_Dev, "CDAudio: Bad track number %d.", track);
237 return;
238 }
239
240 // don't try to play a non-audio track
241 entry.cdte_track = track;
242 entry.cdte_format = CDROM_MSF;
243 if (ioctl(CDFile, CDROMREADTOCENTRY, &entry) == -1)
244 {
245 GCon->Log(NAME_Dev, "ioctl cdromreadtocentry failed");
246 return;
247 }
248 if (entry.cdte_ctrl == CDROM_DATA_TRACK)
249 {
250 GCon->Logf("CDAudio: track %d is not audio", track);
251 return;
252 }
253
254 if (Playing)
255 {
256 if (PlayTrack == track)
257 return;
258 Stop();
259 }
260
261 ti.cdti_trk0 = track;
262 ti.cdti_trk1 = track;
263 ti.cdti_ind0 = 1;
264 ti.cdti_ind1 = 99;
265
266 if (ioctl(CDFile, CDROMPLAYTRKIND, &ti) == -1)
267 {
268 GCon->Log(NAME_Dev, "ioctl cdromplaytrkind failed");
269 return;
270 }
271
272 if (ioctl(CDFile, CDROMRESUME) == -1)
273 GCon->Log(NAME_Dev, "ioctl cdromresume failed");
274
275 PlayLooping = looping;
276 PlayTrack = track;
277 Playing = true;
278 unguard;
279 }
280
281 //==========================================================================
282 //
283 // VLinuxCDAudioDevice::Pause
284 //
285 //==========================================================================
286
Pause()287 void VLinuxCDAudioDevice::Pause()
288 {
289 guard(VLinuxCDAudioDevice::Pause);
290 if (!Playing)
291 return;
292
293 if (ioctl(CDFile, CDROMPAUSE) == -1)
294 GCon->Log(NAME_Dev, "ioctl cdrompause failed");
295
296 WasPlaying = Playing;
297 Playing = false;
298 unguard;
299 }
300
301 //==========================================================================
302 //
303 // VLinuxCDAudioDevice::Resume
304 //
305 //==========================================================================
306
Resume()307 void VLinuxCDAudioDevice::Resume()
308 {
309 guard(VLinuxCDAudioDevice::Resume);
310 if (!WasPlaying)
311 return;
312
313 if (ioctl(CDFile, CDROMRESUME) == -1)
314 GCon->Log(NAME_Dev, "ioctl cdromresume failed");
315
316 Playing = true;
317 unguard;
318 }
319
320 //==========================================================================
321 //
322 // VLinuxCDAudioDevice::Stop
323 //
324 //==========================================================================
325
Stop()326 void VLinuxCDAudioDevice::Stop()
327 {
328 guard(VLinuxCDAudioDevice::Stop);
329 if (!Playing)
330 return;
331
332 if (ioctl(CDFile, CDROMSTOP) == -1)
333 GCon->Log(NAME_Dev, "ioctl cdromstop failed");
334
335 WasPlaying = false;
336 Playing = false;
337 unguard;
338 }
339
340 //==========================================================================
341 //
342 // VLinuxCDAudioDevice::OpenDoor
343 //
344 //==========================================================================
345
OpenDoor()346 void VLinuxCDAudioDevice::OpenDoor()
347 {
348 guard(VLinuxCDAudioDevice::OpenDoor);
349 if (ioctl(CDFile, CDROMEJECT) == -1)
350 GCon->Log(NAME_Dev, "ioctl cdromeject failed");
351 unguard;
352 }
353
354 //==========================================================================
355 //
356 // VLinuxCDAudioDevice::CloseDoor
357 //
358 //==========================================================================
359
CloseDoor()360 void VLinuxCDAudioDevice::CloseDoor()
361 {
362 guard(VLinuxCDAudioDevice::CloseDoor);
363 if (ioctl(CDFile, CDROMCLOSETRAY) == -1)
364 GCon->Log(NAME_Dev, "ioctl cdromclosetray failed");
365 unguard;
366 }
367