zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

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 }