1 /*
2     AlsaEngine.h
3 
4     Copyright 2009-2010, Alan Calvert
5     Copyright 2014-2019, Will Godfrey & others
6 
7     This file is part of yoshimi, which is free software: you can
8     redistribute it and/or modify it under the terms of the GNU General
9     Public License as published by the Free Software Foundation, either
10     version 2 of the License, or (at your option) any later version.
11 
12     yoshimi 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 yoshimi.  If not, see <http://www.gnu.org/licenses/>.
19 
20     Modified May 2019
21 */
22 
23 #if defined(HAVE_ALSA)
24 
25 #ifndef ALSA_ENGINE_H
26 #define ALSA_ENGINE_H
27 
28 #include <pthread.h>
29 #include <alsa/asoundlib.h>
30 #include <string>
31 
32 #include "MusicIO/MusicIO.h"
33 
34 #define MIDI_CLOCKS_PER_BEAT 24
35 #define MIDI_CLOCK_DIVISION 3
36 
37 #define MIDI_SONGPOS_BEAT_DIVISION 4
38 
39 #define ALSA_MIDI_BPM_MEDIAN_WINDOW 48
40 #define ALSA_MIDI_BPM_MEDIAN_AVERAGE_WINDOW 20
41 
42 class SynthEngine;
43 
44 class AlsaEngine : public MusicIO
45 {
46     public:
47         AlsaEngine(SynthEngine *_synth, BeatTracker *_beatTracker);
~AlsaEngine()48         ~AlsaEngine() { }
49 
50         bool openAudio(void);
51         bool openMidi(void);
52         bool Start(void);
53         void Close(void);
54 
getSamplerate(void)55         unsigned int getSamplerate(void) { return audio.samplerate; }
getBuffersize(void)56         int getBuffersize(void) { return audio.period_size; }
57 
58         std::string audioClientName(void);
59         std::string midiClientName(void);
audioClientId(void)60         int audioClientId(void) { return audio.alsaId; }
midiClientId(void)61         int midiClientId(void) { return midi.alsaId; }
registerAudioPort(int)62         virtual void registerAudioPort(int )  {}
63 
64         bool little_endian;
65         bool card_endian;
66         int card_bits;
67         bool card_signed;
68         unsigned int card_chans;
69 
70     private:
71         bool prepHwparams(void);
72         bool prepSwparams(void);
73         void Interleave(int buffersize);
74         void Write(snd_pcm_uframes_t towrite);
75         bool Recover(int err);
76         bool xrunRecover(void);
77         bool alsaBad(int op_result, std::string err_msg);
78         void closeAudio(void);
79         void closeMidi(void);
80 
81         std::string findMidiClients(snd_seq_t *seq);
82 
83         void *AudioThread(void);
84         static void *_AudioThread(void *arg);
85         void *MidiThread(void);
86         static void *_MidiThread(void *arg);
87 
88         snd_pcm_sframes_t (*pcmWrite)(snd_pcm_t *handle, const void *data,
89                                       snd_pcm_uframes_t nframes);
90 
91         void handleSongPos(float beat);
92         void handleMidiClock();
93 
94         struct {
95             std::string        device;
96             snd_pcm_t         *handle;
97             unsigned int       period_count;
98             unsigned int       samplerate;
99             snd_pcm_uframes_t  period_size;
100             snd_pcm_uframes_t  buffer_size;
101             int                alsaId;
102             snd_pcm_state_t    pcm_state;
103             pthread_t          pThread;
104         } audio;
105 
106         struct {
107             std::string        device;
108             snd_seq_t         *handle;
109             snd_seq_addr_t     addr;
110             int                alsaId;
111             pthread_t          pThread;
112 
113             // When receiving MIDI clock messages, to avoid precision errors
114             // (MIDI_CLOCKS_PER_BEAT (24) does not cleanly divide 1), store
115             // every third (MIDI_CLOCK_DIVISION) beat here. This is reset only
116             // every third clock ticks or on song repositioning. Note that the
117             // value is not necessarily an exact multiple of
118             // 1/MIDI_CLOCK_DIVISION, but we only ever add
119             // (1/MIDI_CLOCK_DIVISION) beats to it.
120             float             lastDivSongBeat;
121             float             lastDivMonotonicBeat;
122             // Reset to zero every MIDI_CLOCK_DIVISION. This is actually an
123             // integer, but stored as float for calculation purposes.
124             float             clockCount;
125 
126             float             prevBpms[ALSA_MIDI_BPM_MEDIAN_WINDOW];
127             int               prevBpmsPos;
128             int64_t           prevClockUs;
129         } midi;
130 };
131 
132 #endif
133 
134 #endif
135