Subversion Repositories svn.mios32

Rev

Rev 2577 | Rev 2594 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2576 hawkeye 1
// LoopA Core Logic
2571 hawkeye 2
// (c) Hawkeye 2015-2018
2184 hawkeye 3
//
2571 hawkeye 4
// This unit provides the loopA core data structures and the menu navigation and multi-clip support
2184 hawkeye 5
//
2185 hawkeye 6
// In playback, all clips will start to play back, regardless of their mute state.
2576 hawkeye 7
// Also, all clips will loop by default (it is called LoopA, anyways)
2185 hawkeye 8
// This emulates the behaviour of the MBSEQ.
9
//
2571 hawkeye 10
// INTERFACE (LoopA v2 - with limited encoders/buttons)
11
// ----------------------------------------------------
2185 hawkeye 12
//
2571 hawkeye 13
// Right encoder: flips through pages aka modes (unless a command is selected, then it changes its data value)
2185 hawkeye 14
//
2571 hawkeye 15
// Left encoder: changes global data of mode:
16
//    - in play mode, changes currently active track
17
//    - in note mode, changes currently selected note
18
//    - in disk mode, changes selected session number
2185 hawkeye 19
//
2571 hawkeye 20
// To modify command data (e.g. transpose), the GP button under the command must be pushed, then the
21
// right encoder can change the command data. Pushing the GP button again exits command data modification mode, then
22
// the right encoder can be used again to change pages aka modes.
23
//
24
// Pushing the right or left encoder always returns back to play mode (shortcut)
25
// (v3 needs no pushable encoders, as we will have more buttons and encoders directly under the commands)
2185 hawkeye 26
// =================================================================================================
2184 hawkeye 27
 
2593 hawkeye 28
#include "commonIncludes.h"
2185 hawkeye 29
 
2184 hawkeye 30
#include "tasks.h"
31
#include "file.h"
32
#include "loopa.h"
33
#include "hardware.h"
34
#include "screen.h"
2576 hawkeye 35
#include "app_lcd.h"
2593 hawkeye 36
#include "voxelspace.h"
2184 hawkeye 37
 
38
// --  Local types ---
39
 
2593 hawkeye 40
 
2184 hawkeye 41
// --- Global vars ---
42
 
2186 hawkeye 43
static s32 (*clipPlayEventCallback)(u8 clipNumber, mios32_midi_package_t midi_package, u32 tick) = 0;     // fetchClipEvents() callback
44
static s32 (*clipMetaEventCallback)(u8 clipNumber, u8 meta, u32 len, u8 *buffer, u32 tick) = 0; // fetchClipEvents() callback
2185 hawkeye 45
 
2571 hawkeye 46
u32 tick_ = 0;                        // global seq tick
47
u16 bpm_ = 120;                       // bpm
48
u16 sessionNumber_ = 0;               // currently active session number (directory e.g. /SESSIONS/0001)
49
u8 sessionExistsOnDisk_ = 0;          // is the currently selected session number already saved on disk/sd card
2593 hawkeye 50
enum LoopaPage page_ = PAGE_MUTE;     // currently active page/view
2571 hawkeye 51
enum Command command_ = COMMAND_NONE; // currently active command
2184 hawkeye 52
 
2571 hawkeye 53
u8 activeTrack_ = 0;                  // currently active or last active clip number (0..5)
54
u8 activeScene_ = 0;                  // currently active scene number (0-15)
55
u16 beatLoopSteps_ = 16;              // number of steps for one beatloop (adjustable)
56
u8 isRecording_ = 0;                  // set, if currently recording to the selected clip
57
u8 oledBeatFlashState_ = 0;           // 0: don't flash, 1: flash slightly (normal 1/4th note), 2: flash intensively (after four 1/4th notes or 16 steps)
58
 
2185 hawkeye 59
u16 seqRecEnabledPorts_;
2571 hawkeye 60
s16 notePtrsOn_[128];                 // during recording - pointers to notes that are currently "on" (and waiting for an "off", therefore length yet undetermined) (-1: note not depressed)
2185 hawkeye 61
 
2571 hawkeye 62
static u8 ffwdSilentMode_;            // for FFWD function
63
static u8 seqClkLocked;               // lock BPM, so that it can't be changed from MIDI player
64
static char filename_[20];            // global, for filename operations
2184 hawkeye 65
 
2571 hawkeye 66
// --- Track data (saved to session on disk) ---
67
u8 trackMute_[TRACKS];                // mute state of each clip
68
u8 trackMidiPort_[TRACKS];
69
u8 trackMidiChannel_[TRACKS];
2184 hawkeye 70
 
2571 hawkeye 71
// --- Clip data (saved to session on disk) ---
72
u16 clipSteps_[TRACKS][SCENES];       // number of steps for each clip
73
u32 clipQuantize_[TRACKS][SCENES];    // brings all clip notes close to the specified timing, e.g. quantize = 4 - close to 4th notes, 16th = quantize to step, ...
74
s8 clipTranspose_[TRACKS][SCENES];
75
s16 clipScroll_[TRACKS][SCENES];
76
u8 clipStretch_[TRACKS][SCENES];      // 1: compress to 1/16th, 2: compress to 1/8th ... 16: no stretch, 32: expand 2x, 64: expand 4x, 128: expand 8x
77
NoteData clipNotes_[TRACKS][SCENES][MAXNOTES];  // clip note data storage (chained list, note start time and length/velocity)
78
u16 clipNotesSize_[TRACKS][SCENES];   // active number of notes currently in use for that clip
2184 hawkeye 79
 
2571 hawkeye 80
// --- Copy/paste buffer
81
u16 copiedClipSteps_;
82
u32 copiedClipQuantize_;
83
s8 copiedClipTranspose_;
84
s16 copiedClipScroll_;
85
u8 copiedClipStretch_;
86
NoteData copiedClipNotes_[MAXNOTES];
87
u16 copiedClipNotesSize_;
88
 
89
// --- Secondary data (not on disk) ---
90
u8 trackMuteToggleRequested_[TRACKS]; // 1: perform a mute/unmute toggle of the clip at the next beatstep (synced mute/unmute)
91
u8 sceneChangeRequested_ = 0;         // If != activeScene_, this will be the scene we are changing to
92
u16 clipActiveNote_[TRACKS][SCENES];  // currently active edited note number, when in noteroll editor
93
 
2185 hawkeye 94
// =================================================================================================
95
 
96
 
2184 hawkeye 97
/**
98
 * Help function: convert tick number to step number
99
 * @return step
100
 *
101
 */
102
u16 tickToStep(u32 tick)
103
{
2185 hawkeye 104
   return tick / (SEQ_BPM_PPQN_Get() / 4);
2184 hawkeye 105
}
106
// -------------------------------------------------------------------------------------------------
107
 
108
 
109
/**
2571 hawkeye 110
 * Help function: convert step number to tick number
111
 * @return step
2184 hawkeye 112
 *
113
 */
2571 hawkeye 114
u32 stepToTick(u16 step)
2184 hawkeye 115
{
2571 hawkeye 116
   return step * (SEQ_BPM_PPQN_Get() / 4);
2184 hawkeye 117
}
118
// -------------------------------------------------------------------------------------------------
119
 
120
 
121
/**
2571 hawkeye 122
 * Hard bound tick to the configured length (number of steps) of a clip
2184 hawkeye 123
 *
124
 */
2571 hawkeye 125
u32 boundTickToClipSteps(u32 tick, u8 clip)
2184 hawkeye 126
{
2571 hawkeye 127
   return tick % stepToTick(clipSteps_[clip][activeScene_]);
2184 hawkeye 128
}
129
// -------------------------------------------------------------------------------------------------
130
 
131
 
132
/**
2571 hawkeye 133
 * Quantize a "tick" time event to a tick quantization measure (e.g. 384), wrap around
134
 * ticks going over the clip length (clipLengthInTicks)
2184 hawkeye 135
 *
136
 */
2571 hawkeye 137
u32 quantize(u32 tick, u32 quantizeMeasure, u32 clipLengthInTicks)
2184 hawkeye 138
{
2571 hawkeye 139
   if (quantizeMeasure < 3)
140
      return tick; // no quantization
2184 hawkeye 141
 
2571 hawkeye 142
   u32 mod = tick % quantizeMeasure;
143
   u32 tickBase = tick - (tick % quantizeMeasure); // default: snap to previous measure
2184 hawkeye 144
 
2571 hawkeye 145
   // note: i did not like this improvement, as notes were cut off as they were moved newly ahead of the current play position and
146
   // thus were retriggered while still being held on the keyboard, but i fixed it, now not playing notes with length 0 (still being held) ;-)
147
   if (mod > (quantizeMeasure >> 1))
148
      tickBase = (tick - (tick % quantizeMeasure) + quantizeMeasure) % clipLengthInTicks; // snap to next measure as we are closer to this one
2184 hawkeye 149
 
2571 hawkeye 150
   return tickBase;
2184 hawkeye 151
}
152
// -------------------------------------------------------------------------------------------------
153
 
154
 
155
/**
2571 hawkeye 156
 * Transform (stretch and scroll) and then quantize a note in a clip
2184 hawkeye 157
 *
158
 */
2571 hawkeye 159
s32 quantizeTransform(u8 clip, u16 noteNumber)
2184 hawkeye 160
{
161
 
2571 hawkeye 162
   // Idea: scroll first, and modulo-map to trackstart/end boundaries
163
   //       scale afterwards
164
   //       apply track len afterwards
165
   //       drop notes with ticks < 0 and ticks > tracklen
2184 hawkeye 166
 
2571 hawkeye 167
   s32 tick = clipNotes_[clip][activeScene_][noteNumber].tick;
168
   s32 quantizeMeasure = clipQuantize_[clip][activeScene_];
169
   s32 clipLengthInTicks = getClipLengthInTicks(clip);
2184 hawkeye 170
 
2571 hawkeye 171
   // stretch
172
   tick *= clipStretch_[clip][activeScene_];
173
   tick = tick >> 4; // divide by 16 (stretch base)
2184 hawkeye 174
 
2571 hawkeye 175
   // only consider notes, that are within the clip length after stretching
176
   if (tick >= clipLengthInTicks)
177
      return -1;
2184 hawkeye 178
 
2571 hawkeye 179
   // scroll
180
   tick += clipScroll_[clip][activeScene_] * 24;
2184 hawkeye 181
 
2571 hawkeye 182
   while (tick < 0)
183
      tick += clipLengthInTicks;
2184 hawkeye 184
 
2571 hawkeye 185
   tick %= clipLengthInTicks;
186
 
187
   return quantize(tick, quantizeMeasure, clipLengthInTicks);
2184 hawkeye 188
}
189
// -------------------------------------------------------------------------------------------------
190
 
191
 
192
/**
2571 hawkeye 193
 * Get the clip length in ticks
2185 hawkeye 194
 */
2571 hawkeye 195
u32 getClipLengthInTicks(u8 clip)
2185 hawkeye 196
{
2571 hawkeye 197
   return stepToTick(clipSteps_[clip][activeScene_]);
2185 hawkeye 198
}
199
// -------------------------------------------------------------------------------------------------
200
 
201
 
202
/**
2571 hawkeye 203
 * Update a single GP Led, only change state if
2185 hawkeye 204
 */
2571 hawkeye 205
void updateGPLed(u8 number, u8 newState)
2185 hawkeye 206
{
2571 hawkeye 207
   static s8 s1 = -1, s2 = -1, s3 = -1, s4 = -1, s5 = -1, s6 = -1;
2185 hawkeye 208
 
2571 hawkeye 209
   switch (number)
210
   {
211
      case 1:
212
         if (s1 != newState)
213
         {
2593 hawkeye 214
            MIOS32_DOUT_PinSet(LED_GP1, newState);
2571 hawkeye 215
            s1 = newState;
216
         }
217
         break;
2185 hawkeye 218
 
2571 hawkeye 219
      case 2:
220
         if (s2 != newState)
221
         {
222
            MIOS32_DOUT_PinSet(led_gp2, newState);
223
            s2 = newState;
224
         }
225
         break;
2185 hawkeye 226
 
2571 hawkeye 227
      case 3:
228
         if (s3 != newState)
229
         {
230
            MIOS32_DOUT_PinSet(led_gp3, newState);
231
            s3 = newState;
232
         }
233
         break;
2185 hawkeye 234
 
2571 hawkeye 235
      case 4:
236
         if (s4 != newState)
237
         {
238
            MIOS32_DOUT_PinSet(led_gp4, newState);
239
            s4 = newState;
240
         }
241
         break;
2185 hawkeye 242
 
2571 hawkeye 243
      case 5:
244
         if (s5 != newState)
245
         {
246
            MIOS32_DOUT_PinSet(led_gp5, newState);
247
            s5 = newState;
248
         }
249
         break;
2185 hawkeye 250
 
2571 hawkeye 251
      case 6:
252
         if (s6 != newState)
253
         {
254
            MIOS32_DOUT_PinSet(led_gp6, newState);
255
            s6 = newState;
256
         }
257
         break;
2185 hawkeye 258
   }
259
 
260
}
261
// -------------------------------------------------------------------------------------------------
262
 
263
 
264
/**
2571 hawkeye 265
 *  Update the six general purpose LED states (called periodically from app.c)
2185 hawkeye 266
 *
2184 hawkeye 267
 */
2571 hawkeye 268
void updateGPLeds()
2184 hawkeye 269
{
2571 hawkeye 270
   MUTEX_DIGITALOUT_TAKE;
2184 hawkeye 271
 
2593 hawkeye 272
   if (screenIsInMenu())
2185 hawkeye 273
   {
274
 
2593 hawkeye 275
   }
2185 hawkeye 276
 
2593 hawkeye 277
   else
278
   {
279
      switch (page_) {
280
         case PAGE_MUTE:
281
            updateGPLed(1, !trackMute_[0]);
282
            updateGPLed(2, !trackMute_[1]);
283
            updateGPLed(3, !trackMute_[2]);
284
            updateGPLed(4, !trackMute_[3]);
285
            updateGPLed(5, !trackMute_[4]);
286
            updateGPLed(6, !trackMute_[5]);
287
            break;
2184 hawkeye 288
 
2593 hawkeye 289
         case PAGE_CLIP:
290
            updateGPLed(1, command_ == COMMAND_CLIPLEN);
291
            updateGPLed(2, command_ == COMMAND_QUANTIZE);
292
            updateGPLed(3, command_ == COMMAND_TRANSPOSE);
293
            updateGPLed(4, command_ == COMMAND_SCROLL);
294
            updateGPLed(5, command_ == COMMAND_STRETCH);
295
            updateGPLed(6, command_ == COMMAND_CLEAR);
296
            break;
2184 hawkeye 297
 
2593 hawkeye 298
         case PAGE_NOTES:
299
            updateGPLed(1, command_ == COMMAND_POSITION);
300
            updateGPLed(2, command_ == COMMAND_NOTE);
301
            updateGPLed(3, command_ == COMMAND_VELOCITY);
302
            updateGPLed(4, command_ == COMMAND_LENGTH);
303
            updateGPLed(5, 0);
304
            updateGPLed(6, command_ == COMMAND_DELETENOTE);
305
            break;
2184 hawkeye 306
 
2593 hawkeye 307
         case PAGE_TRACK:
308
            updateGPLed(1, command_ == COMMAND_PORT);
309
            updateGPLed(2, command_ == COMMAND_CHANNEL);
310
            updateGPLed(3, 0);
311
            updateGPLed(4, 0);
312
            updateGPLed(5, 0);
313
            updateGPLed(6, 0);
314
            break;
315
 
316
         case PAGE_DISK:
317
            updateGPLed(1, command_ == COMMAND_SAVE);
318
            updateGPLed(2, command_ == COMMAND_LOAD);
319
            updateGPLed(3, command_ == COMMAND_NEW);
320
            updateGPLed(4, 0);
321
            updateGPLed(5, 0);
322
            updateGPLed(6, 0);
323
            break;
324
 
325
         case PAGE_TEMPO:
326
            updateGPLed(1, command_ == COMMAND_BPM);
327
            updateGPLed(2, command_ == COMMAND_BPMFLASH);
328
            updateGPLed(3, 0);
329
            updateGPLed(4, 0);
330
            updateGPLed(5, 0);
331
            updateGPLed(6, 0);
332
            break;
333
      }
2571 hawkeye 334
   }
2184 hawkeye 335
 
2571 hawkeye 336
   MUTEX_DIGITALOUT_GIVE;
337
}
338
// -------------------------------------------------------------------------------------------------
2184 hawkeye 339
 
340
 
2571 hawkeye 341
/**
342
 * Set a different active track
343
 */
344
void setActiveTrack(u8 trackNumber)
345
{
346
   activeTrack_ = trackNumber;
347
   screenSetClipSelected(activeTrack_);
2184 hawkeye 348
 
2571 hawkeye 349
   MUTEX_DIGITALOUT_TAKE;
350
   MIOS32_DOUT_PinSet(led_active1, activeTrack_ == 0);
351
   MIOS32_DOUT_PinSet(led_active2, activeTrack_ == 1);
352
   MIOS32_DOUT_PinSet(led_active3, activeTrack_ == 2);
353
   MIOS32_DOUT_PinSet(led_active4, activeTrack_ == 3);
354
   MIOS32_DOUT_PinSet(led_active5, activeTrack_ == 4);
355
   MIOS32_DOUT_PinSet(led_active6, activeTrack_ == 5);
356
   MUTEX_DIGITALOUT_GIVE;
2184 hawkeye 357
}
358
// -------------------------------------------------------------------------------------------------
359
 
360
/**
2571 hawkeye 361
 * Set a new active scene number
2184 hawkeye 362
 */
2571 hawkeye 363
void setActiveScene(u8 sceneNumber)
2184 hawkeye 364
{
2571 hawkeye 365
   activeScene_ = sceneNumber;
2185 hawkeye 366
 
2571 hawkeye 367
   MUTEX_DIGITALOUT_TAKE;
368
   MIOS32_DOUT_PinSet(led_scene1, activeScene_ == 0);
369
   MIOS32_DOUT_PinSet(led_scene2, activeScene_ == 1);
370
   MIOS32_DOUT_PinSet(led_scene3, activeScene_ == 2);
371
   MIOS32_DOUT_PinSet(led_scene4, activeScene_ == 3);
372
   MIOS32_DOUT_PinSet(led_scene5, activeScene_ == 4);
373
   MIOS32_DOUT_PinSet(led_scene6, activeScene_ == 5);
374
   MUTEX_DIGITALOUT_GIVE;
375
}
376
// -------------------------------------------------------------------------------------------------
2185 hawkeye 377
 
2184 hawkeye 378
 
2571 hawkeye 379
void setActivePage(enum LoopaPage page)
380
{
381
   page_ = page;
382
 
383
   MUTEX_DIGITALOUT_TAKE;
2593 hawkeye 384
   MIOS32_DOUT_PinSet(led_page_1, page == PAGE_MUTE);
385
   MIOS32_DOUT_PinSet(led_page_2, page == PAGE_CLIP);
2571 hawkeye 386
   MIOS32_DOUT_PinSet(led_page_3, page == PAGE_NOTES);
2593 hawkeye 387
   MIOS32_DOUT_PinSet(led_page_4, page == PAGE_TRACK);
2571 hawkeye 388
   MIOS32_DOUT_PinSet(led_page_5, page == PAGE_DISK);
2593 hawkeye 389
   MIOS32_DOUT_PinSet(led_page_6, page == PAGE_TEMPO);
2571 hawkeye 390
   MUTEX_DIGITALOUT_GIVE;
2184 hawkeye 391
}
392
// -------------------------------------------------------------------------------------------------
393
 
394
 
395
/**
2571 hawkeye 396
 * Request (or cancel) a synced mute/unmute toggle
2186 hawkeye 397
 *
398
 */
2571 hawkeye 399
void toggleMute(u8 clipNumber)
2186 hawkeye 400
{
2571 hawkeye 401
   if (trackMute_[clipNumber])
402
      setActiveTrack(clipNumber); // if track was unmuted, set it as active track
2186 hawkeye 403
 
2571 hawkeye 404
   if (SEQ_BPM_IsRunning())
405
      trackMuteToggleRequested_[clipNumber] = !trackMuteToggleRequested_[clipNumber];
406
   else
407
      trackMute_[clipNumber] = !trackMute_[clipNumber];
2186 hawkeye 408
}
409
// -------------------------------------------------------------------------------------------------
410
 
411
 
412
/**
2571 hawkeye 413
 * Synchronized (to beatstep) mutes and unmutes
2184 hawkeye 414
 *
415
 */
2571 hawkeye 416
void performSyncedMutesAndUnmutes()
2184 hawkeye 417
{
2571 hawkeye 418
   u8 clip;
2185 hawkeye 419
 
2571 hawkeye 420
   for (clip = 0; clip < TRACKS; clip++)
2184 hawkeye 421
   {
2571 hawkeye 422
      if (trackMuteToggleRequested_[clip])
2184 hawkeye 423
      {
2571 hawkeye 424
         if (tickToStep(tick_) % beatLoopSteps_ == 0)
2184 hawkeye 425
         {
2571 hawkeye 426
            u8 state = trackMute_[clip];
427
            trackMute_[clip] = !state;
2184 hawkeye 428
 
2571 hawkeye 429
            trackMuteToggleRequested_[clip] = 0;
2184 hawkeye 430
         }
431
      }
2571 hawkeye 432
   }
433
};
434
// -------------------------------------------------------------------------------------------------
2184 hawkeye 435
 
2186 hawkeye 436
 
2571 hawkeye 437
/**
438
 * Synchronized (to beatstep) scene changes
439
 *
440
 */
441
void performSyncedSceneChanges()
442
{
443
   if (sceneChangeRequested_ != activeScene_)
444
   {
445
      u8 sceneChangeInTicks = beatLoopSteps_ - (tickToStep(tick_) % beatLoopSteps_);
2186 hawkeye 446
 
2571 hawkeye 447
      // flash indicate target scene (after synced scene switch)
448
      if (tick_ % 16 < 8)
449
      {
450
         MUTEX_DIGITALOUT_TAKE;
451
         MIOS32_DOUT_PinSet(led_scene1, sceneChangeRequested_ == 0);
452
         MIOS32_DOUT_PinSet(led_scene2, sceneChangeRequested_ == 1);
453
         MIOS32_DOUT_PinSet(led_scene3, sceneChangeRequested_ == 2);
454
         MIOS32_DOUT_PinSet(led_scene4, sceneChangeRequested_ == 3);
455
         MIOS32_DOUT_PinSet(led_scene5, sceneChangeRequested_ == 4);
456
         MIOS32_DOUT_PinSet(led_scene6, sceneChangeRequested_ == 5);
457
         MUTEX_DIGITALOUT_GIVE;
2184 hawkeye 458
      }
459
      else
460
      {
2571 hawkeye 461
         MUTEX_DIGITALOUT_TAKE;
462
         MIOS32_DOUT_PinSet(led_scene1, 0);
463
         MIOS32_DOUT_PinSet(led_scene2, 0);
464
         MIOS32_DOUT_PinSet(led_scene3, 0);
465
         MIOS32_DOUT_PinSet(led_scene4, 0);
466
         MIOS32_DOUT_PinSet(led_scene5, 0);
467
         MIOS32_DOUT_PinSet(led_scene6, 0);
468
         MUTEX_DIGITALOUT_GIVE;
2184 hawkeye 469
      }
470
 
2571 hawkeye 471
      if (tickToStep(tick_) % beatLoopSteps_ == 0)
2184 hawkeye 472
      {
2571 hawkeye 473
         setActiveScene(sceneChangeRequested_);
474
         sceneChangeRequested_ = activeScene_;
475
         sceneChangeInTicks = 0;
2184 hawkeye 476
      }
2571 hawkeye 477
 
478
      screenSetSceneChangeInTicks(sceneChangeInTicks);
2184 hawkeye 479
   }
480
}
481
// -------------------------------------------------------------------------------------------------
482
 
483
 
484
/**
2571 hawkeye 485
 * convert sessionNumber to global filename_
2184 hawkeye 486
 */
2571 hawkeye 487
void sessionNumberToFilename_(u16 sessionNumber)
2184 hawkeye 488
{
2571 hawkeye 489
   sprintf(filename_, "/SESSIONS/%04d.LPA", sessionNumber);
2184 hawkeye 490
}
491
// -------------------------------------------------------------------------------------------------
492
 
493
 
494
/**
2571 hawkeye 495
 * Save session to defined session number file
2184 hawkeye 496
 *
497
 */
2571 hawkeye 498
void saveSession(u16 sessionNumber)
2184 hawkeye 499
{
2571 hawkeye 500
   sessionNumberToFilename_(sessionNumber);
2184 hawkeye 501
 
2571 hawkeye 502
   MUTEX_SDCARD_TAKE;
2185 hawkeye 503
 
2571 hawkeye 504
   s32 status;
505
   if ((status = FILE_WriteOpen(filename_, 1)) < 0)
2185 hawkeye 506
   {
2571 hawkeye 507
      DEBUG_MSG("[FILE] Failed to open/create %s, status: %d\n", filename_, status);
2185 hawkeye 508
   }
2571 hawkeye 509
   else
510
   {
511
      FILE_WriteBuffer((u8*)"LPAV2200", 8);
2185 hawkeye 512
 
2571 hawkeye 513
      status |= FILE_WriteBuffer((u8*)trackMute_, sizeof(trackMute_));
514
      status |= FILE_WriteBuffer((u8*)trackMidiPort_, sizeof(trackMidiPort_));
515
      status |= FILE_WriteBuffer((u8*)trackMidiChannel_, sizeof(trackMidiChannel_));
516
 
517
      status |= FILE_WriteBuffer((u8*)clipSteps_, sizeof(clipSteps_));
518
      status |= FILE_WriteBuffer((u8*)clipQuantize_, sizeof(clipQuantize_));
519
      status |= FILE_WriteBuffer((u8*)clipTranspose_, sizeof(clipTranspose_));
520
      status |= FILE_WriteBuffer((u8*)clipScroll_, sizeof(clipScroll_));
521
      status |= FILE_WriteBuffer((u8*)clipStretch_, sizeof(clipStretch_));
522
 
523
      status |= FILE_WriteBuffer((u8*)clipNotes_, sizeof(clipNotes_));
524
      status |= FILE_WriteBuffer((u8*)clipNotesSize_, sizeof(clipNotesSize_));
525
      status |= FILE_WriteBuffer((u8*)notePtrsOn_, sizeof(notePtrsOn_));
2186 hawkeye 526
 
2571 hawkeye 527
      status |= FILE_WriteClose();
528
   }
529
 
530
   if (status == 0)
531
      screenFormattedFlashMessage("Saved");
532
   else
533
      screenFormattedFlashMessage("Save failed");
534
 
535
   MUTEX_SDCARD_GIVE;
2184 hawkeye 536
}
537
// -------------------------------------------------------------------------------------------------
538
 
539
 
2571 hawkeye 540
 
2184 hawkeye 541
/**
2571 hawkeye 542
 * Load session from defined session number file
2184 hawkeye 543
 *
544
 */
2571 hawkeye 545
void loadSession(u16 sessionNumber)
2184 hawkeye 546
{
2571 hawkeye 547
   sessionNumberToFilename_(sessionNumber);
2184 hawkeye 548
 
2571 hawkeye 549
   MUTEX_SDCARD_TAKE;
2185 hawkeye 550
 
2571 hawkeye 551
   s32 status;
552
   file_t file;
553
   if ((status = FILE_ReadOpen(&file, filename_)) < 0)
554
   {
555
      DEBUG_MSG("[FILE] Failed to open %s, status: %d\n", filename_, status);
556
   }
557
   else
558
   {
559
      char version[8];
560
      FILE_ReadBuffer((u8*)version, 8);
561
 
562
      status |= FILE_ReadBuffer((u8*)trackMute_, sizeof(trackMute_));
563
      status |= FILE_ReadBuffer((u8*)trackMidiPort_, sizeof(trackMidiPort_));
564
      status |= FILE_ReadBuffer((u8*)trackMidiChannel_, sizeof(trackMidiChannel_));
565
 
566
      status |= FILE_ReadBuffer((u8*)clipSteps_, sizeof(clipSteps_));
567
      status |= FILE_ReadBuffer((u8*)clipQuantize_, sizeof(clipQuantize_));
568
      status |= FILE_ReadBuffer((u8*)clipTranspose_, sizeof(clipTranspose_));
569
      status |= FILE_ReadBuffer((u8*)clipScroll_, sizeof(clipScroll_));
570
      status |= FILE_ReadBuffer((u8*)clipStretch_, sizeof(clipStretch_));
571
 
572
      status |= FILE_ReadBuffer((u8*)clipNotes_, sizeof(clipNotes_));
573
      status |= FILE_ReadBuffer((u8*)clipNotesSize_, sizeof(clipNotesSize_));
574
      status |= FILE_ReadBuffer((u8*)notePtrsOn_, sizeof(notePtrsOn_));
575
 
576
      status |= FILE_ReadClose(&file);
577
   }
578
 
579
   if (status == 0)
580
      screenFormattedFlashMessage("Loaded");
581
   else
582
      screenFormattedFlashMessage("Load failed");
583
 
584
   MUTEX_SDCARD_GIVE;
2184 hawkeye 585
}
586
// -------------------------------------------------------------------------------------------------
587
 
588
 
589
/**
590
 * First callback from app - render Loopa Startup logo on screen
591
 *
592
 */
593
void loopaStartup()
594
{
595
   screenShowLoopaLogo(1);
2571 hawkeye 596
 
597
   // Set up fixed MIDI router
598
   DEBUG_MSG("Setting up MIDI router");
599
 
600
   // Router: IN1 all -> OUT2 all 
601
   midi_router_node_entry_t *n = &midi_router_node[0];
602
   n->src_port = USB0 + ((4&0xc) << 2) + (4&3);;
603
   n->src_chn = 17;
604
   n->dst_port = USB0 + ((5&0xc) << 2) + (5&3);
605
   n->dst_chn = 17;
606
 
607
   // Router: IN2 all -> OUT1 all
608
   n = &midi_router_node[1];
609
   n->src_port = USB0 + ((5&0xc) << 2) + (5&3);;
610
   n->src_chn = 17;
611
   n->dst_port = USB0 + ((4&0xc) << 2) + (4&3);
612
   n->dst_chn = 17;
613
 
614
   // Router: IN1 all -> OUT3 all 
615
   n = &midi_router_node[2];
616
   n->src_port = USB0 + ((4&0xc) << 2) + (4&3);;
617
   n->src_chn = 17;
618
   n->dst_port = USB0 + ((6&0xc) << 2) + (6&3);
619
   n->dst_chn = 17;
620
 
621
   // Router: IN3 all -> OUT1 all
622
   n = &midi_router_node[3];
623
   n->src_port = USB0 + ((6&0xc) << 2) + (6&3);;
624
   n->src_chn = 17;
625
   n->dst_port = USB0 + ((4&0xc) << 2) + (4&3);
626
   n->dst_chn = 17;
2184 hawkeye 627
}
628
// -------------------------------------------------------------------------------------------------
629
 
630
 
2185 hawkeye 631
////////////////////////////////////////////////////////////////////////////////////////////////////
632
// LOOPA Sequencer Section
633
////////////////////////////////////////////////////////////////////////////////////////////////////
634
 
635
static s32  seqPlayOffEvents(void);
636
static s32  seqReset(u8 play_off_events);
637
static s32  seqSongPos(u16 new_song_pos);
638
static void seqUpdateBeatLEDs(u32 bpm_tick);
639
static s32  seqTick(u32 bpm_tick);
640
static s32  hookMIDISendPackage(mios32_midi_port_t port, mios32_midi_package_t package);
641
static s32  seqPlayEvent(u8 clipNumber, mios32_midi_package_t midi_package, u32 tick);
2186 hawkeye 642
static s32  seqIgnoreMetaEvent(u8 clipNumber, u8 meta, u32 len, u8 *buffer, u32 tick);
2185 hawkeye 643
 
644
 
2184 hawkeye 645
/**
2185 hawkeye 646
 * Get Clock Mode
647
 * adds a fourth mode which locks the BPM so that it can't be modified by the MIDI file
648
 *
649
 */
2571 hawkeye 650
/*u8 seqClockModeGet(void)
2185 hawkeye 651
{
652
   if (seqClkLocked)
653
      return 3;
654
 
655
   return SEQ_BPM_ModeGet();
656
}
657
// -------------------------------------------------------------------------------------------------
2571 hawkeye 658
*/
2185 hawkeye 659
 
660
/**
661
 * Set Clock Mode
662
 * adds a fourth mode which locks the BPM so that it can't be modified by the MIDI file
663
 *
664
 */
2571 hawkeye 665
/*
2185 hawkeye 666
s32 seqClockModeSet(u8 mode)
667
{
668
  if (mode > 3)
669
     return -1; // invalid mode
670
 
671
  if (mode == 3)
672
  {
673
     SEQ_BPM_ModeSet(SEQ_BPM_MODE_Master);
674
     seqClkLocked = 1;
675
  }
676
  else
677
  {
678
     SEQ_BPM_ModeSet(mode);
679
     seqClkLocked = 0;
680
  }
681
 
682
  return 0; // no error
683
}
684
// -------------------------------------------------------------------------------------------------
2571 hawkeye 685
*/
2185 hawkeye 686
 
687
/**
688
 * This main sequencer handler is called periodically to poll the clock/current tick
689
 * from BPM generator
690
 *
691
 */
692
s32 seqHandler(void)
693
{
694
   // handle BPM requests
695
 
696
   // note: don't remove any request check - clocks won't be propagated
697
   // so long any Stop/Cont/Start/SongPos event hasn't been flagged to the sequencer
698
   if (SEQ_BPM_ChkReqStop())
699
   {
700
      seqPlayOffEvents();
701
      MIDI_ROUTER_SendMIDIClockEvent(0xfc, 0);
702
   }
703
 
2571 hawkeye 704
   /* Hawkeye new - no continuation if (SEQ_BPM_ChkReqCont())
2185 hawkeye 705
   {
706
      MIDI_ROUTER_SendMIDIClockEvent(0xfb, 0);
707
   }
2571 hawkeye 708
   */
2185 hawkeye 709
 
710
   if (SEQ_BPM_ChkReqStart())
711
   {
712
      MIDI_ROUTER_SendMIDIClockEvent(0xfa, 0);
713
      seqReset(1);
714
      seqSongPos(0);
715
   }
716
 
717
   u16 new_song_pos;
718
   if (SEQ_BPM_ChkReqSongPos(&new_song_pos))
719
   {
720
      seqSongPos(new_song_pos);
721
   }
722
 
723
   u32 bpm_tick;
724
   if (SEQ_BPM_ChkReqClk(&bpm_tick) > 0)
725
   {
726
      seqUpdateBeatLEDs(bpm_tick);
2186 hawkeye 727
 
728
      // set initial BPM according to MIDI spec
729
      if (bpm_tick == 0 && !seqClkLocked)
730
         SEQ_BPM_Set(120.0);
731
 
732
      if (bpm_tick == 0) // send start (again) to synchronize with new MIDI songs
733
         MIDI_ROUTER_SendMIDIClockEvent(0xfa, 0);
734
 
735
      seqTick(bpm_tick);
2185 hawkeye 736
   }
737
 
738
   return 0; // no error
739
}
740
// -------------------------------------------------------------------------------------------------
741
 
742
 
743
/**
744
 * This function plays all "off" events
745
 * Should be called on sequencer reset/restart/pause to avoid hanging notes
746
 *
747
 */
748
static s32 seqPlayOffEvents(void)
749
{
750
  // play "off events"
751
  SEQ_MIDI_OUT_FlushQueue();
752
 
753
  // send Note Off to all channels
754
  // TODO: howto handle different ports?
755
  // TODO: should we also send Note Off events? Or should we trace Note On events and send Off if required?
756
  int chn;
757
  mios32_midi_package_t midi_package;
758
  midi_package.type = CC;
759
  midi_package.event = CC;
760
  midi_package.evnt2 = 0;
761
 
762
  for (chn=0; chn<16; ++chn)
763
  {
764
     midi_package.chn = chn;
765
     midi_package.evnt1 = 123; // All Notes Off
766
     hookMIDISendPackage(UART0, midi_package);
767
     midi_package.evnt1 = 121; // Controller Reset
768
     hookMIDISendPackage(UART0, midi_package);
769
  }
770
 
771
  return 0; // no error
772
}
773
// -------------------------------------------------------------------------------------------------
774
 
775
 
776
/**
777
 * Reset song position of sequencer
778
 *
779
 */
780
s32 seqReset(u8 play_off_events)
781
{
782
   // install seqPlayEvent callback for clipFetchEvents()
783
   clipPlayEventCallback = seqPlayEvent;
784
 
785
   // since timebase has been changed, ensure that Off-Events are played
786
   // (otherwise they will be played much later...)
787
   if (play_off_events)
788
      seqPlayOffEvents();
789
 
790
   // release  FFWD mode
791
   ffwdSilentMode_ = 0;
792
 
2571 hawkeye 793
   // set pulses per quarter note (internal resolution, 96 is standard)
794
   SEQ_BPM_PPQN_Set(96); // 384
2185 hawkeye 795
 
796
   // reset BPM tick
797
   SEQ_BPM_TickSet(0);
798
 
799
   return 0; // no error
800
}
801
// -------------------------------------------------------------------------------------------------
802
 
803
 
804
/**
805
 * Sets new song position (new_song_pos resolution: 16th notes)
806
 *
807
 */
808
static s32 seqSongPos(u16 new_song_pos)
809
{
810
   u32 new_tick = new_song_pos * (SEQ_BPM_PPQN_Get() / 4);
811
 
812
   portENTER_CRITICAL();
813
 
814
   // set new tick value
815
   SEQ_BPM_TickSet(new_tick);
816
 
2571 hawkeye 817
   // DEBUG_MSG("[SEQ] Setting new song position %u (-> %u ticks)\n", new_song_pos, new_tick);
2185 hawkeye 818
 
819
   // since timebase has been changed, ensure that Off-Events are played
820
   // (otherwise they will be played much later...)
821
   seqPlayOffEvents();
822
 
823
   portEXIT_CRITICAL();
824
 
825
   return 0; // no error
826
}
827
// -------------------------------------------------------------------------------------------------
828
 
829
 
830
/**
2571 hawkeye 831
 * Update BEAT LEDs / Clip positions
2185 hawkeye 832
 *
833
 */
834
static void seqUpdateBeatLEDs(u32 bpm_tick)
835
{
836
   static u8 lastLEDstate = 255;
837
 
838
   u16 ticksPerStep = SEQ_BPM_PPQN_Get() / 4;
839
 
840
   u8 beatled = (bpm_tick / ticksPerStep) % 4;
841
 
842
   if (beatled != lastLEDstate)
843
   {
844
      lastLEDstate = beatled;
845
 
2571 hawkeye 846
      MUTEX_DIGITALOUT_TAKE;
2185 hawkeye 847
      switch (beatled)
848
      {
849
      case 0:
2571 hawkeye 850
         oledBeatFlashState_ = (bpm_tick / (ticksPerStep * 4) % 4 == 0) ? 2 : 1; // flash background (strong/normal)
2185 hawkeye 851
         MIOS32_DOUT_PinSet(led_beat0, 1);
852
         MIOS32_DOUT_PinSet(led_beat1, 0);
853
         MIOS32_DOUT_PinSet(led_beat2, 0);
854
         MIOS32_DOUT_PinSet(led_beat3, 0);
855
         break;
856
      case 1:
857
         MIOS32_DOUT_PinSet(led_beat0, 0);
858
         MIOS32_DOUT_PinSet(led_beat1, 1);
859
         MIOS32_DOUT_PinSet(led_beat2, 0);
860
         MIOS32_DOUT_PinSet(led_beat3, 0);
861
         break;
862
      case 2:
863
         MIOS32_DOUT_PinSet(led_beat0, 0);
864
         MIOS32_DOUT_PinSet(led_beat1, 0);
865
         MIOS32_DOUT_PinSet(led_beat2, 1);
866
         MIOS32_DOUT_PinSet(led_beat3, 0);
867
         break;
868
      case 3:
869
         MIOS32_DOUT_PinSet(led_beat0, 0);
870
         MIOS32_DOUT_PinSet(led_beat1, 0);
871
         MIOS32_DOUT_PinSet(led_beat2, 0);
872
         MIOS32_DOUT_PinSet(led_beat3, 1);
873
         break;
874
      }
2571 hawkeye 875
      MUTEX_DIGITALOUT_GIVE;
2185 hawkeye 876
 
877
      // New step, Update clip positions
878
      u8 i;
2571 hawkeye 879
      for (i = 0; i < TRACKS; i++)
2187 hawkeye 880
      {
2571 hawkeye 881
         screenSetClipPosition(i, ((u32) (bpm_tick / ticksPerStep) % clipSteps_[i][activeScene_]));
2187 hawkeye 882
      }
2185 hawkeye 883
 
884
      // Set global song step (non-wrapping), e.g. for recording clips
885
      screenSetSongStep(bpm_tick / ticksPerStep);
886
   }
887
}
888
// -------------------------------------------------------------------------------------------------
889
 
890
 
891
/**
892
 * Perform a single bpm tick
893
 *
894
 */
895
static s32 seqTick(u32 bpm_tick)
896
{
2571 hawkeye 897
   tick_ = bpm_tick;
898
 
2185 hawkeye 899
   // send MIDI clock depending on ppqn
2571 hawkeye 900
   if ((bpm_tick % (SEQ_BPM_PPQN_Get()/24)) == 0)
2185 hawkeye 901
   {
2571 hawkeye 902
      // TODO: Don't send MIDI clock, when receiving external clock!
903
 
2185 hawkeye 904
      // DEBUG_MSG("Tick %d, SEQ BPM PPQN/24 %d", bpm_tick, SEQ_BPM_PPQN_Get()/24);
905
      MIDI_ROUTER_SendMIDIClockEvent(0xf8, bpm_tick);
906
   }
907
 
2571 hawkeye 908
   // perform synced clip mutes/unmutes (only at full steps)
909
   if ((bpm_tick % (SEQ_BPM_PPQN_Get()/4)) == 0)
910
   {
911
      performSyncedMutesAndUnmutes();
912
      performSyncedSceneChanges();
913
   }
2185 hawkeye 914
 
2571 hawkeye 915
   u8 clip;
916
   for (clip = 0; clip < TRACKS; clip++)
2185 hawkeye 917
   {
2571 hawkeye 918
      if (!trackMute_[clip])
2185 hawkeye 919
      {
2571 hawkeye 920
         u32 clipNoteTime = boundTickToClipSteps(bpm_tick, clip);
921
         u16 i;
2185 hawkeye 922
 
2571 hawkeye 923
         for (i=0; i < clipNotesSize_[clip][activeScene_]; i++)
2185 hawkeye 924
         {
2571 hawkeye 925
            if (clipNotes_[clip][activeScene_][i].length > 0) // not still being held/recorded!
2185 hawkeye 926
            {
2571 hawkeye 927
               if (/*quantize(clipNotes_[clip][activeScene_][i].tick,
928
                            clipQuantize_[clip][activeScene_],
929
                            getClipLengthInTicks(clip)) == clipNoteTime */
930
                     quantizeTransform(clip, i) == clipNoteTime)
931
               {
932
                  s16 note = clipNotes_[clip][activeScene_][i].note + clipTranspose_[clip][activeScene_];
933
                  note = note < 0 ? 0 : note;
934
                  note = note > 127 ? 127 : note;
2185 hawkeye 935
 
2571 hawkeye 936
                  mios32_midi_package_t package;
937
                  package.type = NoteOn;
938
                  package.event = NoteOn;
939
                  package.chn = trackMidiChannel_[clip];
940
                  package.note = note;
941
                  package.velocity = clipNotes_[clip][activeScene_][i].velocity;
942
                  hookMIDISendPackage(clip, package); // play NOW
2185 hawkeye 943
 
2571 hawkeye 944
                  package.type = NoteOff;
945
                  package.event = NoteOff;
946
                  package.note = note;
947
                  package.velocity = 0;
948
                  seqPlayEvent(clip, package, bpm_tick + clipNotes_[clip][activeScene_][i].length); // always play off event (schedule later)
949
               }
950
            }
2185 hawkeye 951
         }
952
      }
953
   }
954
 
955
   return 0; // no error
956
}
957
// -------------------------------------------------------------------------------------------------
958
 
959
 
960
/**
961
 * Schedule a MIDI event to be played at a given tick
962
 *
963
 */
964
static s32 seqPlayEvent(u8 clipNumber, mios32_midi_package_t midi_package, u32 tick)
965
{
966
   // DEBUG_MSG("[seqPlayEvent] silent:%d type:%d", ffwdSilentMode_, midi_package.type);
967
 
968
   // ignore all events in silent mode (for SEQ_SongPos function)
969
   // we could implement a more intelligent parser, which stores the sent CC/program change, etc...
970
   // and sends the last received values before restarting the song...
971
   if (ffwdSilentMode_)
972
      return 0;
973
 
974
   // In order to support an unlimited SysEx stream length, we pass them as single bytes directly w/o the sequencer!
975
   if (midi_package.type == 0xf)
976
   {
977
      hookMIDISendPackage(clipNumber, midi_package);
978
      return 0;
979
   }
980
 
981
   seq_midi_out_event_type_t event_type = SEQ_MIDI_OUT_OnEvent;
982
   if (midi_package.event == NoteOff || (midi_package.event == NoteOn && midi_package.velocity == 0))
983
      event_type = SEQ_MIDI_OUT_OffEvent;
984
 
2571 hawkeye 985
   // output events on "clipNumber" port (which are then redirected just in time by the SEQ back to hookMIDISendPackage)
2185 hawkeye 986
   u32 status = 0;
987
   status |= SEQ_MIDI_OUT_Send(clipNumber, midi_package, event_type, tick, 0);
988
 
989
   return status;
990
}
991
// -------------------------------------------------------------------------------------------------
992
 
993
 
994
/**
2186 hawkeye 995
 * Ignore track meta events
996
 *
997
 */
998
static s32 seqIgnoreMetaEvent(u8 clipNumber, u8 meta, u32 len, u8 *buffer, u32 tick)
999
{
1000
   return 0;
1001
}
1002
// -------------------------------------------------------------------------------------------------
1003
 
1004
 
1005
/**
2185 hawkeye 1006
 * Realtime output hook: called exactly, when the MIDI scheduler has a package to send
1007
 *
1008
 */
1009
static s32 hookMIDISendPackage(mios32_midi_port_t clipNumber, mios32_midi_package_t package)
1010
{
2571 hawkeye 1011
   // realtime events are already scheduled by MIDI_ROUTER_SendMIDIClockEvent()
1012
   if (package.evnt0 >= 0xf8)
2185 hawkeye 1013
   {
2571 hawkeye 1014
      MIOS32_MIDI_SendPackage(UART0, package);
1015
   }
1016
   else
1017
   {
1018
      mios32_midi_port_t port = trackMidiPort_[clipNumber];
1019
      MIOS32_MIDI_SendPackage(port, package);
2185 hawkeye 1020
 
2571 hawkeye 1021
      // screenFormattedFlashMessage("play %d on %x", package.note, port);
2185 hawkeye 1022
 
2571 hawkeye 1023
      // DEBUG: SEND TO USB0, too (can be removed)
1024
      port = USB0;
1025
      MIOS32_MIDI_SendPackage(port, package);
2185 hawkeye 1026
   }
1027
 
1028
   return 0; // no error
1029
}
1030
// -------------------------------------------------------------------------------------------------
1031
 
1032
 
1033
/**
2571 hawkeye 1034
 * Handle a stop request
2186 hawkeye 1035
 *
1036
 */
1037
void handleStop()
1038
{
2571 hawkeye 1039
   screenFormattedFlashMessage("Stopped");
2186 hawkeye 1040
 
2571 hawkeye 1041
   MUTEX_DIGITALOUT_TAKE;
1042
   MIOS32_DOUT_PinSet(led_startstop, 0);
1043
   MIOS32_DOUT_PinSet(led_armrecord, 0);
1044
   MUTEX_DIGITALOUT_GIVE;
2186 hawkeye 1045
 
2571 hawkeye 1046
   SEQ_BPM_Stop(); // stop sequencer
1047
 
1048
   // Clear "stuck" notes, that may still be depressed while stop has been hit
1049
   u8 i;
1050
   for (i=0; i<128; i++)
1051
      notePtrsOn_[i] = -1;
1052
 
2186 hawkeye 1053
   isRecording_ = 0;
1054
}
1055
// -------------------------------------------------------------------------------------------------
1056
 
1057
 
1058
/**
2185 hawkeye 1059
 * Control the play/stop button function
1060
 *
1061
 */
1062
s32 seqPlayStopButton(void)
1063
{
1064
  if (SEQ_BPM_IsRunning())
1065
  {
2186 hawkeye 1066
     handleStop();
2185 hawkeye 1067
  }
1068
  else
1069
  {
1070
     SEQ_BPM_CheckAutoMaster();
1071
 
1072
     // reset sequencer
1073
     seqReset(1);
1074
 
1075
     // start sequencer
1076
     SEQ_BPM_Start();
1077
 
2571 hawkeye 1078
     MUTEX_DIGITALOUT_TAKE;
2185 hawkeye 1079
     MIOS32_DOUT_PinSet(led_startstop, 1);
2571 hawkeye 1080
     MUTEX_DIGITALOUT_GIVE;
2186 hawkeye 1081
 
1082
     screenFormattedFlashMessage("Play");
2185 hawkeye 1083
  }
1084
 
1085
  return 0; // no error
1086
}
1087
// -------------------------------------------------------------------------------------------------
1088
 
1089
 
1090
/**
1091
 * To control the rec/stop button function
1092
 *
1093
 */
2571 hawkeye 1094
s32 seqArmButton(void)
2185 hawkeye 1095
{
2571 hawkeye 1096
  if (isRecording_ == 0)
2185 hawkeye 1097
  {
2571 hawkeye 1098
     screenFormattedFlashMessage("Armed");
1099
     isRecording_ = 1;
1100
 
1101
     MUTEX_DIGITALOUT_TAKE;
1102
     MIOS32_DOUT_PinSet(led_armrecord, 1);
1103
     MUTEX_DIGITALOUT_GIVE;
2185 hawkeye 1104
  }
1105
  else
1106
  {
2571 hawkeye 1107
     screenFormattedFlashMessage("Disarmed");
1108
     isRecording_ = 0;
2185 hawkeye 1109
 
2571 hawkeye 1110
     MUTEX_DIGITALOUT_TAKE;
1111
     MIOS32_DOUT_PinSet(led_armrecord, 0);
1112
     MUTEX_DIGITALOUT_GIVE;
2185 hawkeye 1113
  }
1114
 
1115
  return 0; // no error
1116
}
1117
// -------------------------------------------------------------------------------------------------
1118
 
1119
 
1120
/**
2571 hawkeye 1121
 * Fast forward
1122
 * is put on right encoder as "scrub"
2185 hawkeye 1123
 *
1124
 */
1125
s32 seqFFwdButton(void)
1126
{
1127
   u32 tick = SEQ_BPM_TickGet();
1128
   u32 ticks_per_step = SEQ_BPM_PPQN_Get() / 4;
1129
   u32 ticks_per_measure = ticks_per_step * 16;
1130
 
1131
   int measure = tick / ticks_per_measure;
1132
   int song_pos = 16 * (measure + 1);
1133
   if (song_pos > 65535)
1134
      song_pos = 65535;
1135
 
1136
   return seqSongPos(song_pos);
1137
}
1138
// -------------------------------------------------------------------------------------------------
1139
 
1140
 
1141
/**
2571 hawkeye 1142
 * Rewind
1143
 * is put on right encoder as "scrub"
2185 hawkeye 1144
 *
1145
 */
1146
s32 seqFRewButton(void)
1147
{
1148
   u32 tick = SEQ_BPM_TickGet();
1149
   u32 ticks_per_step = SEQ_BPM_PPQN_Get() / 4;
1150
   u32 ticks_per_measure = ticks_per_step * 16;
1151
 
1152
   int measure = tick / ticks_per_measure;
1153
   int song_pos = 16 * (measure - 1);
1154
   if (song_pos < 0)
1155
      song_pos = 0;
1156
 
1157
   return seqSongPos(song_pos);
1158
}
1159
// -------------------------------------------------------------------------------------------------
1160
 
1161
 
2571 hawkeye 1162
/**
1163
 * Initialize Loopa SEQ
1164
 *
1165
 */
1166
s32 seqInit()
1167
{
1168
   // play mode
1169
   seqClkLocked = 0;
2185 hawkeye 1170
 
2571 hawkeye 1171
   // record over USB0 and UART0/1
1172
   seqRecEnabledPorts_ = 0x01 | (0x03 << 4);
2185 hawkeye 1173
 
2571 hawkeye 1174
   // reset sequencer
1175
   seqReset(0);
1176
 
1177
   // init BPM generator
1178
   SEQ_BPM_Init(0);
1179
   SEQ_BPM_ModeSet(SEQ_BPM_MODE_Auto);
1180
   SEQ_BPM_Set(120.0);
1181
   bpm_ = 120;
1182
 
1183
   // scheduler should send packages to private hook
1184
   SEQ_MIDI_OUT_Callback_MIDI_SendPackage_Set(hookMIDISendPackage);
1185
 
1186
   // install seqPlayEvent callback for clipFetchEvents()
1187
   clipPlayEventCallback = seqPlayEvent;
1188
   clipMetaEventCallback = seqIgnoreMetaEvent;
1189
 
1190
   u8 i, j;
1191
   for (i = 0; i < TRACKS; i++)
1192
   {
1193
      trackMute_[i] = 0;
1194
      trackMidiPort_[i] = 0x20; // UART0 aka OUT1
1195
      trackMidiChannel_[i] = 0; // Channel 1
1196
      trackMuteToggleRequested_[i] = 0;
1197
 
1198
      for (j = 0; j < SCENES; j++)
1199
      {
1200
         clipQuantize_[i][j] = 1;
1201
         clipTranspose_[i][j] = 0;
1202
         clipScroll_[i][j] = 0;
1203
         clipStretch_[i][j] = 16;
1204
         clipSteps_[i][j] = 64;
1205
         clipNotesSize_[i][j] = 0;
1206
         clipActiveNote_[i][j] = 0;
1207
      }
1208
   }
1209
 
1210
   for (i=0; i<128; i++)
1211
      notePtrsOn_[i] = -1;
1212
 
1213
   // turn on LEDs for active track, scene and page
1214
   setActiveTrack(activeTrack_);
1215
   setActiveScene(activeScene_);
1216
   setActivePage(page_);
1217
 
1218
   return 0; // no error
1219
}
1220
// -------------------------------------------------------------------------------------------------
1221
 
1222
 
2185 hawkeye 1223
/**
2571 hawkeye 1224
 * SD Card available, initialize LOOPA, load session, prepare screen and menus
2184 hawkeye 1225
 *
1226
 */
1227
void loopaSDCardAvailable()
1228
{
1229
   loadSession(1); // -> XXX: load latest session with a free clip slot instead
2185 hawkeye 1230
   seqInit();
2571 hawkeye 1231
   trackMute_[0] = 0;
1232
   seqArmButton();
2184 hawkeye 1233
   screenShowLoopaLogo(0); // Disable logo, normal operations started
2571 hawkeye 1234
}
1235
// -------------------------------------------------------------------------------------------------
2185 hawkeye 1236
 
2571 hawkeye 1237
// -------------------------------------------------------------------------------------------------
1238
// --- PAGE COMMANDS -------------------------------------------------------------------------------
1239
// -------------------------------------------------------------------------------------------------
1240
 
1241
 
1242
/**
1243
 * Edit Clip Page: change clip length
1244
 *
1245
 */
1246
void editLen()
1247
{
1248
   command_ = command_ == COMMAND_CLIPLEN ? COMMAND_NONE : COMMAND_CLIPLEN;
2184 hawkeye 1249
}
1250
// -------------------------------------------------------------------------------------------------
1251
 
1252
 
1253
/**
2571 hawkeye 1254
 * Edit Clip Page: change clip quantization
1255
 *
1256
 */
1257
void editQuantize()
1258
{
1259
   command_ = command_ == COMMAND_QUANTIZE ? COMMAND_NONE : COMMAND_QUANTIZE;
1260
}
1261
// -------------------------------------------------------------------------------------------------
1262
 
1263
 
1264
/**
1265
 * Edit Clip Page: change clip transpose
1266
 *
1267
 */
1268
void editTranspose()
1269
{
1270
   command_ = command_ == COMMAND_TRANSPOSE ? COMMAND_NONE : COMMAND_TRANSPOSE;
1271
}
1272
// -------------------------------------------------------------------------------------------------
1273
 
1274
 
1275
/**
1276
 * Edit Clip Page: change clip scrolling
1277
 *
1278
 */
1279
void editScroll()
1280
{
1281
   command_ = command_ == COMMAND_SCROLL ? COMMAND_NONE : COMMAND_SCROLL;
1282
}
1283
// -------------------------------------------------------------------------------------------------
1284
 
1285
 
1286
/**
1287
 * Edit Clip Page: change clip stretching
1288
 *
1289
 */
1290
void editStretch()
1291
{
1292
   command_ = command_ == COMMAND_STRETCH ? COMMAND_NONE : COMMAND_STRETCH;
1293
}
1294
// -------------------------------------------------------------------------------------------------
1295
 
1296
 
1297
/**
1298
 * Edit Clip Page: Clear clip
1299
 *
1300
 */
1301
void editClear()
1302
{
1303
   command_ = command_ == COMMAND_CLEAR ? COMMAND_NONE : COMMAND_CLEAR;
1304
 
1305
   clipNotesSize_[activeTrack_][activeScene_] = 0;
1306
 
1307
   u8 i;
1308
   for (i=0; i<128; i++)
1309
      notePtrsOn_[i] = -1;
1310
 
1311
   screenFormattedFlashMessage("Clip %d cleared", activeTrack_ + 1);
1312
}
1313
// -------------------------------------------------------------------------------------------------
1314
 
1315
 
1316
/**
1317
 * Notes: change note position
1318
 *
1319
 */
1320
void notesPosition()
1321
{
1322
   command_ = command_ == COMMAND_POSITION ? COMMAND_NONE : COMMAND_POSITION;
1323
}
1324
// -------------------------------------------------------------------------------------------------
1325
 
1326
 
1327
/**
1328
 * Notes: change note value
1329
 *
1330
 */
1331
void notesNote()
1332
{
1333
   command_ = command_ == COMMAND_NOTE ? COMMAND_NONE : COMMAND_NOTE;
1334
}
1335
// -------------------------------------------------------------------------------------------------
1336
 
1337
 
1338
/**
1339
 * Notes: change note velocity
1340
 *
1341
 */
1342
void notesVelocity()
1343
{
1344
   command_ = command_ == COMMAND_VELOCITY ? COMMAND_NONE : COMMAND_VELOCITY;
1345
}
1346
// -------------------------------------------------------------------------------------------------
1347
 
1348
 
1349
/**
1350
 * Notes: change note length
1351
 *
1352
 */
1353
void notesLength()
1354
{
1355
   command_ = command_ == COMMAND_LENGTH ? COMMAND_NONE : COMMAND_LENGTH;
1356
}
1357
// -------------------------------------------------------------------------------------------------
1358
 
1359
 
1360
/**
1361
 * Notes: delete note
1362
 *
1363
 */
1364
void notesDeleteNote()
1365
{
1366
   command_ = command_ == COMMAND_DELETENOTE ? COMMAND_NONE : COMMAND_DELETENOTE;
1367
 
1368
   u16 activeNote = clipActiveNote_[activeTrack_][activeScene_];
1369
 
1370
   if (activeNote < clipNotesSize_[activeTrack_][activeScene_])
1371
   {
1372
      // only perform changes, if we are in range (still on the same clip)
1373
      clipNotes_[activeTrack_][activeScene_][activeNote].velocity = 0;
1374
   }
1375
}
1376
// -------------------------------------------------------------------------------------------------
1377
 
1378
 
1379
/**
1380
 * Midi Track Port: change clip output MIDI port
1381
 *
1382
 */
1383
void midiTrackPort()
1384
{
1385
   command_ = command_ == COMMAND_PORT ? COMMAND_NONE : COMMAND_PORT;
1386
}
1387
// -------------------------------------------------------------------------------------------------
1388
 
1389
 
1390
/**
1391
 * Midi Track Channel: change clip output MIDI channel
1392
 *
1393
 */
1394
void midiTrackChannel()
1395
{
1396
   command_ = command_ == COMMAND_CHANNEL ? COMMAND_NONE : COMMAND_CHANNEL;
1397
}
1398
// -------------------------------------------------------------------------------------------------
1399
 
1400
 
1401
/**
1402
 * Scan if selected sessionNumber_ is available on disk
1403
 *
1404
 */
1405
void diskScanSessionFileAvailable()
1406
{
1407
   MUTEX_SDCARD_TAKE;
1408
   sessionNumberToFilename_(sessionNumber_);
1409
   sessionExistsOnDisk_ = FILE_FileExists(filename_) == 1 ? 1 : 0;
1410
   MUTEX_SDCARD_GIVE;
1411
}
1412
// -------------------------------------------------------------------------------------------------
1413
 
1414
 
1415
/**
1416
 * Main Menu: Save session command
1417
 *
1418
 */
1419
void diskSave()
1420
{
1421
   command_ = COMMAND_NONE;
1422
 
1423
   saveSession(sessionNumber_);
1424
   diskScanSessionFileAvailable();
1425
 
2593 hawkeye 1426
   page_ = PAGE_MUTE;
2571 hawkeye 1427
   screenNotifyPageChanged();
1428
}
1429
// -------------------------------------------------------------------------------------------------
1430
 
1431
 
1432
/**
1433
 * Main Menu: Save session command
1434
 *
1435
 */
1436
void diskLoad()
1437
{
1438
   command_ = COMMAND_NONE;
1439
 
1440
   loadSession(sessionNumber_);
1441
 
2593 hawkeye 1442
   page_ = PAGE_MUTE;
2571 hawkeye 1443
   screenNotifyPageChanged();
1444
}
1445
// -------------------------------------------------------------------------------------------------
1446
 
1447
 
1448
/**
1449
 * Main Menu: Save session command
1450
 *
1451
 */
1452
void diskNew()
1453
{
1454
   command_ = COMMAND_NONE;
1455
 
1456
   seqInit();
2593 hawkeye 1457
   page_ = PAGE_MUTE;
2571 hawkeye 1458
   screenNotifyPageChanged();
1459
 
1460
   screenFormattedFlashMessage("A fresh start... :-)");
1461
}
1462
// -------------------------------------------------------------------------------------------------
1463
 
1464
 
1465
/**
1466
 * Main Menu: Change BPM
1467
 *
1468
 */
1469
void bpmBpm()
1470
{
1471
   command_ = command_ == COMMAND_BPM ? COMMAND_NONE : COMMAND_BPM;
1472
}
1473
// -------------------------------------------------------------------------------------------------
1474
 
1475
 
1476
/**
1477
 * Main Menu: enable disable display flashing to beat
1478
 *
1479
 */
1480
void bpmBpmflash()
1481
{
1482
   command_ = command_ == COMMAND_BPMFLASH ? COMMAND_NONE : COMMAND_BPMFLASH;
1483
}
1484
// -------------------------------------------------------------------------------------------------
1485
 
1486
 
1487
// -------------------------------------------------------------------------------------------------
1488
// --- EVENT DISPATCHING ---------------------------------------------------------------------------
1489
// -------------------------------------------------------------------------------------------------
1490
 
1491
/**
2184 hawkeye 1492
 * A buttonpress has occured
1493
 *
1494
 */
1495
void loopaButtonPressed(s32 pin)
1496
{
2571 hawkeye 1497
   DEBUG_MSG("Button: %d pressed\n", pin);
1498
 
2184 hawkeye 1499
   if (pin == sw_startstop)
1500
   {
2185 hawkeye 1501
      seqPlayStopButton();
2184 hawkeye 1502
   }
2185 hawkeye 1503
   else if (pin == sw_armrecord)
2184 hawkeye 1504
   {
2571 hawkeye 1505
      seqArmButton();
2184 hawkeye 1506
   }
2593 hawkeye 1507
   else if (pin == sw_shift)
1508
   {
1509
      if (screenIsInMenu())
1510
      {
1511
         page_ = PAGE_MUTE;
1512
      }
1513
      else
1514
      {
1515
         // normal mode "shift"
1516
         screenShowShift(1);
1517
      }
1518
   }
1519
   else if (pin == sw_menu)
1520
   {
1521
      /*if (screenIsInShift())
1522
      {
1523
 
1524
      }
1525
      else */
1526
      {
1527
         // normal mode "menu"
1528
         DEBUG_MSG("enter menu");
1529
         calcField();
1530
         screenShowMenu(1);
1531
      }
1532
   }
2571 hawkeye 1533
   else if (pin == sw_copy)
2184 hawkeye 1534
   {
2593 hawkeye 1535
      if (screenIsInMenu())
1536
      {
1537
         page_ = PAGE_TRACK;
1538
      }
1539
      else
1540
      {
1541
         // normal mode "copy"
1542
         copiedClipSteps_ = clipSteps_[activeTrack_][activeScene_];
1543
         copiedClipQuantize_ = clipQuantize_[activeTrack_][activeScene_];
1544
         copiedClipTranspose_ = clipTranspose_[activeTrack_][activeScene_];
1545
         copiedClipScroll_ = clipScroll_[activeTrack_][activeScene_];
1546
         copiedClipStretch_ = clipStretch_[activeTrack_][activeScene_];
1547
         memcpy(copiedClipNotes_, clipNotes_[activeTrack_][activeScene_], sizeof(copiedClipNotes_));
1548
         copiedClipNotesSize_ = clipNotesSize_[activeTrack_][activeScene_];
1549
         screenFormattedFlashMessage("copied clip to buffer");
1550
      }
2184 hawkeye 1551
   }
2571 hawkeye 1552
   else if (pin == sw_paste)
2184 hawkeye 1553
   {
2593 hawkeye 1554
      voxelFrame();
2577 hawkeye 1555
      // paste only, if we have a clip in memory
1556
      if (copiedClipSteps_ > 0)
1557
      {
1558
         clipSteps_[activeTrack_][activeScene_] = copiedClipSteps_;
1559
         clipQuantize_[activeTrack_][activeScene_] = copiedClipQuantize_;
1560
         clipTranspose_[activeTrack_][activeScene_] = copiedClipTranspose_;
1561
         clipScroll_[activeTrack_][activeScene_] = copiedClipScroll_;
1562
         clipStretch_[activeTrack_][activeScene_] = copiedClipStretch_;
1563
         memcpy(clipNotes_[activeTrack_][activeScene_], copiedClipNotes_, sizeof(copiedClipNotes_));
1564
         clipNotesSize_[activeTrack_][activeScene_] = copiedClipNotesSize_;
1565
         screenFormattedFlashMessage("pasted clip from buffer");
1566
      }
1567
      else
1568
         screenFormattedFlashMessage("no clip in buffer");
1569
 
2184 hawkeye 1570
   }
2593 hawkeye 1571
   else if (pin == sw_delete)
1572
   {
1573
       editClear(); // shortcut: clear track
1574
       command_ = COMMAND_NONE;
1575
   }
2571 hawkeye 1576
   else if (pin == sw_gp1)
2185 hawkeye 1577
   {
2571 hawkeye 1578
      switch (page_)
1579
      {
2593 hawkeye 1580
         case PAGE_MUTE:
2571 hawkeye 1581
            toggleMute(0);
1582
            break;
2593 hawkeye 1583
         case PAGE_CLIP:
2571 hawkeye 1584
            editLen();
1585
            break;
1586
         case PAGE_NOTES:
1587
            notesPosition();
1588
            break;
2593 hawkeye 1589
         case PAGE_TRACK:
2571 hawkeye 1590
            midiTrackPort();
1591
            break;
1592
         case PAGE_DISK:
1593
            diskSave();
1594
            break;
2593 hawkeye 1595
         case PAGE_TEMPO:
2571 hawkeye 1596
            bpmBpm();
1597
            break;
1598
      }
2185 hawkeye 1599
   }
2571 hawkeye 1600
   else if (pin == sw_gp2)
2185 hawkeye 1601
   {
2571 hawkeye 1602
      switch (page_)
1603
      {
2593 hawkeye 1604
         case PAGE_MUTE:
2571 hawkeye 1605
            toggleMute(1);
1606
            break;
2593 hawkeye 1607
         case PAGE_CLIP:
2571 hawkeye 1608
            editQuantize();
1609
            break;
1610
         case PAGE_NOTES:
1611
            notesNote();
1612
            break;
2593 hawkeye 1613
         case PAGE_TRACK:
2571 hawkeye 1614
            midiTrackChannel();
1615
            break;
1616
         case PAGE_DISK:
1617
            diskLoad();
1618
            break;
2593 hawkeye 1619
         case PAGE_TEMPO:
2571 hawkeye 1620
            bpmBpmflash();
1621
            break;
1622
      }
2185 hawkeye 1623
   }
2571 hawkeye 1624
   else if (pin == sw_gp3)
2185 hawkeye 1625
   {
2593 hawkeye 1626
      if (screenIsInMenu())
2571 hawkeye 1627
      {
2593 hawkeye 1628
         page_ = PAGE_CLIP;
2571 hawkeye 1629
      }
2593 hawkeye 1630
      else
1631
      {
1632
         // Normal GP3 page handling
1633
 
1634
         switch (page_)
1635
         {
1636
            case PAGE_MUTE:
1637
               toggleMute(2);
1638
               break;
1639
            case PAGE_CLIP:
1640
               editTranspose();
1641
               break;
1642
            case PAGE_NOTES:
1643
               notesVelocity();
1644
               break;
1645
            case PAGE_DISK:
1646
               diskNew();
1647
               break;
1648
         }
1649
      }
2185 hawkeye 1650
   }
2571 hawkeye 1651
   else if (pin == sw_gp4)
2185 hawkeye 1652
   {
2571 hawkeye 1653
      switch (page_)
1654
      {
2593 hawkeye 1655
         case PAGE_MUTE:
2571 hawkeye 1656
            toggleMute(3);
1657
            break;
2593 hawkeye 1658
         case PAGE_CLIP:
2571 hawkeye 1659
            editScroll();
1660
            break;
1661
         case PAGE_NOTES:
1662
            notesLength();
1663
            break;
1664
      }
2185 hawkeye 1665
   }
2571 hawkeye 1666
   else if (pin == sw_gp5)
2185 hawkeye 1667
   {
2571 hawkeye 1668
      switch (page_)
1669
      {
2593 hawkeye 1670
         case PAGE_MUTE:
2571 hawkeye 1671
            toggleMute(4);
1672
            break;
2593 hawkeye 1673
         case PAGE_CLIP:
2571 hawkeye 1674
            editStretch();
1675
            break;
1676
      }
2185 hawkeye 1677
   }
2571 hawkeye 1678
   else if (pin == sw_gp6)
2185 hawkeye 1679
   {
2571 hawkeye 1680
      switch (page_)
1681
      {
2593 hawkeye 1682
         case PAGE_MUTE:
2571 hawkeye 1683
            toggleMute(5);
1684
            break;
2593 hawkeye 1685
         case PAGE_CLIP:
2571 hawkeye 1686
            editClear();
1687
            break;
1688
         case PAGE_NOTES:
1689
            notesDeleteNote();
1690
            break;
1691
      }
2185 hawkeye 1692
   }
2571 hawkeye 1693
   else if (pin == sw_encoder2)
2185 hawkeye 1694
   {
2593 hawkeye 1695
      page_ = PAGE_MUTE; // shortcut back to track display
2571 hawkeye 1696
      command_ = COMMAND_NONE;
1697
      screenNotifyPageChanged();
2185 hawkeye 1698
   }
2184 hawkeye 1699
}
1700
// -------------------------------------------------------------------------------------------------
1701
 
1702
 
1703
/**
2593 hawkeye 1704
 * A button release has occured
1705
 *
1706
 */
1707
void loopaButtonReleased(s32 pin)
1708
{
1709
   if (screenIsInMenu() && pin == sw_menu)
1710
   {
1711
      DEBUG_MSG("leave menu");
1712
      screenShowMenu(0); // Left the menu by releasing the menu button
1713
   }
1714
 
1715
   if (screenIsInShift() && pin == sw_shift)
1716
   {
1717
      screenShowShift(0); // Left the shift overlay by releasing the shift button
1718
   }
1719
}
1720
 
1721
// -------------------------------------------------------------------------------------------------
1722
 
1723
 
1724
/**
2184 hawkeye 1725
 * An encoder has been turned
1726
 *
1727
 */
1728
void loopaEncoderTurned(s32 encoder, s32 incrementer)
1729
{
2571 hawkeye 1730
   incrementer = -incrementer;
1731
   DEBUG_MSG("[Encoder] %d turned, direction %d\n", encoder, incrementer);
1732
 
1733
   if (encoder == enc_scene_id)
2184 hawkeye 1734
   {
2571 hawkeye 1735
      if (incrementer < 0)
1736
      {
1737
         s8 newScene = sceneChangeRequested_ - 1;
1738
         if (newScene < 0)
1739
            newScene = 0;
2186 hawkeye 1740
 
2571 hawkeye 1741
         sceneChangeRequested_ = newScene;
1742
 
1743
         if (sceneChangeRequested_ == activeScene_ || !SEQ_BPM_IsRunning()) // if (after changing) now no change requested or not playing, switch at once
1744
            setActiveScene(newScene);
1745
      }
1746
      else
1747
      {
1748
         s8 newScene = sceneChangeRequested_ + 1;
1749
         if (newScene >= SCENES)
1750
            newScene = SCENES - 1;
1751
 
1752
         sceneChangeRequested_ = newScene;
1753
 
1754
         if (sceneChangeRequested_ == activeScene_ || !SEQ_BPM_IsRunning()) // if (after changing) now no change requested or not playing, switch at once
1755
            setActiveScene(newScene);
1756
      }
2184 hawkeye 1757
   }
2571 hawkeye 1758
 
1759
   if (encoder == enc_track_id)
1760
   {
1761
      if (page_ == PAGE_DISK) // Disk page - left encoder changes selected session number
1762
      {
1763
         s16 newSessionNumber = sessionNumber_ + incrementer;
1764
         newSessionNumber = newSessionNumber < 0 ? 0 : newSessionNumber;
1765
         sessionNumber_ = newSessionNumber;
1766
 
1767
         diskScanSessionFileAvailable();
1768
      }
1769
      else if (page_ == PAGE_NOTES) // Notes page -left encoder changes selected note
1770
      {
1771
         s16 newNote = clipActiveNote_[activeTrack_][activeScene_] += incrementer;
1772
         if (newNote < 0)
1773
            newNote = clipNotesSize_[activeTrack_][activeScene_] - 1;
1774
         if (newNote >= clipNotesSize_[activeTrack_][activeScene_])
1775
            newNote = 0;
1776
         clipActiveNote_[activeTrack_][activeScene_] = newNote;
1777
      }
1778
      else // all other pages - change active clip number
1779
      {
1780
         s8 newTrack = activeTrack_ + incrementer;
1781
         newTrack = newTrack < 0 ? 0 : newTrack;
1782
         setActiveTrack(newTrack >= TRACKS ? TRACKS-1 : newTrack);
1783
      }
1784
   }
1785
   else if (encoder == enc_page_id)
1786
   {
1787
      // switch through pages
1788
 
1789
      enum LoopaPage page = page_;
1790
 
2593 hawkeye 1791
      if (page == PAGE_MUTE && incrementer < 0)
1792
         page = PAGE_MUTE;
1793
      else if (page >= PAGE_TEMPO && incrementer > 0)
1794
         page = PAGE_TEMPO;
2571 hawkeye 1795
      else
1796
         page += incrementer;
1797
 
1798
      if (page == PAGE_DISK)
1799
         diskScanSessionFileAvailable();
1800
 
1801
      setActivePage(page);
1802
 
1803
      screenNotifyPageChanged();
1804
   }
1805
   else if (encoder == enc_data_id)
1806
   {
1807
      if (command_ == COMMAND_BPM)
1808
      {
1809
         bpm_ += incrementer;
1810
         if (bpm_ < 30)
1811
            bpm_ = 30;
1812
         if (bpm_ > 300)
1813
            bpm_ = 300;
1814
 
1815
         SEQ_BPM_Set(bpm_);
1816
      }
1817
      else if (command_ == COMMAND_CLIPLEN)
1818
      {
1819
         if (incrementer > 0)
1820
            clipSteps_[activeTrack_][activeScene_] *= 2;
1821
         else
1822
            clipSteps_[activeTrack_][activeScene_] /= 2;
1823
 
1824
         if (clipSteps_[activeTrack_][activeScene_] < 4)
1825
            clipSteps_[activeTrack_][activeScene_] = 4;
1826
 
1827
         if (clipSteps_[activeTrack_][activeScene_] > 128)
1828
            clipSteps_[activeTrack_][activeScene_] = 128;
1829
      }
1830
      else if (command_ == COMMAND_QUANTIZE)
1831
      {
1832
         if (incrementer > 0)
1833
            clipQuantize_[activeTrack_][activeScene_] *= 2;
1834
         else
1835
            clipQuantize_[activeTrack_][activeScene_] /= 2;
1836
 
1837
         if (clipQuantize_[activeTrack_][activeScene_] < 2)
1838
            clipQuantize_[activeTrack_][activeScene_] = 1;  // no quantization
1839
 
1840
         if (clipQuantize_[activeTrack_][activeScene_] == 2)
1841
            clipQuantize_[activeTrack_][activeScene_] = 3;  // 1/128th note quantization
1842
 
1843
         if (clipQuantize_[activeTrack_][activeScene_] > 384)
1844
            clipQuantize_[activeTrack_][activeScene_] = 384;
1845
      }
1846
      else if (command_ == COMMAND_TRANSPOSE)
1847
      {
1848
         if (incrementer > 0)
1849
            clipTranspose_[activeTrack_][activeScene_]++;
1850
         else
1851
            clipTranspose_[activeTrack_][activeScene_]--;
1852
 
1853
         if (clipTranspose_[activeTrack_][activeScene_] < -96)
1854
            clipTranspose_[activeTrack_][activeScene_] = -96;
1855
 
1856
         if (clipTranspose_[activeTrack_][activeScene_] > 96)
1857
            clipTranspose_[activeTrack_][activeScene_] = 96;
1858
      }
1859
      else if (command_ == COMMAND_SCROLL)
1860
      {
1861
         clipScroll_[activeTrack_][activeScene_] += incrementer;
1862
 
1863
         if (clipScroll_[activeTrack_][activeScene_] < -1024)
1864
            clipScroll_[activeTrack_][activeScene_] = -1024;
1865
 
1866
         if (clipScroll_[activeTrack_][activeScene_] > 1024)
1867
            clipScroll_[activeTrack_][activeScene_] = 1024;
1868
      }
1869
      else if (command_ == COMMAND_STRETCH)
1870
      {
1871
         s16 newStretch = clipStretch_[activeTrack_][activeScene_];
1872
         if (incrementer > 0)
1873
            newStretch *= 2;
1874
         else
1875
            newStretch /= 2;
1876
 
1877
         if (newStretch < 1)
1878
            newStretch = 1;
1879
 
1880
         if (newStretch > 128)
1881
            newStretch = 128;
1882
 
1883
         clipStretch_[activeTrack_][activeScene_] = newStretch;
1884
      }
1885
      else if (command_ == COMMAND_POSITION)
1886
      {
1887
         u16 activeNote = clipActiveNote_[activeTrack_][activeScene_];
1888
 
1889
         if (activeNote < clipNotesSize_[activeTrack_][activeScene_])
1890
         {
1891
            // only perform changes, if we are in range (still on the same clip)
1892
 
1893
            s16 newTick = clipNotes_[activeTrack_][activeScene_][activeNote].tick;
1894
            newTick += incrementer > 0 ? 24 : -24;
1895
            u32 clipLength = getClipLengthInTicks(activeTrack_);
1896
 
1897
            if (newTick < 0)
1898
               newTick += clipLength;
1899
 
1900
            if (newTick >= clipLength)
1901
               newTick -= clipLength;
1902
 
1903
            clipNotes_[activeTrack_][activeScene_][activeNote].tick = newTick;
1904
         }
1905
      }
1906
      else if (command_ == COMMAND_NOTE)
1907
      {
1908
         u16 activeNote = clipActiveNote_[activeTrack_][activeScene_];
1909
 
1910
         if (activeNote < clipNotesSize_[activeTrack_][activeScene_])
1911
         {
1912
            // only perform changes, if we are in range (still on the same clip)
1913
 
1914
            s16 newNote = clipNotes_[activeTrack_][activeScene_][activeNote].note;
1915
            newNote += incrementer;
1916
 
1917
            if (newNote < 1)
1918
               newNote = 1;
1919
 
1920
            if (newNote >= 127)
1921
               newNote = 127;
1922
 
1923
            clipNotes_[activeTrack_][activeScene_][activeNote].note = newNote;
1924
         }
1925
      }
1926
      else if (command_ == COMMAND_LENGTH)
1927
      {
1928
         u16 activeNote = clipActiveNote_[activeTrack_][activeScene_];
1929
 
1930
         if (activeNote < clipNotesSize_[activeTrack_][activeScene_])
1931
         {
1932
            // only perform changes, if we are in range (still on the same clip)
1933
 
1934
            s16 newLength = clipNotes_[activeTrack_][activeScene_][activeNote].length;
1935
            newLength += incrementer * 4;
1936
 
1937
            if (newLength <= 1)
1938
               newLength = 1;
1939
 
1940
            if (newLength >= 1536)
1941
               newLength = 1536;
1942
 
1943
            clipNotes_[activeTrack_][activeScene_][activeNote].length = newLength;
1944
         }
1945
      }
1946
      else if (command_ == COMMAND_VELOCITY)
1947
      {
1948
         u16 activeNote = clipActiveNote_[activeTrack_][activeScene_];
1949
 
1950
         if (activeNote < clipNotesSize_[activeTrack_][activeScene_])
1951
         {
1952
            // only perform changes, if we are in range (still on the same clip)
1953
 
1954
            s16 newVel = clipNotes_[activeTrack_][activeScene_][activeNote].velocity;
1955
            newVel += incrementer;
1956
 
1957
            if (newVel < 1)
1958
               newVel = 1;
1959
 
1960
            if (newVel >= 127)
1961
               newVel = 127;
1962
 
1963
            clipNotes_[activeTrack_][activeScene_][activeNote].velocity = newVel;
1964
         }
1965
      }
1966
      else if (command_ == COMMAND_DELETENOTE)
1967
      {
1968
         u16 activeNote = clipActiveNote_[activeTrack_][activeScene_];
1969
 
1970
         if (activeNote < clipNotesSize_[activeTrack_][activeScene_])
1971
         {
1972
            // only perform changes, if we are in range (still on the same clip)
1973
            clipNotes_[activeTrack_][activeScene_][activeNote].velocity = 0;
1974
         }
1975
      }
1976
      else if (command_ == COMMAND_PORT)
1977
      {
1978
         u8 newPort = trackMidiPort_[activeTrack_] += incrementer;
1979
 
1980
         newPort = newPort < 0x20 ? 0x20: newPort;
1981
         newPort = newPort > 0x23 ? 0x23 : newPort;
1982
 
1983
         trackMidiPort_[activeTrack_] = newPort;
1984
      }
1985
      else if (command_ == COMMAND_CHANNEL)
1986
      {
1987
         s8 newChannel = trackMidiChannel_[activeTrack_] += incrementer;
1988
         newChannel = newChannel > 15 ? 0 : newChannel;
1989
         newChannel = newChannel < 0 ? 15 : newChannel;
1990
 
1991
         trackMidiChannel_[activeTrack_] = newChannel;
1992
      }
1993
 
1994
   }
2184 hawkeye 1995
}
1996
// -------------------------------------------------------------------------------------------------
2571 hawkeye 1997
 
1998
 
1999
/**
2000
 * Record midi event
2001
 *
2002
 */
2003
void loopaRecord(mios32_midi_port_t port, mios32_midi_package_t midi_package)
2004
{
2005
   u16 clipNoteNumber = clipNotesSize_[activeTrack_][activeScene_];
2006
 
2007
   if (isRecording_ && SEQ_BPM_IsRunning())
2008
   {
2009
      u32 clipNoteTime = boundTickToClipSteps(tick_, activeTrack_);
2010
 
2011
      if (clipNoteNumber < MAXNOTES && midi_package.type == NoteOn && midi_package.velocity > 0)
2012
      {
2013
         clipNotes_[activeTrack_][activeScene_][clipNoteNumber].tick = clipNoteTime;
2014
         clipNotes_[activeTrack_][activeScene_][clipNoteNumber].length = 0; // not yet determined
2015
         clipNotes_[activeTrack_][activeScene_][clipNoteNumber].note = midi_package.note;
2016
         clipNotes_[activeTrack_][activeScene_][clipNoteNumber].velocity = midi_package.velocity;
2017
 
2018
         if (notePtrsOn_[midi_package.note] >= 0)
2019
            screenFormattedFlashMessage("dbg: note alrdy on");
2020
 
2021
         notePtrsOn_[midi_package.note] = clipNoteNumber;
2022
 
2023
         // screenFormattedFlashMessage("Note %d on - ptr %d", midi_package.note, clipNoteNumber);
2024
         clipNotesSize_[activeTrack_][activeScene_]++;
2025
      }
2026
      else if (midi_package.type == NoteOff || (midi_package.type == NoteOn && midi_package.velocity == 0))
2027
      {
2028
         s16 notePtr = notePtrsOn_[midi_package.note];
2029
 
2030
         if (notePtr >= 0)
2031
         {
2032
            s32 len = clipNoteTime - clipNotes_[activeTrack_][activeScene_][notePtr].tick;
2033
 
2034
            if (len == 0)
2035
               len = 1;
2036
 
2037
            if (len < 0)
2038
               len += getClipLengthInTicks(activeTrack_);
2039
 
2040
            // screenFormattedFlashMessage("o %d - p %d - l %d", midi_package.note, notePtr, len);
2041
            clipNotes_[activeTrack_][activeScene_][notePtr].length = len;
2042
         }
2043
         notePtrsOn_[midi_package.note] = -1;
2044
      }
2045
   }
2046
}
2047
// -------------------------------------------------------------------------------------------------