#include "CdIo.h" #define GET_SUB(p,s) \ cdio_subchannel_t sub; \ verify_return_code(cdio_audio_read_subchannel(p_cdio, &sub)); /** * CDDB * Extraido desde cd-info. **/ #ifdef HAVE_CDDB static int cddb_dec_digit_sum(int n) { int ret = 0; for (;;) { ret += n % 10; n = n / 10; if (!n) return ret; } } /* * Return the number of seconds (discarding frame portion) of an MSF */ static inline unsigned int msf_seconds(msf_t * msf) { return cdio_from_bcd8(msf->m) * 60 + cdio_from_bcd8(msf->s); } static unsigned long cddb_discid(CdIo * p_cdio, int i_tracks) { int i, t, n = 0; msf_t start_msf; msf_t msf; for (i = 1; i <= i_tracks; i++) { cdio_get_track_msf(p_cdio, i, &msf); n += cddb_dec_digit_sum(msf_seconds(&msf)); } cdio_get_track_msf(p_cdio, 1, &start_msf); cdio_get_track_msf(p_cdio, CDIO_CDROM_LEADOUT_TRACK, &msf); t = msf_seconds(&msf) - msf_seconds(&start_msf); return ((n % 0xff) << 24 | t << 8 | i_tracks); } static cddb_disc_t * cd_create(int dlength, int tcount, int *foffset, int use_time) { int i; cddb_disc_t *disc; cddb_track_t *track; /* * Create a new disc structure. */ disc = cddb_disc_new(); /* * If the pointer is NULL then an error occured (out of memory). * Otherwise we continue. */ if (disc) { /* * Initialize the disc length in the structure. */ cddb_disc_set_length(disc, dlength); /* * Now we have to add the basic track data. */ for (i = 0; i < tcount; i++) { /* * Create a new libcddb track structure for this track. */ track = cddb_track_new(); /* * If the pointer is NULL then an error occured (out of * memory). Otherwise we continue. */ if (!track) { /* * Destroy the disc because we can not return half of it. * Return NULL to signal failure. */ cddb_disc_destroy(disc); return NULL; } /* * First add the track to the disc. */ cddb_disc_add_track(disc, track); if (use_time) { /* * Set length in track structure. Since this track is * already part of a disc, the frame offset for the track * will be calculated automatically when we set its * length. */ cddb_track_set_length(track, foffset[i]); } else { /* * Set frame offset in track structure. */ cddb_track_set_frame_offset(track, foffset[i]); } } } return disc; } static cddb_disc_t * cd_read(VALUE obj) { GET_CDIO(obj, cdio); cddb_disc_t *disc = NULL; /* libcddb disc structure */ track_t cnt, t; /* track counters */ lsn_t lsn; /* Logical Sector Number */ int *foffset = NULL; /* list of frame offsets */ /* * Get the track count for the CD. */ cnt = cdio_get_num_tracks(cdio); if (cnt == 0) { libcdio_error_exit("no audio tracks on CD"); } // printf("CD contains %d track(s)\n", cnt); /* * Reserve some memory for the frame offsets. */ foffset = calloc(cnt, sizeof(int)); /* * Now we go and fetch the track data. */ for (t = 1; t <= cnt; t++) { /* * We only want to process audio CDs. */ if (cdio_get_track_format(cdio, t) != TRACK_FORMAT_AUDIO) { // libcdio_error_exit("track %d is not an audio track", t); } /* * Get frame offset of next track. */ lsn = cdio_get_track_lsn(cdio, t); if (lsn == CDIO_INVALID_LSN) { // libcdio_error_exit ("track %d has invalid Logical Sector // Number", t); } /* * Add this offset to the list. We have to make sure that we add * two seconds of lead-in. */ foffset[t - 1] = lsn + SECONDS_TO_FRAMES(2); } /* * Now all we still have to do, is calculate the length of the disc in * seconds. We use the LEADOUT_TRACK for this. */ lsn = cdio_get_track_lsn(cdio, CDIO_CDROM_LEADOUT_TRACK); if (lsn == CDIO_INVALID_LSN) { // libcdio_error_exit ("LEADOUT_TRACK has invalid Logical Sector // Number"); } /* * Now we have to create the libcddb disc structure. */ disc = cd_create(FRAMES_TO_SECONDS(lsn), cnt, foffset, 0); /* * Free all resources held by libcdio CD access structure. */ /* * more clean up */ if (foffset != NULL) { free(foffset); } return disc; } static void rb_cdio_cddb_destroy(cddb_disc_t * disc, cddb_conn_t * conn) { if (conn != NULL) { cddb_destroy(conn); } if (disc != NULL) { cddb_disc_destroy(disc); } } #endif /** * Audio Functions */ VALUE rb_cdio_cdda_get_volume(VALUE obj) { int i; cdio_audio_volume_t vol; GET_CDIO(obj, p_cdio); VALUE out = rb_ary_new(); verify_return_code(cdio_audio_get_volume(p_cdio, &vol)); for (i = 0; i < 4; i++) { rb_ary_push(out, INT2FIX(vol.level[i])); } return out; } VALUE rb_cdio_cdda_set_volume(VALUE obj, VALUE volume) { int i; cdio_audio_volume_t vol; GET_CDIO(obj, p_cdio); Check_Type(volume, T_ARRAY); if (RARRAY(volume)->len != 4) { rb_raise(rb_eArgError, "Your array must have 4 nums"); } for (i = 0; i < 4; i++) { FIXNUM_P(rb_ary_entry(volume, i)); vol.level[i] = FIX2INT(rb_ary_entry(volume, i)); } verify_return_code(cdio_audio_set_volume(p_cdio, &vol)); return Qtrue; } VALUE rb_cdio_cdda_pause(VALUE obj) { GET_CDIO(obj, p_cdio); GET_SUB(p_cdio, sub); if (sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY) { verify_return_code(cdio_audio_pause(p_cdio)); } return Qtrue; } VALUE rb_cdio_cdda_resume(VALUE obj) { GET_CDIO(obj, p_cdio); GET_SUB(p_cdio, sub); // printf("%d", sub.audio_status); if (sub.audio_status == CDIO_MMC_READ_SUB_ST_PAUSED) { verify_return_code(cdio_audio_resume(p_cdio)); } return Qtrue; } VALUE rb_cdio_cdda_stop(VALUE obj) { GET_CDIO(obj, p_cdio); verify_return_code(cdio_audio_stop(p_cdio)); return Qtrue; } VALUE rb_cdio_cdda_play(int argc, VALUE * argv, VALUE obj) { VALUE Vbegin, Vend; track_t begin, end, first, last; GET_CDIO(obj, p_cdio); first = cdio_get_first_track_num(p_cdio); last = cdio_get_last_track_num(p_cdio); rb_scan_args(argc, argv, "02", &Vbegin, &Vend); if (Qnil == Vend) { end = last; } else { FIXNUM_P(Vend); end = FIX2INT(Vend); } if (Qnil == Vbegin) { begin = first; } else { FIXNUM_P(Vbegin); begin = FIX2INT(Vbegin); } // verify than begin and end are corrects if (begin > end) { end = begin; } if (begin > last) { begin = last; } if (begin < first) { begin = first; } if (end < first) { end = first; } if (end > last) { end = last; } VALUE res = rb_ary_new(); rb_ary_push(res, INT2FIX(begin)); rb_ary_push(res, INT2FIX(end)); rb_cdio_cdda_pause(obj); msf_t msf_start, msf_end; cdio_get_track_msf(p_cdio, begin, &msf_start); cdio_get_track_msf(p_cdio, end + 1, &msf_end); verify_return_code(cdio_audio_play_msf(p_cdio, &msf_start, &msf_end)); return res; } VALUE rb_cdio_cdda_cddb_id(VALUE obj) { #ifdef HAVE_CDDB char cddb[8]; GET_CDIO(obj, p_cdio); track_t nt = cdio_get_num_tracks(p_cdio); sprintf(cddb, "%08lx", cddb_discid(p_cdio, nt)); return rb_str_new2(cddb); #else rb_raise(rb_eException, "You need libcddb"); #endif } VALUE rb_cdio_cdda_cddb_matches(VALUE obj) { #ifdef HAVE_CDDB cddb_disc_t *disc = NULL; cddb_conn_t *conn = cddb_new(); VALUE res = rb_ary_new(); if (conn == NULL) { rb_raise(eCdIoCddbConn, "Unable to create connection structure"); } disc = cd_read(obj); /* * (2) execute query command */ int matches = cddb_query(conn, disc); if (matches == -1) { rb_raise(rb_eException, (cddb_error_str(cddb_errno(conn)))); } while (matches > 0) { VALUE hMatch = rb_hash_new(); /* * (3) ... use disc ... */ char year[4]; char id[20]; char length[20]; sprintf(id, "%u", cddb_disc_get_discid(disc)); sprintf(year, "%d", cddb_disc_get_year(disc)); sprintf(length, "%d", cddb_disc_get_length(disc)); HASH_STR(hMatch, "id", id); HASH_STR(hMatch, "category", cddb_disc_get_category_str(disc)); HASH_STR(hMatch, "genre", cddb_disc_get_genre(disc)); HASH_STR(hMatch, "artist", cddb_disc_get_artist(disc)); HASH_STR(hMatch, "title", cddb_disc_get_title(disc)); HASH_STR(hMatch, "year", year); HASH_STR(hMatch, "length", length); rb_ary_push(res, hMatch); /* * (4) get next query result if there is one left */ matches--; if (matches > 0) { if (!cddb_query_next(conn, disc)) { rb_raise(eCdIoCddbConn, "Unable to create connection structure"); } } } rb_cdio_cddb_destroy(disc, conn); return res; #else rb_raise(rb_eException, "You need libcddb"); #endif } VALUE rb_cdio_cdda_cddb_fetch(VALUE obj, VALUE match) { #ifdef HAVE_CDDB FIXNUM_P(match); int match_i, i; match_i = FIX2INT(match); if (match_i < 1) { rb_raise(rb_eArgError, "Match must be >1"); } cddb_disc_t *disc = NULL; cddb_conn_t *conn = cddb_new(); if (conn == NULL) { rb_raise(eCdIoCddbConn, "Unable to create connection structure"); } disc = cd_read(obj); /* * (2) execute query command */ int matches = cddb_query(conn, disc); if (matches == -1) { rb_raise(rb_eException, (cddb_error_str(cddb_errno(conn)))); } else if (match_i > matches) { char error[255]; sprintf(error, "Match %d requested, %d availables", match_i, matches); rb_raise(eCdIoCddbError, error); } i = 1; while (matches > 0) { if (i == match_i) { int success; cddb_disc_set_category(disc, cddb_disc_get_category(disc)); cddb_disc_set_discid(disc, cddb_disc_get_discid(disc)); success = cddb_read(conn, disc); if (!success) { rb_raise(rb_eException, (cddb_error_str(cddb_errno(conn)))); } /* * put data on the object */ char year[4]; char id[20]; char length[20]; sprintf(year, "%d", cddb_disc_get_year(disc)); sprintf(length, "%d", cddb_disc_get_length(disc)); rb_iv_set(obj, "@category", rb_str_new2(cddb_disc_get_category_str(disc))); rb_iv_set(obj, "@genre", rb_str_new2(cddb_disc_get_genre(disc))); rb_iv_set(obj, "@artist", rb_str_new2(cddb_disc_get_artist(disc))); rb_iv_set(obj, "@title", rb_str_new2(cddb_disc_get_title(disc))); rb_iv_set(obj, "@year", rb_str_new2(year)); rb_iv_set(obj, "@length", rb_str_new2(length)); if (NULL != cddb_disc_get_ext_data(disc)) { rb_iv_set(obj, "@extra_data", rb_str_new2(cddb_disc_get_ext_data(disc))); } /* * put data on tracks */ VALUE oTrack, oTracks; cddb_track_t *track; int track_i; rb_cdio_cd_get_tracks(obj); oTracks = rb_iv_get(obj, "@tracks"); track = cddb_disc_get_track_first(disc); while (track != NULL) { track_i = cddb_track_get_number(track); oTrack = rb_cdio_tracks_index(oTracks, INT2FIX(track_i)); const char *artist = cddb_track_get_artist(track); char *title = cddb_track_get_title(track); rb_iv_set(oTrack, "@artist", rb_str_new2((NULL != artist) ? artist : "")); rb_iv_set(oTrack, "@title", rb_str_new2((NULL != title) ? title : "")); // track = cddb_disc_get_track_next(disc); } // destroy and send ok rb_cdio_cddb_destroy(disc, conn); return Qtrue; } i++; /* * (4) get next query result if there is one left */ matches--; if (matches > 0) { if (!cddb_query_next(conn, disc)) { rb_raise(eCdIoCddbConn, "Unable to create connection structure"); } } } return Qfalse; #else rb_raise(rb_eException, "You need libcddb"); #endif } VALUE rb_cdio_cdda_category(VALUE obj) { return rb_iv_get(obj, "@category"); } VALUE rb_cdio_cdda_title(VALUE obj) { return rb_iv_get(obj, "@title"); } VALUE rb_cdio_cdda_artist(VALUE obj) { return rb_iv_get(obj, "@artist"); } VALUE rb_cdio_cdda_year(VALUE obj) { return rb_iv_get(obj, "@year"); } VALUE rb_cdio_cdda_genre(VALUE obj) { return rb_iv_get(obj, "@genre"); } VALUE rb_cdio_cdda_length(VALUE obj) { return rb_iv_get(obj, "@length"); } VALUE rb_cdio_cdda_extra_data(VALUE obj) { return rb_iv_get(obj, "@extra_data"); } #define SET_SUB_I(o,k,v) rb_hash_aset(o,rb_str_new2(k),INT2FIX(v)); #define SET_SUB_S(o,k,v) rb_hash_aset(o,rb_str_new2(k),rb_str_new2(v)); VALUE rb_cdio_cdda_subchannel(VALUE obj) { GET_CDIO(obj, p_cdio); GET_SUB(p_cdio, sub); VALUE out = rb_hash_new(); /* * { uint8_t format; uint8_t audio_status; uint8_t address: 4; uint8_t * control: 4; uint8_t track; uint8_t index; msf_t abs_addr; msf_t * rel_addr; } cdio_subchannel_t; */ char *as_s; switch (sub.audio_status) { case CDIO_MMC_READ_SUB_ST_INVALID: as_s = "invalid"; break; case CDIO_MMC_READ_SUB_ST_PLAY: as_s = "play"; break; case CDIO_MMC_READ_SUB_ST_PAUSED: as_s = "paused"; break; case CDIO_MMC_READ_SUB_ST_COMPLETED: as_s = "completed"; break; case CDIO_MMC_READ_SUB_ST_ERROR: as_s = "error"; break; case CDIO_MMC_READ_SUB_ST_NO_STATUS: as_s = "no_status"; break; } msf_t msf_abs, msf_rel; char *msf_abs_s, *msf_rel_s; msf_abs_s = cdio_msf_to_str(&sub.abs_addr); msf_rel_s = cdio_msf_to_str(&sub.rel_addr); SET_SUB_I(out, "format", sub.format); SET_SUB_I(out, "audio_status", sub.audio_status); SET_SUB_S(out, "audio_status_desc", as_s); SET_SUB_I(out, "address", sub.address); SET_SUB_I(out, "control", sub.control); SET_SUB_I(out, "track", sub.track); SET_SUB_I(out, "index", sub.index); SET_SUB_S(out, "abs_addr", msf_abs_s); SET_SUB_S(out, "rel_addr", msf_rel_s); free(msf_abs_s); free(msf_rel_s); return out; } #undef SET_SUB_I #undef SET_SUB_S