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