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
|
/***************************************************************************
enginesidechain.cpp
-------------------
copyright : (C) 2008 Albert Santoni
email : gamegod \a\t users.sf.net
***************************************************************************/
/***************************************************************************
* *
* 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 class provides a way to do audio processing that does not need
// to be executed in real-time. For example, shoutcast encoding/broadcasting
// and recording encoding can be done here. This class uses double-buffering
// to increase the amount of time the CPU has to do whatever work needs to
// be done, and that work is executed in a separate thread. (Threading
// allows the next buffer to be filled while processing a buffer that's is
// already full.)
#include "engine/sidechain/enginesidechain.h"
#include <QtDebug>
#include <QMutexLocker>
#include "engine/sidechain/sidechainworker.h"
#include "util/counter.h"
#include "util/event.h"
#include "util/sample.h"
#include "util/timer.h"
#include "util/trace.h"
#define SIDECHAIN_BUFFER_SIZE 65536
EngineSideChain::EngineSideChain(ConfigObject<ConfigValue>* pConfig)
: m_pConfig(pConfig),
m_bStopThread(false),
m_sampleFifo(SIDECHAIN_BUFFER_SIZE),
m_pWorkBuffer(SampleUtil::alloc(SIDECHAIN_BUFFER_SIZE)) {
// We use HighPriority to prevent starvation by lower-priority processes (Qt
// main thread, analysis, etc.). This used to be LowPriority but that is not
// a suitable choice since we do semi-realtime tasks (write to broadcast
// servers) in the sidechain thread. To get reliable timing, it's important
// that this work be prioritized over the GUI and non-realtime tasks. See
// discussion on Bug #1270583 and Bug #1194543.
start(QThread::HighPriority);
}
EngineSideChain::~EngineSideChain() {
m_waitLock.lock();
m_bStopThread = true;
m_waitForSamples.wakeAll();
m_waitLock.unlock();
// Wait until the thread has finished.
wait();
QMutexLocker locker(&m_workerLock);
while (!m_workers.empty()) {
SideChainWorker* pWorker = m_workers.takeLast();
pWorker->shutdown();
delete pWorker;
}
locker.unlock();
SampleUtil::free(m_pWorkBuffer);
}
void EngineSideChain::addSideChainWorker(SideChainWorker* pWorker) {
QMutexLocker locker(&m_workerLock);
m_workers.append(pWorker);
}
void EngineSideChain::writeSamples(const CSAMPLE* newBuffer, int buffer_size) {
Trace sidechain("EngineSideChain::writeSamples");
int samples_written = m_sampleFifo.write(newBuffer, buffer_size);
if (samples_written != buffer_size) {
Counter("EngineSideChain::writeSamples buffer overrun").increment();
}
if (m_sampleFifo.writeAvailable() < SIDECHAIN_BUFFER_SIZE/5) {
// Signal to the sidechain that samples are available.
Trace wakeup("EngineSideChain::writeSamples wake up");
m_waitForSamples.wakeAll();
}
}
void EngineSideChain::run() {
// the id of this thread, for debugging purposes //XXX copypasta (should
// factor this out somehow), -kousu 2/2009
unsigned static id = 0;
QThread::currentThread()->setObjectName(QString("EngineSideChain %1").arg(++id));
Event::start("EngineSideChain");
while (!m_bStopThread) {
// Sleep until samples are available.
m_waitLock.lock();
Event::end("EngineSideChain");
m_waitForSamples.wait(&m_waitLock);
m_waitLock.unlock();
Event::start("EngineSideChain");
int samples_read;
while ((samples_read = m_sampleFifo.read(m_pWorkBuffer,
SIDECHAIN_BUFFER_SIZE))) {
Trace process("EngineSideChain::process");
QMutexLocker locker(&m_workerLock);
foreach (SideChainWorker* pWorker, m_workers) {
pWorker->process(m_pWorkBuffer, samples_read);
}
}
// Check to see if we're supposed to exit/stop this thread.
if (m_bStopThread) {
return;
}
}
}
|