00001 #include <stdio.h>
00002 #include <stdlib.h>
00003 #include <iostream>
00004 #include <string>
00005 #include <qobject.h>
00006 #include <qiodevice.h>
00007 #include <qfile.h>
00008 using namespace std;
00009
00010 #include "cddecoder.h"
00011 #include "constants.h"
00012 #include <mythtv/audiooutput.h>
00013 #include "metadata.h"
00014
00015 #include <mythtv/mythcontext.h>
00016 #include <mythtv/mythmediamonitor.h>
00017
00018 CdDecoder::CdDecoder(const QString &file, DecoderFactory *d, QIODevice *i,
00019 AudioOutput *o)
00020 : Decoder(d, i, o)
00021 {
00022 filename = file;
00023 inited = FALSE;
00024 user_stop = FALSE;
00025 stat = 0;
00026 bks = 0;
00027 done = FALSE;
00028 finish = FALSE;
00029 len = 0;
00030 freq = 0;
00031 bitrate = 0;
00032 seekTime = -1.0;
00033 totalTime = 0.0;
00034 chan = 0;
00035 output_buf = 0;
00036 output_bytes = 0;
00037 output_at = 0;
00038
00039 device = NULL;
00040 paranoia = NULL;
00041
00042 settracknum = -1;
00043 }
00044
00045 CdDecoder::~CdDecoder(void)
00046 {
00047 if (inited)
00048 deinit();
00049
00050 if (output_buf)
00051 delete [] output_buf;
00052 output_buf = 0;
00053 }
00054
00055 void CdDecoder::stop()
00056 {
00057 user_stop = TRUE;
00058 }
00059
00060 void CdDecoder::flush(bool final)
00061 {
00062 ulong min = final ? 0 : bks;
00063
00064 while ((! done && ! finish) && output_bytes > min) {
00065
00066 if (user_stop || finish) {
00067 inited = FALSE;
00068 done = TRUE;
00069 } else {
00070 ulong sz = output_bytes < bks ? output_bytes : bks;
00071
00072 int samples = (sz*8)/(chan*16);
00073 if (output()->AddSamples(output_buf, samples, -1))
00074 {
00075 output_bytes -= sz;
00076 memmove(output_buf, output_buf + sz, output_bytes);
00077 output_at = output_bytes;
00078 } else {
00079 unlock();
00080 usleep(500);
00081 lock();
00082 done = user_stop;
00083 }
00084 }
00085 }
00086 }
00087
00088 bool CdDecoder::initialize()
00089 {
00090 bks = blockSize();
00091
00092 inited = user_stop = done = finish = FALSE;
00093 len = freq = bitrate = 0;
00094 stat = chan = 0;
00095 seekTime = -1.0;
00096 totalTime = 0.0;
00097
00098 filename = ((QFile *)input())->name();
00099 tracknum = atoi(filename.ascii());
00100
00101 if (!output_buf)
00102 output_buf = new char[globalBufferSize];
00103 output_at = 0;
00104 output_bytes = 0;
00105
00106 device = cdda_identify(devicename.ascii(), 0, NULL);
00107 if (!device)
00108 return FALSE;
00109
00110 if (cdda_open(device))
00111 {
00112 cdda_close(device);
00113 return FALSE;
00114 }
00115
00116 cdda_verbose_set(device, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);
00117 start = cdda_track_firstsector(device, tracknum);
00118 end = cdda_track_lastsector(device, tracknum);
00119
00120 if (start > end || end == start)
00121 {
00122 cdda_close(device);
00123 return FALSE;
00124 }
00125
00126 paranoia = paranoia_init(device);
00127 paranoia_modeset(paranoia, PARANOIA_MODE_OVERLAP);
00128 paranoia_seek(paranoia, start, SEEK_SET);
00129
00130 curpos = start;
00131
00132 totalTime = ((end - start + 1) * CD_FRAMESAMPLES) / 44100.0;
00133
00134 chan = 2;
00135 freq = 44100;
00136
00137 if (output())
00138 {
00139 output()->Reconfigure(16, chan, freq, false );
00140 output()->SetSourceBitrate(44100 * 2 * 16);
00141 }
00142
00143 setCDSpeed(2);
00144 inited = TRUE;
00145 return TRUE;
00146 }
00147
00148 void CdDecoder::seek(double pos)
00149 {
00150 seekTime = pos;
00151 }
00152
00153 void CdDecoder::deinit()
00154 {
00155 setCDSpeed(-1);
00156 if (paranoia)
00157 paranoia_free(paranoia);
00158 if (device)
00159 cdda_close(device);
00160
00161 device = NULL;
00162 paranoia = NULL;
00163
00164 inited = user_stop = done = finish = FALSE;
00165 len = freq = bitrate = 0;
00166 stat = chan = 0;
00167 setInput(0);
00168 setOutput(0);
00169 }
00170
00171 static void paranoia_cb(long inpos, int function)
00172 {
00173 inpos = inpos; function = function;
00174 }
00175
00176 void CdDecoder::run()
00177 {
00178 lock();
00179
00180 if (! inited) {
00181 unlock();
00182
00183 return;
00184 }
00185
00186 stat = DecoderEvent::Decoding;
00187
00188 unlock();
00189
00190 {
00191 DecoderEvent e((DecoderEvent::Type) stat);
00192 dispatch(e);
00193 }
00194
00195 int16_t *cdbuffer;
00196
00197 while (! done && ! finish) {
00198 lock();
00199
00200
00201 if (seekTime >= 0.0) {
00202 curpos = (int)(((seekTime * 44100) / CD_FRAMESAMPLES) + start);
00203 paranoia_seek(paranoia, curpos, SEEK_SET);
00204
00205 seekTime = -1.0;
00206 }
00207
00208 curpos++;
00209 if (curpos <= end)
00210 {
00211 cdbuffer = paranoia_read(paranoia, paranoia_cb);
00212
00213 memcpy((char *)(output_buf + output_at), (char *)cdbuffer,
00214 CD_FRAMESIZE_RAW);
00215 output_at += CD_FRAMESIZE_RAW;
00216 output_bytes += CD_FRAMESIZE_RAW;
00217
00218 if (output())
00219 flush();
00220 }
00221 else
00222 {
00223 flush(TRUE);
00224
00225 if (output()) {
00226 output()->Drain();
00227 }
00228
00229 done = TRUE;
00230 if (! user_stop) {
00231 finish = TRUE;
00232 }
00233 }
00234
00235 unlock();
00236 }
00237
00238 lock();
00239
00240 if (finish)
00241 stat = DecoderEvent::Finished;
00242 else if (user_stop)
00243 stat = DecoderEvent::Stopped;
00244
00245 unlock();
00246
00247 {
00248 DecoderEvent e((DecoderEvent::Type) stat);
00249 dispatch(e);
00250 }
00251
00252 deinit();
00253 }
00254
00255 void CdDecoder::setCDSpeed(int speed)
00256 {
00257 QMutexLocker lock(getMutex());
00258 MediaMonitor::SetCDSpeed(devicename, speed);
00259 }
00260
00261 int CdDecoder::getNumTracks(void)
00262 {
00263 int cd = cd_init_device((char *)devicename.ascii());
00264
00265 struct disc_info discinfo;
00266 if (cd_stat(cd, &discinfo) != 0)
00267 {
00268 error("Couldn't stat CD, Error.");
00269 cd_finish(cd);
00270 return 0;
00271 }
00272
00273 if (!discinfo.disc_present)
00274 {
00275 error("No disc present");
00276 cd_finish(cd);
00277 return 0;
00278 }
00279
00280 int retval = discinfo.disc_total_tracks;
00281
00282 cd_finish(cd);
00283
00284 return retval;
00285 }
00286
00287 int CdDecoder::getNumCDAudioTracks(void)
00288 {
00289 int cd = cd_init_device((char *)devicename.ascii());
00290
00291 struct disc_info discinfo;
00292 if (cd_stat(cd, &discinfo) != 0)
00293 {
00294 error("Couldn't stat CD, Error.");
00295 cd_finish(cd);
00296 return 0;
00297 }
00298
00299 if (!discinfo.disc_present)
00300 {
00301 error("No disc present");
00302 cd_finish(cd);
00303 return 0;
00304 }
00305
00306 int retval = 0;
00307 for( int i = 0 ; i < discinfo.disc_total_tracks; ++i)
00308 {
00309 if(discinfo.disc_track[i].track_type == CDAUDIO_TRACK_AUDIO)
00310 {
00311 ++retval;
00312 }
00313 }
00314
00315 cd_finish(cd);
00316
00317 return retval;
00318 }
00319
00320 Metadata* CdDecoder::getMetadata(int track)
00321 {
00322 settracknum = track;
00323 return getMetadata();
00324 }
00325
00326 Metadata *CdDecoder::getLastMetadata()
00327 {
00328 Metadata *return_me;
00329 for(int i = getNumTracks(); i > 0; --i)
00330 {
00331 settracknum = i;
00332 return_me = getMetadata();
00333 if(return_me)
00334 {
00335 return return_me;
00336 }
00337 }
00338 return NULL;
00339 }
00340
00341 Metadata *CdDecoder::getMetadata()
00342 {
00343
00344 QString artist = "", album = "", compilation_artist = "", title = "", genre = "";
00345 int year = 0, tracknum = 0, length = 0;
00346
00347 int cd = cd_init_device((char *)devicename.ascii());
00348
00349 struct disc_info discinfo;
00350 if (cd_stat(cd, &discinfo) != 0)
00351 {
00352 error("Couldn't stat CD, Error.");
00353 cd_finish(cd);
00354 return NULL;
00355 }
00356
00357 if (!discinfo.disc_present)
00358 {
00359 error("No disc present");
00360 cd_finish(cd);
00361 return NULL;
00362 }
00363
00364 if (settracknum == -1)
00365 tracknum = atoi(filename.ascii());
00366 else
00367 {
00368 tracknum = settracknum;
00369 filename = QString("%1.cda").arg(tracknum);
00370 }
00371
00372 settracknum = -1;
00373
00374 if (tracknum > discinfo.disc_total_tracks)
00375 {
00376 error("No such track on CD");
00377 cd_finish(cd);
00378 return NULL;
00379 }
00380
00381 if(discinfo.disc_track[tracknum - 1].track_type != CDAUDIO_TRACK_AUDIO )
00382 {
00383 error("Exclude non audio tracks");
00384 cd_finish(cd);
00385 return NULL;
00386 }
00387
00388
00389 struct disc_data discdata;
00390 memset(&discdata, 0, sizeof(discdata));
00391
00392 int ret = cddb_read_disc_data(cd, &discdata);
00393
00394 if (ret < 0)
00395 {
00396 cd_finish(cd);
00397 VERBOSE(VB_IMPORTANT, QString("Error during CD lookup: %1").arg(ret));
00398 VERBOSE(VB_MEDIA, QString("cddb_read_disc_data() said: ")
00399 + cddb_message);
00400 return NULL;
00401 }
00402
00403 compilation_artist = M_QSTRING_UNICODE(discdata.data_artist);
00404
00405 if (compilation_artist.lower().left(7) == "various")
00406 {
00407 compilation_artist = QObject::tr("Various Artists");
00408 }
00409
00410
00411 album = M_QSTRING_UNICODE(discdata.data_title);
00412 genre = cddb_genre(discdata.data_genre);
00413
00414 if (!genre.isEmpty())
00415 {
00416 QString flet = genre.upper().left(1);
00417 QString rt = genre.right(genre.length()-1).lower();
00418 genre = flet + rt;
00419 }
00420
00421 title = M_QSTRING_UNICODE(discdata.data_track[tracknum - 1].track_name);
00422 artist = M_QSTRING_UNICODE(discdata.data_track[tracknum - 1].track_artist);
00423
00424 if (artist.length() < 1)
00425 {
00426 artist = compilation_artist;
00427 compilation_artist = "";
00428 }
00429
00430 if (title.length() < 1)
00431 title = QString(QObject::tr("Track %1")).arg(tracknum);
00432
00433 cddb_write_data(cd, &discdata);
00434
00435 length = discinfo.disc_track[tracknum - 1].track_length.minutes * 60 +
00436 discinfo.disc_track[tracknum - 1].track_length.seconds;
00437 length = length < 0 ? 0 : length;
00438 length *= 1000;
00439
00440 Metadata *retdata = new Metadata(filename, artist, compilation_artist,
00441 album, title, genre, year, tracknum, length);
00442
00443 retdata->determineIfCompilation(true);
00444
00445 cd_finish(cd);
00446 return retdata;
00447 }
00448
00449 void CdDecoder::commitMetadata(Metadata *mdata)
00450 {
00451 int cd = cd_init_device((char *)devicename.ascii());
00452
00453 struct disc_info discinfo;
00454 if (cd_stat(cd, &discinfo) != 0)
00455 {
00456 error("Couldn't stat CD, Error.");
00457 cd_finish(cd);
00458 return;
00459 }
00460
00461 if (!discinfo.disc_present)
00462 {
00463 error("No disc present");
00464 cd_finish(cd);
00465 return;
00466 }
00467
00468 tracknum = mdata->Track();
00469
00470 if (tracknum > discinfo.disc_total_tracks)
00471 {
00472 error("No such track on CD");
00473 cd_finish(cd);
00474 return;
00475 }
00476
00477
00478 struct disc_data discdata;
00479 int ret = cddb_read_disc_data(cd, &discdata);
00480
00481 if (ret < 0)
00482 {
00483 cd_finish(cd);
00484 VERBOSE(VB_IMPORTANT, QString("Error during CD lookup: %1").arg(ret));
00485 return;
00486 }
00487
00488 if (mdata->Compilation())
00489 {
00490 if (mdata->CompilationArtist() != discdata.data_artist)
00491 strncpy(discdata.data_artist, mdata->CompilationArtist().utf8(), 256);
00492 }
00493 else
00494 {
00495 if (mdata->Artist() != discdata.data_artist)
00496 strncpy(discdata.data_artist, mdata->Artist().utf8(), 256);
00497 }
00498 if (mdata->Album() != discdata.data_title)
00499 strncpy(discdata.data_title, mdata->Album().utf8(), 256);
00500 if (mdata->Title() != discdata.data_track[tracknum - 1].track_name)
00501 {
00502 strncpy(discdata.data_track[tracknum - 1].track_name, mdata->Title().utf8(),
00503 256);
00504
00505 }
00506
00507 if (mdata->Compilation())
00508 {
00509 if (mdata->Artist() != discdata.data_track[tracknum - 1].track_artist)
00510 {
00511 strncpy(discdata.data_track[tracknum - 1].track_artist,
00512 mdata->Artist().utf8(), 256);
00513 }
00514 }
00515 else
00516 {
00517 if ("" != discdata.data_track[tracknum - 1].track_artist)
00518 {
00519 strncpy(discdata.data_track[tracknum - 1].track_artist, "", 256);
00520 }
00521 }
00522
00523 cddb_write_data(cd, &discdata);
00524
00525 cd_finish(cd);
00526 }
00527
00528 bool CdDecoderFactory::supports(const QString &source) const
00529 {
00530 return (source.right(extension().length()).lower() == extension());
00531 }
00532
00533 const QString &CdDecoderFactory::extension() const
00534 {
00535 static QString ext(".cda");
00536 return ext;
00537 }
00538
00539
00540 const QString &CdDecoderFactory::description() const
00541 {
00542 static QString desc(QObject::tr("Ogg Vorbis Audio"));
00543 return desc;
00544 }
00545
00546 Decoder *CdDecoderFactory::create(const QString &file, QIODevice *input,
00547 AudioOutput *output, bool deletable)
00548 {
00549 if (deletable)
00550 return new CdDecoder(file, this, input, output);
00551
00552 static CdDecoder *decoder = 0;
00553 if (! decoder) {
00554 decoder = new CdDecoder(file, this, input, output);
00555 } else {
00556 decoder->setInput(input);
00557 decoder->setFilename(file);
00558 decoder->setOutput(output);
00559 }
00560
00561 return decoder;
00562 }
00563