1 #include "sources/soundsourcewv.h"
2
3 #include <wavpack.h>
4
5 #include "util/logger.h"
6
7 namespace mixxx {
8
9 namespace {
10
11 const Logger kLogger("SoundSourceWV");
12
13 static WavpackStreamReader s_streamReader = {
14 SoundSourceWV::ReadBytesCallback,
15 SoundSourceWV::GetPosCallback,
16 SoundSourceWV::SetPosAbsCallback,
17 SoundSourceWV::SetPosRelCallback,
18 SoundSourceWV::PushBackByteCallback,
19 SoundSourceWV::GetlengthCallback,
20 SoundSourceWV::CanSeekCallback,
21 SoundSourceWV::WriteBytesCallback};
22
23 } // anonymous namespace
24
25 //static
26 const QString SoundSourceProviderWV::kDisplayName = QStringLiteral("WavPack");
27
28 //static
29 const QStringList SoundSourceProviderWV::kSupportedFileExtensions = {
30 QStringLiteral("wv"),
31 };
32
getPriorityHint(const QString & supportedFileExtension) const33 SoundSourceProviderPriority SoundSourceProviderWV::getPriorityHint(
34 const QString& supportedFileExtension) const {
35 Q_UNUSED(supportedFileExtension)
36 // This reference decoder is supposed to produce more accurate
37 // and reliable results than any other DEFAULT provider.
38 return SoundSourceProviderPriority::Higher;
39 }
40
newSoundSource(const QUrl & url)41 SoundSourcePointer SoundSourceProviderWV::newSoundSource(const QUrl& url) {
42 return newSoundSourceFromUrl<SoundSourceWV>(url);
43 }
44
SoundSourceWV(const QUrl & url)45 SoundSourceWV::SoundSourceWV(const QUrl& url)
46 : SoundSource(url),
47 m_wpc(nullptr),
48 m_sampleScaleFactor(CSAMPLE_ZERO),
49 m_pWVFile(nullptr),
50 m_pWVCFile(nullptr),
51 m_curFrameIndex(0) {
52 }
53
~SoundSourceWV()54 SoundSourceWV::~SoundSourceWV() {
55 close();
56 }
57
tryOpen(OpenMode,const OpenParams & params)58 SoundSource::OpenResult SoundSourceWV::tryOpen(
59 OpenMode /*mode*/,
60 const OpenParams& params) {
61 DEBUG_ASSERT(!m_wpc);
62 char msg[80]; // hold possible error message
63 int openFlags = OPEN_WVC | OPEN_NORMALIZE;
64 if ((params.getSignalInfo().getChannelCount() == 1) ||
65 (params.getSignalInfo().getChannelCount() == 2)) {
66 openFlags |= OPEN_2CH_MAX;
67 }
68
69 // We use WavpackOpenFileInputEx to support Unicode paths on windows
70 // http://www.wavpack.com/lib_use.txt
71 QString wavPackFileName = getLocalFileName();
72 m_pWVFile = new QFile(wavPackFileName);
73 m_pWVFile->open(QFile::ReadOnly);
74 QString correctionFileName(wavPackFileName + "c");
75 if (QFile::exists(correctionFileName)) {
76 // If there is a correction file, open it as well
77 m_pWVCFile = new QFile(correctionFileName);
78 m_pWVCFile->open(QFile::ReadOnly);
79 }
80 m_wpc = WavpackOpenFileInputEx(&s_streamReader, m_pWVFile, m_pWVCFile, msg, openFlags, 0);
81 if (!m_wpc) {
82 kLogger.warning() << "failed to open file : " << msg;
83 return OpenResult::Failed;
84 }
85
86 initChannelCountOnce(WavpackGetReducedChannels(static_cast<WavpackContext*>(m_wpc)));
87 initSampleRateOnce(WavpackGetSampleRate(static_cast<WavpackContext*>(m_wpc)));
88 initFrameIndexRangeOnce(
89 mixxx::IndexRange::forward(
90 0,
91 WavpackGetNumSamples(static_cast<WavpackContext*>(m_wpc))));
92
93 if (WavpackGetMode(static_cast<WavpackContext*>(m_wpc)) & MODE_FLOAT) {
94 m_sampleScaleFactor = CSAMPLE_PEAK;
95 } else {
96 const int bitsPerSample = WavpackGetBitsPerSample(static_cast<WavpackContext*>(m_wpc));
97 if ((bitsPerSample >= 8) && (bitsPerSample <= 32)) {
98 // Range of signed sample values: [-2 ^ (bitsPerSample - 1), 2 ^ (bitsPerSample - 1) - 1]
99 const uint32_t absSamplePeak = 1u << (bitsPerSample - 1);
100 DEBUG_ASSERT(absSamplePeak > 0);
101 // Scaled range of sample values: [-CSAMPLE_PEAK, CSAMPLE_PEAK)
102 m_sampleScaleFactor = CSAMPLE_PEAK / absSamplePeak;
103 } else {
104 kLogger.warning()
105 << "Invalid bits per sample:"
106 << bitsPerSample;
107 return OpenResult::Aborted;
108 }
109 }
110
111 m_curFrameIndex = frameIndexMin();
112
113 return OpenResult::Succeeded;
114 }
115
close()116 void SoundSourceWV::close() {
117 if (m_wpc) {
118 WavpackCloseFile(static_cast<WavpackContext*>(m_wpc));
119 m_wpc = nullptr;
120 }
121 if (m_pWVFile) {
122 m_pWVFile->close();
123 delete m_pWVFile;
124 m_pWVFile = nullptr;
125 }
126 if (m_pWVCFile) {
127 m_pWVCFile->close();
128 delete m_pWVCFile;
129 m_pWVCFile = nullptr;
130 }
131 }
132
readSampleFramesClamped(const WritableSampleFrames & writableSampleFrames)133 ReadableSampleFrames SoundSourceWV::readSampleFramesClamped(
134 const WritableSampleFrames& writableSampleFrames) {
135 const SINT firstFrameIndex = writableSampleFrames.frameIndexRange().start();
136
137 if (m_curFrameIndex != firstFrameIndex) {
138 if (WavpackSeekSample(static_cast<WavpackContext*>(m_wpc), firstFrameIndex)) {
139 m_curFrameIndex = firstFrameIndex;
140 } else {
141 kLogger.warning()
142 << "Could not seek to first frame index"
143 << firstFrameIndex;
144 m_curFrameIndex = WavpackGetSampleIndex(static_cast<WavpackContext*>(m_wpc));
145 return ReadableSampleFrames(IndexRange::between(m_curFrameIndex, m_curFrameIndex));
146 }
147 }
148 DEBUG_ASSERT(m_curFrameIndex == firstFrameIndex);
149
150 const SINT numberOfFramesTotal = writableSampleFrames.frameLength();
151
152 static_assert(sizeof(CSAMPLE) == sizeof(int32_t),
153 "CSAMPLE and int32_t must have the same size");
154 CSAMPLE* pOutputBuffer = writableSampleFrames.writableData();
155 SINT unpackCount = WavpackUnpackSamples(static_cast<WavpackContext*>(m_wpc),
156 reinterpret_cast<int32_t*>(pOutputBuffer),
157 numberOfFramesTotal);
158 DEBUG_ASSERT(unpackCount >= 0);
159 DEBUG_ASSERT(unpackCount <= numberOfFramesTotal);
160 if (!(WavpackGetMode(static_cast<WavpackContext*>(m_wpc)) & MODE_FLOAT)) {
161 // signed integer -> float
162 const SINT sampleCount = getSignalInfo().frames2samples(unpackCount);
163 for (SINT i = 0; i < sampleCount; ++i) {
164 const int32_t sampleValue =
165 *reinterpret_cast<int32_t*>(pOutputBuffer);
166 *pOutputBuffer++ = CSAMPLE(sampleValue) * m_sampleScaleFactor;
167 }
168 }
169 const auto resultRange = IndexRange::forward(m_curFrameIndex, unpackCount);
170 m_curFrameIndex += unpackCount;
171 return ReadableSampleFrames(
172 resultRange,
173 SampleBuffer::ReadableSlice(
174 writableSampleFrames.writableData(),
175 getSignalInfo().frames2samples(unpackCount)));
176 }
177
178 //static
ReadBytesCallback(void * id,void * data,int bcount)179 int32_t SoundSourceWV::ReadBytesCallback(void* id, void* data, int bcount) {
180 QFile* pFile = static_cast<QFile*>(id);
181 if (!pFile) {
182 return 0;
183 }
184 return pFile->read((char*)data, bcount);
185 }
186
187 // static
GetPosCallback(void * id)188 uint32_t SoundSourceWV::GetPosCallback(void* id) {
189 QFile* pFile = static_cast<QFile*>(id);
190 if (!pFile) {
191 return 0;
192 }
193 return pFile->pos();
194 }
195
196 //static
SetPosAbsCallback(void * id,unsigned int pos)197 int SoundSourceWV::SetPosAbsCallback(void* id, unsigned int pos) {
198 QFile* pFile = static_cast<QFile*>(id);
199 if (!pFile) {
200 return 0;
201 }
202 return pFile->seek(pos) ? 0 : -1;
203 }
204
205 //static
SetPosRelCallback(void * id,int delta,int mode)206 int SoundSourceWV::SetPosRelCallback(void* id, int delta, int mode) {
207 QFile* pFile = static_cast<QFile*>(id);
208 if (!pFile) {
209 return 0;
210 }
211
212 switch (mode) {
213 case SEEK_SET:
214 return pFile->seek(delta) ? 0 : -1;
215 case SEEK_CUR:
216 return pFile->seek(pFile->pos() + delta) ? 0 : -1;
217 case SEEK_END:
218 return pFile->seek(pFile->size() + delta) ? 0 : -1;
219 default:
220 return -1;
221 }
222 }
223
224 //static
PushBackByteCallback(void * id,int c)225 int SoundSourceWV::PushBackByteCallback(void* id, int c) {
226 QFile* pFile = static_cast<QFile*>(id);
227 if (!pFile) {
228 return 0;
229 }
230 pFile->ungetChar((char)c);
231 return 1;
232 }
233
234 //static
GetlengthCallback(void * id)235 uint32_t SoundSourceWV::GetlengthCallback(void* id) {
236 QFile* pFile = static_cast<QFile*>(id);
237 if (!pFile) {
238 return 0;
239 }
240 return pFile->size();
241 }
242
243 //static
CanSeekCallback(void * id)244 int SoundSourceWV::CanSeekCallback(void* id) {
245 QFile* pFile = static_cast<QFile*>(id);
246 if (!pFile) {
247 return 0;
248 }
249 return pFile->isSequential() ? 0 : 1;
250 }
251
252 //static
WriteBytesCallback(void * id,void * data,int32_t bcount)253 int32_t SoundSourceWV::WriteBytesCallback(void* id, void* data, int32_t bcount) {
254 QFile* pFile = static_cast<QFile*>(id);
255 if (!pFile) {
256 return 0;
257 }
258 return (int32_t)pFile->write((char*)data, bcount);
259 }
260
261 } // namespace mixxx
262