1 /*
2 * Cantata
3 *
4 * Copyright (c) 2011-2020 Craig Drummond <craig.p.drummond@gmail.com>
5 *
6 * ----
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 */
22
23 #include "extractjob.h"
24 #include "device.h"
25 #include "support/utils.h"
26 #include "tags/tags.h"
27 #include "cdparanoia.h"
28 #include "gui/covers.h"
29 #include "mpd-interface/mpdconnection.h"
30 #include "gui/settings.h"
31 #include <QStringList>
32 #include <QProcess>
33 #include <QFile>
34
35 const int ExtractJob::constWavHeaderSize=44; // ffmpeg uses 46 byte header?
36
insertSize(unsigned char * data,qint32 size)37 static void insertSize(unsigned char *data, qint32 size)
38 {
39 data[0]=(size&0x000000ff);
40 data[1]=(size&0x0000ff00)>>8;
41 data[2]=(size&0x00ff0000)>>16;
42 data[3]=(size&0xff000000)>>24;
43 }
44
writeWavHeader(QIODevice & dev,qint32 size)45 void ExtractJob::writeWavHeader(QIODevice &dev, qint32 size)
46 {
47 unsigned char riffHeader[] = {
48 0x52, 0x49, 0x46, 0x46, // 0 "RIFF"
49 0x00, 0x00, 0x00, 0x00, // 4 wavSize
50 0x57, 0x41, 0x56, 0x45, // 8 "WAVE"
51 0x66, 0x6d, 0x74, 0x20, // 12 "fmt "
52 0x10, 0x00, 0x00, 0x00, // 16 Size of WAVE section chunk (ffmpeg has 12 here???)
53 0x01, 0x00, 0x02, 0x00, // 20 WAVE type format / Number of channels
54 0x44, 0xac, 0x00, 0x00, // 24 Samples per second
55 0x10, 0xb1, 0x02, 0x00, // 28 Bytes per second
56 0x04, 0x00, 0x10, 0x00, // 32 Block alignment / Bits per sample
57 0x64, 0x61, 0x74, 0x61, // 36 "data" (ffmpeg preceeds this with two 0 bytes)
58 0x00, 0x00, 0x00, 0x00 // 40 byteCount
59 };
60
61 if (0!=size) {
62 insertSize(&riffHeader[4], (size+constWavHeaderSize)-8);
63 insertSize(&riffHeader[40], size);
64 }
65
66 dev.write((char*)riffHeader, constWavHeaderSize);
67 }
68
ExtractJob(const Encoders::Encoder & enc,int val,const QString & src,const QString & dest,const Song & s,const QString & cover)69 ExtractJob::ExtractJob(const Encoders::Encoder &enc, int val, const QString &src, const QString &dest, const Song &s, const QString &cover)
70 : encoder(enc)
71 , value(val)
72 , srcFile(src)
73 , destFile(dest)
74 , song(s)
75 , coverFile(cover)
76 , copiedCover(false)
77 {
78 }
79
~ExtractJob()80 ExtractJob::~ExtractJob()
81 {
82 }
83
run()84 void ExtractJob::run()
85 {
86 if (stopRequested) {
87 emit result(Device::Cancelled);
88 } else {
89 QStringList encParams=encoder.params(value, encoder.transcoder ? "pipe:" : "-", destFile);
90 CdParanoia cdparanoia(srcFile, Settings::self()->paranoiaFull(), Settings::self()->paranoiaNeverSkip(), false, Settings::self()->paranoiaOffset());
91
92 if (!cdparanoia) {
93 emit result(Device::FailedToLockDevice);
94 return;
95 }
96 QProcess process;
97 QString cmd=encParams.takeFirst();
98 process.start(cmd, encParams, QIODevice::WriteOnly);
99 process.waitForStarted();
100
101 if (stopRequested) {
102 emit result(Device::Cancelled);
103 process.close();
104 return;
105 }
106 int firstSector = cdparanoia.firstSectorOfTrack(song.id);
107 int lastSector = cdparanoia.lastSectorOfTrack(song.id);
108 int total=lastSector-firstSector;
109 int count=0;
110
111 cdparanoia.seek(firstSector, SEEK_SET);
112
113 writeWavHeader(process);
114 while ((firstSector+count) <= lastSector) {
115 qint16 *buf = cdparanoia.read();
116 if (!buf) {
117 emit result(Device::Failed);
118 process.close();
119 return;
120 }
121 if (stopRequested) {
122 emit result(Device::Cancelled);
123 process.close();
124 return;
125 }
126
127 char *buffer=(char *)buf;
128
129 qint64 writePos=0;
130 do {
131 qint64 bytesWritten = process.write(&buffer[writePos], CD_FRAMESIZE_RAW - writePos);
132 if (stopRequested) {
133 emit result(Device::Cancelled);
134 process.close();
135 QFile::remove(destFile);
136 return;
137 }
138 if (-1==bytesWritten) {
139 emit result(Device::WriteFailed);
140 process.close();
141 QFile::remove(destFile);
142 return;
143 }
144 writePos+=bytesWritten;
145 } while (writePos<CD_FRAMESIZE_RAW);
146
147 count++;
148 setPercent((count*100/total)+0.5);
149 }
150 process.closeWriteChannel();
151 process.waitForFinished();
152 Utils::setFilePerms(destFile);
153 Tags::update(destFile, Song(), song, 3);
154
155 if (!stopRequested && !coverFile.isEmpty()) {
156 copiedCover=Covers::copyImage(Utils::getDir(coverFile), Utils::getDir(destFile), Utils::getFile(coverFile), Covers::albumFileName(song)+Utils::getExtension(coverFile), 0);
157 }
158
159 emit result(Device::Ok);
160 }
161 }
162
163 #include "moc_extractjob.cpp"
164