1 /*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 *
5 * http://www.hydrogen-music.org
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY, without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23 #include <hydrogen/IO/OssDriver.h>
24
25 // check if OSS support is enabled
26 #if defined(H2CORE_HAVE_OSS) || _DOXYGEN_
27
28 #include <hydrogen/Preferences.h>
29
30 #include <pthread.h>
31
32 namespace H2Core
33 {
34
35 audioProcessCallback ossDriver_audioProcessCallback;
36 bool ossDriver_running;
37 pthread_t ossDriverThread;
38 int oss_driver_bufferSize = -1;
39 OssDriver *m_pOssDriverInstance = NULL;
40
41 unsigned nNextFrames = 0;
42
ossDriver_processCaller(void * param)43 void* ossDriver_processCaller( void* param )
44 {
45 Object* __object = ( Object* )param;
46 // stolen from amSynth
47 struct sched_param sched;
48 sched.sched_priority = 50;
49 int res = sched_setscheduler( 0, SCHED_FIFO, &sched );
50 sched_getparam( 0, &sched );
51 if ( res ) {
52 __WARNINGLOG( "Can't set realtime scheduling for OSS Driver" );
53 }
54 __INFOLOG( QString( "Scheduling priority = %1" ).arg( sched.sched_priority ) );
55
56 OssDriver *ossDriver = ( OssDriver* )param;
57
58 sleep( 1 );
59
60 while ( ossDriver_running ) {
61 ossDriver_audioProcessCallback( oss_driver_bufferSize, NULL );
62 ossDriver->write();
63 }
64
65 pthread_exit( NULL );
66 return NULL;
67 }
68
69
70
71 const char* OssDriver::__class_name = "OssDriver";
72
OssDriver(audioProcessCallback processCallback)73 OssDriver::OssDriver( audioProcessCallback processCallback )
74 : AudioOutput( __class_name )
75 {
76 INFOLOG( "INIT" );
77 audioBuffer = NULL;
78 ossDriver_running = false;
79 this->processCallback = processCallback;
80 ossDriver_audioProcessCallback = processCallback;
81 m_pOssDriverInstance = this;
82 }
83
84
85
86
87
88
~OssDriver()89 OssDriver::~OssDriver()
90 {
91 INFOLOG( "DESTROY" );
92 }
93
94
95
init(unsigned nBufferSize)96 int OssDriver::init( unsigned nBufferSize )
97 {
98 oss_driver_bufferSize = nBufferSize;
99
100 delete[] audioBuffer;
101 audioBuffer = NULL;
102
103 audioBuffer = new short[nBufferSize * 2];
104
105 out_L = new float[nBufferSize];
106 out_R = new float[nBufferSize];
107
108 // clear buffers
109 memset( out_L, 0, nBufferSize * sizeof( float ) );
110 memset( out_R, 0, nBufferSize * sizeof( float ) );
111
112 return 0;
113 }
114
115
116
117 /// Connect
118 /// return 0: Ok
119 /// return 1: Generic error
connect()120 int OssDriver::connect()
121 {
122 INFOLOG( "connect" );
123
124 Preferences *preferencesMng = Preferences::get_instance();
125
126 // initialize OSS
127 int bits = 16;
128 int speed = preferencesMng->m_nSampleRate;
129 int stereo = 1;
130 int bs;
131
132 QString audioDevice;
133 #ifdef __NetBSD__
134 audioDevice = "/dev/audio";
135 #else
136 audioDevice = preferencesMng->m_sOSSDevice;
137 #endif
138
139 // Non blocking OSS open code stolen from GLAME
140 fd = open( audioDevice.toLocal8Bit(), O_WRONLY | O_NONBLOCK ); // test with non blocking open
141 int arg = fcntl( fd, F_GETFL, 0 );
142 if ( arg != -1 ) { // back to blocking mode...
143 fcntl( fd, F_SETFL, arg & ~O_NONBLOCK );
144 }
145
146 if ( fd == -1 ) {
147 ERRORLOG( "DSP ERROR_OPEN" );
148 return 1;
149 }
150 if ( ioctl( fd, SNDCTL_DSP_SYNC, NULL ) < 0 ) {
151 ERRORLOG( "ERROR_IOCTL" );
152 close( fd );
153 return 1;
154 }
155 if ( ioctl( fd, SNDCTL_DSP_SAMPLESIZE, &bits ) < 0 ) {
156 ERRORLOG( "ERROR_IOCTL" );
157 close( fd );
158 return 1;
159 }
160 if ( ioctl( fd, SNDCTL_DSP_SPEED, &speed ) < 0 ) {
161 ERRORLOG( "ERROR_IOCTL" );
162 close( fd );
163 return 1;
164 }
165 if ( ioctl( fd, SNDCTL_DSP_STEREO, &stereo ) < 0 ) {
166 ERRORLOG( "ERROR_IOCTL" );
167 close( fd );
168 return 1;
169 }
170
171 unsigned bufferBits = log2( speed / 60 );
172 int fragSize = 0x00200000 | bufferBits;
173
174 ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &fragSize );
175
176 if ( ioctl( fd, SNDCTL_DSP_GETBLKSIZE, &bs ) < 0 ) {
177 ERRORLOG( "ERROR_IOCTL" );
178 close( fd );
179 return 1;
180 }
181
182 INFOLOG( QString( "Blocksize audio = %1" ).arg( bs ) );
183
184 /* commenting this is a fix against buffersize problems described in ticket 119
185 if ( bs != ( 1 << bufferBits ) ) {
186 ERRORLOG( "ERROR_IOCTL: unable to set BlockSize" );
187 close( fd );
188 return 1;
189 }
190 */// end comment
191
192 int format = AFMT_S16_LE;
193 if ( ioctl( fd, SNDCTL_DSP_SETFMT, &format ) == -1 ) {
194 ERRORLOG( "ERROR_IOCTL unable to set format" );
195 close( fd );
196 return 1;
197 }
198
199 // start main thread
200 ossDriver_running = true;
201 pthread_attr_t attr;
202 pthread_attr_init( &attr );
203
204 pthread_create( &ossDriverThread, &attr, ossDriver_processCaller, this );
205
206 return 0;
207 }
208
209
210
211
disconnect()212 void OssDriver::disconnect()
213 {
214 INFOLOG( "disconnect" );
215
216 ossDriver_running = false;
217
218 // join ossDriverThread
219 pthread_join( ossDriverThread, NULL );
220
221 if ( fd != -1 ) {
222 if ( close( fd ) ) {
223 ERRORLOG( "Error closing audio device" );
224 }
225 }
226
227 delete [] out_L;
228 out_L = NULL;
229
230 delete [] out_R;
231 out_R = NULL;
232
233 delete[] audioBuffer;
234 audioBuffer = NULL;
235 }
236
237
238
239
240 /// Write the audio data
write()241 void OssDriver::write()
242 {
243 // infoLog("write");
244 unsigned size = oss_driver_bufferSize * 2;
245
246 // prepare the 2-channel array of short
247 for ( unsigned i = 0; i < ( unsigned )oss_driver_bufferSize; ++i ) {
248 audioBuffer[i * 2] = ( short )( out_L[i] * 32768.0 );
249 audioBuffer[i * 2 + 1] = ( short )( out_R[i] * 32768.0 );
250 }
251
252 unsigned long written = ::write( fd, audioBuffer, size * 2 );
253
254 if ( written != ( size * 2 ) ) {
255 ERRORLOG( "OssDriver: Error writing samples to audio device." );
256 // std::cerr << "written = " << written << " of " << (size*2) << endl;
257 }
258 }
259
260
261
262
263
log2(int n)264 int OssDriver::log2( int n )
265 {
266 int result = 0;
267 while ( ( n >>= 1 ) > 0 )
268 result++;
269 return result;
270 }
271
272
273
274
getBufferSize()275 unsigned OssDriver::getBufferSize()
276 {
277 return oss_driver_bufferSize;
278 }
279
280
getSampleRate()281 unsigned OssDriver::getSampleRate()
282 {
283 Preferences *preferencesMng = Preferences::get_instance();
284 return preferencesMng->m_nSampleRate;
285 }
286
287
getOut_L()288 float* OssDriver::getOut_L()
289 {
290 return out_L;
291 }
getOut_R()292 float* OssDriver::getOut_R()
293 {
294 return out_R;
295 }
296
297
play()298 void OssDriver::play()
299 {
300 m_transport.m_status = TransportInfo::ROLLING;
301 }
302
stop()303 void OssDriver::stop()
304 {
305 m_transport.m_status = TransportInfo::STOPPED;
306 }
307
locate(unsigned long nFrame)308 void OssDriver::locate( unsigned long nFrame )
309 {
310 m_transport.m_nFrames = nFrame;
311 }
312
313
updateTransportInfo()314 void OssDriver::updateTransportInfo()
315 {
316 // not used
317 }
318
setBpm(float fBPM)319 void OssDriver::setBpm( float fBPM )
320 {
321 INFOLOG( QString( "setBpm: %1" ).arg( fBPM ) );
322 m_transport.m_fBPM = fBPM;
323 }
324
325 };
326
327 #endif // OSS support
328
329