OssEngine.cpp (14518B)
1 /* 2 ZynAddSubFX - a software synthesizer 3 4 OSSaudiooutput.C - Audio output for Open Sound System 5 Copyright (C) 2002-2005 Nasca Octavian Paul 6 Author: Nasca Octavian Paul 7 8 This program is free software; you can redistribute it and/or 9 modify it under the terms of the GNU General Public License 10 as published by the Free Software Foundation; either version 2 11 of the License, or (at your option) any later version. 12 */ 13 14 #include "OssEngine.h" 15 #include "Compressor.h" 16 17 #include "../Misc/Util.h" 18 #include "../Misc/Config.h" 19 #include "../globals.h" 20 21 #include <cstring> 22 #include <stdlib.h> 23 #include <stdio.h> 24 #include <fcntl.h> 25 #include <errno.h> 26 #include <sys/soundcard.h> 27 #include <sys/stat.h> 28 #include <sys/ioctl.h> 29 #include <unistd.h> 30 #include <iostream> 31 #include <signal.h> 32 33 #include "InMgr.h" 34 35 using namespace std; 36 37 namespace zyn { 38 39 static const int OssNonBlocking = 0; 40 41 /* 42 * The following statemachine converts MIDI commands to USB MIDI 43 * packets, derived from Linux's usbmidi.c, which was written by 44 * "Clemens Ladisch". It is used to figure out when a MIDI command is 45 * complete, without having to read the first byte of the next MIDI 46 * command. This is useful when connecting to so-called system PIPEs 47 * and FIFOs. See "man mkfifo". 48 * 49 * Return values: 50 * 0: No command 51 * Else: Command is complete 52 */ 53 static unsigned char 54 OssMidiParse(struct OssMidiParse &midi_parse, 55 unsigned char cn, unsigned char b) 56 { 57 unsigned char p0 = (cn << 4); 58 59 if(b >= 0xf8) { 60 midi_parse.temp_0[0] = p0 | 0x0f; 61 midi_parse.temp_0[1] = b; 62 midi_parse.temp_0[2] = 0; 63 midi_parse.temp_0[3] = 0; 64 midi_parse.temp_cmd = midi_parse.temp_0; 65 return (1); 66 67 } else if(b >= 0xf0) { 68 switch (b) { 69 case 0xf0: /* system exclusive begin */ 70 midi_parse.temp_1[1] = b; 71 midi_parse.state = OSSMIDI_ST_SYSEX_1; 72 break; 73 case 0xf1: /* MIDI time code */ 74 case 0xf3: /* song select */ 75 midi_parse.temp_1[1] = b; 76 midi_parse.state = OSSMIDI_ST_1PARAM; 77 break; 78 case 0xf2: /* song position pointer */ 79 midi_parse.temp_1[1] = b; 80 midi_parse.state = OSSMIDI_ST_2PARAM_1; 81 break; 82 case 0xf4: /* unknown */ 83 case 0xf5: /* unknown */ 84 midi_parse.state = OSSMIDI_ST_UNKNOWN; 85 break; 86 case 0xf6: /* tune request */ 87 midi_parse.temp_1[0] = p0 | 0x05; 88 midi_parse.temp_1[1] = 0xf6; 89 midi_parse.temp_1[2] = 0; 90 midi_parse.temp_1[3] = 0; 91 midi_parse.temp_cmd = midi_parse.temp_1; 92 midi_parse.state = OSSMIDI_ST_UNKNOWN; 93 return (1); 94 95 case 0xf7: /* system exclusive end */ 96 switch (midi_parse.state) { 97 case OSSMIDI_ST_SYSEX_0: 98 midi_parse.temp_1[0] = p0 | 0x05; 99 midi_parse.temp_1[1] = 0xf7; 100 midi_parse.temp_1[2] = 0; 101 midi_parse.temp_1[3] = 0; 102 midi_parse.temp_cmd = midi_parse.temp_1; 103 midi_parse.state = OSSMIDI_ST_UNKNOWN; 104 return (1); 105 case OSSMIDI_ST_SYSEX_1: 106 midi_parse.temp_1[0] = p0 | 0x06; 107 midi_parse.temp_1[2] = 0xf7; 108 midi_parse.temp_1[3] = 0; 109 midi_parse.temp_cmd = midi_parse.temp_1; 110 midi_parse.state = OSSMIDI_ST_UNKNOWN; 111 return (1); 112 case OSSMIDI_ST_SYSEX_2: 113 midi_parse.temp_1[0] = p0 | 0x07; 114 midi_parse.temp_1[3] = 0xf7; 115 midi_parse.temp_cmd = midi_parse.temp_1; 116 midi_parse.state = OSSMIDI_ST_UNKNOWN; 117 return (1); 118 } 119 midi_parse.state = OSSMIDI_ST_UNKNOWN; 120 break; 121 } 122 } else if(b >= 0x80) { 123 midi_parse.temp_1[1] = b; 124 if((b >= 0xc0) && (b <= 0xdf)) { 125 midi_parse.state = OSSMIDI_ST_1PARAM; 126 } else { 127 midi_parse.state = OSSMIDI_ST_2PARAM_1; 128 } 129 } else { /* b < 0x80 */ 130 switch (midi_parse.state) { 131 case OSSMIDI_ST_1PARAM: 132 if(midi_parse.temp_1[1] < 0xf0) { 133 p0 |= midi_parse.temp_1[1] >> 4; 134 } else { 135 p0 |= 0x02; 136 midi_parse.state = OSSMIDI_ST_UNKNOWN; 137 } 138 midi_parse.temp_1[0] = p0; 139 midi_parse.temp_1[2] = b; 140 midi_parse.temp_1[3] = 0; 141 midi_parse.temp_cmd = midi_parse.temp_1; 142 return (1); 143 case OSSMIDI_ST_2PARAM_1: 144 midi_parse.temp_1[2] = b; 145 midi_parse.state = OSSMIDI_ST_2PARAM_2; 146 break; 147 case OSSMIDI_ST_2PARAM_2: 148 if(midi_parse.temp_1[1] < 0xf0) { 149 p0 |= midi_parse.temp_1[1] >> 4; 150 midi_parse.state = OSSMIDI_ST_2PARAM_1; 151 } else { 152 p0 |= 0x03; 153 midi_parse.state = OSSMIDI_ST_UNKNOWN; 154 } 155 midi_parse.temp_1[0] = p0; 156 midi_parse.temp_1[3] = b; 157 midi_parse.temp_cmd = midi_parse.temp_1; 158 return (1); 159 case OSSMIDI_ST_SYSEX_0: 160 midi_parse.temp_1[1] = b; 161 midi_parse.state = OSSMIDI_ST_SYSEX_1; 162 break; 163 case OSSMIDI_ST_SYSEX_1: 164 midi_parse.temp_1[2] = b; 165 midi_parse.state = OSSMIDI_ST_SYSEX_2; 166 break; 167 case OSSMIDI_ST_SYSEX_2: 168 midi_parse.temp_1[0] = p0 | 0x04; 169 midi_parse.temp_1[3] = b; 170 midi_parse.temp_cmd = midi_parse.temp_1; 171 midi_parse.state = OSSMIDI_ST_SYSEX_0; 172 return (1); 173 default: 174 break; 175 } 176 } 177 return (0); 178 } 179 180 OssEngine::OssEngine(const SYNTH_T &synth, 181 const oss_devs_t& oss_devs) 182 :AudioOut(synth), audioThread(NULL), midiThread(NULL), 183 linux_oss_wave_out_dev(oss_devs.linux_wave_out), 184 linux_oss_seq_in_dev(oss_devs.linux_seq_in) 185 { 186 name = "OSS"; 187 188 midi.handle = -1; 189 audio.handle = -1; 190 191 /* allocate worst case audio buffer */ 192 audio.smps.ps32 = new int[synth.buffersize * 2]; 193 memset(audio.smps.ps32, 0, sizeof(int) * synth.buffersize * 2); 194 memset(&midi.state, 0, sizeof(midi.state)); 195 196 audio.peaks[0] = 0; 197 } 198 199 OssEngine::~OssEngine() 200 { 201 Stop(); 202 delete [] audio.smps.ps32; 203 } 204 205 bool OssEngine::openAudio() 206 { 207 int x; 208 209 if(audio.handle != -1) 210 return true; //already open 211 212 int snd_fragment; 213 int snd_stereo = 1; //stereo; 214 int snd_samplerate = synth.samplerate; 215 216 const char *device = getenv("DSP_DEVICE"); 217 if(device == NULL) 218 device = linux_oss_wave_out_dev; 219 220 /* NOTE: PIPEs and FIFOs can block when opening them */ 221 audio.handle = open(device, O_WRONLY | O_NONBLOCK); 222 if(audio.handle == -1) { 223 cerr << "ERROR - I can't open the " 224 << device << '.' << endl; 225 return false; 226 } 227 ioctl(audio.handle, FIONBIO, &OssNonBlocking); 228 ioctl(audio.handle, SNDCTL_DSP_RESET, NULL); 229 230 /* Figure out the correct format first */ 231 232 int snd_format16 = AFMT_S16_NE; 233 234 #ifdef AFMT_S32_NE 235 int snd_format32 = AFMT_S32_NE; 236 if (ioctl(audio.handle, SNDCTL_DSP_SETFMT, &snd_format32) == 0) { 237 audio.is32bit = true; 238 } else 239 #endif 240 if (ioctl(audio.handle, SNDCTL_DSP_SETFMT, &snd_format16) == 0) { 241 audio.is32bit = false; 242 } else { 243 cerr << "ERROR - I cannot set DSP format for " 244 << device << '.' << endl; 245 goto error; 246 } 247 ioctl(audio.handle, SNDCTL_DSP_STEREO, &snd_stereo); 248 ioctl(audio.handle, SNDCTL_DSP_SPEED, &snd_samplerate); 249 250 if (snd_samplerate != (int)synth.samplerate) { 251 cerr << "ERROR - Cannot set samplerate for " 252 << device << ". " << snd_samplerate 253 << " != " << synth.samplerate << endl; 254 goto error; 255 } 256 257 /* compute buffer size for 16-bit stereo samples */ 258 audio.buffersize = 4 * synth.buffersize; 259 if (audio.is32bit) 260 audio.buffersize *= 2; 261 262 for (x = 4; x < 20; x++) { 263 if ((1 << x) >= audio.buffersize) 264 break; 265 } 266 267 snd_fragment = 0x20000 | x; /* 2x buffer */ 268 269 ioctl(audio.handle, SNDCTL_DSP_SETFRAGMENT, &snd_fragment); 270 271 pthread_attr_t attr; 272 pthread_attr_init(&attr); 273 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 274 audioThread = new pthread_t; 275 pthread_create(audioThread, &attr, _audioThreadCb, this); 276 277 return true; 278 279 error: 280 close(audio.handle); 281 audio.handle = -1; 282 return false; 283 } 284 285 void OssEngine::stopAudio() 286 { 287 int handle = audio.handle; 288 if(handle == -1) //already closed 289 return; 290 audio.handle = -1; 291 292 /* close handle first, so that write() exits */ 293 close(handle); 294 295 pthread_join(*audioThread, NULL); 296 delete audioThread; 297 audioThread = NULL; 298 } 299 300 bool OssEngine::Start() 301 { 302 bool good = true; 303 304 if(!openAudio()) { 305 cerr << "Failed to open OSS audio" << endl; 306 good = false; 307 } 308 309 if(!openMidi()) { 310 cerr << "Failed to open OSS midi" << endl; 311 good = false; 312 } 313 314 return good; 315 } 316 317 void OssEngine::Stop() 318 { 319 stopAudio(); 320 stopMidi(); 321 } 322 323 void OssEngine::setMidiEn(bool nval) 324 { 325 if(nval) 326 openMidi(); 327 else 328 stopMidi(); 329 } 330 331 bool OssEngine::getMidiEn() const 332 { 333 return midi.handle != -1; 334 } 335 336 void OssEngine::setAudioEn(bool nval) 337 { 338 if(nval) 339 openAudio(); 340 else 341 stopAudio(); 342 } 343 344 bool OssEngine::getAudioEn() const 345 { 346 return audio.handle != -1; 347 } 348 349 bool OssEngine::openMidi() 350 { 351 int handle = midi.handle; 352 if(handle != -1) 353 return true; //already open 354 355 const char *device = getenv("MIDI_DEVICE"); 356 if(device == NULL) 357 device = linux_oss_seq_in_dev; 358 359 /* NOTE: PIPEs and FIFOs can block when opening them */ 360 handle = open(device, O_RDONLY | O_NONBLOCK); 361 362 if(-1 == handle) 363 return false; 364 midi.handle = handle; 365 366 ioctl(midi.handle, FIONBIO, &OssNonBlocking); 367 368 pthread_attr_t attr; 369 pthread_attr_init(&attr); 370 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 371 midiThread = new pthread_t; 372 pthread_create(midiThread, &attr, _midiThreadCb, this); 373 374 return true; 375 } 376 377 void OssEngine::stopMidi() 378 { 379 int handle = midi.handle; 380 if(handle == -1) //already closed 381 return; 382 383 midi.handle = -1; 384 385 /* close handle first, so that read() exits */ 386 close(handle); 387 388 pthread_join(*midiThread, NULL); 389 delete midiThread; 390 midiThread = NULL; 391 } 392 393 void *OssEngine::_audioThreadCb(void *arg) 394 { 395 return (static_cast<OssEngine *>(arg))->audioThreadCb(); 396 } 397 398 void *OssEngine::_midiThreadCb(void *arg) 399 { 400 return (static_cast<OssEngine *>(arg))->midiThreadCb(); 401 } 402 403 void *OssEngine::audioThreadCb() 404 { 405 /* 406 * In case the audio device is a PIPE/FIFO, 407 * we need to ignore any PIPE signals: 408 */ 409 signal(SIGPIPE, SIG_IGN); 410 411 set_realtime(); 412 while(getAudioEn()) { 413 const Stereo<float *> smps = getNext(); 414 415 for(int i = 0; i < synth.buffersize; ++i) { 416 float l = smps.l[i]; 417 float r = smps.r[i]; 418 if(isOutputCompressionEnabled) 419 stereoCompressor(synth.samplerate, audio.peaks[0], l, r); 420 421 if (audio.is32bit) { 422 audio.smps.ps32[i * 2] = (int) (l * 2147483647.0f); 423 audio.smps.ps32[i * 2 + 1] = (int) (r * 2147483647.0f); 424 } else {/* 16bit */ 425 audio.smps.ps16[i * 2] = (short int) (l * 32767.0f); 426 audio.smps.ps16[i * 2 + 1] = (short int) (r * 32767.0f); 427 } 428 } 429 430 int error; 431 do { 432 /* make a copy of handle, in case of OSS audio disable */ 433 int handle = audio.handle; 434 if(handle == -1) 435 goto done; 436 error = write(handle, audio.smps.ps32, audio.buffersize); 437 } while (error == -1 && errno == EINTR); 438 439 if(error == -1) 440 goto done; 441 } 442 done: 443 pthread_exit(NULL); 444 return NULL; 445 } 446 447 void *OssEngine::midiThreadCb() 448 { 449 /* 450 * In case the MIDI device is a PIPE/FIFO, 451 * we need to ignore any PIPE signals: 452 */ 453 signal(SIGPIPE, SIG_IGN); 454 set_realtime(); 455 while(getMidiEn()) { 456 unsigned char tmp; 457 int error; 458 do { 459 /* make a copy of handle, in case of OSS MIDI disable */ 460 int handle = midi.handle; 461 if(handle == -1) 462 goto done; 463 error = read(handle, &tmp, 1); 464 } while (error == -1 && errno == EINTR); 465 466 /* check that we got one byte */ 467 if(error != 1) 468 goto done; 469 470 /* feed MIDI byte into statemachine */ 471 if(OssMidiParse(midi.state, 0, tmp)) { 472 /* we got a complete MIDI command */ 473 midiProcess(midi.state.temp_cmd[1], 474 midi.state.temp_cmd[2], 475 midi.state.temp_cmd[3]); 476 } 477 } 478 done: 479 pthread_exit(NULL); 480 return NULL; 481 } 482 483 }