Real Time Open Sound Control librtosc
 All Classes Namespaces Files Functions Variables Typedefs Macros Pages
miditable.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012 Mark McCurry
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include <rtosc/ports.h>
26 #include <array>
27 #include <string.h>
28 #include <algorithm>
29 
30 namespace rtosc {
31 
32 #define RTOSC_INVALID_MIDI 255
33 template<int len>
34 struct MidiAddr
35 {
37  //The midi values that map to the specified action
38  uint8_t ch, ctl;
39 
40  //The type of the event 'f', 'i', 'T'
41  char type;
42  //The path of the event
43  char path[len];
44  //The conversion function for 'f' types
45  char conversion[len];
46 };
47 
48 
52 template<int len, int elms>
53 struct MidiTable
54 {
55  std::array<MidiAddr<len>,elms> table;
56 
58  short unhandled_ch;
60  char unhandled_path[len];
61 
62  void (*error_cb)(const char *, const char *);
63  void (*event_cb)(const char *);
64 
65  MidiTable(Ports &_dispatch_root)
66  :dispatch_root(_dispatch_root), unhandled_ch(-1), unhandled_ctl(-1),
68  {
69  memset(unhandled_path, 0, sizeof(unhandled_path));
70  }
71 
72 
73  bool has(uint8_t ch, uint8_t ctl) const
74  {
75  for(auto e:table) {
76  if(e.ch == ch && e.ctl == ctl)
77  return true;
78  }
79  return false;
80  }
81 
82  MidiAddr<len> *get(uint8_t ch, uint8_t ctl)
83  {
84  for(auto &e:table)
85  if(e.ch==ch && e.ctl == ctl)
86  return &e;
87  return NULL;
88  }
89 
90  const MidiAddr<len> *get(uint8_t ch, uint8_t ctl) const
91  {
92  for(auto &e:table)
93  if(e.ch==ch && e.ctl == ctl)
94  return &e;
95  return NULL;
96  }
97 
98  bool mash_port(MidiAddr<len> &e, const Port &port)
99  {
100  const char *args = index(port.name, ':');
101 
102  //Consider a path to be typed based upon the argument restrictors
103  if(index(args, 'f')) {
104  e.type = 'f';
105  memset(e.conversion, 0, sizeof(e.conversion));
106  //Take care of mapping
107  const char *start = port.metadata,
108  *end = index(start,':');
109 
110  if((end-start)<len)
111  std::copy(start, end, e.conversion);
112  else
113  return false;
114  } else if(index(args, 'i'))
115  e.type = 'i';
116  else if(index(args, 'T'))
117  e.type = 'T';
118  else
119  return false;
120  return true;
121  }
122 
123  void addElm(uint8_t ch, uint8_t ctl, const char *path)
124  {
125  const Port *port = dispatch_root.apropos(path);
126 
127  if(!port || port->ports) {//missing or directory node
128  error_cb("Bad path", path);
129  return;
130  }
131 
132  if(MidiAddr<len> *e = this->get(ch,ctl)) {
133  strncpy(e->path,path,len);
134  if(!mash_port(*e, *port)) {
135  e->ch = RTOSC_INVALID_MIDI;
136  e->ctl = RTOSC_INVALID_MIDI;
137  error_cb("Failed to read metadata", path);
138  }
139  return;
140  }
141 
142  for(MidiAddr<len> &e:table) {
143  if(e.ch == RTOSC_INVALID_MIDI) {//free spot
144  e.ch = ch;
145  e.ctl = ctl;
146  strncpy(e.path,path,len);
147  if(!mash_port(e, *port)) {
148  e.ch = RTOSC_INVALID_MIDI;
149  e.ctl = RTOSC_INVALID_MIDI;
150  error_cb("Failed to read metadata", path);
151  }
152  return;
153  }
154  }
155  }
156 
157  void check_learn(void)
158  {
159  if(unhandled_ctl == RTOSC_INVALID_MIDI || unhandled_path[0] == '\0')
160  return;
163  memset(unhandled_path, 0, sizeof(unhandled_path));
164  }
165 
166  void learn(const char *s)
167  {
168  if(strlen(s) > len) {
169  error_cb("String too long", s);
170  return;
171  }
172  strcpy(unhandled_path, s);
173  check_learn();
174  }
175 
176  void process(uint8_t ch, uint8_t ctl, uint8_t val)
177  {
178  const MidiAddr<len> *addr = get(ch,ctl);
179  if(!addr) {
180  unhandled_ctl = ctl;
181  unhandled_ch = ch;
182  check_learn();
183  return;
184  }
185 
186  char buffer[1024];
187  switch(addr->type)
188  {
189  case 'f':
190  rtosc_message(buffer, 1024, addr->path,
191  "f", translate(val,addr->conversion));
192  break;
193  case 'i':
194  rtosc_message(buffer, 1024, addr->path,
195  "i", val);
196  break;
197  case 'T':
198  rtosc_message(buffer, 1024, addr->path,
199  (val<64 ? "F" : "T"));
200  break;
201  }
202 
203  event_cb(buffer);
204  }
205 
206  static float translate(uint8_t val, const char *meta)
207  {
208  //Allow for middle value to be set
209  float x = val!=64.0 ? val/127.0 : 0.5;
210 
211  //Gather type
212  char shape[4] = {0};
213  unsigned pos = 0;
214  while(*meta && *meta != ',' && pos < 3)
215  shape[pos++] = *meta++;
216 
217 
218  //Gather args
219  while(*meta && *meta!=',') meta++; meta++;
220  float min = atof(meta);
221  while(*meta && *meta!=',') meta++; meta++;
222  float max = atof(meta);
223 
224 
225  //Translate
226  if(!strcmp("lin",shape))
227  return x*(max-min)+min;
228  else if(!strcmp("log", shape)) {
229  const float b = log(min);
230  const float a = log(max)-b;
231  return expf(a*x+b);
232  }
233 
234  return 0.0f;
235  }
236  static void black_hole2(const char *, const char *){}
237  static void black_hole1(const char *){}
238 
239 };
240 
241 };