zynaddsubfx

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

SndioEngine.cpp (8409B)


      1 /*
      2   ZynAddSubFX - a software synthesizer
      3 
      4   SndioEngine.cpp - SNDIO Driver
      5   Copyright (C) 2020 Kinichiro Inoguchi
      6 
      7   This program is free software; you can redistribute it and/or
      8   modify it under the terms of the GNU General Public License
      9   as published by the Free Software Foundation; either version 2
     10   of the License, or (at your option) any later version.
     11 */
     12 
     13 #include <cmath>
     14 #include <iostream>
     15 #include <poll.h>
     16 #include <stdlib.h>
     17 
     18 #include "Compressor.h"
     19 #include "InMgr.h"
     20 #include "Nio.h"
     21 #include "SndioEngine.h"
     22 #include "../Misc/Config.h"
     23 #include "../Misc/Util.h"
     24 
     25 using namespace std;
     26 
     27 namespace zyn {
     28 
     29 SndioEngine::SndioEngine(const SYNTH_T &synth)
     30     :AudioOut(synth)
     31 {
     32     name = "SNDIO";
     33     audio.handle = NULL;
     34     audio.buffer = new short[synth.buffersize * 2];
     35     audio.buffer_size = synth.buffersize * 2 * sizeof(short);
     36     audio.peaks[0] = 0;
     37     audio.pThread = 0;
     38 
     39     midi.handle  = NULL;
     40     midi.pThread = 0;
     41 }
     42 
     43 SndioEngine::~SndioEngine()
     44 {
     45     Stop();
     46     delete[] audio.buffer;
     47 }
     48 
     49 bool SndioEngine::Start()
     50 {
     51     return openAudio() && openMidi();
     52 }
     53 
     54 void SndioEngine::Stop()
     55 {
     56     if(getMidiEn())
     57         setMidiEn(false);
     58     if(getAudioEn())
     59         setAudioEn(false);
     60 }
     61 
     62 void SndioEngine::setAudioEn(bool nval)
     63 {
     64     if(nval)
     65         openAudio();
     66     else
     67         stopAudio();
     68 }
     69 
     70 bool SndioEngine::getAudioEn() const
     71 {
     72     return audio.handle;
     73 }
     74 
     75 void SndioEngine::setMidiEn(bool nval)
     76 {
     77     if(nval)
     78         openMidi();
     79     else
     80         stopMidi();
     81 }
     82 
     83 bool SndioEngine::getMidiEn() const
     84 {
     85     return midi.handle;
     86 }
     87 
     88 void *SndioEngine::AudioThread()
     89 {
     90     set_realtime();
     91     return processAudio();
     92 }
     93 
     94 void *SndioEngine::_AudioThread(void *arg)
     95 {
     96     return (static_cast<SndioEngine *>(arg))->AudioThread();
     97 }
     98 
     99 void *SndioEngine::MidiThread(void)
    100 {
    101     set_realtime();
    102     return processMidi();
    103 }
    104 
    105 void *SndioEngine::_MidiThread(void *arg)
    106 {
    107     return static_cast<SndioEngine *>(arg)->MidiThread();
    108 }
    109 
    110 bool SndioEngine::openAudio()
    111 {
    112     int rc;
    113 
    114     if(getAudioEn())
    115         return true;
    116 
    117     audio.handle = NULL;
    118 
    119     if((audio.handle = sio_open(SIO_DEVANY, SIO_PLAY, 0)) == NULL) {
    120         fprintf(stderr, "unable to open sndio audio device\n");
    121         return false;
    122     }
    123 
    124     sio_initpar(&audio.params);
    125     audio.params.rate = synth.samplerate;
    126     audio.params.appbufsz = audio.params.rate * 0.05;
    127     audio.params.xrun = SIO_SYNC;
    128 
    129     rc = sio_setpar(audio.handle, &audio.params);
    130     if(rc != 1) {
    131         fprintf(stderr, "unable to set sndio audio parameters");
    132         return false;
    133     }
    134 
    135     showAudioInfo(audio.handle);
    136 
    137     rc = sio_start(audio.handle);
    138     if(rc != 1) {
    139         fprintf(stderr, "unable to start sndio audio");
    140         return false;
    141     }
    142 
    143     pthread_attr_t attr;
    144     pthread_attr_init(&attr);
    145     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    146     pthread_create(&audio.pThread, &attr, _AudioThread, this);
    147     return true;
    148 }
    149 
    150 void SndioEngine::stopAudio()
    151 {
    152     struct sio_hdl *handle = audio.handle;
    153     int rc;
    154 
    155     if(!getAudioEn())
    156         return;
    157 
    158     audio.handle = NULL;
    159 
    160     pthread_join(audio.pThread, NULL);
    161 
    162     rc = sio_stop(handle);
    163     if(rc != 1)
    164         fprintf(stderr, "unable to stop sndio audio");
    165 
    166     sio_close(handle);
    167 }
    168 
    169 bool SndioEngine::openMidi()
    170 {
    171     if(getMidiEn())
    172         return true;
    173 
    174     midi.handle = NULL;
    175 
    176     if((midi.handle = mio_open(MIO_PORTANY, MIO_IN, 1)) == NULL) {
    177         fprintf(stderr, "unable to open sndio midi device\n");
    178         return false;
    179     }
    180 
    181     midi.exiting = false;
    182     pthread_attr_t attr;
    183 
    184     pthread_attr_init(&attr);
    185     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    186     pthread_create(&midi.pThread, &attr, _MidiThread, this);
    187     return true;
    188 }
    189 
    190 void SndioEngine::stopMidi()
    191 {
    192     struct mio_hdl *handle = midi.handle;
    193 
    194     if(!getMidiEn())
    195         return;
    196 
    197     if((midi.handle != NULL) && midi.pThread) {
    198         midi.exiting = true;
    199         pthread_join(midi.pThread, 0);
    200     }
    201 
    202     midi.handle = NULL;
    203 
    204     if(handle)
    205         mio_close(handle);
    206 }
    207 
    208 void *SndioEngine::processAudio()
    209 {
    210     size_t len;
    211     struct sio_hdl *handle;
    212 
    213     while(audio.handle) {
    214         audio.buffer = interleave(getNext());
    215         handle = audio.handle;
    216         len = sio_write(handle, audio.buffer, audio.buffer_size);
    217         if(len == 0) // write error according to sndio examples
    218             cerr << "sio_write error" << endl;
    219     }
    220     return NULL;
    221 }
    222 
    223 void *SndioEngine::processMidi()
    224 {
    225     int n;
    226     int nfds;
    227     struct pollfd *pfd;
    228     int rc;
    229     int revents;
    230     size_t len;
    231     unsigned char buf[3];
    232 
    233     n = mio_nfds(midi.handle);
    234     if(n <= 0) {
    235         cerr << "mio_nfds error" << endl;
    236         return NULL;
    237     }
    238 
    239     pfd = (struct pollfd *) calloc(n, sizeof(struct pollfd));
    240     if(pfd == NULL) {
    241         cerr << "calloc error" << endl;
    242         return NULL;
    243     }
    244 
    245     while(1) {
    246         if(midi.exiting)
    247             break;
    248 
    249         nfds = mio_pollfd(midi.handle, pfd, POLLIN);
    250 
    251         rc = poll(pfd, nfds, 1000);
    252         if(rc < 0 && rc != EAGAIN && rc != EINTR) {
    253             cerr << "poll error" << endl;
    254             break;
    255         }
    256 
    257         revents = mio_revents(midi.handle, pfd);
    258         if(revents & POLLHUP) {
    259             cerr << "mio_revents catches POLLHUP" << endl;
    260             continue;
    261         }
    262         if(!(revents & POLLIN))
    263             continue;
    264 
    265         memset(buf, 0, sizeof(buf));
    266         len = mio_read(midi.handle, buf, sizeof(buf));
    267         if(len == 0) {
    268             // since mio_read is non-blocking, this must indicate an error
    269             // so stop processing all MIDI
    270             break;
    271         } else if(len > sizeof(buf)) {
    272             fprintf(stderr, "mio_read invalid len = %zu\n", len);
    273             continue;
    274         }
    275 
    276         midiProcess(buf[0], buf[1], buf[2]);
    277     }
    278     free(pfd);
    279     return NULL;
    280 }
    281 
    282 short *SndioEngine::interleave(const Stereo<float *> &smps)
    283 {
    284     short *shortInterleaved;
    285     int frame, idx;
    286     float l, r;
    287     double scaled;
    288 
    289     shortInterleaved = audio.buffer;
    290     memset(shortInterleaved, 0, audio.buffer_size);
    291 
    292     for(frame = idx = 0; frame < synth.buffersize; ++frame) {
    293         l = smps.l[frame];
    294         r = smps.r[frame];
    295         stereoCompressor(synth.samplerate, audio.peaks[0], l, r);
    296 
    297         scaled = l * (8.0f * 0x10000000);
    298         shortInterleaved[idx++] = (short int)(lrint(scaled) >> 16);
    299         scaled = r * (8.0f * 0x10000000);
    300         shortInterleaved[idx++] = (short int)(lrint(scaled) >> 16);
    301     }
    302     return shortInterleaved;
    303 }
    304 
    305 void SndioEngine::showAudioInfo(struct sio_hdl *handle)
    306 {
    307     int rc;
    308     struct sio_par par;
    309     struct sio_cap cap;
    310     unsigned int i;
    311 
    312     rc = sio_getpar(handle, &par);
    313     if(rc != 1) {
    314         fprintf(stderr, "unable to get sndio audio parameters");
    315         return;
    316     }
    317 
    318     fprintf(stderr, "sndio audio parameters:\n");
    319     fprintf(stderr,
    320         "  bits = %u bps = %u sig = %u le = %u msb = %u rchan = %u pchan = %u\n"
    321         "  rate = %u appbufsz = %u bufsz = %u round = %u xrun = %u\n",
    322         par.bits, par.bps, par.sig, par.le, par.msb, par.rchan, par.pchan,
    323         par.rate, par.appbufsz, par.bufsz, par.round, par.xrun);
    324 
    325     rc = sio_getcap(handle, &cap);
    326     if(rc != 1) {
    327         fprintf(stderr, "unable to get sndio audio capabilities");
    328         return;
    329     }
    330 
    331     fprintf(stderr, "sndio audio capabilities:\n");
    332     fprintf(stderr, "  supported encodings:\n");
    333     for(i = 0; i < SIO_NENC; ++i)
    334         fprintf(stderr,
    335             "    [%d] bits = %u bps = %u sig = %u le = %u msb = %u\n",
    336             i, cap.enc[i].bits, cap.enc[i].bps, cap.enc[i].sig,
    337             cap.enc[i].le, cap.enc[i].msb);
    338 
    339     fprintf(stderr, "  supported channel numbers of recording:\n");
    340     for(i = 0; i < SIO_NCHAN; ++i)
    341         fprintf(stderr, "    [%d] rchan = %u\n", i, cap.rchan[i]);
    342 
    343     fprintf(stderr, "  supported channel numbers of playback:\n");
    344     for(i = 0; i < SIO_NCHAN; ++i)
    345         fprintf(stderr, "    [%d] pchan = %u\n", i, cap.pchan[i]);
    346 
    347     fprintf(stderr, "  supported sample rates:\n");
    348     for(i = 0; i < SIO_NRATE; ++i)
    349         fprintf(stderr, "    [%d] rate = %u\n", i, cap.rate[i]);
    350 
    351     fprintf(stderr, "  available configurations:\n");
    352     for(i = 0; i < cap.nconf; ++i)
    353         fprintf(stderr,
    354             "    [%d] enc = %x rchan = %x pchan = %x rate = %x\n",
    355             i, cap.confs[i].enc, cap.confs[i].rchan, cap.confs[i].pchan,
    356             cap.confs[i].rate);
    357 }
    358 
    359 }