A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 Karl Kurbjun based on midi2wav by Stepan Moskovchenko
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "plugin.h"
23#include "midi/guspat.h"
24#include "midi/midiutil.h"
25#include "midi/synth.h"
26#include "midi/sequencer.h"
27#include "midi/midifile.h"
28
29
30/* variable button definitions */
31#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
32#define BTN_QUIT BUTTON_OFF
33#define BTN_RIGHT BUTTON_RIGHT
34#define BTN_UP BUTTON_UP
35#define BTN_DOWN BUTTON_DOWN
36
37#define BTN_RC_QUIT BUTTON_RC_STOP
38
39#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
40 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
41#define BTN_QUIT (BUTTON_SELECT | BUTTON_MENU)
42#define BTN_RIGHT BUTTON_RIGHT
43#define BTN_UP BUTTON_SCROLL_FWD
44#define BTN_DOWN BUTTON_SCROLL_BACK
45
46#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
47#define BTN_QUIT BUTTON_POWER
48#define BTN_RIGHT BUTTON_RIGHT
49#define BTN_UP BUTTON_UP
50#define BTN_DOWN BUTTON_DOWN
51
52#elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
53(CONFIG_KEYPAD == SANSA_C200_PAD)
54#define BTN_QUIT BUTTON_POWER
55#define BTN_RIGHT BUTTON_RIGHT
56#define BTN_UP BUTTON_UP
57#define BTN_DOWN BUTTON_DOWN
58
59
60#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
61#define BTN_QUIT BUTTON_POWER
62#define BTN_RIGHT BUTTON_RIGHT
63#define BTN_UP BUTTON_UP
64#define BTN_DOWN BUTTON_DOWN
65
66#elif CONFIG_KEYPAD == IRIVER_H10_PAD
67#define BTN_QUIT BUTTON_POWER
68#define BTN_RIGHT BUTTON_RIGHT
69#define BTN_UP BUTTON_SCROLL_UP
70#define BTN_DOWN BUTTON_SCROLL_DOWN
71
72#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
73#define BTN_QUIT BUTTON_POWER
74#define BTN_RIGHT BUTTON_RIGHT
75#define BTN_UP BUTTON_UP
76#define BTN_DOWN BUTTON_DOWN
77
78#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
79#define BTN_QUIT BUTTON_POWER
80#define BTN_RIGHT BUTTON_NEXT
81#define BTN_UP BUTTON_UP
82#define BTN_DOWN BUTTON_DOWN
83
84#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
85#define BTN_QUIT BUTTON_POWER
86#define BTN_RIGHT BUTTON_NEXT
87#define BTN_UP BUTTON_UP
88#define BTN_DOWN BUTTON_DOWN
89
90#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \
91 (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD)
92#define BTN_QUIT BUTTON_PLAY
93#define BTN_RIGHT BUTTON_RIGHT
94#define BTN_UP BUTTON_UP
95#define BTN_DOWN BUTTON_DOWN
96
97#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
98#define BTN_QUIT BUTTON_REC
99#define BTN_RIGHT BUTTON_NEXT
100#define BTN_UP BUTTON_UP
101#define BTN_DOWN BUTTON_DOWN
102
103#elif CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD
104#define BTN_QUIT BUTTON_PLAY
105#define BTN_RIGHT BUTTON_MENU
106#define BTN_UP BUTTON_UP
107#define BTN_DOWN BUTTON_DOWN
108
109#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
110#define BTN_QUIT BUTTON_POWER
111#define BTN_RIGHT BUTTON_RIGHT
112#define BTN_UP BUTTON_UP
113#define BTN_DOWN BUTTON_DOWN
114
115#endif
116
117#define FRACTSIZE 10
118
119#ifndef SIMULATOR
120
121#if (HW_SAMPR_CAPS & SAMPR_CAP_22)
122#define SAMPLE_RATE SAMPR_22 // 44100 22050 11025
123#else
124#define SAMPLE_RATE SAMPR_44 // 44100 22050 11025
125#endif
126
127#define MAX_VOICES 20 // Note: 24 midi channels is the minimum general midi
128 // spec implementation
129
130#else // Simulator requires 44100, and we can afford to use more voices
131
132#define SAMPLE_RATE SAMPR_44
133#define MAX_VOICES 48
134
135#endif
136
137
138#define BUF_SIZE 256
139#define NBUF 2
140
141#undef SYNC
142
143#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
144 #define SYNC
145#endif
146
147struct MIDIfile * mf IBSS_ATTR;
148
149int numberOfSamples IBSS_ATTR;
150long bpm IBSS_ATTR;
151
152const unsigned char * drumNames[]={
153 "Bass Drum 2 ",
154 "Bass Drum 1 ",
155 "Side Stick ",
156 "Snare Drum 1 ",
157 "Hand Clap ",
158 "Snare Drum 2 ",
159 "Low Tom 2 ",
160 "Closed Hi-hat ",
161 "Low Tom 1 ",
162 "Pedal Hi-hat ",
163 "Mid Tom 2 ",
164 "Open Hi-hat ",
165 "Mid Tom 1 ",
166 "High Tom 2 ",
167 "Crash Cymbal 1 ",
168 "High Tom 1 ",
169 "Ride Cymbal 1 ",
170 "Chinese Cymbal ",
171 "Ride Bell ",
172 "Tambourine ",
173 "Splash Cymbal ",
174 "Cowbell ",
175 "Crash Cymbal 2 ",
176 "Vibra Slap ",
177 "Ride Cymbal 2 ",
178 "High Bongo ",
179 "Low Bongo ",
180 "Mute High Conga",
181 "Open High Conga",
182 "Low Conga ",
183 "High Timbale ",
184 "Low Timbale ",
185 "High Agogo ",
186 "Low Agogo ",
187 "Cabasa ",
188 "Maracas ",
189 "Short Whistle ",
190 "Long Whistle ",
191 "Short Guiro ",
192 "Long Guiro ",
193 "Claves ",
194 "High Wood Block",
195 "Low Wood Block ",
196 "Mute Cuica ",
197 "Open Cuica ",
198 "Mute Triangle ",
199 "Open Triangle ",
200 "Shaker ",
201 "Jingle Bell ",
202 "Bell Tree ",
203 "Castenets ",
204 "Mute Surdo ",
205 "Open Surdo "
206};
207
208long gmbuf[BUF_SIZE*NBUF];
209
210int quit=0;
211
212#define STATE_STOPPED 0
213#define STATE_PAUSED 1
214#define STATE_PLAYING 2
215
216
217#define BEATBOX_UP BUTTON_UP
218#define BEATBOX_DOWN BUTTON_DOWN
219#define BEATBOX_LEFT BUTTON_LEFT
220#define BEATBOX_RIGHT BUTTON_RIGHT
221#define BEATBOX_SELECT BUTTON_SELECT
222
223#if (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
224#define BEATBOX_PLAY BUTTON_PLAYPAUSE
225#define BEATBOX_STOP BUTTON_BACK
226#else
227#define BEATBOX_PLAY BUTTON_ON
228#define BEATBOX_STOP BUTTON_OFF
229#endif
230
231#define VAL_NONE 0
232#define VAL_ENABLED 1
233#define VAL_LOOP 2
234
235#define H_NUMCELLS 24
236#define V_NUMCELLS 8
237
238#define HILIGHT_NONE 0
239#define HILIGHT_PLAY 1
240#define HILIGHT_USER 2
241
242#define CELL_XSIZE 9
243#define CELL_YSIZE 9
244
245#define GRID_XPOS 2
246#define GRID_YPOS 10
247
248
249#define COLOR_NAME_TEXT LCD_RGBPACK(0xFF,0xFF,0xFF)
250#define COLOR_NORMAL LCD_RGBPACK(0xFF,0xFF,0xFF)
251#define COLOR_PLAY LCD_RGBPACK(0xFF,0xFF,0x00)
252#define COLOR_DISABLED LCD_RGBPACK(0xA0,0xA0,0xA0)
253#define COLOR_LOOPCELL LCD_RGBPACK(0xC0,0xC0,0xC0)
254#define COLOR_EDIT LCD_RGBPACK(0x30,0x30,0xFF)
255#define COLOR_GRID LCD_RGBPACK(0xD0,0xD0,0xD0)
256
257#define EDITSTATE_PATTERN 0
258
259int xCursor=0, yCursor=0;
260
261int editState=EDITSTATE_PATTERN;
262
263int playState=STATE_STOPPED, stepFlag=0;
264
265
266enum plugin_status plugin_start(const void* parameter)
267{
268 int retval = 0;
269
270 rb->lcd_setfont(FONT_SYSFIXED);
271
272#if defined(HAVE_ADJUSTABLE_CPU_FREQ)
273 rb->cpu_boost(true);
274#endif
275
276#ifdef RB_PROFILE
277 rb->profile_thread();
278#endif
279 if (initSynth(NULL, ROCKBOX_DIR "/patchset/patchset.cfg",
280 ROCKBOX_DIR "/patchset/drums.cfg") == -1)
281 {
282 printf("\nINIT ERROR\n");
283 return -1;
284 }
285//#ifndef SIMULATOR
286 rb->pcm_play_stop();
287#if INPUT_SRC_CAPS != 0
288 /* Select playback */
289 rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
290 rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
291#endif
292 rb->pcm_set_frequency(SAMPLE_RATE); // 44100 22050 11025
293
294
295 retval = beatboxmain();
296
297#ifdef RB_PROFILE
298 rb->profstop();
299#endif
300
301 rb->pcm_play_stop();
302 rb->pcm_set_frequency(HW_SAMPR_DEFAULT);
303
304#if defined(HAVE_ADJUSTABLE_CPU_FREQ)
305 rb->cpu_boost(false);
306#endif
307
308
309 if(retval == -1)
310 return PLUGIN_ERROR;
311 return PLUGIN_OK;
312}
313
314bool swap=0;
315bool lastswap=1;
316
317inline void synthbuf(void)
318{
319 long *outptr;
320 register int i;
321 static int currentSample=0;
322 int synthtemp[2];
323
324#ifndef SYNC
325 if(lastswap==swap) return;
326 lastswap=swap;
327
328 outptr=(swap ? gmbuf : gmbuf+BUF_SIZE);
329#else
330 outptr=gmbuf;
331#endif
332
333 for(i=0; i<BUF_SIZE/2; i++)
334 {
335 synthSample(&synthtemp[0], &synthtemp[1]);
336 currentSample++;
337 *outptr=((synthtemp[0]&0xFFFF) << 16) | (synthtemp[1]&0xFFFF);
338 outptr++;
339 if(currentSample==numberOfSamples)
340 {
341 if(playState == STATE_PLAYING)
342 {
343 stepFlag=1;
344 }
345
346 currentSample=0;
347 }
348 }
349}
350
351
352
353
354
355unsigned char trackPos[V_NUMCELLS];
356unsigned char trackData[H_NUMCELLS][V_NUMCELLS];
357unsigned char trackMap[V_NUMCELLS] = {38, 39, 40, 41, 42, 43, 44, 56};
358
359
360struct Cell
361{
362 unsigned char val;
363 int color;
364};
365
366struct Cell pattern[H_NUMCELLS][V_NUMCELLS];
367struct Cell dispPattern[H_NUMCELLS][V_NUMCELLS];
368
369
370void advancePosition()
371{
372 int i=0;
373 for(i=0; i<V_NUMCELLS; i++)
374 {
375 trackPos[i]++;
376 if(trackPos[i] == H_NUMCELLS || trackData[trackPos[i]][i] == VAL_LOOP)
377 trackPos[i]=0;
378 }
379}
380
381
382void sendEvents()
383{
384 int i;
385 for(i=0; i<V_NUMCELLS; i++)
386 {
387 if(trackData[trackPos[i]][i] == VAL_ENABLED)
388 pressNote(9, trackMap[i], 127);
389 }
390}
391
392#define NAME_POSX 10
393#define NAME_POSY 100
394void showDrumName(int trackNum)
395{
396 rb->lcd_set_foreground(COLOR_NAME_TEXT);
397 rb->lcd_putsxy(NAME_POSX, NAME_POSY, drumNames[trackMap[trackNum]-35]);
398}
399
400void updateDisplay()
401{
402 int i, j;
403 int grayOut=0;
404
405 for(j=0; j<V_NUMCELLS; j++)
406 {
407 grayOut=0;
408 for(i=0; i<H_NUMCELLS; i++)
409 {
410 pattern[i][j].color = COLOR_NORMAL;
411 pattern[i][j].val = trackData[i][j];
412
413 if(trackPos[j] == i)
414 pattern[i][j].color = COLOR_PLAY;
415
416 if(grayOut)
417 pattern[i][j].color = COLOR_DISABLED;
418
419 if(trackData[i][j] == VAL_LOOP)
420 {
421 pattern[i][j].color = COLOR_LOOPCELL;
422 grayOut=1;
423 }
424
425 if(xCursor == i && yCursor == j && editState == EDITSTATE_PATTERN)
426 pattern[i][j].color = COLOR_EDIT;
427 }
428 }
429
430}
431
432void resetPosition()
433{
434 int i;
435 for(i=0; i<V_NUMCELLS; i++)
436 trackPos[i]=0;
437}
438
439void clearCells()
440{
441 int i,j;
442 for(i=0; i<H_NUMCELLS; i++)
443 for(j=0; j<V_NUMCELLS; j++)
444 {
445 pattern[i][j].val=VAL_NONE;
446 dispPattern[i][j].val=VAL_NONE;
447 pattern[i][j].color = 0;
448 dispPattern[i][j].color = 0;
449 }
450}
451
452
453
454
455void drawGrid()
456{
457 int i, j;
458
459 rb->lcd_set_foreground(COLOR_GRID);
460
461 for(i=0; i<H_NUMCELLS+1; i++)
462 rb->lcd_vline(i*CELL_XSIZE+GRID_XPOS, GRID_YPOS, GRID_YPOS+CELL_YSIZE*V_NUMCELLS);
463
464 for(i=0; i<V_NUMCELLS+1; i++)
465 rb->lcd_hline(GRID_XPOS, GRID_XPOS+CELL_XSIZE*H_NUMCELLS, GRID_YPOS+i*CELL_YSIZE);
466
467
468 rb->lcd_update();
469}
470
471void drawCell(int i, int j)
472{
473 int cellX, cellY;
474
475 cellX = GRID_XPOS + CELL_XSIZE*i+1;
476 cellY = GRID_YPOS + CELL_YSIZE*j+1;
477
478 rb->lcd_set_foreground(pattern[i][j].color);
479 rb->lcd_fillrect(cellX, cellY, CELL_XSIZE-1, CELL_YSIZE-1);
480
481 rb->lcd_set_foreground(0);
482
483 if(pattern[i][j].val == VAL_LOOP)
484 {
485 rb->lcd_drawline(cellX, cellY, cellX+CELL_XSIZE-2, cellY+CELL_YSIZE-2);
486 }
487
488 if(pattern[i][j].val == VAL_ENABLED)
489 {
490 rb->lcd_fillrect(cellX+1, cellY+1, CELL_XSIZE-3, CELL_YSIZE-3);
491 }
492
493}
494
495void redrawScreen(unsigned char force)
496{
497 int i, j;
498
499 for(i=0; i<H_NUMCELLS; i++)
500 {
501 for(j=0; j<V_NUMCELLS; j++)
502 {
503 if(force || (pattern[i][j].val != dispPattern[i][j].val || pattern[i][j].color != dispPattern[i][j].color))
504 {
505 drawCell(i, j);
506 dispPattern[i][j].val = pattern[i][j].val;
507 dispPattern[i][j].color = pattern[i][j].color;
508 }
509 }
510 }
511 rb->lcd_update();
512}
513
514void get_more(const void** start, size_t* size)
515{
516#ifndef SYNC
517 if(lastswap!=swap)
518 {
519// printf("Buffer miss!"); // Comment out the printf to make missses less noticable.
520 }
521
522#else
523 synthbuf(); // For some reason midiplayer crashes when an update is forced
524#endif
525
526 *size = BUF_SIZE*sizeof(short);
527#ifndef SYNC
528 *start = swap ? gmbuf : gmbuf + BUF_SIZE;
529 swap=!swap;
530#else
531 *start = gmbuf;
532#endif
533}
534
535int beatboxmain()
536{
537 int vol=0;
538
539
540 numberOfSamples=44100/10;
541 synthbuf();
542 rb->pcm_play_data(&get_more, NULL, NULL, 0);
543
544 rb->lcd_set_background(0x000000);
545 rb->lcd_clear_display();
546
547 resetPosition();
548
549 int i, j;
550
551 /* Start at 16 cells/loop for now. User can un-loop if more are needed */
552 for(i=0; i<V_NUMCELLS; i++)
553 trackData[16][i] = VAL_LOOP;
554
555
556/* Very very rough beat to 'Goodbye Horses'
557 trackData[16][3] = VAL_LOOP;
558 trackData[16][2] = VAL_LOOP;
559
560 trackData[0][3] = 1;
561 trackData[4][3] = 1;
562 trackData[8][3] = 1;
563 trackData[9][3] = 1;
564 trackData[12][3] = 1;
565 trackData[13][3] = 1;
566
567 trackData[2][2] = 1;
568 trackData[6][2] = 1;
569 trackData[10][2] = 1;
570 trackData[14][2] = 1;
571*/
572
573 drawGrid();
574 showDrumName(yCursor);
575 updateDisplay();
576 redrawScreen(1);
577
578
579 while(!quit)
580 {
581 #ifndef SYNC
582 synthbuf();
583 #endif
584 rb->yield();
585
586 if(stepFlag)
587 {
588 advancePosition();
589 sendEvents();
590 updateDisplay();
591 redrawScreen(0);
592 stepFlag=0;
593 }
594
595 /* Prevent idle poweroff */
596 rb->reset_poweroff_timer();
597
598 /* Code taken from Oscilloscope plugin */
599 switch(rb->button_get(false))
600 {
601 /*
602 case BTN_UP:
603 case BTN_UP | BUTTON_REPEAT:
604 vol = rb->global_status->volume;
605 if (vol < rb->sound_max(SOUND_VOLUME))
606 {
607 vol++;
608 rb->sound_set(SOUND_VOLUME, vol);
609 }
610 break;
611
612 case BTN_DOWN:
613 case BTN_DOWN | BUTTON_REPEAT:
614 vol = rb->global_status->volume;
615 if (vol > rb->sound_min(SOUND_VOLUME))
616 {
617 vol--;
618 rb->sound_set(SOUND_VOLUME, vol);
619 }
620 break;
621
622 case BTN_RIGHT:
623 {
624 //pressNote(9, 40, 127);
625 // resetPosition();
626 advancePosition();
627 sendEvents();
628 updateDisplay();
629 redrawScreen(0);
630 break;
631 }
632
633 case BUTTON_LEFT:
634 {
635
636// isPlaying=1;
637 resetPosition();
638 updateDisplay();
639 redrawScreen(0);
640 //pressNote(9, 39, 127);
641 break;
642 }
643*/
644
645 case BEATBOX_UP:
646 case BEATBOX_UP | BUTTON_REPEAT:
647 {
648 if(editState == EDITSTATE_PATTERN)
649 {
650 if(yCursor > 0)
651 {
652 yCursor--;
653 showDrumName(yCursor);
654 updateDisplay();
655 redrawScreen(0);
656 }
657 }
658 break;
659 }
660
661 case BEATBOX_DOWN:
662 case BEATBOX_DOWN | BUTTON_REPEAT:
663 {
664 if(editState == EDITSTATE_PATTERN)
665 {
666 if(yCursor < V_NUMCELLS-1)
667 {
668 yCursor++;
669 showDrumName(yCursor);
670 updateDisplay();
671 redrawScreen(0);
672 }
673 }
674 break;
675 }
676
677 case BEATBOX_LEFT:
678 case BEATBOX_LEFT | BUTTON_REPEAT:
679 {
680 if(editState == EDITSTATE_PATTERN)
681 {
682 if(xCursor > 0)
683 {
684 xCursor--;
685 updateDisplay();
686 redrawScreen(0);
687 }
688 }
689 break;
690 }
691
692 case BEATBOX_RIGHT:
693 case BEATBOX_RIGHT | BUTTON_REPEAT:
694 {
695 if(editState == EDITSTATE_PATTERN)
696 {
697 if(xCursor < H_NUMCELLS-1)
698 {
699 xCursor++;
700 updateDisplay();
701 redrawScreen(0);
702 }
703 }
704 break;
705 }
706
707 case BEATBOX_SELECT:
708 {
709 if(editState == EDITSTATE_PATTERN)
710 {
711 int cv = trackData[xCursor][yCursor];
712 cv++;
713 if(cv > VAL_LOOP)
714 cv = VAL_NONE;
715
716 trackData[xCursor][yCursor] = cv;
717
718 updateDisplay();
719 redrawScreen(0);
720 }
721 break;
722 }
723
724
725 case BEATBOX_PLAY:
726 {
727 if(playState == STATE_PLAYING)
728 playState = STATE_PAUSED;
729 else
730 {
731 updateDisplay();
732 redrawScreen(0);
733 sendEvents();
734 playState = STATE_PLAYING;
735 }
736 break;
737 }
738
739 case BEATBOX_STOP:
740 {
741 if(playState == STATE_STOPPED)
742 {
743 quit=1;
744 } else
745 {
746 playState =STATE_STOPPED;
747 resetPosition();
748 updateDisplay();
749 redrawScreen(0);
750 }
751 break;
752 }
753 }
754
755
756 }
757
758 return 0;
759}
760