Subversion Repositories svn.mios32

Rev

Rev 1144 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1144 tk 1
// $Id: app.c 2425 2016-11-03 00:44:22Z tk $
2
/*
3
 * CV Autotune
4
 *
5
 * ==========================================================================
6
 *
7
 *  Copyright (C) 2011 Thorsten Klose (tk@midibox.org)
8
 *  Licensed for personal non-commercial use only.
9
 *  All other rights reserved.
10
 *
11
 * ==========================================================================
12
 */
13
 
14
/////////////////////////////////////////////////////////////////////////////
15
// Include files
16
/////////////////////////////////////////////////////////////////////////////
17
 
18
#include <mios32.h>
19
#include "app.h"
20
#include "frq_meter.h"
21
 
22
#include <notestack.h>
23
#include <aout.h>
24
 
25
#include <FreeRTOS.h>
26
#include <portmacro.h>
27
#include <task.h>
28
#include <queue.h>
29
#include <semphr.h>
30
 
31
 
32
// define priority level for FRQ_MEASURE task:
33
// use same priority as MIOS32 specific tasks (3)
34
#define PRIORITY_TASK_FRQ_MEASURE   ( tskIDLE_PRIORITY + 3 )
35
 
36
// local prototype of the task function
37
static void TASK_Frq_Measure(void *pvParameters);
38
 
39
 
40
/////////////////////////////////////////////////////////////////////////////
41
// Local definitions
42
/////////////////////////////////////////////////////////////////////////////
43
 
44
#define NOTESTACK_SIZE 16
45
 
46
#define AUTOTUNE_STATE_NOP        0
47
#define AUTOTUNE_STATE_START      1
48
#define AUTOTUNE_STATE_SWEEP      2
49
#define AUTOTUNE_STATE_PASSED     3
50
#define AUTOTUNE_STATE_FAILED     4
51
 
52
 
53
 
54
/////////////////////////////////////////////////////////////////////////////
55
// Local variables
56
/////////////////////////////////////////////////////////////////////////////
57
 
58
static notestack_t notestack;
59
static notestack_item_t notestack_items[NOTESTACK_SIZE];
60
 
61
static volatile u8 autotune_state;
62
static u32 autotune_last_aout_value;
63
static u8 autotune_current_note;
64
static u16 autotune_note_aout_value[128];
65
static u32 autotune_note_frq[128];
66
 
67
/////////////////////////////////////////////////////////////////////////////
68
// This hook is called after startup to initialize the application
69
/////////////////////////////////////////////////////////////////////////////
70
void APP_Init(void)
71
{
72
  // initialize all LEDs
73
  MIOS32_BOARD_LED_Init(0xffffffff);
74
 
75
  // initialize frequency meter
76
  FRQ_METER_Init(0);
77
 
78
  // initialize the Notestack
79
  NOTESTACK_Init(&notestack, NOTESTACK_MODE_PUSH_TOP, &notestack_items[0], NOTESTACK_SIZE);
80
 
81
  // initialize AOUT module
82
  AOUT_Init(0);
83
 
84
  // configure interface
85
  // see AOUT module documentation for available interfaces and options
86
  aout_config_t config;
87
  config = AOUT_ConfigGet();
88
  config.if_type = AOUT_IF_TLV5630;
89
  config.if_option = 0;
90
  config.num_channels = 8;
91
  config.chn_inverted = 0;
92
  AOUT_ConfigSet(config);
93
  AOUT_IF_Init(0);
94
 
95
  // clear autotune state
96
  autotune_state = AUTOTUNE_STATE_NOP;
97
 
98
  // calculate note frequencies
99
  float fact = 1.059463;
100
  float a_freq = 13.75;
101
  int i;
102
  for(i=0; i<128; i+=12, a_freq *= 2) {
103
    int j;
104
    float note_frq[12];
105
 
106
    note_frq[9] = a_freq;
107
    for(j=10; j<=11; ++j)
108
      note_frq[j] = note_frq[j-1] * fact;
109
    for(j=8; j>=0; --j)
110
      note_frq[j] = note_frq[j+1] / fact;
111
 
112
    for(j=0; j<12 && (j+i)<128; ++j) {
113
      autotune_note_frq[i+j] = (int)(note_frq[j] * 1000);
114
      autotune_note_aout_value[i+j] = (i+j)*0x200; // default for V/Oct
115
    }
116
  }
117
 
118
  // start task
2425 tk 119
  xTaskCreate(TASK_Frq_Measure, "Frq_Measure", configMINIMAL_STACK_SIZE, NULL, PRIORITY_TASK_FRQ_MEASURE, NULL);
1144 tk 120
}
121
 
122
 
123
/////////////////////////////////////////////////////////////////////////////
124
// This task is running endless in background
125
/////////////////////////////////////////////////////////////////////////////
126
void APP_Background(void)
127
{
128
}
129
 
130
 
131
/////////////////////////////////////////////////////////////////////////////
132
// This hook is called when a MIDI package has been received
133
/////////////////////////////////////////////////////////////////////////////
134
void APP_MIDI_NotifyPackage(mios32_midi_port_t port, mios32_midi_package_t midi_package)
135
{
136
  // Note On received?
137
  if( midi_package.chn == Chn1 &&
138
      (midi_package.type == NoteOn || midi_package.type == NoteOff) ) {
139
 
140
    // skip if autotune running
141
    if( midi_package.type == NoteOn && midi_package.velocity > 0 ) {
142
      if( autotune_state != AUTOTUNE_STATE_NOP ) {
143
    MIOS32_MIDI_SendDebugMessage("Autotune running - Note ignored!\n");
144
    return;
145
      }
146
 
147
      // if Note c-2 is played: activate autotune
148
      if( midi_package.note == 0x00 ) {
149
    autotune_state = AUTOTUNE_STATE_START;
150
    return;
151
      }
152
    }
153
 
154
 
155
    // branch depending on Note On/Off event
156
    if( midi_package.event == NoteOn && midi_package.velocity > 0 ) {
157
      // push note into note stack
158
      NOTESTACK_Push(&notestack, midi_package.note, midi_package.velocity);
159
    } else {
160
      // remove note from note stack
161
      NOTESTACK_Pop(&notestack, midi_package.note);
162
    }
163
 
164
 
165
    // still a note in stack?
166
    if( notestack.len ) {
167
      // take first note of stack
168
      // we have to convert the 7bit value to a 16bit value
169
      //u16 note_cv = notestack_items[0].note << 9;
170
      u16 note_cv = autotune_note_aout_value[notestack_items[0].note];
171
      u16 velocity_cv = notestack_items[0].tag << 9;
172
 
173
      // change voltages
174
      AOUT_PinSet(0, note_cv);
175
      AOUT_PinSet(1, velocity_cv);
176
 
177
      // set board LED
178
      MIOS32_BOARD_LED_Set(1, 1);
179
 
180
      // set AOUT digital output (if available)
181
      AOUT_DigitalPinSet(0, 1);
182
    } else {
183
      // turn off LED (can also be used as a gate output!)
184
      MIOS32_BOARD_LED_Set(1, 0);
185
 
186
      // clear AOUT digital output (if available)
187
      AOUT_DigitalPinSet(0, 0);
188
    }
189
 
190
    // update AOUT channel(s)
191
    AOUT_Update();
192
 
193
#if 1
194
    // optional debug messages
195
    NOTESTACK_SendDebugMessage(&notestack);
196
#endif
197
 
198
  // CC received?
199
  } else if( midi_package.chn == Chn1 && midi_package.type == CC ) {
200
 
201
    // we have to convert the 7bit value to a 16bit value
202
    u16 cc_cv = midi_package.value << 9;
203
 
204
    switch( midi_package.cc_number ) {
205
      case 16: AOUT_PinSet(2, cc_cv); break;
206
      case 17: AOUT_PinSet(3, cc_cv); break;
207
      case 18: AOUT_PinSet(4, cc_cv); break;
208
      case 19: AOUT_PinSet(5, cc_cv); break;
209
      case 20: AOUT_PinSet(6, cc_cv); break;
210
      case 21: AOUT_PinSet(7, cc_cv); break;
211
    }
212
 
213
    // update AOUT channel(s)
214
    // register upload will be omitted automatically if no channel value has changed
215
    AOUT_Update();
216
  }
217
}
218
 
219
 
220
/////////////////////////////////////////////////////////////////////////////
221
// This hook is called before the shift register chain is scanned
222
/////////////////////////////////////////////////////////////////////////////
223
void APP_SRIO_ServicePrepare(void)
224
{
225
}
226
 
227
 
228
/////////////////////////////////////////////////////////////////////////////
229
// This hook is called after the shift register chain has been scanned
230
/////////////////////////////////////////////////////////////////////////////
231
void APP_SRIO_ServiceFinish(void)
232
{
233
}
234
 
235
 
236
/////////////////////////////////////////////////////////////////////////////
237
// This hook is called when a button has been toggled
238
// pin_value is 1 when button released, and 0 when button pressed
239
/////////////////////////////////////////////////////////////////////////////
240
void APP_DIN_NotifyToggle(u32 pin, u32 pin_value)
241
{
242
}
243
 
244
 
245
/////////////////////////////////////////////////////////////////////////////
246
// This hook is called when an encoder has been moved
247
// incrementer is positive when encoder has been turned clockwise, else
248
// it is negative
249
/////////////////////////////////////////////////////////////////////////////
250
void APP_ENC_NotifyChange(u32 encoder, s32 incrementer)
251
{
252
}
253
 
254
 
255
/////////////////////////////////////////////////////////////////////////////
256
// This hook is called when a pot has been moved
257
/////////////////////////////////////////////////////////////////////////////
258
void APP_AIN_NotifyChange(u32 pin, u32 pin_value)
259
{
260
}
261
 
262
 
263
/////////////////////////////////////////////////////////////////////////////
264
// This task measures the frequency
265
/////////////////////////////////////////////////////////////////////////////
266
static void TASK_Frq_Measure(void *pvParameters)
267
{
268
  portTickType xLastExecutionTime;
269
  u8 frq_was_too_low = 0;
270
  u8 frq_was_too_high = 0;
271
  u32 last_frq = 0;
272
  u32 last_displayed_frq = 0;
273
 
274
  // Initialise the xLastExecutionTime variable on task entry
275
  xLastExecutionTime = xTaskGetTickCount();
276
 
277
  while( 1 ) {
278
    vTaskDelayUntil(&xLastExecutionTime, 1 / portTICK_RATE_MS);
279
 
280
    u32 frq = FRQ_METER_Tick();
281
 
282
    if( frq > FRQ_METER_TOO_LOW && frq < FRQ_METER_TOO_HIGH ) {
283
 
284
      // print new frequency if it changed more than 0.5%
285
      u32 frq_change = abs(frq-last_displayed_frq);
286
      u32 permill = (1000*frq_change) / frq;
287
      if( permill >= 5 ) {
288
    if( autotune_state == AUTOTUNE_STATE_NOP )
289
      MIOS32_MIDI_SendDebugMessage("-> %3d.%02d Hz\n", frq / 1000, (frq % 1000) / 10);
290
    last_displayed_frq = frq;
291
      }
292
 
293
      // toggle Status LED to as a sign of live
294
      MIOS32_BOARD_LED_Set(1, ~MIOS32_BOARD_LED_Get());
295
    } else {      
296
      if( frq != FRQ_METER_NO_UPDATE ) {
297
    if( frq == FRQ_METER_TOO_LOW )
298
      ++frq_was_too_low;
299
    else
300
      ++frq_was_too_high;
301
 
302
    if( frq_was_too_low >= 2 || frq_was_too_high >= 10 ) {
303
      if( autotune_state == AUTOTUNE_STATE_NOP ) {
304
        if( frq_was_too_low && !frq_was_too_high )
305
          MIOS32_MIDI_SendDebugMessage("-> frequency too low!\n");
306
        else if( frq_was_too_high && !frq_was_too_low )
307
          MIOS32_MIDI_SendDebugMessage("-> frequency too high!\n");
308
        else
309
          MIOS32_MIDI_SendDebugMessage("-> frequency very unstable!\n");
310
      }
311
 
312
      frq_was_too_low = 0;
313
      frq_was_too_high = 0;
314
 
315
      last_displayed_frq = frq;
316
    }
317
      }
318
    }
319
 
320
    // autotune
321
    if( frq != FRQ_METER_NO_UPDATE ) {
322
      switch( autotune_state ) {
323
      case AUTOTUNE_STATE_START:
324
    MIOS32_MIDI_SendDebugMessage("Autotune started.\n");
325
    autotune_last_aout_value = 0;
326
    autotune_current_note = 0;
327
    AOUT_PinSet(0, autotune_last_aout_value);
328
    AOUT_Update();
329
    autotune_state = AUTOTUNE_STATE_SWEEP;
330
    break;
331
 
332
      case AUTOTUNE_STATE_SWEEP: {
333
    u32 search_frq = autotune_note_frq[autotune_current_note];
334
    MIOS32_MIDI_SendDebugMessage("0x%03x: %3d.%02d Hz (searching for #%d: %3d.%02d Hz)\n",
335
                     autotune_last_aout_value,
336
                     frq / 1000, (frq % 1000) / 10,
337
                     autotune_current_note,
338
                     search_frq / 1000, (search_frq % 1000) / 10);
339
 
340
    if( frq >= search_frq ) {
341
      autotune_note_aout_value[autotune_current_note] = autotune_last_aout_value;
342
      if( ++autotune_current_note >= 0x80 ) {
343
        autotune_state = AUTOTUNE_STATE_PASSED;
344
      }
345
    } else {
346
      if( autotune_last_aout_value >= 0xfff0 ) {
347
        autotune_state = AUTOTUNE_STATE_FAILED;
348
      } else {
349
        autotune_last_aout_value += 0x10;
350
        AOUT_PinSet(0, autotune_last_aout_value);
351
        AOUT_Update();
352
      }
353
    }
354
      } break;
355
 
356
      case AUTOTUNE_STATE_PASSED:
357
    AOUT_PinSet(0, 0x0000);
358
    AOUT_Update();
359
    autotune_state = AUTOTUNE_STATE_NOP;
360
    MIOS32_MIDI_SendDebugMessage("Autotune finished!\n");
361
    break;
362
 
363
      case AUTOTUNE_STATE_FAILED:
364
    AOUT_PinSet(0, 0x0000);
365
    AOUT_Update();
366
    autotune_state = AUTOTUNE_STATE_NOP;
367
    MIOS32_MIDI_SendDebugMessage("Autotune failed, frequency for note >= %d not assigned!\n", autotune_current_note);
368
    break;
369
      }
370
    }
371
 
372
 
373
    // store current frequency
374
    if( frq != FRQ_METER_NO_UPDATE )
375
      last_frq = frq;
376
  }
377
}