1 /*************************************************************************
2 RecordThread.cpp - thread for lowlevel audio recording
3 -------------------
4 begin : Mon Oct 20 2003
5 copyright : (C) 2003 by Thomas Eschenbacher
6 email : Thomas.Eschenbacher@gmx.de
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "config.h"
19
20 #include <errno.h>
21
22 #include <QMutexLocker>
23 #include <QVariant>
24
25 #include "RecordDevice.h"
26 #include "RecordThread.h"
27
28 //***************************************************************************
RecordThread()29 Kwave::RecordThread::RecordThread()
30 :Kwave::WorkerThread(Q_NULLPTR, QVariant()),
31 m_lock(),
32 m_device(Q_NULLPTR),
33 m_empty_queue(),
34 m_full_queue(),
35 m_buffer_count(0),
36 m_buffer_size(0)
37 {
38 }
39
40 //***************************************************************************
~RecordThread()41 Kwave::RecordThread::~RecordThread()
42 {
43 stop();
44
45 {
46 QMutexLocker lock(&m_lock);
47 m_full_queue.clear();
48 m_empty_queue.clear();
49 }
50 }
51
52 //***************************************************************************
setRecordDevice(Kwave::RecordDevice * device)53 void Kwave::RecordThread::setRecordDevice(Kwave::RecordDevice *device)
54 {
55 Q_ASSERT(!isRunning());
56 if (isRunning()) return;
57
58 m_device = device;
59 }
60
61 //***************************************************************************
setBuffers(unsigned int count,unsigned int size)62 int Kwave::RecordThread::setBuffers(unsigned int count, unsigned int size)
63 {
64 QMutexLocker lock(&m_lock);
65
66 Q_ASSERT(!isRunning());
67 if (isRunning()) return -EBUSY;
68
69 // flush all queues
70 m_full_queue.clear();
71 m_empty_queue.clear();
72
73 // fill the "empty" queue again
74 QByteArray buf(size, 0x00);
75 for (unsigned int i = 0; i < count; i++)
76 m_empty_queue.enqueue(buf);
77
78 // take the new settings
79 m_buffer_size = size;
80 m_buffer_count = count;
81
82 // return number of buffers or -ENOMEM if not even two allocated
83 return (m_empty_queue.count() >= 2) ? m_empty_queue.count() : -ENOMEM;
84 }
85
86 //***************************************************************************
remainingBuffers()87 unsigned int Kwave::RecordThread::remainingBuffers()
88 {
89 QMutexLocker lock(&m_lock);
90 return (m_empty_queue.count());
91 }
92
93 //***************************************************************************
queuedBuffers()94 unsigned int Kwave::RecordThread::queuedBuffers()
95 {
96 QMutexLocker lock(&m_lock);
97 return (m_full_queue.count());
98 }
99
100 //***************************************************************************
dequeue()101 QByteArray Kwave::RecordThread::dequeue()
102 {
103 QMutexLocker lock(&m_lock);
104
105 if (m_full_queue.count()) {
106 // de-queue the buffer from the full list
107 QByteArray buf = m_full_queue.dequeue();
108
109 // put the buffer back to the empty list
110 m_empty_queue.enqueue(buf);
111
112 // return the buffer
113 return buf;
114 } else {
115 // return an empty buffer
116 return QByteArray();
117 }
118 }
119
120 //***************************************************************************
run()121 void Kwave::RecordThread::run()
122 {
123 int result = 0;
124 bool interrupted = false;
125
126 // read data until we receive a close signal
127 while (!isInterruptionRequested() && !interrupted) {
128 // dequeue a buffer from the "empty" queue
129
130 QByteArray buffer;
131 int len;
132 {
133 QMutexLocker lock(&m_lock);
134
135 if (m_empty_queue.isEmpty()) {
136 // we had a "buffer overflow"
137 qWarning("RecordThread::run() -> NO EMPTY BUFFER FOUND !!!");
138 result = -ENOBUFS;
139 break;
140 }
141
142 buffer = m_empty_queue.dequeue();
143 len = buffer.size();
144 Q_ASSERT(buffer.size());
145 if (!len) {
146 result = -ENOBUFS;
147 break;
148 }
149 }
150
151 // read into the current buffer
152 unsigned int offset = 0;
153 while (len && !interrupted && !isInterruptionRequested()) {
154 // read raw data from the record device
155 result = (m_device) ?
156 m_device->read(buffer, offset) : -EBADF;
157
158 if ((result < 0) && (result != -EAGAIN))
159 qWarning("RecordThread: read result = %d (%s)",
160 result, strerror(-result));
161
162 if (result == -EAGAIN) {
163 continue;
164 } else if (result == -EBADF) {
165 // file open has failed
166 interrupted = true;
167 break;
168 } else if (result == -EINTR) {
169 // thread was interrupted, received signal?
170 interrupted = true;
171 break;
172 } else if (result < 1) {
173 // something went wrong !?
174 interrupted = true;
175 qWarning("RecordThread::run(): read returned %d", result);
176 break;
177 } else {
178 offset += result;
179 len = buffer.size() - offset;
180 Q_ASSERT(len >= 0);
181 if (len < 0) len = 0;
182 }
183 }
184
185 // return buffer into the empty queue and abort on errors
186 // do not use it
187 if (interrupted && (result < 0)) {
188 QMutexLocker lock(&m_lock);
189 m_empty_queue.enqueue(buffer);
190 break;
191 }
192
193 // inform the application that there is something to dequeue
194 {
195 QMutexLocker lock(&m_lock);
196 m_full_queue.enqueue(buffer);
197 }
198 emit bufferFull();
199 }
200
201 // do not evaluate the result of the last operation if there
202 // was the external request to stop
203 if (isInterruptionRequested() || (interrupted && (result > 0)))
204 result = 0;
205
206 if (result) emit stopped(result);
207 }
208
209 //***************************************************************************
210 //***************************************************************************
211