Subversion Repositories svn.mios32

Rev

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

Rev Author Line No. Line
2182 hawkeye 1
// $Id: seq.c 1942 2014-01-26 21:25:53Z tk $
2
/*
3
 * Sequencer Routines
4
 *
5
 * ==========================================================================
6
 *
7
 *  Copyright (C) 2008 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 <string.h>
20
 
21
#include "tasks.h"
22
#include <seq_bpm.h>
23
#include <seq_midi_out.h>
24
#include <midi_port.h>
25
#include <file.h>
26
#include <osc_client.h>
27
#include <midi_router.h>
28
 
29
#include <mid_parser.h>
30
 
31
#include "hardware.h"
32
#include "seq.h"
2183 hawkeye 33
#include "screen.h"
2182 hawkeye 34
#include "voxelspace.h"
35
 
36
 
37
/////////////////////////////////////////////////////////////////////////////
38
// for optional debugging messages via MIDI
39
/////////////////////////////////////////////////////////////////////////////
2184 hawkeye 40
#define DEBUG_VERBOSE_LEVEL 1
2182 hawkeye 41
#define DEBUG_MSG MIOS32_MIDI_SendDebugMessage
42
 
43
 
44
/////////////////////////////////////////////////////////////////////////////
45
// Local definitions
46
/////////////////////////////////////////////////////////////////////////////
47
 
48
// how much time has to be bridged between prefetch cycles (time in mS)
49
#define PREFETCH_TIME_MS 50 // mS
50
 
51
 
52
/////////////////////////////////////////////////////////////////////////////
53
// Local prototypes
54
/////////////////////////////////////////////////////////////////////////////
55
 
56
static s32 SEQ_PlayOffEvents(void);
57
static s32 SEQ_SongPos(u16 new_song_pos);
58
static s32 SEQ_Tick(u32 bpm_tick);
59
static s32 SEQ_CheckSongFinished(u32 bpm_tick);
60
 
61
static s32 Hook_MIDI_SendPackage(mios32_midi_port_t port, mios32_midi_package_t package);
62
 
63
s32 SEQ_PlayNextFile(s8 next);
64
 
65
static s32 SEQ_PlayEvent(u8 track, mios32_midi_package_t midi_package, u32 tick);
66
static s32 SEQ_PlayMeta(u8 track, u8 meta, u32 len, u8 *buffer, u32 tick);
67
 
68
 
69
/////////////////////////////////////////////////////////////////////////////
70
// Global variables
71
/////////////////////////////////////////////////////////////////////////////
72
 
73
u16 seq_play_enabled_ports;
74
u16 seq_rec_enabled_ports;
75
 
76
u8 seq_play_enable_dout;
77
u8 seq_rec_enable_din;
78
 
79
 
80
/////////////////////////////////////////////////////////////////////////////
81
// Local variables
82
/////////////////////////////////////////////////////////////////////////////
83
 
84
// for FFWD function
85
static u8 ffwd_silent_mode;
86
 
87
// next tick at which the prefetch should take place
88
static u32 next_prefetch;
89
 
90
// end of file reached
91
static u32 end_of_file;
92
 
93
// already prefetched ticks
94
static u32 prefetch_offset;
95
 
96
// request to play the next file
97
static s8 next_file_req;
98
 
99
// the MIDI play mode
100
static u8 midi_play_mode;
101
 
102
// pause mode
103
static u8 seq_pause;
104
 
105
// lock BPM, so that it can't be changed from MIDI player
106
static u8 seq_clk_locked;
107
 
108
/////////////////////////////////////////////////////////////////////////////
109
// Initialisation
110
/////////////////////////////////////////////////////////////////////////////
111
s32 SEQ_Init(u32 mode)
112
{
113
  // play mode
114
  midi_play_mode = SEQ_MIDI_PLAY_MODE_ALL;
115
  seq_clk_locked = 0;
116
 
117
  // play over USB0 and UART0/1
118
  seq_play_enabled_ports = 0x01 | (0x03 << 4);
119
 
120
  // record over USB0 and UART0/1
121
  seq_rec_enabled_ports = 0x01 | (0x03 << 4);
122
 
123
  // play/record over DOUT/DIN
124
  seq_play_enable_dout = 0;
125
  seq_rec_enable_din = 0;
126
 
127
  // init MIDI file handler
128
  MID_FILE_Init(0);
129
 
130
  // init MIDI parser module
131
  MID_PARSER_Init(0);
132
 
133
  // install callback functions
134
  MID_PARSER_InstallEventCallbacks(&SEQ_PlayEvent, &SEQ_PlayMeta);
135
 
136
  // reset sequencer
137
  SEQ_Reset(0);
138
 
139
  // start with pause after power-on
140
  SEQ_SetPauseMode(1);
141
 
142
  // init BPM generator
143
  SEQ_BPM_Init(0);
144
  SEQ_BPM_Set(120.0);
145
 
146
  // scheduler should send packages to private hook
147
  SEQ_MIDI_OUT_Callback_MIDI_SendPackage_Set(Hook_MIDI_SendPackage);
148
 
149
  return 0; // no error
150
}
151
 
152
/////////////////////////////////////////////////////////////////////////////
153
// set/get Clock mode
154
// adds a fourth mode which locks the BPM so that it can't be modified by the MIDI file
155
/////////////////////////////////////////////////////////////////////////////
156
u8 SEQ_ClockModeGet(void)
157
{
2184 hawkeye 158
  if (seq_clk_locked)
159
     return 3;
2182 hawkeye 160
 
161
  return SEQ_BPM_ModeGet();
162
}
163
 
164
s32 SEQ_ClockModeSet(u8 mode)
165
{
2184 hawkeye 166
  if (mode > 3)
167
     return -1; // invalid mode
2182 hawkeye 168
 
2184 hawkeye 169
  if( mode == 3 )
170
  {
171
     SEQ_BPM_ModeSet(SEQ_BPM_MODE_Master);
172
     seq_clk_locked = 1;
2182 hawkeye 173
  }
2184 hawkeye 174
  else
175
  {
176
     SEQ_BPM_ModeSet(mode);
177
     seq_clk_locked = 0;
178
  }
2182 hawkeye 179
 
180
  return 0; // no error
181
}
182
 
183
/////////////////////////////////////////////////////////////////////////////
184
// set/get MIDI play mode
185
/////////////////////////////////////////////////////////////////////////////
186
s32 SEQ_MidiPlayModeGet(void)
187
{
188
  return midi_play_mode;
189
}
190
 
191
s32 SEQ_MidiPlayModeSet(u8 mode)
192
{
193
  if( mode >= SEQ_MIDI_PLAY_MODE_NUM )
194
    return -1;
195
 
196
  midi_play_mode = mode;
197
 
198
  return 0; // no error
199
}
200
 
201
 
202
/////////////////////////////////////////////////////////////////////////////
203
// this sequencer handler is called periodically to check for new requests
204
// from BPM generator
205
/////////////////////////////////////////////////////////////////////////////
206
s32 SEQ_Handler(void)
207
{
208
   // a lower priority task requested to play the next file
209
   if( next_file_req != 0 )
210
   {
211
      SEQ_PlayNextFile(next_file_req & (s8)~0x40);
212
      next_file_req = 0;
213
   };
214
 
215
 
216
   // handle BPM requests
217
   u8 num_loops = 0;
218
   u8 again = 0;
219
   do
220
   {
221
      ++num_loops;
222
 
223
      // note: don't remove any request check - clocks won't be propagated
224
      // so long any Stop/Cont/Start/SongPos event hasn't been flagged to the sequencer
2185 hawkeye 225
      if (SEQ_BPM_ChkReqStop())
2182 hawkeye 226
      {
227
         SEQ_PlayOffEvents();
228
         MID_FILE_SetRecordMode(0);
229
 
230
         MIDI_ROUTER_SendMIDIClockEvent(0xfc, 0);
231
      }
232
 
2185 hawkeye 233
      if (SEQ_BPM_ChkReqCont())
2182 hawkeye 234
      {
235
         // release pause mode
236
         SEQ_SetPauseMode(0);
237
 
238
         MIDI_ROUTER_SendMIDIClockEvent(0xfb, 0);
239
      }
240
 
2185 hawkeye 241
      if (SEQ_BPM_ChkReqStart())
2182 hawkeye 242
      {
243
         MIDI_ROUTER_SendMIDIClockEvent(0xfa, 0);
244
         SEQ_Reset(1);
245
         SEQ_SongPos(0);
246
      }
247
 
248
      u16 new_song_pos;
2185 hawkeye 249
      if (SEQ_BPM_ChkReqSongPos(&new_song_pos))
2182 hawkeye 250
      {
251
         SEQ_SongPos(new_song_pos);
252
      }
253
 
254
 
255
      u32 bpm_tick;
2185 hawkeye 256
      if (SEQ_BPM_ChkReqClk(&bpm_tick) > 0)
2182 hawkeye 257
      {
258
         if (!MID_FILE_RecordingEnabled())
259
         {
260
            // check if song is finished
261
            if( SEQ_CheckSongFinished(bpm_tick) >= 1 )
262
            {
263
               bpm_tick = 0;
264
            }
265
 
266
            // set initial BPM according to MIDI spec
2185 hawkeye 267
            if (bpm_tick == 0 && !seq_clk_locked)
2182 hawkeye 268
               SEQ_BPM_Set(120.0);
269
 
2185 hawkeye 270
            if (bpm_tick == 0) // send start (again) to synchronize with new MIDI songs
2182 hawkeye 271
               MIDI_ROUTER_SendMIDIClockEvent(0xfa, 0);
272
 
273
            again = 1; // check all requests again after execution of this part
274
            SEQ_Tick(bpm_tick);
275
         }
276
      }
277
   } while (again && num_loops < 10);
278
 
279
   return 0; // no error
280
}
281
 
282
 
283
/////////////////////////////////////////////////////////////////////////////
284
// This function plays all "off" events
285
// Should be called on sequencer reset/restart/pause to avoid hanging notes
286
/////////////////////////////////////////////////////////////////////////////
287
static s32 SEQ_PlayOffEvents(void)
288
{
289
  // play "off events"
290
  SEQ_MIDI_OUT_FlushQueue();
291
 
292
  // send Note Off to all channels
293
  // TODO: howto handle different ports?
294
  // TODO: should we also send Note Off events? Or should we trace Note On events and send Off if required?
295
  int chn;
296
  mios32_midi_package_t midi_package;
297
  midi_package.type = CC;
298
  midi_package.event = CC;
299
  midi_package.evnt2 = 0;
300
  for(chn=0; chn<16; ++chn) {
301
    midi_package.chn = chn;
302
    midi_package.evnt1 = 123; // All Notes Off
303
    Hook_MIDI_SendPackage(UART0, midi_package);
304
    midi_package.evnt1 = 121; // Controller Reset
305
    Hook_MIDI_SendPackage(UART0, midi_package);
306
  }
307
 
308
  return 0; // no error
309
}
310
 
311
 
312
/////////////////////////////////////////////////////////////////////////////
313
// Resets song position of sequencer
314
/////////////////////////////////////////////////////////////////////////////
315
s32 SEQ_Reset(u8 play_off_events)
316
{
317
  // since timebase has been changed, ensure that Off-Events are played
318
  // (otherwise they will be played much later...)
319
  if( play_off_events )
320
    SEQ_PlayOffEvents();
321
 
322
  // release pause and FFWD mode
323
  SEQ_SetPauseMode(0);
324
  ffwd_silent_mode = 0;
325
  next_prefetch = 0;
326
  end_of_file = 0;
327
  prefetch_offset = 0;
328
 
329
  // set initial BPM (according to MIDI file spec)
330
  SEQ_BPM_PPQN_Set(384); // not specified
331
  //SEQ_BPM_Set(120.0);
332
  // will be done at tick 0 to avoid overwrite in record mode!
333
 
334
  // reset BPM tick
335
  SEQ_BPM_TickSet(0);
336
 
337
  // restart song
338
  MID_PARSER_RestartSong();
339
 
340
  return 0; // no error
341
}
342
 
343
 
344
/////////////////////////////////////////////////////////////////////////////
345
// Sets new song position (new_song_pos resolution: 16th notes)
346
/////////////////////////////////////////////////////////////////////////////
347
static s32 SEQ_SongPos(u16 new_song_pos)
348
{
2184 hawkeye 349
   if( MID_FILE_RecordingEnabled() )
350
      return 0; // nothing to do
2182 hawkeye 351
 
2184 hawkeye 352
   u32 new_tick = new_song_pos * (SEQ_BPM_PPQN_Get() / 4);
2182 hawkeye 353
 
2184 hawkeye 354
   portENTER_CRITICAL();
2182 hawkeye 355
 
2184 hawkeye 356
   // set new tick value
357
   SEQ_BPM_TickSet(new_tick);
2182 hawkeye 358
 
2184 hawkeye 359
   DEBUG_MSG("[SEQ] Setting new song position %u (-> %u ticks)\n", new_song_pos, new_tick);
2182 hawkeye 360
 
2184 hawkeye 361
   // since timebase has been changed, ensure that Off-Events are played
362
   // (otherwise they will be played much later...)
363
   SEQ_PlayOffEvents();
2182 hawkeye 364
 
2184 hawkeye 365
   // restart song
366
   MID_PARSER_RestartSong();
2182 hawkeye 367
 
2184 hawkeye 368
   // release pause
369
   u8 pause = SEQ_PauseEnabled();
370
   SEQ_SetPauseMode(0);
2182 hawkeye 371
 
2184 hawkeye 372
   if( new_song_pos > 1 )
373
   {
374
      // (silently) fast forward to requested position
375
      ffwd_silent_mode = 1;
376
      MID_PARSER_FetchEvents(0, new_tick-1);
377
      ffwd_silent_mode = 0;
378
   }
2182 hawkeye 379
 
2184 hawkeye 380
   // when do we expect the next prefetch:
381
   end_of_file = 0;
382
   next_prefetch = new_tick;
383
   prefetch_offset = new_tick;
2182 hawkeye 384
 
2184 hawkeye 385
   // restore pause mode
386
   SEQ_SetPauseMode(pause);
2182 hawkeye 387
 
2184 hawkeye 388
   portEXIT_CRITICAL();
2182 hawkeye 389
 
2184 hawkeye 390
   return 0; // no error
2182 hawkeye 391
}
392
 
393
 
394
/////////////////////////////////////////////////////////////////////////////
395
// Plays the given .mid file
396
/////////////////////////////////////////////////////////////////////////////
397
s32 SEQ_PlayFile(char *midifile)
398
{
2184 hawkeye 399
   SEQ_BPM_Stop(); // stop BPM generator
2182 hawkeye 400
 
2184 hawkeye 401
   // play off events before loading new file
402
   SEQ_PlayOffEvents();
2182 hawkeye 403
 
2184 hawkeye 404
   // reset BPM tick (to ensure that next file will start at 0 if we are currently in pause mode)
405
   SEQ_BPM_TickSet(0);
406
   end_of_file = next_prefetch = prefetch_offset = 0;
2182 hawkeye 407
 
2184 hawkeye 408
   if (MID_FILE_open(midifile))
409
   {
410
      // try to open next file
411
      DEBUG_MSG("[SEQ] file %s cannot be opened (wrong directory?)\n", midifile);
412
      return -1; // file cannot be opened
413
   }
414
   if (MID_PARSER_Read() < 0)
415
   {
416
      // read file, stop on failure
417
      DEBUG_MSG("[SEQ] file %s is invalid!\n", midifile);
418
      return -2; // file is invalid
419
   }
2182 hawkeye 420
 
2184 hawkeye 421
   // restart BPM generator if not in pause mode
422
   if (!SEQ_PauseEnabled())
423
      SEQ_BPM_Start();
2182 hawkeye 424
 
2184 hawkeye 425
   strcpy(screenMode, "Play");
426
   strcpy(screenFile , midifile);
2183 hawkeye 427
 
2184 hawkeye 428
   return 0; // no error
2182 hawkeye 429
}
430
 
431
 
432
/////////////////////////////////////////////////////////////////////////////
433
// Plays the first .mid file if next == 0, the next file if next > 0, the
434
// 0: plays the current .mid file
435
// 1: plays the next .mid file
436
// -1: plays the previous .mid file
437
/////////////////////////////////////////////////////////////////////////////
438
s32 SEQ_PlayNextFile(s8 next)
439
{
440
  char next_file[13];
441
  next_file[0] = 0;
442
 
2184 hawkeye 443
  // reinstall callback functions
444
  MID_PARSER_InstallEventCallbacks(&SEQ_PlayEvent, &SEQ_PlayMeta);
445
 
446
 
447
  if( next == 0 && MID_FILE_UI_NameGet()[0] != 0 )
448
  {
449
     memcpy(next_file, MID_FILE_UI_NameGet(), 13);
450
     DEBUG_MSG("[SEQ] play current file '%s'\n", next_file);
2182 hawkeye 451
  }
2184 hawkeye 452
  else if (next < 0 &&
453
           (MID_FILE_FindPrev(MID_FILE_UI_NameGet(), next_file) == 1 ||
454
            MID_FILE_FindPrev(NULL, next_file) == 1))
455
  {
456
     // if previous file not found, try last file
457
     DEBUG_MSG("[SEQ] previous file found '%s'\n", next_file);
458
  }
459
  else
460
     if (MID_FILE_FindNext(next ? MID_FILE_UI_NameGet() : NULL, next_file) == 1 ||
461
         MID_FILE_FindNext(NULL, next_file) == 1)
462
     {
463
        // if next file not found, try first file
464
        DEBUG_MSG("[SEQ] next file found '%s'\n", next_file);
465
     }
2182 hawkeye 466
 
2184 hawkeye 467
  if( next_file[0] == 0 )
468
  {
469
     if( next < 0 )
470
        return 0; // ignore silently
2182 hawkeye 471
 
2184 hawkeye 472
     SEQ_BPM_Stop();           // stop BPM generator
2182 hawkeye 473
 
2184 hawkeye 474
     DEBUG_MSG("[SEQ] no file found\n");
475
     return -1; // file not found
2182 hawkeye 476
  }
2184 hawkeye 477
  else
478
  {
479
     SEQ_PlayFile(next_file);
480
  }
2182 hawkeye 481
 
482
  return 0; // no error
483
}
484
 
485
 
486
/////////////////////////////////////////////////////////////////////////////
487
// Allows to request to play the next file from a lower priority task
488
// 0: request first
489
// 1: request next
490
// -1: request previous
491
//
492
// if force is set, the next/previous song will be played regardless of current MIDI play mode
493
/////////////////////////////////////////////////////////////////////////////
494
s32 SEQ_PlayFileReq(s8 next, u8 force)
495
{
2184 hawkeye 496
  if (force || !next || midi_play_mode == SEQ_MIDI_PLAY_MODE_ALL)
497
  {
498
     // stop generator
499
     SEQ_BPM_Stop();
2182 hawkeye 500
 
2184 hawkeye 501
     // request next file
502
     next_file_req = next | 0x40; // ensure that next_file is always != 0
2182 hawkeye 503
  }
2184 hawkeye 504
  else
505
  {
506
     // play current MIDI file again
507
     SEQ_Reset(1);
508
     SEQ_SongPos(0);
509
  }
2182 hawkeye 510
 
511
  return 0; // no error
512
}
513
 
514
 
515
/////////////////////////////////////////////////////////////////////////////
516
// enables/disables pause
517
/////////////////////////////////////////////////////////////////////////////
518
s32 SEQ_SetPauseMode(u8 enable)
519
{
2184 hawkeye 520
   seq_pause = enable;
521
   return 0; // no error
2182 hawkeye 522
}
523
 
524
/////////////////////////////////////////////////////////////////////////////
525
// returns 1 if pause enabled
526
/////////////////////////////////////////////////////////////////////////////
527
s32 SEQ_PauseEnabled(void)
528
{
2184 hawkeye 529
   return seq_pause;
2182 hawkeye 530
}
531
 
532
 
533
 
2183 hawkeye 534
 
2184 hawkeye 535
/**
536
 * performs a single bpm tick
537
 *
538
 * idea: MID_FILE has access to all 8 clip files and current MID positions stored
539
 *       if a clip is unmuted, ask MID_PARSER to prefetch from respective clip and
540
 *       insert into sequencer
541
 *
542
 */
2182 hawkeye 543
static s32 SEQ_Tick(u32 bpm_tick)
544
{
2184 hawkeye 545
   // send MIDI clock depending on ppqn
546
   if( (bpm_tick % (SEQ_BPM_PPQN_Get()/24)) == 0 )
547
   {
548
      // DEBUG_MSG("Tick %d, SEQ BPM PPQN/24 %d", bpm_tick, SEQ_BPM_PPQN_Get()/24);
549
      MIDI_ROUTER_SendMIDIClockEvent(0xf8, bpm_tick);
550
   }
2182 hawkeye 551
 
2184 hawkeye 552
   if (!end_of_file && bpm_tick >= next_prefetch)
553
   {
554
      // get number of prefetch ticks depending on current BPM
555
      u32 prefetch_ticks = SEQ_BPM_TicksFor_mS(PREFETCH_TIME_MS);
2182 hawkeye 556
 
2184 hawkeye 557
      if (bpm_tick >= prefetch_offset)
558
      {
559
         // buffer underrun - fetch more!
560
         prefetch_ticks += (bpm_tick - prefetch_offset);
561
         next_prefetch = bpm_tick; // ASAP
562
      }
563
      else if ((prefetch_offset - bpm_tick) < prefetch_ticks)
564
      {
565
         // close to a buffer underrun - fetch more!
566
         prefetch_ticks *= 2;
567
         next_prefetch = bpm_tick; // ASAP
568
      }
569
      else
570
      {
571
         next_prefetch += prefetch_ticks;
572
      }
2182 hawkeye 573
 
574
#if DEBUG_VERBOSE_LEVEL >= 3
2184 hawkeye 575
      DEBUG_MSG("[SEQ] Prefetch started at tick %u (prefetching %u..%u)\n", bpm_tick, prefetch_offset, prefetch_offset+prefetch_ticks-1);
2182 hawkeye 576
#endif
577
 
2184 hawkeye 578
      if (MID_PARSER_FetchEvents(prefetch_offset, prefetch_ticks) == 0)
579
      {
580
         end_of_file = 1;
581
      }
582
      else
583
      {
584
         prefetch_offset += prefetch_ticks;
585
      }
2182 hawkeye 586
 
587
#if DEBUG_VERBOSE_LEVEL >= 3
2184 hawkeye 588
      DEBUG_MSG("[SEQ] Prefetch finished at tick %u\n", SEQ_BPM_TickGet());
2182 hawkeye 589
#endif
2184 hawkeye 590
   }
2182 hawkeye 591
 
2184 hawkeye 592
   return 0; // no error
2182 hawkeye 593
}
594
 
595
 
596
/////////////////////////////////////////////////////////////////////////////
597
// handles song restart
598
// returns 1 if song has been restarted, otherwise 0
599
/////////////////////////////////////////////////////////////////////////////
600
static s32 SEQ_CheckSongFinished(u32 bpm_tick)
601
{
602
  // synchronized switch to next file
2184 hawkeye 603
  if (end_of_file && ((bpm_tick+1) % SEQ_BPM_PPQN_Get()) == 0)
604
  {
605
     if( midi_play_mode == SEQ_MIDI_PLAY_MODE_SINGLE )
606
     {
607
        DEBUG_MSG("[SEQ] End of song reached after %u ticks - stopping sequencer!\n", bpm_tick);
2182 hawkeye 608
 
2184 hawkeye 609
        SEQ_BPM_Stop();
610
        SEQ_Reset(1);
611
        SEQ_SetPauseMode(1);
612
     }
613
     else if( midi_play_mode == SEQ_MIDI_PLAY_MODE_SINGLE_LOOP )
614
     {
615
        DEBUG_MSG("[SEQ] End of song reached after %u ticks - restarting song!\n", bpm_tick);
616
        SEQ_Reset(1);
617
     }
618
     else
619
     {
620
        DEBUG_MSG("[SEQ] End of song reached after %u ticks - loading next file!\n", bpm_tick);
2182 hawkeye 621
 
2184 hawkeye 622
        SEQ_PlayFileReq(1, 0);
623
     }
2182 hawkeye 624
 
2184 hawkeye 625
     return 1;
2182 hawkeye 626
  }
627
 
628
  return 0; // no error
629
}
630
 
631
/////////////////////////////////////////////////////////////////////////////
632
// called when a MIDI event should be played at a given tick
633
/////////////////////////////////////////////////////////////////////////////
634
static s32 SEQ_PlayEvent(u8 track, mios32_midi_package_t midi_package, u32 tick)
635
{
636
  // ignore all events in silent mode (for SEQ_SongPos function)
637
  // we could implement a more intelligent parser, which stores the sent CC/program change, etc...
638
  // and sends the last received values before restarting the song...
2184 hawkeye 639
  if (ffwd_silent_mode)
640
     return 0;
2182 hawkeye 641
 
642
  // In order to support an unlimited SysEx stream length, we pass them as single bytes directly w/o the sequencer!
2184 hawkeye 643
  if (midi_package.type == 0xf)
644
  {
645
     Hook_MIDI_SendPackage(UART0, midi_package);
646
     return 0;
2182 hawkeye 647
  }
648
 
649
  // Voxelspace note rendering
2184 hawkeye 650
  if (midi_package.event == NoteOn && midi_package.velocity > 0)
2182 hawkeye 651
     voxelNoteOn(midi_package.note, midi_package.velocity);
652
 
2184 hawkeye 653
  if (midi_package.event == NoteOff || (midi_package.event == NoteOn && midi_package.velocity == 0))
2182 hawkeye 654
     voxelNoteOff(midi_package.note);
655
 
656
  seq_midi_out_event_type_t event_type = SEQ_MIDI_OUT_OnEvent;
2184 hawkeye 657
  if (midi_package.event == NoteOff || (midi_package.event == NoteOn && midi_package.velocity == 0))
658
     event_type = SEQ_MIDI_OUT_OffEvent;
2182 hawkeye 659
 
660
  // output events on UART0 port
661
  u32 status = 0;
662
  status |= SEQ_MIDI_OUT_Send(UART0, midi_package, event_type, tick, 0);
663
 
2184 hawkeye 664
  /// DEBUG_MSG("in SEQ_PlayEvent");
665
 
2182 hawkeye 666
  return status;
667
}
668
 
669
 
670
/////////////////////////////////////////////////////////////////////////////
671
// called when a Meta event should be played/processed at a given tick
672
/////////////////////////////////////////////////////////////////////////////
673
static s32 SEQ_PlayMeta(u8 track, u8 meta, u32 len, u8 *buffer, u32 tick)
674
{
2184 hawkeye 675
  switch (meta)
676
  {
677
  case 0x00: // Sequence Number
678
     if (len == 2)
679
     {
680
        u32 seq_number = (buffer[0] << 8) | buffer[1];
2182 hawkeye 681
#if DEBUG_VERBOSE_LEVEL >= 2
2184 hawkeye 682
        DEBUG_MSG("[SEQ:%d:%u] Meta - Sequence Number %u\n", track, tick, seq_number);
2182 hawkeye 683
#endif
2184 hawkeye 684
     }
685
     else
686
     {
2182 hawkeye 687
#if DEBUG_VERBOSE_LEVEL >= 2
2184 hawkeye 688
        DEBUG_MSG("[SEQ:%d:%u] Meta - Sequence Number with %d bytes -- ERROR: expecting 2 bytes!\n", track, tick, len);
2182 hawkeye 689
#endif
2184 hawkeye 690
     }
2182 hawkeye 691
      break;
692
 
693
    case 0x01: // Text Event
694
#if DEBUG_VERBOSE_LEVEL >= 2
695
      DEBUG_MSG("[SEQ:%d:%u] Meta - Text: %s\n", track, tick, buffer);
696
#endif
697
      break;
698
 
699
    case 0x02: // Copyright Notice
700
#if DEBUG_VERBOSE_LEVEL >= 2
701
      DEBUG_MSG("[SEQ:%d:%u] Meta - Copyright: %s\n", track, tick, buffer);
702
#endif
703
      break;
704
 
705
    case 0x03: // Sequence/Track Name
706
#if DEBUG_VERBOSE_LEVEL >= 2
707
      DEBUG_MSG("[SEQ:%d:%u] Meta - Track Name: %s\n", track, tick, buffer);
708
#endif
709
      break;
710
 
711
    case 0x04: // Instrument Name
712
#if DEBUG_VERBOSE_LEVEL >= 2
713
      DEBUG_MSG("[SEQ:%d:%u] Meta - Instr. Name: %s\n", track, tick, buffer);
714
#endif
715
      break;
716
 
717
    case 0x05: // Lyric
718
#if DEBUG_VERBOSE_LEVEL >= 2
719
      DEBUG_MSG("[SEQ:%d:%u] Meta - Lyric: %s\n", track, tick, buffer);
720
#endif
721
      break;
722
 
723
    case 0x06: // Marker
724
#if DEBUG_VERBOSE_LEVEL >= 2
725
      DEBUG_MSG("[SEQ:%d:%u] Meta - Marker: %s\n", track, tick, buffer);
726
#endif
727
      break;
728
 
729
    case 0x07: // Cue Point
730
#if DEBUG_VERBOSE_LEVEL >= 2
731
      DEBUG_MSG("[SEQ:%d:%u] Meta - Cue Point: %s\n", track, tick, buffer);
732
#endif
733
      break;
734
 
735
    case 0x20: // Channel Prefix
736
      if( len == 1 ) {
737
   u32 prefix = *buffer;
738
#if DEBUG_VERBOSE_LEVEL >= 2
739
   DEBUG_MSG("[SEQ:%d:%u] Meta - Channel Prefix %u\n", track, tick, prefix);
740
#endif
741
      } else {
742
#if DEBUG_VERBOSE_LEVEL >= 2
743
   DEBUG_MSG("[SEQ:%d:%u] Meta - Channel Prefix with %d bytes -- ERROR: expecting 1 byte!\n", track, tick, len);
744
#endif
745
      }
746
      break;
747
 
748
    case 0x2f: // End of Track
749
#if DEBUG_VERBOSE_LEVEL >= 2
750
      DEBUG_MSG("[SEQ:%d:%u] Meta - End of Track\n", track, tick, meta);
751
#endif
752
      break;
753
 
754
    case 0x51: // Set Tempo
755
      if( len == 3 ) {
756
   u32 tempo_us = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2];
757
   float bpm = 60.0 * (1E6 / (float)tempo_us);
758
   SEQ_BPM_PPQN_Set(MIDI_PARSER_PPQN_Get());
759
 
760
   if( !seq_clk_locked ) {
761
     // set tempo immediately on first tick
762
     if( tick == 0 ) {
763
       SEQ_BPM_Set(bpm);
764
     } else {
765
       // put tempo change request into the queue
766
       mios32_midi_package_t tempo_package; // or Softis?
767
       tempo_package.ALL = (u32)bpm;
768
       SEQ_MIDI_OUT_Send(UART0, tempo_package, SEQ_MIDI_OUT_TempoEvent, tick, 0);
769
     }
770
   }
771
 
772
#if DEBUG_VERBOSE_LEVEL >= 2
773
   DEBUG_MSG("[SEQ:%d:%u] Meta - Tempo to %u uS -> %u BPM%s\n", track, tick, tempo_us, (u32)bpm,
774
        seq_clk_locked ? " IGNORED (locked)" : "");
775
#endif
776
      } else {
777
#if DEBUG_VERBOSE_LEVEL >= 2
778
   DEBUG_MSG("[SEQ:%d:%u] Meta - Tempo with %u bytes -- ERROR: expecting 3 bytes!\n", track, tick, len);
779
#endif
780
      }
781
      break;
782
 
783
    // other known events which are not handled here:
784
    // 0x54: SMPTE offset
785
    // 0x58: Time Signature
786
    // 0x59: Key Signature
787
    // 0x7f: Sequencer Specific Meta Event
788
 
789
#if DEBUG_VERBOSE_LEVEL >= 2
790
    default:
791
      DEBUG_MSG("[SEQ:%d:%u] Meta Event 0x%02x with length %u not processed\n", track, tick, meta, len);
792
#endif
793
  }
794
 
795
  return 0;
796
}
797
 
798
 
799
/////////////////////////////////////////////////////////////////////////////
800
// this hook is called when the MIDI scheduler sends a package
801
/////////////////////////////////////////////////////////////////////////////
802
static s32 Hook_MIDI_SendPackage(mios32_midi_port_t port, mios32_midi_package_t package)
803
{
804
  // realtime events are already scheduled by MIDI_ROUTER_SendMIDIClockEvent()
805
  if( package.evnt0 >= 0xf8 )
806
  {
807
     MIOS32_MIDI_SendPackage(port, package);
808
  } else
809
  {
810
     // forward to enabled MIDI ports
811
     int i;
812
     u16 mask = 1;
813
 
814
     for(i=0; i<16; ++i, mask <<= 1)
815
     {
816
        if( seq_play_enabled_ports & mask )
817
        {
818
           // USB0/1/2/3, UART0/1/2/3, IIC0/1/2/3, OSC0/1/2/3
819
           mios32_midi_port_t port = USB0 + ((i&0xc) << 2) + (i&3);
820
           MIOS32_MIDI_SendPackage(port, package);
821
        }
822
     }
823
  }
824
 
825
  return 0; // no error
826
}
827
 
828
 
829
/////////////////////////////////////////////////////////////////////////////
830
// To control the play/stop button function
831
/////////////////////////////////////////////////////////////////////////////
832
s32 SEQ_PlayStopButton(void)
833
{
2183 hawkeye 834
  if( SEQ_BPM_IsRunning() )
835
  {
836
     SEQ_BPM_Stop();          // stop sequencer
837
     SEQ_SetPauseMode(1);
838
     MID_FILE_SetRecordMode(0);
2182 hawkeye 839
 
2183 hawkeye 840
     strcpy(screenMode, "Pause");
2184 hawkeye 841
  }
842
  else
2183 hawkeye 843
  {
2184 hawkeye 844
     if (SEQ_PauseEnabled())
2183 hawkeye 845
     {
846
        // continue sequencer
847
        SEQ_SetPauseMode(0);
848
        SEQ_BPM_Cont();
2184 hawkeye 849
     }
2183 hawkeye 850
     else
851
     {
852
        MUTEX_SDCARD_TAKE;
2182 hawkeye 853
 
2183 hawkeye 854
        // if in auto mode and BPM generator is clocked in slave mode:
855
        // change to master mode
856
        SEQ_BPM_CheckAutoMaster();
2182 hawkeye 857
 
2183 hawkeye 858
        // request to play currently selected file
859
        SEQ_PlayFileReq(0, 1);
2182 hawkeye 860
 
2183 hawkeye 861
        // reset sequencer
862
        SEQ_Reset(1);
2182 hawkeye 863
 
2183 hawkeye 864
        // start sequencer
865
        SEQ_BPM_Start();
866
 
867
        MUTEX_SDCARD_GIVE;
868
     }
2182 hawkeye 869
  }
870
 
871
  return 0; // no error
872
}
873
 
874
 
875
/////////////////////////////////////////////////////////////////////////////
876
// To control the rec/stop button function
877
/////////////////////////////////////////////////////////////////////////////
878
s32 SEQ_RecStopButton(void)
879
{
2183 hawkeye 880
  if( SEQ_BPM_IsRunning() )
881
  {
882
     SEQ_BPM_Stop();          // stop sequencer
883
     SEQ_SetPauseMode(1);
884
     MID_FILE_SetRecordMode(0);
2182 hawkeye 885
 
2183 hawkeye 886
        strcpy(screenMode, "RecPause");
887
  }
888
  else
889
  {
890
     strcpy(screenMode, "Rec");
891
     SEQ_SetPauseMode(0);
2182 hawkeye 892
 
2183 hawkeye 893
     // if in auto mode and BPM generator is clocked in slave mode:
894
     // change to master mode
895
     SEQ_BPM_CheckAutoMaster();
2182 hawkeye 896
 
2183 hawkeye 897
     // enter record mode
898
     if( MID_FILE_SetRecordMode(1) >= 0 ) {
899
        // reset sequencer
900
        SEQ_Reset(1);
901
 
902
        // start sequencer
903
        SEQ_BPM_Start();
904
     }
2182 hawkeye 905
  }
906
 
907
  return 0; // no error
908
}
909
 
910
s32 SEQ_FFwdButton(void)
911
{
912
  u32 tick = SEQ_BPM_TickGet();
913
  u32 ticks_per_step = SEQ_BPM_PPQN_Get() / 4;
914
  u32 ticks_per_measure = ticks_per_step * 16;
915
 
916
  int measure = tick / ticks_per_measure;
917
  int song_pos = 16 * (measure + 1);
918
  if( song_pos > 65535 )
919
    song_pos = 65535;
920
 
921
  return SEQ_SongPos(song_pos);
922
}
923
 
924
s32 SEQ_FRewButton(void)
925
{
926
  u32 tick = SEQ_BPM_TickGet();
927
  u32 ticks_per_step = SEQ_BPM_PPQN_Get() / 4;
928
  u32 ticks_per_measure = ticks_per_step * 16;
929
 
930
  int measure = tick / ticks_per_measure;
931
  int song_pos = 16 * (measure - 1);
932
  if( song_pos < 0 )
933
    song_pos = 0;
934
 
935
  return SEQ_SongPos(song_pos);
936
}
937