|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399 |
- /*
- ZynAddSubFX - a software synthesizer
-
- AlsaEngine.cpp - ALSA Driver
- Copyright (C) 2009 Alan Calvert
- Copyright (C) 2014 Mark McCurry
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
- */
-
- #include <stdlib.h>
- #include <iostream>
- #include <cmath>
-
- using namespace std;
-
- #include "../Misc/Util.h"
- #include "../Misc/Config.h"
- #include "InMgr.h"
- #include "AlsaEngine.h"
- #include "Compressor.h"
- #include "Nio.h"
-
- AlsaEngine::AlsaEngine(const SYNTH_T &synth)
- :AudioOut(synth)
- {
- audio.buffer = new short[synth.buffersize * 2];
- name = "ALSA";
- audio.handle = NULL;
- audio.peaks[0] = 0;
-
- midi.handle = NULL;
- midi.alsaId = -1;
- midi.pThread = 0;
- }
-
- AlsaEngine::~AlsaEngine()
- {
- Stop();
- delete[] audio.buffer;
- }
-
- void *AlsaEngine::_AudioThread(void *arg)
- {
- return (static_cast<AlsaEngine *>(arg))->AudioThread();
- }
-
- void *AlsaEngine::AudioThread()
- {
- set_realtime();
- return processAudio();
- }
-
- bool AlsaEngine::Start()
- {
- return openAudio() && openMidi();
- }
-
- void AlsaEngine::Stop()
- {
- if(getMidiEn())
- setMidiEn(false);
- if(getAudioEn())
- setAudioEn(false);
- snd_config_update_free_global();
- }
-
- void AlsaEngine::setMidiEn(bool nval)
- {
- if(nval)
- openMidi();
- else
- stopMidi();
- }
-
- bool AlsaEngine::getMidiEn() const
- {
- return midi.handle;
- }
-
- void AlsaEngine::setAudioEn(bool nval)
- {
- if(nval)
- openAudio();
- else
- stopAudio();
- }
-
- bool AlsaEngine::getAudioEn() const
- {
- return audio.handle;
- }
-
- void *AlsaEngine::_MidiThread(void *arg)
- {
- return static_cast<AlsaEngine *>(arg)->MidiThread();
- }
-
-
- void *AlsaEngine::MidiThread(void)
- {
- snd_seq_event_t *event;
- MidiEvent ev;
- set_realtime();
- while(1) {
- if(midi.exiting)
- break;
- if(snd_seq_event_input_pending(midi.handle, 1) <= 0) {
- usleep(10);
- continue;
- }
- if(snd_seq_event_input(midi.handle, &event) < 0)
- break;
- //ensure ev is empty
- ev.channel = 0;
- ev.num = 0;
- ev.value = 0;
- ev.type = 0;
-
- if(!event)
- continue;
- switch(event->type) {
- case SND_SEQ_EVENT_NOTEON:
- if(event->data.note.note) {
- ev.type = M_NOTE;
- ev.channel = event->data.note.channel;
- ev.num = event->data.note.note;
- ev.value = event->data.note.velocity;
- InMgr::getInstance().putEvent(ev);
- }
- break;
-
- case SND_SEQ_EVENT_NOTEOFF:
- ev.type = M_NOTE;
- ev.channel = event->data.note.channel;
- ev.num = event->data.note.note;
- ev.value = 0;
- InMgr::getInstance().putEvent(ev);
- break;
-
- case SND_SEQ_EVENT_KEYPRESS:
- ev.type = M_PRESSURE;
- ev.channel = event->data.note.channel;
- ev.num = event->data.note.note;
- ev.value = event->data.note.velocity;
- InMgr::getInstance().putEvent(ev);
- break;
-
- case SND_SEQ_EVENT_PITCHBEND:
- ev.type = M_CONTROLLER;
- ev.channel = event->data.control.channel;
- ev.num = C_pitchwheel;
- ev.value = event->data.control.value;
- InMgr::getInstance().putEvent(ev);
- break;
-
- case SND_SEQ_EVENT_CONTROLLER:
- ev.type = M_CONTROLLER;
- ev.channel = event->data.control.channel;
- ev.num = event->data.control.param;
- ev.value = event->data.control.value;
- InMgr::getInstance().putEvent(ev);
- break;
-
- case SND_SEQ_EVENT_PGMCHANGE:
- ev.type = M_PGMCHANGE;
- ev.channel = event->data.control.channel;
- ev.num = event->data.control.value;
- InMgr::getInstance().putEvent(ev);
- break;
-
- case SND_SEQ_EVENT_RESET: // reset to power-on state
- ev.type = M_CONTROLLER;
- ev.channel = event->data.control.channel;
- ev.num = C_resetallcontrollers;
- ev.value = 0;
- InMgr::getInstance().putEvent(ev);
- break;
-
- case SND_SEQ_EVENT_PORT_SUBSCRIBED: // ports connected
- if(true)
- cout << "Info, alsa midi port connected" << endl;
- break;
-
- case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: // ports disconnected
- if(true)
- cout << "Info, alsa midi port disconnected" << endl;
- break;
-
- case SND_SEQ_EVENT_SYSEX: // system exclusive
- case SND_SEQ_EVENT_SENSING: // midi device still there
- break;
-
- default:
- if(true)
- cout << "Info, other non-handled midi event, type: "
- << (int)event->type << endl;
- break;
- }
- snd_seq_free_event(event);
- }
- return NULL;
- }
-
- bool AlsaEngine::openMidi()
- {
- if(getMidiEn())
- return true;
-
- int alsaport;
- midi.handle = NULL;
-
- if(snd_seq_open(&midi.handle, "default", SND_SEQ_OPEN_INPUT, 0) != 0)
- return false;
-
- string clientname = "ZynAddSubFX";
- string postfix = Nio::getPostfix();
- if (!postfix.empty())
- clientname += "_" + postfix;
- if(Nio::pidInClientName)
- clientname += "_" + os_pid_as_padded_string();
- snd_seq_set_client_name(midi.handle, clientname.c_str());
-
- alsaport = snd_seq_create_simple_port(
- midi.handle,
- "ZynAddSubFX",
- SND_SEQ_PORT_CAP_WRITE
- | SND_SEQ_PORT_CAP_SUBS_WRITE,
- SND_SEQ_PORT_TYPE_SYNTH);
- if(alsaport < 0)
- return false;
-
- midi.exiting = false;
- pthread_attr_t attr;
-
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- pthread_create(&midi.pThread, &attr, _MidiThread, this);
- return true;
- }
-
- void AlsaEngine::stopMidi()
- {
- if(!getMidiEn())
- return;
-
- snd_seq_t *handle = midi.handle;
- if((NULL != midi.handle) && midi.pThread) {
- midi.exiting = true;
- pthread_join(midi.pThread, 0);
- }
- midi.handle = NULL;
- if(handle)
- snd_seq_close(handle);
- }
-
- short *AlsaEngine::interleave(const Stereo<float *> &smps)
- {
- /**\todo TODO fix repeated allocation*/
- short *shortInterleaved = audio.buffer;
- memset(shortInterleaved, 0, bufferSize * 2 * sizeof(short));
- int idx = 0; //possible off by one error here
- double scaled;
- for(int frame = 0; frame < bufferSize; ++frame) { // with a nod to libsamplerate ...
- float l = smps.l[frame];
- float r = smps.r[frame];
- stereoCompressor(synth.samplerate, audio.peaks[0], l, r);
-
- scaled = l * (8.0f * 0x10000000);
- shortInterleaved[idx++] = (short int)(lrint(scaled) >> 16);
- scaled = r * (8.0f * 0x10000000);
- shortInterleaved[idx++] = (short int)(lrint(scaled) >> 16);
- }
- return shortInterleaved;
- }
-
- bool AlsaEngine::openAudio()
- {
- if(getAudioEn())
- return true;
-
- int rc = 0;
- /* Open PCM device for playback. */
- audio.handle = NULL;
-
- const char *device = getenv("ALSA_DEVICE");
- if(device == 0)
- device = "hw:0";
-
- rc = snd_pcm_open(&audio.handle, device,
- SND_PCM_STREAM_PLAYBACK, 0);
- if(rc < 0) {
- fprintf(stderr,
- "unable to open pcm device: %s\n",
- snd_strerror(rc));
- return false;
- }
-
- /* Allocate a hardware parameters object. */
- snd_pcm_hw_params_alloca(&audio.params);
-
- /* Fill it in with default values. */
- snd_pcm_hw_params_any(audio.handle, audio.params);
-
- /* Set the desired hardware parameters. */
-
- /* Interleaved mode */
- snd_pcm_hw_params_set_access(audio.handle, audio.params,
- SND_PCM_ACCESS_RW_INTERLEAVED);
-
- /* Signed 16-bit little-endian format */
- snd_pcm_hw_params_set_format(audio.handle, audio.params,
- SND_PCM_FORMAT_S16_LE);
-
- /* Two channels (stereo) */
- snd_pcm_hw_params_set_channels(audio.handle, audio.params, 2);
-
- audio.sampleRate = synth.samplerate;
- snd_pcm_hw_params_set_rate_near(audio.handle, audio.params,
- &audio.sampleRate, NULL);
-
- audio.frames = 512;
- snd_pcm_hw_params_set_period_size_near(audio.handle,
- audio.params, &audio.frames, NULL);
-
- audio.periods = 4;
- snd_pcm_hw_params_set_periods_near(audio.handle,
- audio.params, &audio.periods, NULL);
-
- /* Set buffer size (in frames). The resulting latency is given by */
- /* latency = periodsize * periods / (rate * bytes_per_frame) */
- snd_pcm_uframes_t alsa_buffersize = synth.buffersize;
- rc = snd_pcm_hw_params_set_buffer_size_near(audio.handle,
- audio.params,
- &alsa_buffersize);
-
- /* At this place, ALSA's and zyn's buffer sizes may differ. */
- /* This should not be a problem. */
- if((int)alsa_buffersize != synth.buffersize)
- cerr << "ALSA buffer size: " << alsa_buffersize << endl;
-
- /* Write the parameters to the driver */
- rc = snd_pcm_hw_params(audio.handle, audio.params);
- if(rc < 0) {
- fprintf(stderr,
- "unable to set hw parameters: %s\n",
- snd_strerror(rc));
- return false;
- }
-
- //snd_pcm_hw_params_get_period_size(audio.params, &audio.frames, NULL);
- //snd_pcm_hw_params_get_period_time(audio.params, &val, NULL);
-
-
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
- pthread_create(&audio.pThread, &attr, _AudioThread, this);
- return true;
- }
-
- void AlsaEngine::stopAudio()
- {
- if(!getAudioEn())
- return;
-
- snd_pcm_t *handle = audio.handle;
- audio.handle = NULL;
- pthread_join(audio.pThread, NULL);
- snd_pcm_drain(handle);
- if(snd_pcm_close(handle))
- cout << "Error: in snd_pcm_close " << __LINE__ << ' ' << __FILE__
- << endl;
- }
-
- void *AlsaEngine::processAudio()
- {
- while(audio.handle) {
- audio.buffer = interleave(getNext());
- snd_pcm_t *handle = audio.handle;
- int rc = snd_pcm_writei(handle, audio.buffer, synth.buffersize);
- if(rc == -EPIPE) {
- /* EPIPE means underrun */
- cerr << "underrun occurred" << endl;
- snd_pcm_prepare(handle);
- }
- else
- if(rc < 0) {
- cerr << "AlsaEngine: Recovering connection..." << endl;
- rc = snd_pcm_recover(handle, rc, 0);
- if(rc < 0)
- throw "Could not recover ALSA connection";
- }
- }
- return NULL;
- }
|