Discman
Loading...
Searching...
No Matches
cd_drive.cc
Go to the documentation of this file.
1
6
7#include "cd_drive.h"
8
10 : _drive(drive)
11 , _action(Idle)
12 , _buffer(0)
13 , _thread(&CDDrive::Reader::loop, this) {
14
15 _paranoia = paranoia_init(_drive._drive);
16 paranoia_modeset(_paranoia, PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP);
17}
18
21 _thread.join();
22
23 paranoia_free(_paranoia);
24}
25
27 _action_lock.lock();
29 _action_lock.unlock();
30}
31
33 _drive._load_lock.lock();
34 paranoia_seek(_paranoia, cdda_track_firstsector(_drive._drive, track), SEEK_SET);
35 _drive._load_lock.unlock();
36}
37
40}
41
49
50void CDDrive::resize_buffer(unsigned int sectors) {
51 _bufferSectors = sectors;
52 _bufferSamples = (CDIO_CD_FRAMESIZE_RAW / BYTES_PER_SAMPLE * _bufferSectors);
53
54 delete [] _buffers[0];
55 delete [] _buffers[1];
56
57 _buffers[0] = new int16_t[_bufferSamples];
58 _buffers[1] = new int16_t[_bufferSamples];
59}
60
62 _drive._load_lock.lock();
63 for (int i = 0; i < _drive._bufferSectors; i++) {
64 int16_t* sector = paranoia_read(_paranoia, NULL);
65 char* start = reinterpret_cast<char*>(_drive._buffers[_buffer]);
66 memcpy(start + (i * CDIO_CD_FRAMESIZE_RAW), sector, CDIO_CD_FRAMESIZE_RAW);
67
68 Action a = action();
69 if (a == Action::Exit) {
70 _drive._load_lock.unlock();
71 return;
72 }
73 }
74 _drive._load_lock.unlock();
75}
76
78 while (true) {
79 Action a = action();
80
81 switch (a) {
82 case Action::Idle:
83 break;
84 case Action::Load:
85 load();
86
87 if (action() == Action::Exit)
88 return;
89
91 break;
92 case Action::Exit:
93 return;
94 }
95 }
96}
97
99 : _drive(drive)
100 , _exit(false)
101 , _thread(&CDDrive::Poller::poll, this) {
102
103}
105 _exit_lock.lock();
106 _exit = true;
107 _exit_lock.unlock();
108 _thread.join();
109}
110
112 while (true) {
113 _exit_lock.lock();
114 bool exit = _exit;
115 _exit_lock.unlock();
116
117 if (exit) return;
118
119 if (!_drive.identify()) {
120 std::this_thread::sleep_for(std::chrono::milliseconds(250));
121 continue;
122 }
123
124 _drive.open();
125
126 return;
127 }
128}
129
132 , _bufferSamples(CDIO_CD_FRAMESIZE_RAW / BYTES_PER_SAMPLE * _bufferSectors)
133 , _drive(nullptr)
134 , _reader(nullptr)
135 , _poller(new Poller(*this))
136 , _buffer(0)
137 , _buffer_idx(0)
138 , _track(0)
139 , _cursor(0)
140 , _end(0) {
141 _buffers[0] = new int16_t[_bufferSamples];
142 _buffers[1] = new int16_t[_bufferSamples];
143}
144
145
147 if (_reader) delete _reader;
148 delete _poller;
149
150 delete [] _buffers[1];
151 delete [] _buffers[0];
152
153 if (_drive)
154 cdda_close(_drive);
155}
156
157bool CDDrive::present() const {
158 return _reader;
159}
160
162 if (!present()) throw NoDiscPresentException();
163
164 delete _reader;
165 _reader = nullptr;
166
167 cdda_close(_drive);
168 _drive = nullptr;
169
170 cdio_eject_media_drive(_device.c_str());
171 _device.clear();
172
173 _sig_eject.emit();
174
175 if (_poller) delete _poller;
176 _poller = new Poller(*this);
177}
178
179std::vector<std::string> CDDrive::devices() {
180 pid_t pid;
181 int p[2];
182
183 std::vector<std::string> ready_devices;
184
185 int ret = pipe(p);
186
187 if (ret < 0) {
188 return {};
189 }
190
191 pid = fork();
192
193 if (pid == 0) {
194 dup2(p[WRITE_END], STDOUT_FILENO);
195 close(p[READ_END]);
196 close(p[WRITE_END]);
197
198 char* argv[2] = { new char[strlen("lsblk")+1], NULL };
199 strcpy(argv[0], "lsblk");
200 execvp("lsblk", argv);
201 } else {
202 close(p[WRITE_END]);
203
204 std::stringstream ss;
205 char buf[512];
206 int bytes;
207 while ((bytes = read(p[READ_END], buf, 512)) > 0) {
208 buf[bytes] = '\0';
209 ss << buf;
210 }
211
212 int status;
213 close(p[READ_END]);
214 waitpid(pid, &status, 0);
215
216 std::vector<std::string> rom_devices;
217
218 std::string str;
219 std::getline(ss, str);
220 while (ss) {
221 std::getline(ss, str);
222 std::stringstream sss(str);
223
224 std::string name;
225 std::string type;
226 std::string extra;
227
228 sss >> name
229 >> extra >> extra >> extra >> extra
230 >> type;
231
232 if (type == "rom")
233 rom_devices.push_back("/dev/"+name);
234 }
235
236 for (const std::string& device : rom_devices) {
237 int fd = __open(device.c_str(), O_RDONLY | O_NONBLOCK);
238
239 int result = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_NONE);
240
241 if (result == CDS_DISC_OK)
242 ready_devices.push_back(device);
243
244 close(fd);
245 }
246 }
247
248 return ready_devices;
249}
250
252 const std::vector<std::string> ready_devices = CDDrive::devices();
253
254 if (ready_devices.empty()) return false;
255
256 char **ppsz_cd_drives = new char*[ready_devices.size() + 1];
257
258 for (unsigned int i = 0; i < ready_devices.size(); i++) {
259 ppsz_cd_drives[i] = new char[ready_devices[i].length() + 1];
260 strcpy(ppsz_cd_drives[i], ready_devices[i].c_str());
261 }
262 ppsz_cd_drives[ready_devices.size()] = NULL;
263
264 ppsz_cd_drives = cdio_get_devices_with_cap(ppsz_cd_drives, CDIO_FS_AUDIO, false);
265
266 if (ppsz_cd_drives && *ppsz_cd_drives) {
267 _device = *ppsz_cd_drives;
268 _drive = cdda_identify(*ppsz_cd_drives, CDDA_MESSAGE_FORGETIT, NULL);
269 } else {
270 return false;
271 }
272
273 cdio_free_device_list(ppsz_cd_drives);
274
275 if (!_drive) throw DriveErrorException();
276
277 cdda_verbose_set(_drive, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);
278
279 return true;
280}
281
283 if (!_drive) throw NoDiscPresentException();
284
285 if ( 0 != cdda_open(_drive) ) {
286 throw DiscErrorException();
287 }
288
289 _reader = new Reader(*this);
290}
291
295
296void CDDrive::update_track(const unsigned int track) {
297 _track = track;
298
299 lsn_t first_sector = cdio_cddap_track_firstsector(_drive, track);
300 lsn_t last_sector = cdio_cddap_track_lastsector(_drive, track);
301 int sectors = last_sector - first_sector + 1;
302
303 _end = sectors * CDIO_CD_FRAMESIZE_RAW / BYTES_PER_SAMPLE;
304}
305
306unsigned int CDDrive::tracks() const {
307 if (!present()) throw NoDiscPresentException();
308
309 return cdio_cddap_tracks(_drive);
310}
311
312void CDDrive::track(const int track) {
313 if (!present()) throw NoDiscPresentException();
314
316 _buffer = _buffer_idx = 0;
317 _cursor = 0;
318
319 _reader->track(track);
320 _load_lock.lock();
321 _reader->buffer(_buffer);
323 _load_lock.unlock();
324}
325
326unsigned int CDDrive::track() const {
327 return _track;
328}
329
331 if (!present()) throw NoDiscPresentException();
332
333 _cursor_lock.lock();
334 int cursor = _cursor;
335 _cursor_lock.unlock();
336
337 return static_cast<float>(cursor) / (2 * SAMPLE_RATE);
338}
339
341 _cursor_lock.lock();
342 int cursor = _cursor;
343 _cursor_lock.unlock();
344
345 return static_cast<float>(cursor) / static_cast<float>(_end);
346}
347
348lba_t CDDrive::lba(const track_t track) const {
349 if (!present()) throw NoDiscPresentException();
350
351 return cdio_get_track_lba(_drive->p_cdio, track);
352}
353
354unsigned int CDDrive::seconds() const {
355 return lba(CDIO_CDROM_LEADOUT_TRACK) / CDIO_CD_FRAMES_PER_SEC;
356}
357
359 if (!present()) throw NoDiscPresentException();
360
361 return _cursor == _end;
362}
363
364int16_t CDDrive::next() {
365 if (_buffer_idx == 0) {
366 _load_lock.lock();
367 _reader->buffer(1 - _buffer);
369 _load_lock.unlock();
370 }
371
372 if (_cursor == _end) return 0;
373
374 const int16_t sample = _buffers[_buffer][_buffer_idx++];
375
377
378 if (_buffer_idx == 0) {
379 _buffer = 1 - _buffer;
380 }
381
382 _cursor_lock.lock();
383 if (++_cursor == _end) {
384 if (_track < tracks()) {
385 update_track(_track + 1);
386 _cursor = 0;
387 }
388 }
389 _cursor_lock.unlock();
390
391 return sample;
392}
#define READ_END
Definition cd_drive.h:10
#define WRITE_END
Definition cd_drive.h:11
const auto __open
Definition cd_drive.h:35
The Poller uses a separate thread to poll the host for a disc drive with an audio CD.
Definition cd_drive.h:228
Poller(CDDrive &drive)
Poller constructor.
Definition cd_drive.cc:98
std::mutex _exit_lock
Provides mutually exclusive access to the _exit member between the the application thread and the Pol...
Definition cd_drive.h:246
bool _exit
Stores a request to exit poll().
Definition cd_drive.h:242
~Poller()
Poller destructor.
Definition cd_drive.cc:104
std::thread _thread
The thread used to run poll().
Definition cd_drive.h:249
void poll()
The execution loop performed by the thread.
Definition cd_drive.cc:111
CDDrive & _drive
The parent CDDrive.
Definition cd_drive.h:240
The Reader uses a separate thread to load data into the CDDrive's ring buffer.
Definition cd_drive.h:172
Action
Actions are taken consecutively. Setting an action on the Reader takes effect after its current actio...
Definition cd_drive.h:177
void track(const int track)
Seeks to the given track on the disc using 1-based indexing.
Definition cd_drive.cc:32
void buffer(const int buffer)
Sets the index of the buffer in the CDDrive's ring buffer to read samples into.
Definition cd_drive.cc:38
Action _action
The current or next action to be taken by the Reader.
Definition cd_drive.h:212
std::thread _thread
The thread used to run loop().
Definition cd_drive.h:224
void loop()
The execution loop performed by the thread.
Definition cd_drive.cc:77
cdrom_paranoia_t * _paranoia
cdio handle to the sample reader.
Definition cd_drive.h:216
void load()
Fills the current buffer with audio samples.
Definition cd_drive.cc:61
CDDrive & _drive
The parent CDDrive.
Definition cd_drive.h:210
Reader(CDDrive &drive)
Reader constructor.
Definition cd_drive.cc:9
int _buffer
The index of the buffer in the CDDrive's ring buffer to read samples into.
Definition cd_drive.h:214
~Reader()
Reader destructor.
Definition cd_drive.cc:19
Action action()
Returns the current Reader action in a thread-safe way.
Definition cd_drive.cc:42
void action(const Action action)
Set the next action for the Reader.
Definition cd_drive.cc:26
std::mutex _action_lock
Provides mutually exclusive access to the _action member between the the application thread and the R...
Definition cd_drive.h:220
int16_t * _buffers[2]
A ring buffer of size 2. One buffer has new audio samples written to it. When that buffer is full,...
Definition cd_drive.h:153
std::mutex _load_lock
Provides mutually exclusive access to the drive and audio buffers between the application thread and ...
Definition cd_drive.h:164
Poller * _poller
Used to poll for the first openable disc drive.
Definition cd_drive.h:144
void update_track(const unsigned int track)
Sets the current disc track using 1-based indexing.
Definition cd_drive.cc:296
float progress()
The percentage completion of the rip of the current track (0.0 - 1.0).
Definition cd_drive.cc:340
bool present() const
Returns whether a disc is in the drive.
Definition cd_drive.cc:157
void eject()
Ejects the disc in the drive.
Definition cd_drive.cc:161
unsigned int _end
The 0-based index just past the end of the read buffer.
Definition cd_drive.h:160
void track(const int track)
Sets the current track to the given 1-based index.
Definition cd_drive.cc:312
~CDDrive()
CDDrive destructor.
Definition cd_drive.cc:146
sigc::signal< void()> sig_eject
"Eject requested" signal type.
Definition cd_drive.h:48
lba_t lba(const track_t track) const
Returns the LBA (large block address) of the start of the given track on the disc.
Definition cd_drive.cc:348
unsigned int _cursor
The 0-based index into the read buffer.
Definition cd_drive.h:159
cdrom_drive_t * _drive
A cdio handle to the disc drive.
Definition cd_drive.h:142
bool identify()
Finds a disc drive on the host with an audio disc inside.
Definition cd_drive.cc:251
bool done()
Whether the current track has been played or ripped through.
Definition cd_drive.cc:358
static constexpr int BUFFER_SIZE_PLAYING
The audio buffer size when playing, in CD audio frames.
Definition cd_drive.h:42
uint8_t _buffer
The 0-based index of the buffer in the ring currently designated as the write buffer.
Definition cd_drive.h:155
unsigned int _buffer_idx
The 0-based index into the write buffer.
Definition cd_drive.h:156
int16_t next() override
Provides the next audio sample from the current track. This is one half of an audio frame which conta...
Definition cd_drive.cc:364
std::string _device
The path to the device node of the disc drive.
Definition cd_drive.h:141
unsigned int seconds() const
The duration of the entire disc in whole seconds.
Definition cd_drive.cc:354
int _bufferSamples
The number of audio samples (one half of an audio frame) in the audio buffer.
Definition cd_drive.h:128
float elapsed()
The elapsed duration of the track in fractional seconds.
Definition cd_drive.cc:330
int _bufferSectors
The number of CD frames in the audio buffer.
Definition cd_drive.h:127
CDDrive()
CDDrive constructor.
Definition cd_drive.cc:130
sig_eject _sig_eject
Emitted when the disc is successfully ejected.
Definition cd_drive.h:125
static constexpr int BYTES_PER_SAMPLE
The number of bytes in one CD audio sample (an int16_t).
Definition cd_drive.h:122
std::mutex _cursor_lock
Provides mutually exclusive access to the track cursor between the the application thread and the Por...
Definition cd_drive.h:168
static constexpr int SAMPLE_RATE
CD audio sample rate.
Definition cd_drive.h:45
sig_eject signal_eject()
Getter for ::_signal_eject.
Definition cd_drive.cc:292
void open()
Opens the disc for reading.
Definition cd_drive.cc:282
static std::vector< std::string > devices()
Returns the paths of device nodes corresponding to disc drives with discs that can be opened.
Definition cd_drive.cc:179
unsigned int _track
The current track being played or ripped.
Definition cd_drive.h:158
void resize_buffer(unsigned int sectors)
Resizes the audio buffer.
Definition cd_drive.cc:50
Reader * _reader
Used to read audio samples into the audio buffer.
Definition cd_drive.h:143
unsigned int tracks() const
Returns the number of tracks on the disc.
Definition cd_drive.cc:306
unsigned int track() const
Returns the 1-based index of the current track.
Definition cd_drive.cc:326
Thrown when the audio CD was recognized but could not be opened for reading.
Definition cd_drive.h:67
Thrown when an audio CD was internally expected in the disc drive but could not be recognized.
Definition cd_drive.h:52
Called when the presumption that a disc is present is false.
Definition cd_drive.h:59