summaryrefslogtreecommitdiffstats
path: root/plugins/soundsourcewv/soundsourcewv.cpp
blob: c247ac3dbbe27526fb6def149ec6d9e13a91d00a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
//soundsourcewv.cpp : sound source proxy for .wv (WavPack files)
//created by fenugrec
//great help from rryan & others on #mixxx
//format_samples adapted from cmus (Peter Lemenkov)

#include <QtDebug>

#include <taglib/wavpackfile.h>

#include "soundsourcewv.h"

namespace Mixxx {

SoundSourceWV::SoundSourceWV(QString qFilename) : SoundSource(qFilename)
{
    // Initialize variables to invalid values in case loading fails.
    filewvc=NULL;
}


SoundSourceWV::~SoundSourceWV(){
   if (filewvc) {
    WavpackCloseFile(filewvc);
    filewvc=NULL;
   }
}

QList<QString> SoundSourceWV::supportedFileExtensions()
{
    QList<QString> list;
    list.push_back("wv");
    return list;
}


Result SoundSourceWV::open()
{
    QByteArray qBAFilename = m_qFilename.toLocal8Bit();
    char msg[80];   //hold posible error message

    filewvc = WavpackOpenFileInput(qBAFilename.constData(), msg,OPEN_2CH_MAX | OPEN_WVC,0);
    if (!filewvc) {
        qDebug() << "SSWV::open: failed to open file : "<<msg;
        return ERR;
    }
    if (WavpackGetMode(filewvc) & MODE_FLOAT) {
        qDebug() << "SSWV::open: cannot load 32bit float files";
        WavpackCloseFile(filewvc);
        filewvc=NULL;
        return ERR;
    }
    // wavpack_open succeeded -> populate variables
    filelength = WavpackGetNumSamples(filewvc);
    m_iSampleRate=WavpackGetSampleRate(filewvc);
    m_iChannels=WavpackGetReducedChannels(filewvc);
    Bps=WavpackGetBytesPerSample(filewvc);
    qDebug () << "SSWV::open: opened filewvc with filelength: "<<filelength<<" SampleRate: " << m_iSampleRate
        << " channels: " << m_iChannels << " bytes per samp: "<<Bps;
    if (Bps>2) {
        qDebug() << "SSWV::open: warning: input file has > 2 bytes per sample, will be truncated to 16bits";
    }
    return OK;
}


long SoundSourceWV::seek(long filepos){
    if (WavpackSeekSample(filewvc,filepos>>1) != true) {
        qDebug() << "SSWV::seek : could not seek to sample #" << (filepos>>1);
        return 0;
    }
    return filepos;
}


unsigned SoundSourceWV::read(volatile unsigned long size, const SAMPLE* destination){
    //SAMPLE is "short int" => 16bits. [size] is timesamps*2 (because L+R)
    SAMPLE * dest = (SAMPLE*) destination;
    unsigned long sampsread=0;
    unsigned long timesamps, tsdone;

    //tempbuffer is fixed size : WV_BUF_LENGTH of uint32
    while (sampsread != size) {
        timesamps=(size-sampsread)>>1;      //timesamps still remaining
        if (timesamps > (WV_BUF_LENGTH/m_iChannels)) {  //if requested size requires more than one buffer filling
            timesamps=(WV_BUF_LENGTH/m_iChannels);      //tempbuffer must hold (timesamps * channels) samples
            qDebug() << "SSWV::read : performance warning, size requested > buffer size !";
        }

        tsdone=WavpackUnpackSamples(filewvc, tempbuffer, timesamps);    //fill temp buffer with timesamps*4bytes*channels
                //data is right justified, format_samples() fixes that.

        SoundSourceWV::format_samples(Bps, (char *) (dest + (sampsread>>1)*m_iChannels), tempbuffer, tsdone*m_iChannels);
                                //this will unpack the 4byte/sample
                                //output of wUnpackSamples(), sign-extending or truncating to output 16bit / sample.
                                //specifying dest+sampsread should resume the conversion where it was left if size requested
                                //required multiple reads (size req. > fixed buffer size)

        sampsread = sampsread + (tsdone<<1);
        if (tsdone!=timesamps) {
            qDebug () << "SSWV::read : WavpackUnpackSamples read "<<sampsread<<" asamps out of "<<size<<" requested";
            break;  //exit the while loop : subsequent reads are sure to read less than required.
        }

    }

    if (m_iChannels==1) {       //if MONO : expand array to double it's size; see ssov.cpp
        for(int i=(sampsread/2-1); i>=0; i--) { //algo courtesy of rryan !
            dest[i*2]     = dest[i];    //go through array backwards, expanding and copying L -> R
            dest[(i*2)+1] = dest[i];
        }
    }

    return sampsread;
}


inline long unsigned SoundSourceWV::length(){
    //filelength is # of timesamps.
    return filelength<<1;
}


Result SoundSourceWV::parseHeader() {
    setType("wv");

    QByteArray qBAFilename = m_qFilename.toLocal8Bit();
    TagLib::WavPack::File f(qBAFilename.constData());

    // Takes care of all the default metadata
    bool result = processTaglibFile(f);

    TagLib::APE::Tag *ape = f.APETag();
    if (ape) {
        processAPETag(ape);
    }

    if (result)
        return OK;
    return ERR;
}

void SoundSourceWV::format_samples(int Bps, char *dst, int32_t *src, uint32_t count)
{
    //this handles converting the fixed 32bit per sample produced by UnpackSamples
    //to 16 bps, by truncating (24/32) or sign-extending (8)
    //could eventually be asm-optimized..
    int32_t temp;

    switch (Bps) {
    case 1:
        while (count--) {
            *dst++ = (char) 0;      //left shift the 8 bit sample
            *dst++ = (char) *src++ ;//+ 128;        //only works with u8int ?
        }
        break;
    case 2:
        while (count--) {
            *dst++ = (char) (temp = *src++);    //low byte
            *dst++ = (char) (temp >> 8);        //high byte
        }
        break;
    case 3: //modified to truncate to 16bits
        while (count--) {
            *dst++ = (char) (temp = (*src++) >> 8);
            *dst++ = (char) (temp >> 8);
        }
        break;
    case 4: //also truncates
        while (count--) {
            *dst++ = (char) (temp = (*src++) >> 16);
            *dst++ = (char) (temp >> 8);
            //*dst++ = (char) (temp >> 16);
            //*dst++ = (char) (temp >> 24);
        }
        break;
    }

    return;
}

}  // namespace Mixxx