1 /*
2     Copyright (C) 2008 - 2016 Christian Schoenebeck
3  */
4 
5 #include "VirtualMidiDevice.h"
6 
7 #include "../../common/global_private.h"
8 #include "../../common/atomic.h"
9 #include "../../common/RingBuffer.h"
10 
11 #define MIDI_KEYS		128
12 #define MIDI_CONTROLLERS	128
13 
14 // assuming VirtualMidiDevice implementation is only controlled
15 // by mouse (and the user not being Billy the Kid)
16 #define MAX_EVENTS  12
17 
18 namespace LinuxSampler {
19 
20     struct VirtualMidiDevice::private_data_t {
21         atomic_t notesChanged; // whether some key changed at all
22         atomic_t pNoteChanged[MIDI_KEYS]; // which key(s) changed
23         atomic_t pNoteIsActive[MIDI_KEYS]; // status of each key (either active or inactive)
24         atomic_t pNoteOnVelocity[MIDI_KEYS];
25         atomic_t pNoteOffVelocity[MIDI_KEYS];
26         atomic_t ccsChanged; // whether some controller changed at all
27         atomic_t pCCChanged[MIDI_CONTROLLERS]; // which controller(s) changed
28         atomic_t pCCValue[MIDI_CONTROLLERS]; // current value of each controller
29         RingBuffer<VirtualMidiDevice::event_t,false> events;
30 
private_data_tLinuxSampler::VirtualMidiDevice::private_data_t31         private_data_t() : events(MAX_EVENTS, 0) {}
32     };
33 
VirtualMidiDevice()34     VirtualMidiDevice::VirtualMidiDevice() : p(new private_data_t) {
35         atomic_t zero = ATOMIC_INIT(0);
36         atomic_t defaultVelocity = ATOMIC_INIT(127);
37         atomic_t defaultCCValue = ATOMIC_INIT(0);
38         p->notesChanged = zero;
39         p->ccsChanged   = zero;
40         for (int i = 0; i < MIDI_KEYS; i++) {
41             p->pNoteChanged[i]  = zero;
42             p->pNoteIsActive[i] = zero;
43             p->pNoteOnVelocity[i] = defaultVelocity;
44             p->pNoteOffVelocity[i] = defaultVelocity;
45             p->pCCChanged[i] = zero;
46             p->pCCValue[i]   = defaultCCValue;
47         }
48     }
49 
~VirtualMidiDevice()50     VirtualMidiDevice::~VirtualMidiDevice() {
51         delete p;
52     }
53 
SetMaxEvents(int n)54     void VirtualMidiDevice::SetMaxEvents(int n) {
55         p->events.resize(n);
56     }
57 
SendNoteOnToSampler(uint8_t Key,uint8_t Velocity)58     bool VirtualMidiDevice::SendNoteOnToSampler(uint8_t Key, uint8_t Velocity) {
59         if (Key >= MIDI_KEYS || Velocity > 127) return false;
60         if (Velocity == 0) {
61             return SendNoteOffToSampler(Key, Velocity);
62         }
63         event_t ev = { EVENT_TYPE_NOTEON, Key, Velocity };
64         if (p->events.write_space() <= 0) return false;
65         p->events.push(&ev);
66         return true;
67     }
68 
SendNoteOffToSampler(uint8_t Key,uint8_t Velocity)69     bool VirtualMidiDevice::SendNoteOffToSampler(uint8_t Key, uint8_t Velocity) {
70         if (Key >= MIDI_KEYS || Velocity > 127) return false;
71         event_t ev = { EVENT_TYPE_NOTEOFF, Key, Velocity };
72         if (p->events.write_space() <= 0) return false;
73         p->events.push(&ev);
74         return true;
75     }
76 
SendCCToSampler(uint8_t Controller,uint8_t Value)77     bool VirtualMidiDevice::SendCCToSampler(uint8_t Controller, uint8_t Value) {
78         if (Controller >= MIDI_CONTROLLERS || Value > 127) return false;
79         event_t ev = { EVENT_TYPE_CC, Controller, Value };
80         if (p->events.write_space() <= 0) return false;
81         p->events.push(&ev);
82         return true;
83     }
84 
SendChannelPressureToSampler(uint8_t Pressure)85     bool VirtualMidiDevice::SendChannelPressureToSampler(uint8_t Pressure) {
86         if (Pressure > 127) return false;
87         event_t ev = { EVENT_TYPE_CHPRESSURE, 128 /*actually ignored by engine*/, Pressure };
88         if (p->events.write_space() <= 0) return false;
89         p->events.push(&ev);
90         return true;
91     }
92 
SendPitchBendToSampler(int Pitch)93     bool VirtualMidiDevice::SendPitchBendToSampler(int Pitch) {
94         if (Pitch < -8192 || Pitch > 8191) return false;
95         Pitch += 8192;
96         // order: LSB, MSB like it would be in a regular pitch bend MIDI message
97         event_t ev = {
98             EVENT_TYPE_PITCHBEND,
99             static_cast<uint8_t>(Pitch & 0x7f),
100             static_cast<uint8_t>((Pitch >> 7) & 0x7f)
101         };
102         if (p->events.write_space() <= 0) return false;
103         p->events.push(&ev);
104         return true;
105     }
106 
SendProgramChangeToSampler(uint8_t Program)107     bool VirtualMidiDevice::SendProgramChangeToSampler(uint8_t Program) {
108         if (Program > 127) return false;
109         event_t ev = { EVENT_TYPE_PROGRAM, Program, 0 };
110         if (p->events.write_space() <= 0) return false;
111         p->events.push(&ev);
112         return true;
113     }
114 
GetMidiEventFromDevice(event_t & Event)115     bool VirtualMidiDevice::GetMidiEventFromDevice(event_t& Event) {
116         return (p->events.pop(&Event) > 0);
117     }
118 
NotesChanged()119     bool VirtualMidiDevice::NotesChanged() {
120         int c = atomic_read( &p->notesChanged );
121         atomic_sub(c, &p->notesChanged );
122         return c;
123     }
124 
ControllersChanged()125     bool VirtualMidiDevice::ControllersChanged() {
126         int c = atomic_read( &p->ccsChanged );
127         atomic_sub(c, &p->ccsChanged );
128         return c;
129     }
130 
NoteChanged(uint8_t Key)131     bool VirtualMidiDevice::NoteChanged(uint8_t Key) {
132         int c = atomic_read( &(p->pNoteChanged)[Key] );
133         atomic_sub(c, &(p->pNoteChanged)[Key] );
134         return c;
135     }
136 
ControllerChanged(uint8_t Controller)137     bool VirtualMidiDevice::ControllerChanged(uint8_t Controller) {
138         int c = atomic_read( &(p->pCCChanged)[Controller] );
139         atomic_sub(c, &(p->pCCChanged)[Controller] );
140         return c;
141     }
142 
NoteIsActive(uint8_t Key)143     bool VirtualMidiDevice::NoteIsActive(uint8_t Key) {
144         return atomic_read( &(p->pNoteIsActive)[Key] );
145     }
146 
NoteOnVelocity(uint8_t Key)147     uint8_t VirtualMidiDevice::NoteOnVelocity(uint8_t Key) {
148         return atomic_read( &(p->pNoteOnVelocity)[Key] );
149     }
150 
NoteOffVelocity(uint8_t Key)151     uint8_t VirtualMidiDevice::NoteOffVelocity(uint8_t Key) {
152         return atomic_read( &(p->pNoteOffVelocity)[Key] );
153     }
154 
ControllerValue(uint8_t Controller)155     uint8_t VirtualMidiDevice::ControllerValue(uint8_t Controller) {
156         return atomic_read( &(p->pCCValue)[Controller] );
157     }
158 
SendNoteOnToDevice(uint8_t Key,uint8_t Velocity)159     void VirtualMidiDevice::SendNoteOnToDevice(uint8_t Key, uint8_t Velocity) {
160         if (Key >= MIDI_KEYS) return;
161         if (Velocity == 0) {
162             SendNoteOffToDevice(Key, Velocity);
163             return;
164         }
165         atomic_set( &(p->pNoteOnVelocity)[Key], Velocity );
166         atomic_inc( &(p->pNoteIsActive)[Key] );
167         atomic_inc( &(p->pNoteChanged)[Key] );
168         atomic_inc( &p->notesChanged );
169     }
170 
SendNoteOffToDevice(uint8_t Key,uint8_t Velocity)171     void VirtualMidiDevice::SendNoteOffToDevice(uint8_t Key, uint8_t Velocity) {
172         if (Key >= MIDI_KEYS) return;
173         atomic_set( &(p->pNoteOffVelocity)[Key], Velocity );
174         if (atomic_read( &(p->pNoteIsActive)[Key] )) // only decrement if not zero
175             atomic_dec( &(p->pNoteIsActive)[Key] );
176         atomic_inc( &(p->pNoteChanged)[Key] );
177         atomic_inc( &p->notesChanged );
178     }
179 
SendCCToDevice(uint8_t Controller,uint8_t Value)180     void VirtualMidiDevice::SendCCToDevice(uint8_t Controller, uint8_t Value) {
181         if (Controller >= MIDI_CONTROLLERS) return;
182         atomic_set( &(p->pCCValue)[Controller], Value );
183         atomic_inc( &(p->pCCChanged)[Controller] );
184         atomic_inc( &p->ccsChanged );
185     }
186 
187 } // namespace LinuxSampler
188