1 #include "cdiomodule.h"
2 #include <limits.h>
3 #include <cdio/cd_types.h>
4 #include <cdio/audio.h>
5 #include <cdio/track.h>
6 #include <cdio/types.h>
7 #include "pcm.h"
8 #include "pcmconv.h"
9 #include "mod_defs.h"
10 
11 /********************************************************
12  Audio Tools, a module and set of tools for manipulating audio data
13  Copyright (C) 2007-2014  Brian Langenberger
14 
15  This program is free software; you can redistribute it and/or modify
16  it under the terms of the GNU General Public License as published by
17  the Free Software Foundation; either version 2 of the License, or
18  (at your option) any later version.
19 
20  This program is distributed in the hope that it will be useful,
21  but WITHOUT ANY WARRANTY; without even the implied warranty of
22  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  GNU General Public License for more details.
24 
25  You should have received a copy of the GNU General Public License
26  along with this program; if not, write to the Free Software
27  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
28 *******************************************************/
29 
30 
31 #if PY_MAJOR_VERSION >= 3
32 #ifndef PyInt_FromLong
33 #define PyInt_FromLong PyLong_FromLong
34 #endif
35 #endif
36 
37 #ifndef MIN
38 #define MIN(x, y) ((x) < (y) ? (x) : (y))
39 #endif
40 #ifndef MAX
41 #define MAX(x, y) ((x) > (y) ? (x) : (y))
42 #endif
43 
MOD_INIT(cdio)44 MOD_INIT(cdio)
45 {
46     PyObject* m;
47 
48     MOD_DEF(m, "cdio", "a CDDA reading module", cdioMethods)
49 
50     cdio_CDDAReaderType.tp_new = PyType_GenericNew;
51     if (PyType_Ready(&cdio_CDDAReaderType) < 0)
52         return MOD_ERROR_VAL;
53 
54     Py_INCREF(&cdio_CDDAReaderType);
55     PyModule_AddObject(m, "CDDAReader", (PyObject *)&cdio_CDDAReaderType);
56 
57     return MOD_SUCCESS_VAL(m);
58 }
59 
60 static PyObject*
CDDAReader_new(PyTypeObject * type,PyObject * args,PyObject * kwds)61 CDDAReader_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
62 {
63     cdio_CDDAReader *self;
64 
65     self = (cdio_CDDAReader *)type->tp_alloc(type, 0);
66 
67     return (PyObject *)self;
68 }
69 
70 static int
CDDAReader_init(cdio_CDDAReader * self,PyObject * args,PyObject * kwds)71 CDDAReader_init(cdio_CDDAReader *self, PyObject *args, PyObject *kwds)
72 {
73     char *device = NULL;
74     struct stat buf;
75 
76     self->is_cd_image = 0;
77     self->is_logging = 0;
78     self->dealloc = NULL;
79     self->closed = 0;
80     self->audiotools_pcm = NULL;
81     cddareader_reset_log(&(self->log));
82 
83     if (!PyArg_ParseTuple(args, "s|i", &device, &(self->is_logging)))
84         return -1;
85 
86     if ((self->audiotools_pcm = open_audiotools_pcm()) == NULL)
87         return -1;
88 
89     /*identify whether drive is physical or a CD image*/
90     if (stat(device, &buf)) {
91         PyErr_SetFromErrno(PyExc_IOError);
92         return -1;
93     }
94     if (S_ISREG(buf.st_mode)) {
95         if (cdio_is_cuefile(device) ||
96             cdio_is_binfile(device) ||
97             cdio_is_tocfile(device) ||
98             cdio_is_nrg(device)) {
99             /*open CD image and set function pointers*/
100             self->is_cd_image = 1;
101             self->is_logging = 0;
102             return CDDAReader_init_image(self, device);
103         } else {
104             /*unsupported file*/
105             PyErr_SetString(PyExc_ValueError, "unsupported CD image type");
106             return -1;
107         }
108     } else if (S_ISBLK(buf.st_mode)) {
109         if (cdio_is_device(device, DRIVER_LINUX)) {
110             /*open CD device and set function pointers*/
111             self->is_cd_image = 0;
112             return CDDAReader_init_device(self, device);
113         } else {
114             /*unsupported block device*/
115             PyErr_SetString(PyExc_ValueError, "unsupported block device");
116             return -1;
117         }
118     } else {
119         /*unsupported file type*/
120         PyErr_SetString(PyExc_ValueError, "unsupported file type");
121         return -1;
122     }
123 }
124 
125 static int
CDDAReader_init_image(cdio_CDDAReader * self,const char * device)126 CDDAReader_init_image(cdio_CDDAReader *self, const char *device)
127 {
128     self->_.image.image = NULL;
129     self->_.image.current_sector = 0;
130     self->_.image.final_sector = 0;
131     self->first_track_num  = CDDAReader_first_track_num_image;
132     self->last_track_num = CDDAReader_last_track_num_image;
133     self->track_lsn = CDDAReader_track_lsn_image;
134     self->track_last_lsn = CDDAReader_track_last_lsn_image;
135     self->first_sector = CDDAReader_first_sector_image;
136     self->last_sector = CDDAReader_last_sector_image;
137     self->read = CDDAReader_read_image;
138     self->seek = CDDAReader_seek_image;
139     self->set_speed = CDDAReader_set_speed_image;
140     self->dealloc = CDDAReader_dealloc_image;
141 
142     /*open CD image based on what type it is*/
143     if (cdio_is_cuefile(device)) {
144         self->_.image.image = cdio_open_cue(device);
145     } else if (cdio_is_tocfile(device)) {
146         self->_.image.image = cdio_open_bincue(device);
147     } else if (cdio_is_tocfile(device)) {
148         self->_.image.image = cdio_open_cdrdao(device);
149     } else if (cdio_is_nrg(device)) {
150         self->_.image.image = cdio_open_nrg(device);
151     }
152     if (self->_.image.image == NULL) {
153         PyErr_SetString(PyExc_IOError, "unable to open CD image");
154         return -1;
155     }
156     self->_.image.final_sector = (lsn_t)self->last_sector(self);
157     return 0;
158 }
159 
160 static int
CDDAReader_init_device(cdio_CDDAReader * self,const char * device)161 CDDAReader_init_device(cdio_CDDAReader *self, const char *device)
162 {
163     self->_.drive.drive = NULL;
164     self->_.drive.paranoia = NULL;
165     self->_.drive.current_sector = 0;
166     self->_.drive.final_sector = 0;
167 
168     if ((self->_.drive.drive = cdio_cddap_identify(device, 0, NULL)) == NULL) {
169         PyErr_SetString(PyExc_IOError, "error opening CD-ROM");
170         return -1;
171     }
172     if (cdio_cddap_open(self->_.drive.drive) != 0) {
173         PyErr_SetString(PyExc_IOError, "error opening CD-ROM");
174         return -1;
175     }
176     self->_.drive.paranoia = cdio_paranoia_init(self->_.drive.drive);
177     paranoia_modeset(self->_.drive.paranoia,
178                      PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP);
179 
180     self->first_track_num  = CDDAReader_first_track_num_device;
181     self->last_track_num = CDDAReader_last_track_num_device;
182     self->track_lsn = CDDAReader_track_lsn_device;
183     self->track_last_lsn = CDDAReader_track_last_lsn_device;
184     self->first_sector = CDDAReader_first_sector_device;
185     self->last_sector = CDDAReader_last_sector_device;
186     self->read = CDDAReader_read_device;
187     self->seek = CDDAReader_seek_device;
188     self->set_speed = CDDAReader_set_speed_device;
189     self->dealloc = CDDAReader_dealloc_device;
190 
191     self->_.drive.final_sector = self->last_sector(self);
192 
193     if ((self->_.drive.final_sector == -1) ||
194             (self->first_sector(self) == -1)) {
195         PyErr_SetString(PyExc_IOError, "no disc in CD-ROM drive");
196         return -1;
197     }
198 
199     return 0;
200 }
201 
202 static void
CDDAReader_dealloc(cdio_CDDAReader * self)203 CDDAReader_dealloc(cdio_CDDAReader *self)
204 {
205     if (self->dealloc) {
206         self->dealloc(self);
207     }
208     Py_XDECREF(self->audiotools_pcm);
209     Py_TYPE(self)->tp_free((PyObject*)self);
210 }
211 
212 static void
CDDAReader_dealloc_image(cdio_CDDAReader * self)213 CDDAReader_dealloc_image(cdio_CDDAReader *self)
214 {
215     if (self->_.image.image != NULL) {
216         cdio_destroy(self->_.image.image);
217     }
218 }
219 
220 static void
CDDAReader_dealloc_device(cdio_CDDAReader * self)221 CDDAReader_dealloc_device(cdio_CDDAReader *self)
222 {
223     if (self->_.drive.paranoia) {
224         cdio_paranoia_free(self->_.drive.paranoia);
225     }
226     if (self->_.drive.drive) {
227         cdio_cddap_close(self->_.drive.drive);
228     }
229 }
230 
231 static PyObject*
CDDAReader_sample_rate(cdio_CDDAReader * self,void * closure)232 CDDAReader_sample_rate(cdio_CDDAReader *self, void *closure)
233 {
234     const int sample_rate = 44100;
235     return Py_BuildValue("i", sample_rate);
236 }
237 
238 static PyObject*
CDDAReader_bits_per_sample(cdio_CDDAReader * self,void * closure)239 CDDAReader_bits_per_sample(cdio_CDDAReader *self, void *closure)
240 {
241     const int bits_per_sample = 16;
242     return Py_BuildValue("i", bits_per_sample);
243 }
244 
245 static PyObject*
CDDAReader_channels(cdio_CDDAReader * self,void * closure)246 CDDAReader_channels(cdio_CDDAReader *self, void *closure)
247 {
248     const int channels = 2;
249     return Py_BuildValue("i", channels);
250 }
251 
252 static PyObject*
CDDAReader_channel_mask(cdio_CDDAReader * self,void * closure)253 CDDAReader_channel_mask(cdio_CDDAReader *self, void *closure)
254 {
255     const int channel_mask = 0x3;
256     return Py_BuildValue("i", channel_mask);
257 }
258 
259 static PyObject*
CDDAReader_is_cd_image(cdio_CDDAReader * self,void * closure)260 CDDAReader_is_cd_image(cdio_CDDAReader *self, void *closure)
261 {
262     return PyBool_FromLong(self->is_cd_image);
263 }
264 
265 static int
CDDAReader_first_track_num_image(cdio_CDDAReader * self)266 CDDAReader_first_track_num_image(cdio_CDDAReader *self)
267 {
268     return cdio_get_first_track_num(self->_.image.image);
269 }
270 
271 static int
CDDAReader_first_track_num_device(cdio_CDDAReader * self)272 CDDAReader_first_track_num_device(cdio_CDDAReader *self)
273 {
274     /*FIXME - not sure if there's a more accurate way to get this*/
275     return 1;
276 }
277 
278 static int
CDDAReader_last_track_num_image(cdio_CDDAReader * self)279 CDDAReader_last_track_num_image(cdio_CDDAReader *self)
280 {
281     return cdio_get_last_track_num(self->_.image.image);
282 }
283 
284 static int
CDDAReader_last_track_num_device(cdio_CDDAReader * self)285 CDDAReader_last_track_num_device(cdio_CDDAReader *self)
286 {
287     /*FIXME - not sure if there's a more accurate way to get this*/
288     return cdio_cddap_tracks(self->_.drive.drive);
289 }
290 
291 static int
CDDAReader_track_lsn_image(cdio_CDDAReader * self,int track_num)292 CDDAReader_track_lsn_image(cdio_CDDAReader *self, int track_num)
293 {
294     const lsn_t lsn = cdio_get_track_lsn(self->_.image.image,
295                                          (track_t)track_num);
296     return (int)lsn;
297 }
298 
299 static int
CDDAReader_track_lsn_device(cdio_CDDAReader * self,int track_num)300 CDDAReader_track_lsn_device(cdio_CDDAReader *self, int track_num)
301 {
302     const lsn_t lsn = cdio_cddap_track_firstsector(self->_.drive.drive,
303                                                    (track_t)track_num);
304     return lsn;
305 }
306 
307 static int
CDDAReader_track_last_lsn_image(cdio_CDDAReader * self,int track_num)308 CDDAReader_track_last_lsn_image(cdio_CDDAReader *self, int track_num)
309 {
310     const lsn_t lsn = cdio_get_track_last_lsn(self->_.image.image,
311                                               (track_t)track_num);
312     return (int)lsn;
313 }
314 
315 static int
CDDAReader_track_last_lsn_device(cdio_CDDAReader * self,int track_num)316 CDDAReader_track_last_lsn_device(cdio_CDDAReader *self, int track_num)
317 {
318     const lsn_t lsn = cdio_cddap_track_lastsector(self->_.drive.drive,
319                                                   (track_t)track_num);
320     return lsn;
321 }
322 
323 static PyObject*
CDDAReader_track_offsets(cdio_CDDAReader * self,void * closure)324 CDDAReader_track_offsets(cdio_CDDAReader *self, void *closure)
325 {
326     const int first_track_num = self->first_track_num(self);
327     const int last_track_num = self->last_track_num(self);
328     int i;
329     PyObject *offsets = PyDict_New();
330 
331     if (offsets == NULL) {
332         /*error creating the dict*/
333         return NULL;
334     }
335 
336     for (i = first_track_num; i <= last_track_num; i++) {
337         PyObject *track_num = PyInt_FromLong(i);
338         PyObject *track_offset = PyInt_FromLong(
339             self->track_lsn(self, i) * 588);
340         int result;
341         if ((track_num == NULL) || (track_offset == NULL)) {
342             /*error creating one of the two int values*/
343             Py_XDECREF(track_num);
344             Py_XDECREF(track_offset);
345             Py_DECREF(offsets);
346             return NULL;
347         }
348         result = PyDict_SetItem(offsets, track_num, track_offset);
349         Py_DECREF(track_num);
350         Py_DECREF(track_offset);
351         if (result == -1) {
352             /*error setting dictionary item*/
353             Py_DECREF(offsets);
354             return NULL;
355         }
356     }
357 
358     return offsets;
359 }
360 
361 static PyObject*
CDDAReader_track_lengths(cdio_CDDAReader * self,void * closure)362 CDDAReader_track_lengths(cdio_CDDAReader *self, void *closure)
363 {
364     const int first_track_num = self->first_track_num(self);
365     const int last_track_num = self->last_track_num(self);
366     int i;
367     PyObject *lengths = PyDict_New();
368 
369     if (lengths == NULL) {
370         /*error creating the dict*/
371         return NULL;
372     }
373 
374     for (i = first_track_num; i <= last_track_num; i++) {
375         PyObject *track_num = PyInt_FromLong(i);
376         PyObject *track_length = PyInt_FromLong(
377             (self->track_last_lsn(self, i) -
378              self->track_lsn(self, i) + 1) * 588);
379         int result;
380         if ((track_num == NULL) || (track_length == NULL)) {
381             /*error creating one of the two int values*/
382             Py_XDECREF(track_num);
383             Py_XDECREF(track_length);
384             Py_DECREF(lengths);
385             return NULL;
386         }
387         result = PyDict_SetItem(lengths, track_num, track_length);
388         Py_DECREF(track_num);
389         Py_DECREF(track_length);
390         if (result == -1) {
391             /*error setting dictionary item*/
392             Py_DECREF(lengths);
393             return NULL;
394         }
395     }
396 
397     return lengths;
398 }
399 
400 static PyObject*
CDDAReader_first_sector(cdio_CDDAReader * self,void * closure)401 CDDAReader_first_sector(cdio_CDDAReader *self, void *closure)
402 {
403     const int first_sector = self->first_sector(self);
404     return Py_BuildValue("i", first_sector);
405 }
406 
407 static int
CDDAReader_first_sector_image(cdio_CDDAReader * self)408 CDDAReader_first_sector_image(cdio_CDDAReader *self)
409 {
410     return cdio_get_track_lsn(self->_.image.image,
411                               self->first_track_num(self));
412 }
413 
414 static int
CDDAReader_first_sector_device(cdio_CDDAReader * self)415 CDDAReader_first_sector_device(cdio_CDDAReader *self)
416 {
417     return cdio_cddap_track_firstsector(self->_.drive.drive,
418                                         self->first_track_num(self));
419 }
420 
421 static PyObject*
CDDAReader_last_sector(cdio_CDDAReader * self,void * closure)422 CDDAReader_last_sector(cdio_CDDAReader *self, void *closure)
423 {
424     const int last_sector = self->last_sector(self);
425     return Py_BuildValue("i", last_sector);
426 }
427 
428 static int
CDDAReader_last_sector_image(cdio_CDDAReader * self)429 CDDAReader_last_sector_image(cdio_CDDAReader *self)
430 {
431     return cdio_get_track_last_lsn(self->_.image.image,
432                                    self->last_track_num(self));
433 }
434 
435 static int
CDDAReader_last_sector_device(cdio_CDDAReader * self)436 CDDAReader_last_sector_device(cdio_CDDAReader *self)
437 {
438     return cdio_cddap_track_lastsector(self->_.drive.drive,
439                                        self->last_track_num(self));
440 }
441 
442 static PyObject*
CDDAReader_read(cdio_CDDAReader * self,PyObject * args)443 CDDAReader_read(cdio_CDDAReader* self, PyObject *args)
444 {
445     int pcm_frames;
446     unsigned sectors_to_read;
447     a_int *samples = a_int_new();
448     int successful;
449     PyThreadState *thread_state = NULL;
450     PyObject *to_return;
451 
452     if (!PyArg_ParseTuple(args, "i", &pcm_frames)) {
453         return NULL;
454     }
455 
456     if (self->closed) {
457         PyErr_SetString(PyExc_ValueError, "cannot read closed stream");
458         return NULL;
459     }
460 
461     sectors_to_read = MAX(pcm_frames, 0) / (44100 / 75);
462     if (sectors_to_read < 1) {
463         sectors_to_read = 1;
464     }
465 
466     /*if logging is in progress, only let a single thread
467       into this function at once so that the global callback
468       can be set and used atomically
469 
470       since the callback function doesn't take any state
471       we're forced to stick it in a global variable*/
472     if (!self->is_logging) {
473         thread_state = PyEval_SaveThread();
474     }
475     successful = !self->read(self, sectors_to_read, samples);
476     if (!self->is_logging) {
477         PyEval_RestoreThread(thread_state);
478     }
479 
480     if (successful) {
481         to_return = a_int_to_FrameList(self->audiotools_pcm,
482                                        samples, 2, 16);
483         samples->del(samples);
484         return to_return;
485     } else {
486         samples->del(samples);
487         PyErr_SetString(PyExc_IOError, "I/O error reading stream");
488         return NULL;
489     }
490 }
491 
492 static int
CDDAReader_read_image(cdio_CDDAReader * self,unsigned sectors_to_read,a_int * samples)493 CDDAReader_read_image(cdio_CDDAReader *self,
494                       unsigned sectors_to_read,
495                       a_int *samples)
496 {
497     while (sectors_to_read &&
498            (self->_.image.current_sector <= self->_.image.final_sector)) {
499         uint8_t sector[CDIO_CD_FRAMESIZE_RAW];
500         uint8_t *data = sector;
501         const int result = cdio_read_audio_sector(
502             self->_.image.image,
503             sector,
504             self->_.image.current_sector);
505 
506         if (result == DRIVER_OP_SUCCESS) {
507             unsigned i;
508             samples->resize_for(samples, (44100 / 75) * 2);
509             for (i = 0; i < ((44100 / 75) * 2); i++) {
510                 a_append(samples, FrameList_SL16_char_to_int(data));
511                 data += 2;
512             }
513             self->_.image.current_sector++;
514             sectors_to_read--;
515         } else {
516             return 1;
517         }
518     }
519 
520     return 0;
521 }
522 
523 static int
CDDAReader_read_device(cdio_CDDAReader * self,unsigned sectors_to_read,a_int * samples)524 CDDAReader_read_device(cdio_CDDAReader *self,
525                        unsigned sectors_to_read,
526                        a_int *samples)
527 {
528     if (self->is_logging) {
529         log_state = &(self->log);
530     }
531 
532     while (sectors_to_read &&
533            (self->_.drive.current_sector <= self->_.drive.final_sector)) {
534         int16_t *raw_sector =
535             cdio_paranoia_read_limited(
536                 self->_.drive.paranoia,
537                 self->is_logging ? cddareader_callback : NULL,
538                 10);
539         unsigned i;
540 
541         samples->resize_for(samples, (44100 / 75) * 2);
542         for (i = 0; i < ((44100 / 75) * 2); i++) {
543             a_append(samples, raw_sector[i]);
544         }
545 
546         self->_.drive.current_sector++;
547         sectors_to_read--;
548     }
549 
550     if (self->is_logging) {
551         log_state = NULL;
552     }
553 
554     return 0;
555 }
556 
557 static PyObject*
CDDAReader_seek(cdio_CDDAReader * self,PyObject * args)558 CDDAReader_seek(cdio_CDDAReader* self, PyObject *args)
559 {
560     long long seeked_offset;
561     unsigned seeked_sector;
562     unsigned found_sector;
563 
564     if (self->closed) {
565         PyErr_SetString(PyExc_ValueError, "cannot seek closed stream");
566         return NULL;
567     }
568     if (!PyArg_ParseTuple(args, "L", &seeked_offset))
569         return NULL;
570 
571     if (seeked_offset < 0) {
572         seeked_offset = 0;
573     }
574 
575     if (seeked_offset > UINT_MAX) {
576         seeked_sector = UINT_MAX;
577     } else {
578         seeked_sector = (unsigned)(seeked_offset / (44100 / 75));
579     }
580     found_sector = self->seek(self, seeked_sector);
581     return Py_BuildValue("I", found_sector * (44100 / 75));
582 }
583 
584 static unsigned
CDDAReader_seek_image(cdio_CDDAReader * self,unsigned sector)585 CDDAReader_seek_image(cdio_CDDAReader *self, unsigned sector)
586 {
587     self->_.image.current_sector =
588         MIN(sector, self->_.image.final_sector - 1);
589     return self->_.image.current_sector;
590 }
591 
592 static unsigned
CDDAReader_seek_device(cdio_CDDAReader * self,unsigned sector)593 CDDAReader_seek_device(cdio_CDDAReader *self, unsigned sector)
594 {
595     const unsigned desired_sector = MIN(sector, self->_.drive.final_sector - 1);
596     /*not sure what this returns, but it isn't the sector seeked to*/
597     cdio_paranoia_seek(self->_.drive.paranoia,
598                        (int32_t)desired_sector,
599                        (int)SEEK_SET);
600     self->_.drive.current_sector = desired_sector;
601     return self->_.drive.current_sector;
602 }
603 
604 static PyObject*
CDDAReader_close(cdio_CDDAReader * self,PyObject * args)605 CDDAReader_close(cdio_CDDAReader* self, PyObject *args)
606 {
607     self->closed = 1;
608 
609     Py_INCREF(Py_None);
610     return Py_None;
611 }
612 
613 static PyObject*
CDDAReader_set_speed(cdio_CDDAReader * self,PyObject * args)614 CDDAReader_set_speed(cdio_CDDAReader *self, PyObject *args)
615 {
616     int new_speed;
617 
618     if (!PyArg_ParseTuple(args, "i", &new_speed))
619         return NULL;
620 
621     self->set_speed(self, new_speed);
622 
623     Py_INCREF(Py_None);
624     return Py_None;
625 }
626 
627 static void
CDDAReader_set_speed_image(cdio_CDDAReader * self,int new_speed)628 CDDAReader_set_speed_image(cdio_CDDAReader *self, int new_speed)
629 {
630     /*not an actual CD device, so do nothing*/
631 }
632 
633 static void
CDDAReader_set_speed_device(cdio_CDDAReader * self,int new_speed)634 CDDAReader_set_speed_device(cdio_CDDAReader *self, int new_speed)
635 {
636     cdio_cddap_speed_set(self->_.drive.drive, new_speed);
637 }
638 
639 static void
cddareader_callback(long int i,paranoia_cb_mode_t mode)640 cddareader_callback(long int i, paranoia_cb_mode_t mode)
641 {
642     if (log_state) {
643         switch (mode) {
644         case PARANOIA_CB_READ:
645             log_state->read++;
646             break;
647         case PARANOIA_CB_VERIFY:
648             log_state->verify++;
649             break;
650         case PARANOIA_CB_FIXUP_EDGE:
651             log_state->fixup_edge++;
652             break;
653         case PARANOIA_CB_FIXUP_ATOM:
654             log_state->fixup_atom++;
655             break;
656         case PARANOIA_CB_SCRATCH:
657             log_state->scratch++;
658             break;
659         case PARANOIA_CB_REPAIR:
660             log_state->repair++;
661             break;
662         case PARANOIA_CB_SKIP:
663             log_state->skip++;
664             break;
665         case PARANOIA_CB_DRIFT:
666             log_state->drift++;
667             break;
668         case PARANOIA_CB_BACKOFF:
669             log_state->backoff++;
670             break;
671         case PARANOIA_CB_OVERLAP:
672             log_state->overlap++;
673             break;
674         case PARANOIA_CB_FIXUP_DROPPED:
675             log_state->fixup_dropped++;
676             break;
677         case PARANOIA_CB_FIXUP_DUPED:
678             log_state->fixup_duped++;
679             break;
680         case PARANOIA_CB_READERR:
681             log_state->readerr++;
682             break;
683         default:
684             break;
685         }
686     }
687 }
688 
689 static void
cddareader_reset_log(struct cdio_log * log)690 cddareader_reset_log(struct cdio_log *log)
691 {
692     log->read = 0;
693     log->verify = 0;
694     log->fixup_edge = 0;
695     log->fixup_atom = 0;
696     log->scratch = 0;
697     log->repair = 0;
698     log->skip = 0;
699     log->drift = 0;
700     log->backoff = 0;
701     log->overlap = 0;
702     log->fixup_dropped = 0;
703     log->fixup_duped = 0;
704     log->readerr = 0;
705 }
706 
707 static int
cddareader_set_log_item(PyObject * dict,const char * key,int value)708 cddareader_set_log_item(PyObject *dict, const char *key, int value)
709 {
710     PyObject *value_obj = Py_BuildValue("i", value);
711     if (value_obj) {
712         const int result = PyDict_SetItemString(dict, key, value_obj);
713         Py_DECREF(value_obj);
714         if (result == 0) {
715             return 0;
716         } else {
717             return 1;
718         }
719     } else {
720         return 1;
721     }
722 }
723 
724 static PyObject*
CDDAReader_log(cdio_CDDAReader * self,PyObject * args)725 CDDAReader_log(cdio_CDDAReader *self, PyObject *args)
726 {
727     const struct cdio_log *log = &(self->log);
728     PyObject *log_obj = PyDict_New();
729     if (log_obj) {
730         if (cddareader_set_log_item(log_obj, "read", log->read))
731             goto error;
732         if (cddareader_set_log_item(log_obj, "verify", log->verify))
733             goto error;
734         if (cddareader_set_log_item(log_obj, "fixup_edge", log->fixup_edge))
735             goto error;
736         if (cddareader_set_log_item(log_obj, "fixup_atom", log->fixup_atom))
737             goto error;
738         if (cddareader_set_log_item(log_obj, "scratch", log->scratch))
739             goto error;
740         if (cddareader_set_log_item(log_obj, "repair", log->repair))
741             goto error;
742         if (cddareader_set_log_item(log_obj, "skip", log->skip))
743             goto error;
744         if (cddareader_set_log_item(log_obj, "drift", log->drift))
745             goto error;
746         if (cddareader_set_log_item(log_obj, "backoff", log->backoff))
747             goto error;
748         if (cddareader_set_log_item(log_obj, "overlap", log->overlap))
749             goto error;
750         if (cddareader_set_log_item(log_obj, "fixup_dropped",
751                                     log->fixup_dropped))
752             goto error;
753         if (cddareader_set_log_item(log_obj, "fixup_duped", log->fixup_duped))
754             goto error;
755         if (cddareader_set_log_item(log_obj, "readerr", log->readerr))
756             goto error;
757 
758         return log_obj;
759 error:
760         Py_DECREF(log_obj);
761         return NULL;
762     } else {
763         return NULL;
764     }
765 }
766 
767 static PyObject*
CDDAReader_reset_log(cdio_CDDAReader * self,PyObject * args)768 CDDAReader_reset_log(cdio_CDDAReader *self, PyObject *args)
769 {
770     cddareader_reset_log(&(self->log));
771     Py_INCREF(Py_None);
772     return Py_None;
773 }
774