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