aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/Ui/skinned
Commit message (Expand)AuthorAgeFilesLines
* updated Russian translationtrialuser022014-03-0422-174/+174
* fixed 'eject' button functiontrialuser022014-03-044-7/+13
* updated ts files, fixed Russian translationtrialuser022014-02-1722-132/+132
* raise main window when starting another instance w/o params, added --trialuser022014-02-172-0/+13
* updated Serbian translations (author: Mladen Pejaković)trialuser022014-02-062-46/+46
* Updated Ukrainian translationsmotsyo2014-01-161-1/+1
* added Serbian translations (author: Mladen Pejaković)trialuser022014-01-064-1/+2695
* updated Russian translationtrialuser022014-01-011-1/+1
* updated linguist filestrialuser022014-01-0120-1020/+1120
* added volume hotkeys settingstrialuser022014-01-011-0/+6
* refactoringtrialuser022013-12-141-3/+3
* fixed saving of the playlist settings (Fixes issue 622)trialuser022013-12-147-25/+36
* refactoringtrialuser022013-11-252-28/+29
* refactoringtrialuser022013-11-253-35/+35
* fixed drag&drop (Closes issue 310)trialuser022013-11-252-55/+57
* implemented drag&drop to a specific placetrialuser022013-11-251-7/+5
* prepare for insert functions impelementationtrialuser022013-11-202-0/+29
* updated Japanese translation (RyōTa SimaMoto)trialuser022013-11-131-12/+12
* updated Ukrainian translationsmotsyo2013-11-131-1/+1
* updated Russian translationtrialuser022013-11-121-1/+1
* updated .ts filestrialuser022013-11-1220-120/+220
* skinned: improved playlist browsertrialuser022013-11-123-82/+157
* updated Ukrainian translationmotsyo2013-11-071-7/+7
* updated .ts filestrialuser022013-10-3020-440/+440
* skinned: moved to the new volume apitrialuser022013-10-307-53/+23
* removed unused variablestrialuser022013-10-309-15/+7
* updated Russian translationtrialuser022013-10-301-4/+4
* updated .ts filestrialuser022013-10-2920-840/+1540
* cleanuptrialuser022013-10-292-5/+5
* skinned: added volume hotkeystrialuser022013-10-294-2/+19
* fixed possible problems on win64trialuser022013-10-222-2/+2
* skinned: fixed titlebartrialuser022013-10-171-11/+8
* fixed Galician translation (author: Oscar Pereira)trialuser022013-10-141-241/+244
* added Galician translation (author: Óscar Pereira)trialuser022013-10-042-0/+1298
* fixed auto-selection of groupstrialuser022013-09-071-4/+7
* skinned: fixed alt-f4 behaviour (Fixes issue 597)trialuser022013-09-072-4/+4
* select all group if neededtrialuser022013-09-061-0/+5
* skinned: improved keyboard managertrialuser022013-08-302-149/+121
* cleanuptrialuser022013-08-271-4/+0
* updated Ukrainian translationmotsyo2013-08-261-1/+1
* updated Russian translationtrialuser022013-08-231-1/+1
* updated linguist filestrialuser022013-08-2319-266/+361
* removed useless menu itemtrialuser022013-08-231-4/+0
* added sorting by grouptrialuser022013-08-231-0/+8
* fixed rtl locales supporttrialuser022013-08-211-28/+25
* updated Ukrainian translationmotsyo2013-08-201-2/+2
* skinned: added 'Ctrl+G' hotkeytrialuser022013-08-1923-252/+348
* fixed some bugs, updated Russian translationtrialuser022013-08-1620-39/+40
* updated linguist filestrialuser022013-08-1619-399/+494
* enabled "copy to playlist" menutrialuser022013-08-161-5/+5
id='n80' href='#n80'>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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327




















                                                                              
                     



                         
                  



                               


                                                         





                                                  
                     



                                        
                  


                    
                                                                                           

































                                                                                               
                                        


















                                                                                     
                                        






                                                                       
                                                                                              





















                                                                                               
                                           

                                  




















































































                                                                                     




                                                          




































                                                                           





                                                                              

                                        






                                                                   


                                                             















                                                                                      



                                                 






















                                                                                      
  
/***************************************************************************
 *   Copyright (C) 2014 by Ilya Kotov                                      *
 *   forkotov02@hotmail.ru                                                 *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 ***************************************************************************/

#include <QObject>
#include <QSettings>
#include <string.h>
#include <iostream>
#include <unistd.h>
#include <qmmp/buffer.h>
#include <math.h>
#include "outputdirectsound.h"

#define DS_BUFSIZE (48*1024)

OutputDirectSound *OutputDirectSound::instance = 0;
VolumeDirectSound *OutputDirectSound::volumeControl = 0;

OutputDirectSound::OutputDirectSound() : Output()
{
    m_ds = 0;
    m_primaryBuffer = 0;
    m_dsBuffer = 0;
    m_dsBufferAt = 0;
    instance = this;
}

OutputDirectSound::~OutputDirectSound()
{
    instance = 0;
    uninitialize();
}

bool OutputDirectSound::initialize(quint32 freq, ChannelMap map, Qmmp::AudioFormat format)
{
    Q_UNUSED(format);
    DSBUFFERDESC bufferDesc;

    HRESULT result = DirectSoundCreate8(0, &m_ds, 0);
    if(result != DS_OK)
    {
        qWarning("OutputDirectSound: DirectSoundCreate8 failed, error code = 0x%lx", result);
        m_ds = 0;
        return false;
    }

    if((result = m_ds->SetCooperativeLevel(GetDesktopWindow(), DSSCL_PRIORITY)) != DS_OK)
    {
        qWarning("OutputDirectSound: SetCooperativeLevel failed, error code = 0x%lx", result);
        return false;
    }

    ZeroMemory(&bufferDesc, sizeof(DSBUFFERDESC));
    bufferDesc.dwSize        = sizeof(DSBUFFERDESC);
    bufferDesc.dwFlags       = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
    bufferDesc.dwBufferBytes = 0;
    bufferDesc.lpwfxFormat   = NULL;

    if((result = m_ds->CreateSoundBuffer(&bufferDesc, &m_primaryBuffer, NULL)) != DS_OK)
    {
        m_primaryBuffer = 0;
        qWarning("OutputDirectSound: CreateSoundBuffer failed, error code = 0x%lx", result);
        return false;
    }

    WAVEFORMATEX wfex;
    ZeroMemory(&wfex, sizeof(WAVEFORMATEX));
    wfex.wFormatTag      = WAVE_FORMAT_PCM;
    wfex.nChannels       = map.count();
    wfex.nSamplesPerSec  = freq;
    wfex.wBitsPerSample  = 16;
    wfex.nBlockAlign     = (wfex.wBitsPerSample / 8) * wfex.nChannels;
    wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign;

    if((result = m_primaryBuffer->SetFormat(&wfex)) != DS_OK)
    {
        qWarning("OutputDirectSound: SetFormat failed, error code = 0x%lx", result);
        return false;
    }

    if((result = m_primaryBuffer->Play(0, 0, DSBPLAY_LOOPING)) != DS_OK)
    {
        qWarning("OutputDirectSound: Play failed, error code = 0x%lx", result);
        return false;
    }

    ZeroMemory(&wfex, sizeof(WAVEFORMATEX));
    wfex.wFormatTag      = WAVE_FORMAT_PCM;
    wfex.nChannels       = map.count();
    wfex.nSamplesPerSec  = freq;
    wfex.wBitsPerSample  = 16;
    wfex.nBlockAlign     = (wfex.wBitsPerSample / 8) * wfex.nChannels;
    wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign;

    ZeroMemory(&bufferDesc, sizeof(DSBUFFERDESC));
    bufferDesc.dwSize        = sizeof(DSBUFFERDESC);
    bufferDesc.dwFlags       = DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME |
            DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY;
    bufferDesc.lpwfxFormat   = &wfex;
    bufferDesc.dwBufferBytes = DS_BUFSIZE; // buffer size

    IDirectSoundBuffer *pDSB;
    if((result = m_ds->CreateSoundBuffer(&bufferDesc, &pDSB, NULL)) != DS_OK)
    {
        qWarning("OutputDirectSound: CreateSoundBuffer failed, error code = 0x%lx", result);
        return false;
    }

    if((result = pDSB->QueryInterface(IID_IDirectSoundBuffer8, (void**)&m_dsBuffer)) != DS_OK)
    {
        m_dsBuffer = 0;
        qWarning("OutputDirectSound: QueryInterface failed, error code = 0x%lx", result);
        pDSB->Release();
        return false;
    }

    m_dsBuffer->SetCurrentPosition(0);
    m_dsBuffer->Play(0,0,DSBPLAY_LOOPING);
    m_dsBufferAt = 0;
    configure(freq, map, Qmmp::PCM_S16LE);
    if(volumeControl)
        volumeControl->restore();
    return true;
}


qint64 OutputDirectSound::latency()
{
    return 0;
}

qint64 OutputDirectSound::writeAudio(unsigned char *data, qint64 len)
{
    unsigned char *ptr = 0, *ptr2 = 0;
    DWORD size = 0, size2 = 0;

    DWORD available = bytesToWrite(); //available bytes
    if(available < 128)
    {
        usleep(5000);
        return 0;
    }
    DWORD lockSize = qMin((DWORD)len, available); //required size

    HRESULT result = m_dsBuffer->Lock(m_dsBufferAt, lockSize,
                                      (void**)&ptr, (DWORD*)&size,
                                      (void**)&ptr2, (DWORD*)&size2, 0);
    if(result == DSERR_BUFFERLOST)
    {
        m_dsBuffer->Restore();
        result = m_dsBuffer->Lock(m_dsBufferAt, lockSize,
                                  (void**)&ptr, (DWORD*)&size,
                                  (void**)&ptr2, (DWORD*)&size2, 0);
    }
    if(result != DS_OK)
    {
        qWarning("OutputDirectSound: unable to lock buffer, error = 0x%lx", result);
        return -1;
    }

    DWORD totalSize = size + size2; //total locked size
    memmove(ptr, data, size);
    if(size2 > 0)
        memmove(ptr2, data + size, size2);

    m_dsBuffer->Unlock((void*)ptr, size, (void*)ptr2, size2);

    m_dsBufferAt += totalSize;
    m_dsBufferAt %= DS_BUFSIZE;
    return totalSize;
}

void OutputDirectSound::drain()
{
    DWORD dsCurrentPlayCursor = 0;
    m_dsBuffer->GetCurrentPosition((DWORD*)&dsCurrentPlayCursor, 0);

    while(dsCurrentPlayCursor >= m_dsBufferAt)
    {
        m_dsBuffer->GetCurrentPosition((DWORD*)&dsCurrentPlayCursor, 0);
    }
    while (dsCurrentPlayCursor <= m_dsBufferAt)
    {
        m_dsBuffer->GetCurrentPosition((DWORD*)&dsCurrentPlayCursor, 0);
    }
}

void OutputDirectSound::suspend()
{
    m_dsBuffer->Stop();
}

void OutputDirectSound::resume()
{
    HRESULT result = m_dsBuffer->Play(0,0,DSBPLAY_LOOPING);
    if(result == DSERR_BUFFERLOST)
    {
        result = m_dsBuffer->Play(0,0,DSBPLAY_LOOPING);
        m_dsBuffer->Restore();
    }
}

void OutputDirectSound::reset()
{
    m_dsBuffer->SetCurrentPosition(m_dsBufferAt-128);
}

IDirectSoundBuffer8 *OutputDirectSound::secondaryBuffer()
{
    return m_dsBuffer;
}

void OutputDirectSound::uninitialize()
{
    m_dsBufferAt = 0;
    if(m_dsBuffer)
    {
        m_dsBuffer->Stop();
        m_dsBuffer->Release();
        m_dsBuffer = 0;
    }
    if(m_primaryBuffer)
    {
        m_primaryBuffer->Stop();
        m_primaryBuffer->Release();
        m_primaryBuffer = 0;
    }
    if(m_ds)
    {
        m_ds->Release();
        m_ds = 0;
    }
}

DWORD OutputDirectSound::bytesToWrite()
{
    DWORD dsCurrentPlayCursor = 0;
    m_dsBuffer->GetCurrentPosition((DWORD*)&dsCurrentPlayCursor, 0);
    long available = dsCurrentPlayCursor - m_dsBufferAt; //available bytes

    if(available < 0)
    {
        available += DS_BUFSIZE;
    }
    return available;
}

/***** MIXER *****/
VolumeDirectSound::VolumeDirectSound()
{
    OutputDirectSound::volumeControl = this;
    QSettings settings(Qmmp::configFile(), QSettings::IniFormat);
    m_volume.left = settings.value("DirectSound/left_volume", 100).toInt();
    m_volume.right = settings.value("DirectSound/right_volume", 100).toInt();
}

VolumeDirectSound::~VolumeDirectSound()
{
    m_volume = volume();
    QSettings settings(Qmmp::configFile(), QSettings::IniFormat);
    settings.setValue("DirectSound/left_volume", m_volume.left);
    settings.setValue("DirectSound/right_volume", m_volume.right);
    OutputDirectSound::volumeControl = 0;
}

void VolumeDirectSound::setVolume(const VolumeSettings &vol)
{
    if(OutputDirectSound::instance && OutputDirectSound::instance->secondaryBuffer())
    {
        int maxVol = qMax(vol.left, vol.right);
        double voldB = -100.0, pandB = 0;
        if(maxVol)
        {
            voldB = 20.0*log(maxVol/100.0)/log(10);
            int balance = (vol.right - vol.left)*100.0/maxVol;
            pandB = balance ? 20.0*log((100.0 - fabs(balance))/100.0)/log(10) : 0;
            if(balance > 0)
                pandB = -pandB;
        }
        OutputDirectSound::instance->secondaryBuffer()->SetVolume(voldB*100);
        OutputDirectSound::instance->secondaryBuffer()->SetPan(pandB*100);
    }
    m_volume = vol;
}

VolumeSettings VolumeDirectSound::volume() const
{
    VolumeSettings vol;
    if(OutputDirectSound::instance && OutputDirectSound::instance->secondaryBuffer())
    {
        long v = 0;
        double voldB = 0, pandB = 0;
        OutputDirectSound::instance->secondaryBuffer()->GetVolume(&v);
        voldB = v / 100.0;
        OutputDirectSound::instance->secondaryBuffer()->GetPan(&v);
        pandB = v / 100.0;
        int volume = 100*pow(10, voldB/20.0);
        int balance = 100 - 100*pow(10, abs(pandB)/20.0);
        if(pandB > 0)
            balance = -balance;
        vol.left = volume-qMax(balance,0)*volume/100.0;
        vol.right = volume+qMin(balance,0)*volume/100.0;
        return vol;
    }
    return m_volume;
}

void VolumeDirectSound::restore()
{
    setVolume(m_volume);
}