faa2cb9942
- Reduce need to press multiple buttons at the same time to quit a plugin - Have "Menu" be default way to quit plugins or to access plugin menu - Fall back to (Long) "Select" or Long "Menu" in cases where Menu button isn't available (e.g. in ImageViewer and many games) out of scope: boomshine, lua_scripts, Rockpaint, Doom, Duke3D, Pacbox, Quake, Sgt-Puzzles, Wolf3D, XWorld, Minesweeper, Pixel Painter, Spacerocks Change-Id: I6d4dc7174695fe4b8ee9cbaccb21bdbfe6af5c48
1731 lines
61 KiB
C
1731 lines
61 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2004 Matthias Wientapper, 2014-2015 Thomas Orgis
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
* TODO:
|
|
* - Think about generating the sounds on startup with SWCODEC.
|
|
****************************************************************************/
|
|
#include "plugin.h"
|
|
#include "lib/pluginlib_actions.h"
|
|
#include "lib/pluginlib_exit.h"
|
|
#include "fixedpoint.h"
|
|
|
|
/* About time resolution:
|
|
1000 means 1 ms resolution. It should get better with higher values
|
|
in theory, but in practice, too small timer intervals increase the
|
|
chance of being missed and make the metronome lag behind. Mean tempo
|
|
still works out with very small divider values (29 even) as long as
|
|
the rounding error compensation is active, although beat intervals
|
|
become jerky. You compromise between long-term accuracy and steadyness
|
|
from one beat to the next.
|
|
|
|
A drift you have to accept comes just from the audio clock itself, or even
|
|
from the difference between clocks in the device. The Sansa Clip+ has around
|
|
0.04 % error in audio frequency using the "good" PLLB. I presume that the
|
|
difference between timing using PLLA and PLLB is at least that big. Something
|
|
up to 40 ms time difference over one minute when comparing to an external
|
|
reference or just the metronome plugin with playback of a prepared PCM track
|
|
is to be expected.
|
|
|
|
Also, since playback on SWCODEC is not allowed to happen inside the timer
|
|
callback, there is a delay introduced by the main loop scheduling. This
|
|
could be compensated for by delaying the audio depending on a counter
|
|
incremented since the period elapsed in the callback, at the price of
|
|
putting the display out of sync. On a Clip+, the schedule delay isn't
|
|
biggest problem (drift for fine timer resolution is).
|
|
|
|
All in all, 1 ms is too small, 2 ms seems to work fine ...
|
|
4 ms might still be cool, too.
|
|
*/
|
|
#if defined(SIMULATOR)
|
|
/* Simulator really wants 1024. Not 1000, not 512, only 1024.
|
|
Otherwise it is strangely slow. */
|
|
static const unsigned int timerfreq_div = 1024;
|
|
#else
|
|
static const unsigned int timerfreq_div = 500; /* 2 ms resolution */
|
|
#endif
|
|
/* actual (not quarter) beats per minute above which display blinking
|
|
is deactivated (since it is not needed anymore and because of performance
|
|
issues) */
|
|
static const unsigned int blinklimit = 135;
|
|
|
|
enum metronome_errors
|
|
{
|
|
MERR_NOTHING = 0
|
|
, MERR_MISSING
|
|
, MERR_OOM
|
|
, MERR_TEMPO
|
|
, MERR_METER
|
|
, MERR_VOLUME
|
|
, MERR_PATTERN
|
|
};
|
|
|
|
#define PART_MAX 10 /* maximum count of programmed parts */
|
|
|
|
#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD) \
|
|
|| (CONFIG_KEYPAD == SANSA_E200_PAD) || (CONFIG_KEYPAD == SAMSUNG_YH820_PAD)
|
|
#define MET_SYNC
|
|
#endif
|
|
|
|
#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
|
|
|| (CONFIG_KEYPAD == IPOD_3G_PAD) \
|
|
|| (CONFIG_KEYPAD == IPOD_4G_PAD)
|
|
#define METRONOME_QUIT PLA_UP
|
|
#else
|
|
#define METRONOME_QUIT PLA_EXIT
|
|
#endif
|
|
|
|
#ifdef HAVE_SCROLLWHEEL
|
|
#define METRONOME_VOL_UP PLA_SCROLL_FWD
|
|
#define METRONOME_VOL_UP_REP PLA_SCROLL_FWD_REPEAT
|
|
#define METRONOME_VOL_DOWN PLA_SCROLL_BACK
|
|
#define METRONOME_VOL_DOWN_REP PLA_SCROLL_BACK_REPEAT
|
|
#else
|
|
#define METRONOME_VOL_UP PLA_UP
|
|
#define METRONOME_VOL_DOWN PLA_DOWN
|
|
#define METRONOME_VOL_UP_REP PLA_UP_REPEAT
|
|
#define METRONOME_VOL_DOWN_REP PLA_DOWN_REPEAT
|
|
#endif
|
|
#define METRONOME_LEFT PLA_LEFT
|
|
#define METRONOME_RIGHT PLA_RIGHT
|
|
#define METRONOME_LEFT_REP PLA_LEFT_REPEAT
|
|
#define METRONOME_RIGHT_REP PLA_RIGHT_REPEAT
|
|
#define METRONOME_TAP PLA_SELECT_REL
|
|
#define METRONOME_PAUSE PLA_CANCEL
|
|
#define METRONOME_PLAY PLA_SELECT_REPEAT
|
|
|
|
#define METRONOME_START PLA_SELECT
|
|
|
|
#ifdef MET_SYNC
|
|
enum{ METRONOME_SYNC = LAST_PLUGINLIB_ACTION+1 };
|
|
static const struct button_mapping iriver_syncaction[] =
|
|
{
|
|
{ METRONOME_SYNC, BUTTON_REC, BUTTON_NONE },
|
|
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_PLUGIN)
|
|
};
|
|
#endif /* IRIVER_H100_PAD||IRIVER_H300_PAD */
|
|
|
|
const struct button_mapping *plugin_contexts[] =
|
|
{
|
|
pla_main_ctx,
|
|
#if defined(MET_SYNC)
|
|
iriver_syncaction,
|
|
#endif
|
|
};
|
|
#define PLA_ARRAY_COUNT sizeof(plugin_contexts)/sizeof(plugin_contexts[0])
|
|
|
|
/* raw PCM */
|
|
static signed short tick_sound[] =
|
|
{
|
|
32767,32764,32767,32767,32763,32767,32762,32767,32765,32767,32767
|
|
,32766,32767,32766,32767,32767,32765,32767,32764,32767,32764,32767
|
|
,32763,-32764,-32768,-32766,-32768,-32768,-32767,-32767,-32767,-32765,-32768
|
|
,-32764,-32768,-32768,-32766,-32768,-32764,-32768,-32766,-32767,-32768,-32767
|
|
,-32766,32763,32767,32763,32767,32767,32766,32767,32766,32767,32765
|
|
,32767,32763,32767,32766,32767,32766,32767,32763,32767,32765,32767
|
|
,32767,-32768,-32765,-32768,-32767,-32765,-32767,-32765,-32768,-32764,-32768
|
|
,-32766,-32768,-32768,-32766,-32767,-32768,-32764,-32768,-32764,-32768,-32768
|
|
,-32766,32716,32668,32620,32564,32520,32467,32418,32370,32316,32272
|
|
,32214,32176,32114,32074,32020,31972,31922,31873,31823,31775,31726
|
|
,31676,-31627,-31579,-31530,-31479,-31433,-31383,-31333,-31287,-31236,-31188
|
|
,-31141,-31090,-31042,-30998,-30943,-30899,-30849,-30800,-30757,-30702,-30659
|
|
,-30609,30561,30515,30463,30422,30365,30326,30273,30229,30177,30135
|
|
,30084,30035,29995,29936,29903,29844,29802,29755,29706,29661,29614
|
|
,29565,-29519,-29472,-29426,-29381,-29331,-29288,-29239,-29191,-29149,-29099
|
|
,-29055,-29007,-28962,-28914,-28871,-28821,-28779,-28730,-28685,-28638,-28596
|
|
,-28544,28496,28463,28404,28371,28314,28278,28225,28185,28136,28093
|
|
,28048,28001,27956,27912,27865,27824,27773,27736,27682,27646,27593
|
|
,27555,-27509,-27462,-27418,-27375,-27328,-27286,-27239,-27200,-27145,-27116
|
|
,-27055,-27026,-26972,-26930,-26891,-26838,-26805,-26750,-26716,-26663,-26630
|
|
,-26575,26534,26495,26448,26408,26360,26324,26272,26235,26188,26149
|
|
,26101,26061,26016,25976,25930,25888,25847,25800,25763,25714,25676
|
|
,25632,-25589,-25547,-25505,-25461,-25419,-25376,-25337,-25291,-25251,-25209
|
|
,-25162,-25129,-25078,-25043,-24995,-24960,-24910,-24876,-24830,-24787,-24751
|
|
,-24703,24663,24622,24583,24539,24499,24456,24418,24374,24334,24292
|
|
,24252,24209,24173,24124,24092,24042,24011,23960,23930,23879,23845
|
|
,23803,-23762,-23722,-23680,-23644,-23597,-23562,-23518,-23482,-23437,-23402
|
|
,-23357,-23320,-23281,-23236,-23203,-23158,-23119,-23082,-23040,-23000,-22962
|
|
,-22922,22885,22837,22809,22759,22728,22685,22644,22608,22567,22528
|
|
,22491,22449,22412,22372,22334,22295,22254,22221,22173,22146,22096
|
|
,22066,-22026,-21984,-21947,-21911,-21867,-21836,-21790,-21757,-21719,-21677
|
|
,-21644,-21601,-21567,-21526,-21489,-21454,-21411,-21378,-21337,-21301,-21263
|
|
,-21226,21189,21151,21111,21077,21036,21002,20964,20926,20889,20852
|
|
,20814,20779,20741,20703,20666,20632,20590,20562,20512,20490,20440
|
|
,20416,-20380,-20331,-20303,-20261,-20230,-20188,-20158,-20114,-20085,-20045
|
|
,-20009,-19975,-19935,-19904,-19864,-19829,-19797,-19753,-19726,-19685,-19650
|
|
,-19616,19580,19544,19508,19471,19441,19397,19373,19325,19301,19256
|
|
,19229,19189,19155,19122,19081,19055,19013,18980,18947,18908,18879
|
|
,18840,-18807,-18771,-18739,-18701,-18669,-18634,-18600,-18564,-18531,-18497
|
|
,-18461,-18430,-18392,-18360,-18326,-18291,-18258,-18223,-18191,-18154,-18123
|
|
,-18088,18055,18019,17989,17953,17919,17887,17853,17817,17791,17746
|
|
,17726,17680,17656,17619,17585,17555,17519,17488,17453,17423,17385
|
|
,17359,17320,-17287,-17261,-17220,-17196,-17156,-17128,-17093,-17062,-17029
|
|
,-16996,-16966,-16929,-16901,-16868,-16834,-16804,-16769,-16741,-16705,-16675
|
|
,-16644,-16609,16579,16545,16519,16479,16455,16419,16388,16359,16321
|
|
,16300,16256,16235,16198,16166,16141,16102,16076,16043,16011,15981
|
|
,15950,15920,-15891,-15852,-15832,-15789,-15768,-15732,-15703,-15672,-15642
|
|
,-15609,-15582,-15548,-15521,-15487,-15459,-15426,-15401,-15364,-15339,-15305
|
|
,-15275,-15250,15219,15184,15159,15122,15101,15061,15041,15002,14981
|
|
,14944,14919,14886,14858,14829,14797,14772,14736,14715,14675,14657
|
|
,14617,14598,-14567,-14533,-14506,-14475,-14447,-14419,-14389,-14358,-14334
|
|
,-14299,-14277,-14241,-14218,-14186,-14158,-14131,-14100,-14072,-14046,-14012
|
|
,-13991,-13955,13927,13904,13873,13844,13819,13787,13761,13732,13706
|
|
,13674,13651,13619,13590,13570,13530,13516,13475,13457,13423,13401
|
|
,13368,13345,-13317,-13287,-13260,-13234,-13204,-13178,-13152,-13122,-13098
|
|
,-13068,-13043,-13013,-12990,-12958,-12937,-12902,-12884,-12849,-12829,-12798
|
|
,-12771,-12746,12718,12693,12663,12641,12611,12586,12559,12531,12510
|
|
,12475,12459,12421,12408,12367,12357,12315,12301,12268,12244,12218
|
|
,12194,12164,-12140,-12113,-12093,-12056,-12043,-12008,-11987,-11961,-11931
|
|
,-11911,-11884,-11855,-11836,-11804,-11782,-11757,-11729,-11708,-11679,-11655
|
|
,-11631,-11601,11574,11560,11525,11508,11479,11454,11431,11403,11382
|
|
,11356,11329,11307,11279,11260,11229,11211,11179,11161,11133,11110
|
|
,11085,11061,-11038,-11010,-10991,-10960,-10944,-10913,-10893,-10866,-10845
|
|
,-10818,-10796,-10772,-10747,-10725,-10699,-10676,-10653,-10629,-10605,-10580
|
|
,-10561,-10531,10507,10491,10461,10442,10416,10394,10369,10350,10319
|
|
,10305,10276,10252,10236,10202,10190,10160,10138,10117,10093,10070
|
|
,10050,10020,-9997,-9984,-9953,-9935,-9912,-9885,-9871,-9840,-9823
|
|
,-9799,-9773,-9756,-9730,-9710,-9687,-9664,-9642,-9620,-9598,-9576
|
|
,-9554,-9532,9510,9488,9466,9444,9419,9405,9374,9361,9331
|
|
,9314,9293,9268,9250,9225,9206,9184,9160,9143,9118,9098
|
|
,9077,9053,-9029,-9018,-8988,-8973,-8948,-8928,-8906,-8888,-8862
|
|
,-8848,-8819,-8805,-8780,-8760,-8740,-8720,-8695,-8681,-8653,-8638
|
|
,-8615,-8594,8573,8556,8531,8515,8492,8472,8451,8433,8409
|
|
,8393,8368,8354,8326,8315,8284,8277,8243,8236,8206,8192
|
|
,8170,8151,-8133,-8107,-8095,-8068,-8054,-8030,-8014,-7991,-7975
|
|
,-7953,-7933,-7916,-7894,-7876,-7857,-7836,-7818,-7799,-7778,-7761
|
|
,-7740,-7720,7700,7684,7665,7645,7626,7610,7582,7577,7545
|
|
,7534,7513,7493,7477,7455,7438,7419,7402,7379,7368,7341
|
|
,7328,7307,-7289,-7272,-7250,-7237,-7216,-7196,-7183,-7157,-7146
|
|
,-7123,-7108,-7091,-7065,-7058,-7030,-7020,-6998,-6979,-6965,-6944
|
|
,-6927,-6911,6895,6870,6859,6836,6824,6800,6790,6763,6755
|
|
,6730,6717,6699,6679,6667,6642,6632,6610,6596,6574,6564
|
|
,6539,6529,6507,-6492,-6474,-6457,-6442,-6422,-6406,-6392,-6371
|
|
,-6358,-6338,-6322,-6306,-6291,-6269,-6259,-6237,-6223,-6206,-6190
|
|
,-6172,-6157,-6139,6123,6108,6088,6077,6057,6043,6025,6010
|
|
,5992,5978,5962,5944,5928,5914,5897,5879,5869,5844,5837
|
|
,5816,5799,5789,-5773,-5750,-5742,-5720,-5710,-5687,-5680,-5656
|
|
,-5648,-5627,-5613,-5600,-5581,-5568,-5552,-5534,-5525,-5501,-5496
|
|
,-5470,-5464,-5443,5430,5413,5402,5383,5370,5353,5343,5320
|
|
,5314,5292,5280,5267,5248,5237,5219,5208,5190,5176,5162
|
|
,5148,5131,5121,-5107,-5086,-5077,-5058,-5047,-5030,-5017,-5002
|
|
,-4990,-4971,-4963,-4941,-4935,-4915,-4902,-4892,-4869,-4867,-4841
|
|
,-4836,-4816,-4806,4792,4774,4767,4744,4740,4716,4712,4692
|
|
,4680,4668,4649,4642,4624,4612,4599,4584,4570,4560,4541
|
|
,4535,4513,4508,-4496,-4472,-4467,-4450,-4437,-4427,-4407,-4401
|
|
,-4384,-4372,-4358,-4346,-4331,-4322,-4305,-4294,-4281,-4266,-4257
|
|
,-4240,-4231,-4216,4204,4190,4180,4162,4156,4137,4131,4111
|
|
,4104,4091,4075,4068,4048,4043,4026,4016,4002,3990,3980
|
|
,3962,3957,3938,-3926,-3919,-3904,-3892,-3882,-3866,-3858,-3843
|
|
,-3833,-3820,-3810,-3792,-3789,-3769,-3762,-3751,-3732,-3730,-3710
|
|
,-3704,-3689,-3678,3666,3656,3640,3637,3615,3613,3594,3586
|
|
,3576,3560,3555,3537,3529,3518,3504,3497,3481,3473,3462
|
|
,3447,3441,3425,-3414,-3406,-3394,-3383,-3372,-3359,-3353,-3336
|
|
,-3330,-3316,-3306,-3295,-3287,-3270,-3266,-3251,-3241,-3233,-3218
|
|
,-3210,-3197,-3192,3182,3160,3165,3139,3140,3121,3116,3104
|
|
,3093,3083,3075,3057,3058,3038,3033,3021,3009,3003,2990
|
|
,2981,2969,2960,-2947,-2943,-2928,-2921,-2910,-2898,-2891,-2881
|
|
,-2868,-2863,-2846,-2846,-2826,-2824,-2810,-2800,-2795,-2779,-2774
|
|
,-2763,-2751,-2746,2735,2724,2715,2705,2697,2682,2682,2662
|
|
,2663,2645,2641,2629,2620,2612,2602,2592,2585,2572,2566
|
|
,2557,2544,2540,-2530,-2520,-2508,-2503,-2492,-2482,-2477,-2463
|
|
,-2457,-2447,-2439,-2430,-2419,-2416,-2398,-2399,-2382,-2380,-2365
|
|
,-2364,-2346,-2345,2335,2324,2316,2308,2301,2290,2281,2275
|
|
,2263,2262,2241,2250,2220,2232,2211,2208,2199,2189,2180
|
|
,2177,2161,2163,-2154,-2137,-2138,-2119,-2122,-2102,-2107,-2087
|
|
,-2088,-2073,-2073,-2055,-2057,-2040,-2040,-2027,-2022,-2013,-2005
|
|
,-1997,-1991,-1981,1973,1967,1959,1953,1940,1941,1922,1928
|
|
,1908,1911,1892,1896,1879,1882,1862,1866,1850,1847,1840
|
|
,1829,1827,1815,-1809,-1802,-1797,-1785,-1784,-1770,-1768,-1757
|
|
,-1755,-1742,-1740,-1729,-1724,-1716,-1711,-1701,-1697,-1688,-1682
|
|
,-1676,-1665,-1663,1654,1649,1638,1637,1624,1622,1611,1611
|
|
,1594,1599,1582,1582,1573,1566,1560,1554,1547,1538,1538
|
|
,1523,1524,1512,1509,-1502,-1493,-1491,-1480,-1478,-1469,-1462
|
|
,-1458,-1450,-1444,-1441,-1427,-1431,-1414,-1418,-1405,-1400,-1397
|
|
,-1385,-1388,-1371,-1376,1370,1354,1355,1345,1341,1334,1329
|
|
,1323,1316,1311,1305,1299,1294,1287,1282,1276,1270,1266
|
|
,1255,1258,1244,1244,-1239,-1226,-1231,-1212,-1220,-1205,-1202
|
|
,-1199,-1190,-1186,-1182,-1173,-1171,-1163,-1158,-1156,-1145,-1145
|
|
,-1136,-1131,-1128,-1121,1116,1111,1106,1100,1094,1092,1082
|
|
,1084,1068,1075,1061,1061,1053,1047,1047,1037,1036,1025
|
|
,1027,1016,1017,1007,-1005,-996,-998,-986,-986,-980,-973
|
|
,-973,-962,-962,-956,-949,-949,-939,-938,-931,-929,-920
|
|
,-921,-911,-911,-903,899,896,891,885,880,881,869
|
|
,873,858,863,853,851,847,840,840,829,833,819
|
|
,826,811,815,804,-801,-800,-794,-788,-791,-778,-780
|
|
,-774,-766,-771,-755,-764,-747,-754,-742,-743,-736,-734
|
|
,-728,-727,-718,-720,715,708,708,699,703,687,697
|
|
,681,687,675,679,665,675,657,665,653,654,648
|
|
,646,642,637,634,-629,-629,-621,-623,-614,-615,-607
|
|
,-607,-602,-599,-594,-594,-586,-588,-577,-583,-571,-574
|
|
,-567,-564,-562,-559,557,548,552,541,546,532,542
|
|
,527,531,524,522,521,515,513,509,507,504,500
|
|
,497,495,490,489,-485,-482,-479,-476,-474,-468,-470
|
|
,-462,-462,-458,-455,-454,-449,-447,-443,-442,-437,-437
|
|
,-432,-429,-429,-422,420,419,417,412,411,409,404
|
|
,403,400,397,394,393,388,388,382,384,375,381
|
|
,371,372,369,364,-360,-365,-355,-360,-351,-354,-347
|
|
,-347,-345,-340,-341,-336,-335,-331,-333,-323,-329,-322
|
|
,-319,-321,-314,-316,314,308,308,303,304,300,298
|
|
,297,292,294,288,288,285,281,285,275,279,276
|
|
,270,272,269,264,-260,-267,-257,-261,-256,-254,-254
|
|
,-249,-250,-247,-244,-245,-238,-242,-235,-237,-233,-232
|
|
,-229,-229,-225,-225,222,222,218,218,214,215,211
|
|
,212,206,210,201,206,202,199,199,197,192,197
|
|
,191,188,192,183,-185,-179,-189,-174,-183,-177,-174
|
|
,-178,-169,-174,-168,-169,-167,-165,-165,-161,-162,-158
|
|
,-160,-155,-155,-155,154,149,151,148,147,146,143
|
|
,145,138,144,136,139,136,134,136,129,135,125
|
|
,134,123,130,120,-119,-126,-118,-123,-116,-122,-111
|
|
,-120,-113,-111,-115,-108,-111,-109,-108,-104,-107,-103
|
|
,-106,-99,-104,-94,93,100,95,98,93,97,88
|
|
,96,85,96,84,91,85,84,89,81,84,82
|
|
,79,84,75,82,75,-76,-75,-76,-73,-74,-71
|
|
,-73,-70,-68,-72,-65,-70,-65,-65,-67,-61,-66
|
|
,-62,-60,-62,-61,-57,54,64,52,61,52,57
|
|
,54,54,53,53,50,53,48,53,45,53,43
|
|
,51,45,45,47,42,-41,-45,-43,-40,-44,-39
|
|
,-42,-40,-38,-38,-40,-36,-39,-34,-39,-32,-37
|
|
,-33,-34,-34,-34,-29,28,35,28,32,29,30
|
|
,27,31,27,28,27,26,27,24,30,20,28
|
|
,21,27,20,27,18,-19,-23,-21,-22,-19,-23
|
|
,-16,-24,-15,-23,-15,-21,-16,-17,-20,-14,-19
|
|
,-14,-18,-13,-19,-11,11,18,10,19,8,17
|
|
,13,10,18,5,18,7,16,8,12,11,10
|
|
,10,12,6,14,5,-4,-14,-5,-10,-10,-5
|
|
,-11,-6,-8,-8,-6,-9,-5,-7,-8,-2,-12
|
|
,-1,-8,-6,-4,-7,6,5,4,7,2,6
|
|
,4,3,8,1,4,4,4,3,5,1,5
|
|
,3,1,5,2,3,-1,-5,-2,0,-5,1
|
|
,-6,2,-6,3,-6,1,-5,2,-6,5,-8
|
|
,4,-6,3,-5,3,-2,2,1,0,1,4
|
|
,-5,6,-3,1,3,-2,3,-3,5,-5,5
|
|
,-1,0,0,2,-3,4,-3,0,1,-1,-1
|
|
,2,-2,1,-1,0,1,-1,0,0,0,1
|
|
,-1,1,-2,2,-1,1,0,-3,5,-5,4
|
|
,-1,-2,2,-1,1,0,-1,1,-1,2,-3
|
|
,3,-3
|
|
};
|
|
static signed short tock_sound[] =
|
|
{
|
|
32767,32761,32767,32762,32767,32763,32767,32765,32767,32767,32766
|
|
,32767,32764,32767,32765,32767,32763,32767,32761,32767,32765,32767
|
|
,32766,32767,32766,32767,32767,32764,32767,32763,32767,32767,32766
|
|
,32767,32766,32767,32767,32765,32767,32763,32767,32761,32767,32764
|
|
,32767,-32766,-32768,-32765,-32767,-32768,-32762,-32768,-32762,-32768,-32768
|
|
,-32765,-32768,-32765,-32768,-32766,-32766,-32766,-32766,-32768,-32766,-32768
|
|
,-32766,-32768,-32767,-32768,-32766,-32768,-32767,-32767,-32768,-32763,-32768
|
|
,-32765,-32768,-32768,-32765,-32768,-32767,-32768,-32768,-32767,-32766,-32768
|
|
,-32767,32720,32666,32619,32567,32516,32471,32414,32373,32314,32270
|
|
,32220,32167,32123,32068,32023,31970,31924,31872,31824,31775,31725
|
|
,31678,31625,31581,31527,31482,31431,31383,31334,31286,31238,31185
|
|
,31144,31087,31046,30993,30947,30898,30848,30804,30750,30709,30653
|
|
,30614,-30566,-30509,-30470,-30415,-30371,-30322,-30276,-30226,-30181,-30131
|
|
,-30085,-30039,-29988,-29945,-29894,-29849,-29800,-29756,-29707,-29658,-29617
|
|
,-29562,-29522,-29473,-29425,-29380,-29334,-29283,-29242,-29193,-29145,-29103
|
|
,-29051,-29011,-28959,-28917,-28866,-28827,-28773,-28735,-28682,-28641,-28591
|
|
,-28550,28503,28457,28407,28369,28316,28275,28229,28182,28137,28094
|
|
,28044,28006,27952,27915,27864,27823,27775,27734,27682,27645,27597
|
|
,27550,27513,27455,27426,27368,27333,27284,27240,27197,27152,27105
|
|
,27069,27012,26982,26926,26891,26841,26799,26756,26712,26666,26628
|
|
,26574,-26528,-26502,-26443,-26410,-26362,-26318,-26277,-26233,-26189,-26147
|
|
,-26103,-26059,-26019,-25973,-25931,-25888,-25846,-25801,-25763,-25714,-25675
|
|
,-25633,-25587,-25549,-25502,-25463,-25419,-25378,-25333,-25294,-25249,-25208
|
|
,-25167,-25123,-25084,-25037,-25000,-24954,-24917,-24871,-24832,-24788,-24748
|
|
,-24706,24665,24622,24582,24539,24501,24454,24420,24371,24336,24292
|
|
,24251,24212,24167,24131,24084,24052,24001,23969,23921,23886,23842
|
|
,23804,23760,23724,23678,23644,23598,23561,23521,23478,23440,23401
|
|
,23356,23324,23275,23243,23197,23161,23118,23082,23039,23003,22959
|
|
,22922,-22882,-22844,-22801,-22763,-22729,-22679,-22653,-22601,-22569,-22529
|
|
,-22488,-22453,-22409,-22374,-22331,-22297,-22254,-22220,-22176,-22141,-22100
|
|
,-22064,-22024,-21985,-21947,-21910,-21869,-21833,-21794,-21754,-21721,-21675
|
|
,-21645,-21601,-21566,-21529,-21486,-21455,-21410,-21380,-21335,-21304,-21258
|
|
,-21232,21195,21144,21118,21072,21038,21003,20960,20932,20882,20858
|
|
,20810,20781,20740,20703,20668,20628,20595,20557,20518,20483,20447
|
|
,20409,20374,20336,20299,20265,20225,20194,20150,20123,20079,20047
|
|
,20008,19975,19937,19902,19866,19827,19797,19755,19725,19682,19654
|
|
,19614,-19581,-19540,-19512,-19471,-19437,-19405,-19362,-19335,-19292,-19264
|
|
,-19223,-19194,-19151,-19124,-19082,-19053,-19014,-18980,-18946,-18911,-18876
|
|
,-18842,-18804,-18775,-18735,-18704,-18668,-18634,-18598,-18568,-18527,-18500
|
|
,-18460,-18428,-18394,-18360,-18324,-18295,-18254,-18225,-18190,-18154,-18124
|
|
,-18087,18054,18021,17987,17953,17921,17884,17855,17818,17786,17754
|
|
,17718,17685,17654,17619,17586,17554,17519,17488,17454,17421,17387
|
|
,17358,17319,17295,17251,17230,17186,17164,17123,17096,17061,17028
|
|
,16996,16967,16929,16902,16865,16837,16801,16774,16736,16708,16675
|
|
,16641,16612,-16578,-16549,-16513,-16486,-16449,-16423,-16385,-16361,-16321
|
|
,-16297,-16260,-16233,-16198,-16169,-16137,-16102,-16080,-16038,-16017,-15976
|
|
,-15954,-15914,-15892,-15855,-15826,-15796,-15762,-15735,-15702,-15673,-15638
|
|
,-15617,-15572,-15556,-15515,-15489,-15461,-15424,-15400,-15365,-15338,-15304
|
|
,-15281,-15241,15212,15189,15155,15125,15098,15064,15038,15005,14977
|
|
,14947,14918,14886,14859,14827,14799,14771,14738,14711,14681,14651
|
|
,14623,14592,14565,14532,14508,14474,14449,14416,14391,14357,14334
|
|
,14301,14273,14246,14214,14188,14159,14128,14104,14069,14046,14016
|
|
,13986,13958,-13928,-13905,-13869,-13849,-13816,-13789,-13758,-13736,-13702
|
|
,-13678,-13649,-13616,-13598,-13561,-13539,-13508,-13480,-13455,-13424,-13399
|
|
,-13371,-13341,-13318,-13284,-13263,-13232,-13205,-13179,-13150,-13124,-13096
|
|
,-13069,-13041,-13017,-12984,-12965,-12930,-12908,-12881,-12849,-12831,-12793
|
|
,-12779,-12739,12714,12694,12666,12636,12616,12583,12561,12531,12509
|
|
,12476,12459,12421,12406,12373,12347,12325,12294,12271,12244,12218
|
|
,12194,12164,12142,12112,12091,12062,12038,12009,11989,11956,11939
|
|
,11903,11888,11855,11834,11806,11782,11755,11732,11705,11679,11658
|
|
,11628,11605,-11579,-11557,-11527,-11505,-11480,-11455,-11429,-11406,-11380
|
|
,-11355,-11331,-11305,-11283,-11256,-11231,-11210,-11178,-11166,-11126,-11117
|
|
,-11080,-11063,-11037,-11010,-10992,-10961,-10940,-10916,-10890,-10871,-10838
|
|
,-10825,-10791,-10774,-10747,-10724,-10699,-10679,-10648,-10634,-10600,-10586
|
|
,-10554,-10537,10513,10485,10466,10438,10420,10391,10370,10349,10321
|
|
,10305,10273,10257,10228,10211,10183,10164,10138,10115,10096,10066
|
|
,10053,10019,10007,9975,9960,9930,9916,9883,9871,9839,9823
|
|
,9801,9771,9759,9726,9714,9684,9666,9640,9623,9596,9577
|
|
,9553,9532,-9510,-9487,-9467,-9443,-9422,-9401,-9377,-9358,-9334
|
|
,-9313,-9292,-9270,-9248,-9227,-9205,-9182,-9165,-9138,-9121,-9098
|
|
,-9075,-9057,-9032,-9015,-8989,-8973,-8947,-8929,-8907,-8885,-8867
|
|
,-8842,-8824,-8802,-8780,-8763,-8737,-8720,-8699,-8676,-8657,-8637
|
|
,-8614,-8597,8576,8554,8532,8513,8494,8470,8454,8429,8413
|
|
,8390,8371,8351,8328,8314,8286,8274,8247,8233,8207,8192
|
|
,8170,8149,8134,8108,8092,8070,8053,8031,8014,7991,7974
|
|
,7953,7935,7912,7899,7872,7861,7832,7820,7798,7779,7761
|
|
,7739,7724,-7706,-7679,-7669,-7641,-7628,-7607,-7588,-7570,-7551
|
|
,-7530,-7515,-7491,-7478,-7456,-7438,-7419,-7401,-7379,-7369,-7338
|
|
,-7335,-7300,-7295,-7267,-7255,-7233,-7218,-7196,-7181,-7159,-7145
|
|
,-7124,-7107,-7090,-7068,-7055,-7033,-7018,-6999,-6979,-6966,-6942
|
|
,-6929,-6909,6891,6875,6855,6840,6820,6803,6787,6768,6750
|
|
,6733,6717,6695,6687,6657,6650,6629,6608,6599,6573,6562
|
|
,6543,6525,6508,6494,6470,6462,6437,6426,6405,6391,6371
|
|
,6358,6338,6323,6305,6289,6273,6254,6241,6221,6206,6191
|
|
,6170,6160,6136,-6121,-6108,-6090,-6075,-6057,-6044,-6023,-6013
|
|
,-5988,-5983,-5955,-5950,-5926,-5914,-5897,-5879,-5867,-5846,-5837
|
|
,-5813,-5806,-5780,-5774,-5751,-5741,-5720,-5710,-5687,-5680,-5656
|
|
,-5648,-5626,-5616,-5596,-5584,-5566,-5552,-5538,-5519,-5507,-5490
|
|
,-5475,-5463,-5440,5425,5419,5396,5385,5373,5350,5342,5324
|
|
,5308,5297,5278,5266,5250,5236,5218,5209,5190,5176,5162
|
|
,5148,5131,5120,5103,5088,5075,5061,5043,5035,5013,5005
|
|
,4988,4973,4960,4946,4931,4916,4905,4885,4879,4856,4850
|
|
,4829,4823,4799,-4785,-4783,-4758,-4749,-4738,-4718,-4710,-4693
|
|
,-4679,-4669,-4649,-4643,-4621,-4616,-4596,-4585,-4572,-4555,-4548
|
|
,-4527,-4521,-4501,-4492,-4477,-4464,-4451,-4437,-4425,-4410,-4399
|
|
,-4384,-4372,-4359,-4345,-4332,-4320,-4307,-4292,-4283,-4265,-4256
|
|
,-4243,-4227,-4217,4201,4196,4171,4173,4145,4147,4123,4117
|
|
,4103,4087,4079,4065,4051,4042,4026,4014,4005,3988,3981
|
|
,3962,3956,3939,3931,3917,3903,3892,3882,3866,3860,3841
|
|
,3831,3824,3804,3800,3782,3772,3761,3749,3737,3726,3712
|
|
,3702,3690,3679,-3667,-3656,-3640,-3637,-3614,-3614,-3594,-3587
|
|
,-3573,-3564,-3549,-3542,-3528,-3517,-3506,-3494,-3482,-3475,-3458
|
|
,-3452,-3437,-3427,-3417,-3406,-3392,-3384,-3372,-3359,-3354,-3335
|
|
,-3330,-3316,-3306,-3297,-3283,-3276,-3259,-3256,-3239,-3232,-3220
|
|
,-3210,-3196,-3192,3182,3161,3161,3143,3137,3124,3114,3103
|
|
,3096,3080,3075,3060,3054,3040,3034,3018,3013,2999,2992
|
|
,2980,2971,2958,2953,2937,2933,2918,2910,2901,2889,2881
|
|
,2869,2862,2849,2842,2828,2824,2810,2801,2793,2780,2774
|
|
,2762,2755,2740,-2730,-2729,-2711,-2706,-2697,-2683,-2681,-2665
|
|
,-2658,-2650,-2636,-2633,-2618,-2614,-2599,-2593,-2585,-2571,-2568
|
|
,-2556,-2542,-2544,-2521,-2526,-2507,-2502,-2492,-2483,-2474,-2467
|
|
,-2455,-2447,-2440,-2428,-2422,-2412,-2402,-2396,-2385,-2376,-2369
|
|
,-2359,-2351,-2344,2336,2323,2319,2303,2306,2282,2290,2269
|
|
,2268,2254,2251,2238,2234,2221,2217,2203,2203,2187,2184
|
|
,2171,2168,2155,2150,2141,2132,2125,2116,2109,2099,2094
|
|
,2083,2078,2066,2062,2052,2045,2037,2027,2023,2011,2009
|
|
,1994,1992,1980,-1973,-1967,-1959,-1950,-1945,-1937,-1926,-1923
|
|
,-1911,-1907,-1899,-1889,-1885,-1873,-1871,-1860,-1853,-1847,-1838
|
|
,-1832,-1824,-1816,-1811,-1801,-1795,-1789,-1778,-1776,-1765,-1759
|
|
,-1753,-1742,-1742,-1726,-1728,-1714,-1710,-1703,-1696,-1688,-1682
|
|
,-1676,-1664,-1667,1661,1641,1645,1632,1625,1625,1607,1612
|
|
,1596,1596,1585,1581,1570,1571,1556,1556,1546,1539,1536
|
|
,1525,1523,1512,1511,1497,1499,1487,1481,1480,1465,1466
|
|
,1455,1450,1447,1436,1434,1423,1420,1414,1407,1401,1395
|
|
,1388,1382,1379,1369,-1364,-1359,-1352,-1348,-1338,-1336,-1328
|
|
,-1322,-1318,-1310,-1305,-1300,-1291,-1290,-1280,-1277,-1269,-1267
|
|
,-1256,-1255,-1246,-1242,-1238,-1228,-1225,-1221,-1212,-1209,-1203
|
|
,-1195,-1193,-1186,-1180,-1175,-1170,-1163,-1161,-1150,-1150,-1142
|
|
,-1137,-1133,-1126,-1119,1113,1112,1105,1100,1096,1089,1084
|
|
,1080,1075,1068,1067,1054,1059,1045,1045,1041,1031,1031
|
|
,1022,1018,1016,1007,1006,997,996,987,986,979,975
|
|
,970,966,959,957,949,948,940,939,928,932,920
|
|
,918,915,907,906,-900,-896,-890,-885,-883,-877,-871
|
|
,-870,-862,-860,-856,-848,-848,-841,-837,-834,-827,-827
|
|
,-817,-817,-811,-808,-803,-798,-794,-791,-786,-783,-777
|
|
,-774,-769,-765,-763,-756,-754,-748,-746,-741,-737,-732
|
|
,-731,-722,-727,-710,707,714,704,700,702,690,693
|
|
,684,685,676,677,671,665,668,656,657,653,649
|
|
,644,645,632,640,626,631,620,623,614,615,607
|
|
,608,601,598,598,588,592,582,584,576,577,568
|
|
,571,564,561,558,-554,-553,-546,-546,-541,-540,-532
|
|
,-537,-522,-531,-519,-521,-515,-513,-510,-505,-507,-495
|
|
,-503,-489,-495,-486,-486,-481,-480,-476,-472,-472,-465
|
|
,-467,-458,-461,-453,-455,-450,-444,-446,-439,-441,-433
|
|
,-435,-428,-428,-425,424,416,417,413,411,407,407
|
|
,401,400,397,396,389,394,381,388,379,381,375
|
|
,376,368,371,365,365,360,360,356,353,353,347
|
|
,347,346,340,339,339,331,337,326,330,323,325
|
|
,319,321,312,318,-316,-305,-310,-303,-303,-302,-296
|
|
,-298,-292,-293,-289,-288,-285,-283,-281,-279,-278,-274
|
|
,-273,-269,-271,-265,-265,-264,-257,-262,-255,-254,-255
|
|
,-248,-250,-247,-243,-247,-236,-244,-232,-240,-231,-232
|
|
,-232,-223,-232,-220,219,224,217,218,216,212,212
|
|
,212,207,208,204,202,204,200,197,197,195,193
|
|
,194,187,191,186,184,187,177,186,175,180,175
|
|
,173,175,169,172,168,165,167,162,166,157,162
|
|
,156,158,156,152,-152,-151,-149,-149,-146,-147,-142
|
|
,-145,-140,-141,-138,-137,-139,-131,-138,-128,-135,-126
|
|
,-132,-124,-129,-123,-125,-121,-123,-116,-123,-116,-117
|
|
,-115,-113,-114,-112,-112,-107,-110,-108,-104,-109,-100
|
|
,-105,-103,-99,-101,99,99,94,98,93,96,89
|
|
,96,87,91,88,89,86,88,81,86,83,82
|
|
,82,78,83,73,83,73,77,76,72,74,73
|
|
,69,73,70,67,71,63,71,62,69,59,68
|
|
,58,66,58,63,57,-58,-58,-57,-56,-56,-55
|
|
,-56,-51,-57,-47,-56,-48,-53,-49,-48,-50,-45
|
|
,-50,-45,-46,-46,-43,-45,-43,-43,-41,-43,-39
|
|
,-43,-37,-42,-35,-42,-34,-39,-36,-36,-35,-35
|
|
,-34,-34,-34,-31,-35,35,28,34,27,35,23
|
|
,35,23,32,26,27,28,24,28,23,27,24
|
|
,24,25,21,25,22,22,23,19,23,20,20
|
|
,22,16,23,15,23,13,23,12,23,13,18
|
|
,16,15,16,16,14,-14,-14,-16,-11,-18,-9
|
|
,-17,-8,-17,-10,-14,-10,-11,-13,-10,-12,-9
|
|
,-10,-11,-9,-11,-8,-9,-10,-6,-13,-4,-12
|
|
,-4,-12,-2,-14,-1,-12,-3,-8,-6,-7,-6
|
|
,-6,-5,-6,-5,-6,6,3,8,2,7,2
|
|
,7,2,6,1,6,4,1,9,-5,12,-5
|
|
,10,-1,5,0,5,1,5,0,3,3,1
|
|
,3,2,1,3,0,5,-2,4,0,2,3
|
|
,-1,4,-3,6,-3,2,-2,-2,0,-1,-2
|
|
,1,-1,-2,1,-2,0,0,-2,2,-3,2
|
|
,-4,4,-5,5,-5,2,0,-1,0,1,-4
|
|
,5,-5,4,-3,2,-1,-1,1,-2,2,-2
|
|
,2,-2,2,-1,0,1,-1,0,1,-2,3
|
|
,-3,2,-1,0,1,-1,0,0,1,-1,1
|
|
,-2,3
|
|
};
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
Utilities from pdbox plugin (Copyright (C) 2009 Wincent Balin) --- am I
|
|
supposed to supply these functions with the plugin? Should I use a library?
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
/* Implementation of strtod() and atof(),
|
|
taken from SanOS (http://www.jbox.dk/sanos/). */
|
|
static int rb_errno = 0;
|
|
|
|
static double rb_strtod(const char *str, char **endptr)
|
|
{
|
|
double number;
|
|
int exponent;
|
|
int negative;
|
|
char *p = (char *) str;
|
|
double p10;
|
|
int n;
|
|
int num_digits;
|
|
int num_decimals;
|
|
|
|
/* Reset Rockbox errno -- W.B. */
|
|
#ifdef ROCKBOX
|
|
rb_errno = 0;
|
|
#endif
|
|
|
|
// Skip leading whitespace
|
|
while (isspace(*p)) p++;
|
|
|
|
// Handle optional sign
|
|
negative = 0;
|
|
switch (*p)
|
|
{
|
|
case '-': negative = 1; // Fall through to increment position
|
|
case '+': p++;
|
|
}
|
|
|
|
number = 0.;
|
|
exponent = 0;
|
|
num_digits = 0;
|
|
num_decimals = 0;
|
|
|
|
// Process string of digits
|
|
while (isdigit(*p))
|
|
{
|
|
number = number * 10. + (*p - '0');
|
|
p++;
|
|
num_digits++;
|
|
}
|
|
|
|
// Process decimal part
|
|
if (*p == '.')
|
|
{
|
|
p++;
|
|
|
|
while (isdigit(*p))
|
|
{
|
|
number = number * 10. + (*p - '0');
|
|
p++;
|
|
num_digits++;
|
|
num_decimals++;
|
|
}
|
|
|
|
exponent -= num_decimals;
|
|
}
|
|
|
|
if (num_digits == 0)
|
|
{
|
|
#ifdef ROCKBOX
|
|
rb_errno = 1;
|
|
#else
|
|
errno = ERANGE;
|
|
#endif
|
|
return 0.0;
|
|
}
|
|
|
|
// Correct for sign
|
|
if (negative) number = -number;
|
|
|
|
// Process an exponent string
|
|
if (*p == 'e' || *p == 'E')
|
|
{
|
|
// Handle optional sign
|
|
negative = 0;
|
|
switch(*++p)
|
|
{
|
|
case '-': negative = 1; // Fall through to increment pos
|
|
case '+': p++;
|
|
}
|
|
|
|
// Process string of digits
|
|
n = 0;
|
|
while (isdigit(*p))
|
|
{
|
|
n = n * 10 + (*p - '0');
|
|
p++;
|
|
}
|
|
|
|
if (negative)
|
|
exponent -= n;
|
|
else
|
|
exponent += n;
|
|
}
|
|
|
|
#ifndef ROCKBOX
|
|
if (exponent < DBL_MIN_EXP || exponent > DBL_MAX_EXP)
|
|
{
|
|
errno = ERANGE;
|
|
return HUGE_VAL;
|
|
}
|
|
#endif
|
|
|
|
// Scale the result
|
|
p10 = 10.;
|
|
n = exponent;
|
|
if (n < 0) n = -n;
|
|
while (n)
|
|
{
|
|
if (n & 1)
|
|
{
|
|
if (exponent < 0)
|
|
number /= p10;
|
|
else
|
|
number *= p10;
|
|
}
|
|
n >>= 1;
|
|
p10 *= p10;
|
|
}
|
|
|
|
#ifndef ROCKBOX
|
|
if (number == HUGE_VAL) errno = ERANGE;
|
|
#endif
|
|
if (endptr) *endptr = p;
|
|
|
|
return number;
|
|
}
|
|
|
|
static double rb_atof(const char *str)
|
|
{
|
|
return rb_strtod(str, NULL);
|
|
}
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * *
|
|
Actual metronome stuff
|
|
* * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
static int fd = -1; /* file descriptor, global for cleanup(). */
|
|
|
|
/* Round fixed-point number to integer. */
|
|
static int fp_rint(long fp_num)
|
|
{
|
|
fp_num += fp_num > 0 ? +((long)1<<15) : -((long)1<<15);
|
|
return (int)(fp_num / ((long)1<<16));
|
|
}
|
|
/* float to fixed-point */
|
|
static long fp_frac(float fl_num)
|
|
{
|
|
return (long)(fl_num*((long)1<<16));
|
|
}
|
|
|
|
/* simple dynamic memory management
|
|
- only allocate blocks serially
|
|
- deallocation of most recent blocks by resetting the free region pointer
|
|
- everything aligned to 4 bytes (wasting some bytes, but playing safe)
|
|
*/
|
|
static void *mem_begin = NULL; /* beginning of managed region. */
|
|
static void *mem_end = NULL; /* just after end of managed region */
|
|
static void *mem_free_region = NULL; /* pointer to unused free space */
|
|
static void *mem_checkpointer = NULL; /* position to reset to */
|
|
|
|
/* Initialize memory management. */
|
|
static void mem_init(void)
|
|
{
|
|
size_t bufsize;
|
|
/* Can I trust that pointer to be aligned? Better be safe. */
|
|
mem_begin = ALIGN_UP(rb->plugin_get_buffer(&bufsize), 4);
|
|
mem_end = mem_begin + bufsize - 3;
|
|
mem_free_region = mem_begin;
|
|
mem_checkpointer = mem_begin;
|
|
}
|
|
|
|
/* Remember and reset free region, for temporary mem usage. */
|
|
static void mem_checkpoint(void){ mem_checkpointer = mem_free_region; }
|
|
static void mem_reset (void){ mem_free_region = mem_checkpointer; }
|
|
static void *mem_allocate(size_t bytes)
|
|
{
|
|
void *handout = mem_free_region;
|
|
/* Always handing out multiples of alignment size. */
|
|
if(bytes % 4) bytes += 4 - bytes % 4;
|
|
if(mem_free_region + bytes >= mem_end)
|
|
{
|
|
rb->splash(2*HZ, "Out Of Memory");
|
|
return NULL;
|
|
}
|
|
mem_free_region += bytes;
|
|
return handout;
|
|
}
|
|
|
|
struct part;
|
|
struct part /* One part of a track, with one tempo (range), meter, etc. */
|
|
{
|
|
struct part *prev, *next; /* linked list links*/
|
|
unsigned int id; /* index (in order, please) */
|
|
char *label;
|
|
unsigned int bars; /* Duration of part in bars. */
|
|
unsigned int beats_per_bar; /* 3 in 3/4 */
|
|
unsigned int base_beat; /* 4 in 3/4 to adjust bpm value */
|
|
unsigned int bpm; /* base tempo (1/4 notes per minute) */
|
|
unsigned int bpm2; /* end tempo */
|
|
unsigned int *beat_bpm; /* either NULL or (bars*beats_per_bar) values */
|
|
long accel; /* fixed-point acceleration in 1/min (really) */
|
|
int volume; /* volume offset in integer dB */
|
|
/* Store pattern characters verbatim for max. 64 beats (no string
|
|
termination). One could save storage here by encoding things in bits,
|
|
or by allocating dynamically to begin with. */
|
|
char *pattern;
|
|
};
|
|
|
|
static struct part *part_list = NULL; /* linked list of parts */
|
|
static struct part *part = NULL; /* current part */
|
|
static unsigned int parts = 0; /* total number of parts */
|
|
static unsigned int bad_parts = 0; /* Count parts with parsing errors. */
|
|
|
|
/* Initialize a part that is not yet placed into the list. */
|
|
static void part_init(struct part *ps)
|
|
{
|
|
ps->prev = NULL;
|
|
ps->next = NULL;
|
|
ps->id = 0;
|
|
ps->label = NULL;
|
|
ps->bars = 0;
|
|
ps->beats_per_bar = 4;
|
|
ps->base_beat = 4;
|
|
ps->bpm = 120;
|
|
ps->bpm2 = 120;
|
|
ps->beat_bpm = NULL;
|
|
ps->accel = 0;
|
|
ps->volume = 0;
|
|
ps->pattern = NULL;
|
|
}
|
|
|
|
/* Add to the list. */
|
|
static void part_add(struct part *ps)
|
|
{
|
|
if(part)
|
|
{
|
|
part->next = ps;
|
|
ps->prev = part;
|
|
part = ps;
|
|
}
|
|
else part = part_list = ps;
|
|
|
|
ps->id = parts++;
|
|
}
|
|
|
|
/* Stay away from zero. */
|
|
static unsigned int positive(long long value)
|
|
{
|
|
return value > 0 ? value : 1;
|
|
}
|
|
|
|
/* Yay! Global state variables! */
|
|
static bool track_mode = false; /* switch for programmed tracks metronome */
|
|
static int loop = 0; /* Needed? */
|
|
static unsigned int beat = 0;
|
|
static unsigned int bar = 0; /* How big shall this become? */
|
|
/* The currently (approximate) active bpm value, set from calc_period(). */
|
|
static unsigned int bpm = 1;
|
|
|
|
/* Should be unsigned? */
|
|
static unsigned int period = 0; /* beat interval in timer ticks */
|
|
static long period_diff = 0; /* fixed-point error of last period computation */
|
|
static unsigned int minitick = 0; /* elapsed ticks */
|
|
static bool beating = false; /* A beat is/was playing and count needs to increase. */
|
|
static int display_state = 0; /* Current display state code. */
|
|
static bool display_trigger = false; /* Draw display on next occasion */
|
|
|
|
static bool sound_active = false;
|
|
static bool sound_paused = true;
|
|
|
|
/* global static buffer for messages in any situation */
|
|
static char buffer[64];
|
|
/* For line parsing, more is needed, allocated on demand.
|
|
As my memory management doesn't allow to free it, keeping it local
|
|
ist not smart. */
|
|
static char* linebuf = NULL;
|
|
size_t linebuf_size = 0;
|
|
|
|
|
|
/* global state for tempo tapping */
|
|
static bool reset_tap = false;
|
|
static int tap_count = 0;
|
|
static int tap_time = 0;
|
|
static int tap_timeout = 0;
|
|
|
|
static int bpm_step_counter = 0;
|
|
|
|
static bool sound_trigger = false;
|
|
|
|
#define MET_IS_PLAYING rb->pcm_is_playing()
|
|
#define MET_PLAY_STOP rb->audio_stop()
|
|
|
|
/* Really necessary? Cannot just play mono?
|
|
Also: This is wasted memory! */
|
|
static short tick_buf[sizeof(tick_sound)*2];
|
|
static short tock_buf[sizeof(tock_sound)*2];
|
|
|
|
/* Convert the mono samples to interleaved stereo */
|
|
static void prepare_buffers(void)
|
|
{
|
|
size_t i;
|
|
for(i = 0;i < sizeof(tick_sound)/sizeof(short);i++)
|
|
tick_buf[i*2] = tick_buf[i*2+1] = tick_sound[i];
|
|
for(i = 0;i < sizeof(tock_sound)/sizeof(short);i++)
|
|
tock_buf[i*2] = tock_buf[i*2+1] = tock_sound[i];
|
|
}
|
|
|
|
static void play_tick(void)
|
|
{
|
|
rb->pcm_play_data(NULL, NULL, tick_buf, sizeof(tick_buf));
|
|
}
|
|
|
|
static void play_tock(void)
|
|
{
|
|
rb->pcm_play_data(NULL, NULL, tock_buf, sizeof(tock_buf));
|
|
}
|
|
|
|
/* State: 0: blank/title, 1: tick, 2: tock 3: silent klick */
|
|
/* TODO: Could use more smart placement, using
|
|
lcd_getstringsize() and such. */
|
|
static void metronome_draw(struct screen* display, int state)
|
|
{
|
|
struct part *ps;
|
|
int textlen = display->lcdwidth / display->getcharwidth();
|
|
ps = part;
|
|
display->clear_display();
|
|
display->setfont(FONT_SYSFIXED);
|
|
switch(state)
|
|
{
|
|
case 0:
|
|
if(sound_paused)
|
|
{
|
|
if(track_mode) display->puts(0, 0, "Metronome Track");
|
|
else display->puts(0, 0, "Metronome");
|
|
|
|
display->hline(0, display->lcdwidth, 12);
|
|
}
|
|
break;
|
|
/* Draw odd/even ticks/tocks differently to be able to go without
|
|
display clearing in between for fast beats. */
|
|
case 1:
|
|
if((beat+1) % 2 == 0)
|
|
display->fillrect( display->lcdwidth/2, 0
|
|
, display->lcdwidth, 12 );
|
|
else
|
|
display->fillrect( 0, 0
|
|
, display->lcdwidth/2-1, 12 );
|
|
break;
|
|
case 2:
|
|
if((beat+1) % 2 == 0)
|
|
display->fillrect( display->lcdwidth/2, display->lcdheight-13
|
|
, display->lcdwidth, 12 );
|
|
else
|
|
display->fillrect( 0, display->lcdheight-13
|
|
, display->lcdwidth/2-1, 12 );
|
|
break;
|
|
case 3:
|
|
display->puts((textlen-3)/2,0, "o.O");
|
|
break;
|
|
}
|
|
|
|
if(track_mode)
|
|
{
|
|
|
|
/* One line in several. */
|
|
rb->snprintf( buffer, sizeof(buffer), "%u/%u@%u V%d"
|
|
, ps->beats_per_bar, ps->base_beat
|
|
, bpm, rb->global_settings->volume );
|
|
display->puts(0,4, buffer);
|
|
|
|
/* Would it hurt to draw a 3rd line to 2-line display?
|
|
I guess there are 3-line displays out there. */
|
|
if(ps->label && rb->strlen(ps->label))
|
|
{
|
|
rb->snprintf(buffer, sizeof(buffer), "\"%s\"", ps->label);
|
|
display->puts((textlen-rb->strlen(buffer))/2, 2, buffer);
|
|
}
|
|
|
|
/* Wildly guessing here with puts(). */
|
|
if(ps->bars)
|
|
rb->snprintf( buffer, sizeof(buffer), "P%u/%u: B%u/%u+%u"
|
|
, part->id+1, parts, bar+1, ps->bars, beat+1 );
|
|
else
|
|
rb->snprintf( buffer, sizeof(buffer), "P%u/%u: B%u/_+%u"
|
|
, part->id+1, parts, bar+1, beat+1 );
|
|
display->puts(0, 5, buffer);
|
|
|
|
}
|
|
else /* track mode */
|
|
{
|
|
|
|
if(display->screen_type==SCREEN_MAIN)
|
|
{
|
|
#ifdef MET_SYNC
|
|
display->puts(0, 5, "Select=TAP Rec=SYNC");
|
|
#else
|
|
display->puts(0, 5, "Select=TAP");
|
|
#endif
|
|
}
|
|
#ifdef HAVE_REMOTE_LCD
|
|
else
|
|
{
|
|
#ifdef MET_SYNC
|
|
display->puts(0, 5, "Rec=TAP Mode=SYNC");
|
|
#else
|
|
display->puts(0, 5, "Rec=TAP");
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
rb->snprintf( buffer, sizeof(buffer), "BPM: %d Vol: %d"
|
|
, bpm, rb->global_settings->volume );
|
|
display->puts(0,3, buffer);
|
|
|
|
display->hline(0, 111, 12);
|
|
if(sound_paused) display->puts(0,2,"start: hold select");
|
|
else display->puts(0,2,"stop : cancel");
|
|
|
|
} /* !track_mode */
|
|
|
|
display->setfont(FONT_UI);
|
|
display->update();
|
|
}
|
|
|
|
/* Trigger drawing of display at the next occasion using given state. */
|
|
static void trigger_display(int state)
|
|
{
|
|
display_state = state;
|
|
display_trigger = true;
|
|
}
|
|
|
|
/* Actually draw display. */
|
|
static void draw_display(void)
|
|
{
|
|
FOR_NB_SCREENS(i)
|
|
metronome_draw(rb->screens[i], display_state);
|
|
}
|
|
|
|
/* Modify actual volume by given offset without changing the configured one.
|
|
This is for parts with associated volume. */
|
|
static void tweak_volume(int offset)
|
|
{
|
|
int vol = rb->global_settings->volume + offset;
|
|
int minvol = rb->sound_min(SOUND_VOLUME);
|
|
int maxvol = rb->sound_max(SOUND_VOLUME);
|
|
|
|
if (vol > maxvol) vol = maxvol;
|
|
else if(vol < minvol) vol = minvol;
|
|
|
|
rb->sound_set(SOUND_VOLUME, vol);
|
|
}
|
|
|
|
/* tempo at a certain point in beat space in an accelerated part */
|
|
static long accel_tempo(struct part *ps, long offset)
|
|
{
|
|
long fp_bpm = (long)ps->bpm<<16;
|
|
long fp_bpm2 = (long)ps->bpm2<<16;
|
|
long v = fp_bpm + fp_mul(ps->accel, offset, 16);
|
|
/* Offset could be negative, actually, so ensure tempo stays within both
|
|
bounds */
|
|
if(ps->accel > 0)
|
|
{
|
|
if(v < fp_bpm) v = fp_bpm;
|
|
if(v > fp_bpm2) v = fp_bpm2;
|
|
}
|
|
else /* deceleration */
|
|
{
|
|
if(v > fp_bpm) v = fp_bpm;
|
|
if(v < fp_bpm2) v = fp_bpm2;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
/* Calculate number of ticks to wait till next beat. */
|
|
static void calc_period(void)
|
|
{
|
|
struct part *ps = part;
|
|
long deltat;
|
|
long beatlen; /* in quarter notes */
|
|
long period_fp;
|
|
|
|
beatlen = fp_div(4<<16, ps->base_beat<<16, 16);
|
|
/* Hack: Put the factor 60 in before computing deltat, to preserve
|
|
some accuracty. */
|
|
if(ps->beat_bpm)
|
|
{
|
|
bpm = ps->beat_bpm[bar*ps->beats_per_bar+beat];
|
|
deltat = fp_div(fp_mul(60<<16,beatlen,16), bpm<<16, 16);
|
|
} else
|
|
if(ps->accel == 0.f)
|
|
{ /* Fixed tempo. */
|
|
bpm = ps->bpm;
|
|
/* Minutes per base beat, from quarters per minute. */
|
|
deltat = fp_div(fp_mul(60<<16,beatlen,16), bpm<<16, 16);
|
|
}
|
|
else
|
|
{ /* Acceleration, varying period with each beat. */
|
|
long v0, v1;
|
|
long offset = (bar*ps->beats_per_bar + beat) << 16;
|
|
/* Always computed from start of part for seeking and accuracy. */
|
|
v0 = accel_tempo(ps, fp_mul(beatlen, offset, 16));
|
|
offset += 1<<16;
|
|
v1 = accel_tempo(ps, fp_mul(beatlen, offset, 16));
|
|
/* Playing safe with too small tempo changes, avoiding the acceleration
|
|
math that might divide by very small deltat. */
|
|
if(labs(v1-v0) > 1<<8)
|
|
{
|
|
/* deltat = 1.f / ps->accel * rb_log(v1/v0) */
|
|
deltat = fp_mul( fp_div(60<<16, ps->accel, 16)
|
|
, fp16_log(fp_div(v1, v0, 16))
|
|
, 16 );
|
|
bpm = fp_rint(fp_div(fp_mul(60<<16, beatlen, 16), deltat, 16));
|
|
}
|
|
else
|
|
{ /* Arbitrarily choosing v1. */
|
|
bpm = fp_rint(v1);
|
|
deltat = fp_div(fp_mul(60<<16,beatlen,16), v1, 16);
|
|
}
|
|
}
|
|
/* The treatment of the rounding error when converting to integer
|
|
period using period_diff helps a lot to keep track lengths close to
|
|
"correct" even with timerfreq_div as low as 77. Actually, I have _less_
|
|
drift than with timerfreq_div of 1000! */
|
|
period_fp = fp_mul(timerfreq_div<<16, deltat, 16) + period_diff;
|
|
period = positive(fp_rint( period_fp ));
|
|
period_diff = period_fp - (long)(period<<16);
|
|
}
|
|
|
|
/* Last beat finished, to prepare for the next one. */
|
|
static void advance_beat(void)
|
|
{
|
|
if(++beat == part->beats_per_bar)
|
|
{
|
|
beat = 0;
|
|
/* Bar counter always incremented for acceleration, but only checked
|
|
against a limit if there is one. */
|
|
++bar;
|
|
if(part->bars && bar == part->bars)
|
|
{
|
|
bar = 0;
|
|
if(part->next) part = part->next;
|
|
else
|
|
{
|
|
part = part_list;
|
|
if(!loop) sound_paused = true;
|
|
}
|
|
tweak_volume(part->volume);
|
|
}
|
|
}
|
|
/* Always recompute period, as acceleration changes it for each beat. */
|
|
calc_period();
|
|
}
|
|
|
|
/* Decide what to play, update display, play it.
|
|
Beat counting happens here, too. */
|
|
static void play_ticktock(void)
|
|
{
|
|
if(beating) advance_beat();
|
|
|
|
/* Hack: Clear trigger to avoid race condition. */
|
|
display_trigger = 0;
|
|
if(sound_paused)
|
|
{
|
|
beating = false;
|
|
display_state = 0;
|
|
draw_display();
|
|
}
|
|
else
|
|
{
|
|
char pat = 'x';
|
|
if(part->pattern) pat = part->pattern[beat];
|
|
|
|
beating = true;
|
|
/* Blinking and specific sound for tick, tock and silent beat.
|
|
Drawing display first for slow machines (YH820), to avoid
|
|
interrupting audio for regular playback. */
|
|
switch(pat)
|
|
{
|
|
case 'X':
|
|
display_state = 1;
|
|
draw_display();
|
|
play_tick();
|
|
break;
|
|
case 'x':
|
|
display_state = 2;
|
|
draw_display();
|
|
play_tock();
|
|
break;
|
|
default:
|
|
display_state = 3;
|
|
draw_display();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* helper function to change the volume by a certain amount, +/-
|
|
ripped from video.c */
|
|
static void change_volume(int delta)
|
|
{
|
|
int minvol = rb->sound_min(SOUND_VOLUME);
|
|
int maxvol = rb->sound_max(SOUND_VOLUME);
|
|
int vol = rb->global_settings->volume + delta;
|
|
|
|
if (vol > maxvol) vol = maxvol;
|
|
else if(vol < minvol) vol = minvol;
|
|
if(vol != rb->global_settings->volume)
|
|
{
|
|
rb->global_settings->volume = vol;
|
|
tweak_volume(part->volume);
|
|
trigger_display(display_state);
|
|
}
|
|
}
|
|
|
|
/*function to accelerate bpm change*/
|
|
static void change_bpm(int direction)
|
|
{
|
|
if( (bpm_step_counter < 20)
|
|
|| (bpm > 389)
|
|
|| (bpm < 10) )
|
|
bpm = bpm + direction;
|
|
else if(bpm_step_counter < 60)
|
|
bpm = bpm + direction * 2;
|
|
else
|
|
bpm = bpm + direction * 9;
|
|
|
|
if(bpm > 400) bpm = 400;
|
|
if(bpm < 1) bpm = 1;
|
|
|
|
part->bpm = bpm;
|
|
calc_period();
|
|
trigger_display(display_state);
|
|
bpm_step_counter++;
|
|
}
|
|
|
|
/* I presume the timer ensures that not more than one instance
|
|
of the callback is running at a given time. */
|
|
static void timer_callback(void)
|
|
{
|
|
++minitick;
|
|
|
|
/* Clear blinker if tempo is slow enough. */
|
|
if( (bpm*part->base_beat)/4 <= blinklimit &&
|
|
!sound_paused && minitick == period/2 )
|
|
trigger_display(0);
|
|
|
|
if(minitick >= period)
|
|
{
|
|
minitick = 0;
|
|
if(!sound_active && !sound_paused && !tap_count)
|
|
{
|
|
sound_trigger = true;
|
|
rb->reset_poweroff_timer();
|
|
}
|
|
}
|
|
|
|
if(tap_count)
|
|
{
|
|
tap_time++;
|
|
if(tap_count > 1 && tap_time > tap_timeout)
|
|
tap_count = 0;
|
|
}
|
|
}
|
|
|
|
/* Stopping playback means incrementing the beat. Normally, it would be
|
|
incremented after the passing of the current note duration, naturally
|
|
while starting the next one. */
|
|
static void metronome_pause(void)
|
|
{
|
|
if(beating)
|
|
{
|
|
/* Finish the current beat. */
|
|
advance_beat();
|
|
beating = false;
|
|
}
|
|
sound_paused = true;
|
|
trigger_display(0);
|
|
rb->timer_unregister();
|
|
}
|
|
|
|
static void metronome_unpause(void)
|
|
{
|
|
sound_paused = false;
|
|
minitick = period; /* Start playing immediately (or after a millisecond). */
|
|
/* Conserve power: Only start timer when actually playing. */
|
|
rb->timer_register( 1, NULL, TIMER_FREQ/timerfreq_div
|
|
, timer_callback IF_COP(, CPU) );
|
|
}
|
|
|
|
static void cleanup(void)
|
|
{
|
|
if(fd >= 0) rb->close(fd);
|
|
|
|
metronome_pause();
|
|
MET_PLAY_STOP; /* stop audio ISR */
|
|
tweak_volume(0);
|
|
rb->led(0);
|
|
rb->pcm_set_frequency(HW_SAMPR_DEFAULT);
|
|
}
|
|
|
|
/*
|
|
Parse part definitions from tempomap file (see header for format).
|
|
Not bothering with encoding issues here.
|
|
*/
|
|
|
|
/* parse meter spec into part structure if given token matches */
|
|
static bool parse_meter(char *token, struct part *ps)
|
|
{
|
|
char *toktok;
|
|
/* Careful not to misinterpret accelerated tempo specification:
|
|
120-150/4 -> tempo
|
|
3/4 -> meter */
|
|
if( !rb->strchr(token, '-') && (toktok = rb->strchr(token, '/')) )
|
|
{
|
|
/* Number before and after the '/'. */
|
|
int num[2];
|
|
num[0] = rb->atoi(token);
|
|
num[1] = rb->atoi(++toktok);
|
|
/* Only accept positive numbers. */
|
|
if(num[0] > 0 && num[1] > 0)
|
|
{
|
|
ps->beats_per_bar = (unsigned int) num[0];
|
|
ps->base_beat = (unsigned int) num[1];
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* Parse tempo, successful when getting a positive integer out of the token. */
|
|
static bool parse_tempo(char *token, struct part *ps)
|
|
{
|
|
char *toktok;
|
|
/* tempo[-tempo2/accel] ... first number always main tempo */
|
|
int num = rb->atoi(token);
|
|
/* Only positive numbers. This avoids the pattern string and general
|
|
strangeness, unless -150 should mean "from previous tempo to 150". */
|
|
if(num < 1) return false;
|
|
|
|
ps->bpm = (unsigned int) num;
|
|
ps->bpm2 = ps->bpm;
|
|
ps->accel = 0;
|
|
/* This parser is not fool-proof. It parses valid data, but could
|
|
do funny things if you provide tempo/tempo2-accel, for example.
|
|
My credo is that the application doesn't crash, but if you give rubbish,
|
|
you'll get rubbish. */
|
|
if( (toktok = rb->strchr(token, '-')) )
|
|
{
|
|
char *subtok = toktok+1;
|
|
float faccel = 0.;
|
|
ps->bpm2 = positive(rb->atoi(subtok));
|
|
/* Parse or compute accel in bpm/bar. */
|
|
if( (toktok = rb->strchr(subtok, '/')) )
|
|
{ /* bars/bpm */
|
|
float c = rb_atof(++toktok);
|
|
if( (c > 0.f ? c : -c) > 0.0001f)
|
|
faccel = 1./c;
|
|
}
|
|
else if( (toktok = rb->strchr(subtok, '*')) )
|
|
{ /* bpm/bar */
|
|
faccel = rb_atof(++toktok);
|
|
}
|
|
else if(ps->bars > 0)
|
|
{ /* Compute from tempo difference and bar count. */
|
|
faccel = ((float)ps->bpm2 - (float)ps->bpm)/ps->bars;
|
|
}
|
|
/* Correct sign for all cases, starting with positive value. */
|
|
if(faccel < 0) faccel = -faccel;
|
|
/* Negative only when end tempo is smaller. */
|
|
if(ps->bpm2 < ps->bpm) faccel = -faccel;
|
|
/* Convert (quarterbeats-per-minute per bar) -> 1/min, which could be
|
|
seen as beats-per-minute/beat */
|
|
faccel *= 1.f / (4.f/ps->base_beat * ps->beats_per_bar); /* 1/min */
|
|
ps->accel = fp_frac(faccel);
|
|
} else
|
|
/* The other fancy variant: One tempo per beat. */
|
|
if( (toktok = rb->strchr(token, ',')) )
|
|
{
|
|
size_t i;
|
|
char *subtok = token;
|
|
/* It is a bug when the parser called this before. Alloc once. */
|
|
if( ps->beat_bpm || !(ps->beat_bpm =
|
|
mem_allocate(sizeof(unsigned int)*ps->beats_per_bar*ps->bars)) )
|
|
return false;
|
|
for(i=0; i<ps->beats_per_bar*ps->bars; ++i)
|
|
{
|
|
int num;
|
|
if(!subtok) return false;
|
|
if(subtok != token) ++subtok;
|
|
|
|
num = rb->atoi(subtok);
|
|
if(num < 1) return false;
|
|
|
|
ps->beat_bpm[i] = (unsigned int) num;
|
|
subtok = rb->strchr(subtok, ',');
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* The metronome pattern.
|
|
Ensure that the correct meter is present before calling this! */
|
|
static bool parse_pattern(char *token, struct part *ps)
|
|
{
|
|
size_t pi;
|
|
size_t pats = rb->strlen(token);
|
|
|
|
/* First check if the pattern is valid, error out if not. */
|
|
if(pats != ps->beats_per_bar)
|
|
return false;
|
|
for(pi=0; pi<pats; ++pi)
|
|
switch(token[pi])
|
|
{
|
|
case 'X':
|
|
case 'x':
|
|
case '.':
|
|
break;
|
|
default: return false;
|
|
}
|
|
|
|
if(!(ps->pattern || (ps->pattern = mem_allocate(pats))))
|
|
return false;
|
|
/* Now store it. */
|
|
memcpy(ps->pattern, token, pats);
|
|
return true;
|
|
}
|
|
|
|
static bool parse_volume(char *token, struct part *ps)
|
|
{
|
|
float factor = rb_atof(token);
|
|
ps->volume = fp_rint(fp_decibels(fp_frac(factor > 0.f ? factor : 0.f), 16));
|
|
return true;
|
|
}
|
|
|
|
/* Check condition, set error code and bail out if violated. */
|
|
#define CHECK(a, c) if(!(a)){ errcode = c; goto parse_part_revert; }
|
|
|
|
static void parse_part(char *line, unsigned int num)
|
|
{
|
|
char *saveptr;
|
|
char *toktok;
|
|
char *token[5];
|
|
struct part *ps;
|
|
size_t tokens = 0;
|
|
unsigned int errcode = MERR_NOTHING;
|
|
|
|
while (isspace(*line)) line++;
|
|
/* Skip comments and empty lines quickly. */
|
|
if(line[0] == '#' || line[0] == 0) return;
|
|
|
|
mem_checkpoint();
|
|
CHECK(ps = mem_allocate(sizeof(struct part)), MERR_OOM);
|
|
part_init(ps);
|
|
|
|
/* Check for and store label. */
|
|
if( (toktok = rb->strchr(line, ':')) )
|
|
{
|
|
size_t len = toktok-line;
|
|
CHECK(ps->label = mem_allocate(len+1), MERR_OOM);
|
|
rb->memcpy(ps->label, line, len);
|
|
ps->label[len] = 0;
|
|
line = toktok+1;
|
|
}
|
|
|
|
CHECK(token[0] = rb->strtok_r(line, " \t", &saveptr), MERR_MISSING);
|
|
tokens = 1;
|
|
/* After the optional label, there can be up to 5 tokens of interest.
|
|
Collect them in advance to make the parser code more sane. */
|
|
while( tokens < 5
|
|
&& (token[tokens] = rb->strtok_r(NULL, " \t", &saveptr)) )
|
|
{
|
|
if(token[tokens][0] == '#')
|
|
break;
|
|
++tokens;
|
|
}
|
|
|
|
CHECK(tokens >= 2, MERR_MISSING);
|
|
|
|
/* Now try to be smart about guessing which token can be what value.
|
|
Remember: Always parse meter before pattern or tempo! */
|
|
ps->bars = (unsigned int) rb->atoi(token[0]);
|
|
if(tokens == 2) /* <bars> <tempo> */
|
|
{
|
|
CHECK(parse_tempo(token[1], ps), MERR_TEMPO);
|
|
} else
|
|
if(tokens == 3)
|
|
{
|
|
/* <bars> <meter> <tempo> */
|
|
if(parse_meter(token[1], ps))
|
|
{
|
|
CHECK(parse_tempo(token[2], ps), MERR_TEMPO);
|
|
} else
|
|
/* <bars> <tempo> <pattern> */
|
|
if(parse_pattern(token[2], ps))
|
|
{
|
|
CHECK(parse_tempo(token[1], ps), MERR_TEMPO);
|
|
} else
|
|
/* <bars> <tempo> <volume> */
|
|
{
|
|
CHECK(parse_tempo(token[1], ps), MERR_TEMPO);
|
|
CHECK(parse_volume(token[2], ps), MERR_VOLUME);
|
|
}
|
|
} else
|
|
if(tokens == 4)
|
|
{
|
|
/* <bars> <meter> <tempo> <pattern> */
|
|
if(parse_meter(token[1], ps) && parse_pattern(token[3], ps))
|
|
{
|
|
CHECK(parse_tempo(token[2], ps), MERR_TEMPO);
|
|
} else
|
|
/* <bars> <tempo> <pattern> <volume> */
|
|
if(parse_pattern(token[2], ps))
|
|
{
|
|
CHECK(parse_tempo(token[1], ps), MERR_TEMPO);
|
|
CHECK(parse_volume(token[3], ps), MERR_VOLUME);
|
|
} else
|
|
/* <bars> <meter> <tempo> <volume> */
|
|
{
|
|
CHECK(parse_meter(token[1], ps), MERR_METER);
|
|
CHECK(parse_tempo(token[2], ps), MERR_TEMPO);
|
|
CHECK(parse_volume(token[3], ps), MERR_VOLUME);
|
|
}
|
|
} else
|
|
if(tokens == 5) /* the complete set */
|
|
{
|
|
/* <bars> <meter> <tempo> <pattern> <volume> */
|
|
CHECK(parse_meter(token[1], ps), MERR_METER);
|
|
CHECK(parse_tempo(token[2], ps), MERR_TEMPO);
|
|
CHECK(parse_pattern(token[3], ps), MERR_PATTERN);
|
|
CHECK(parse_volume(token[4], ps), MERR_VOLUME);
|
|
}
|
|
|
|
if(!ps->pattern)
|
|
{
|
|
/* For parsed parts default to emphasize every first beat. */
|
|
CHECK(ps->pattern = mem_allocate(ps->beats_per_bar), MERR_OOM);
|
|
memset(ps->pattern, 'x', ps->beats_per_bar);
|
|
ps->pattern[0] = 'X';
|
|
}
|
|
|
|
part_add(ps);
|
|
return; /* all good */
|
|
|
|
/* Remove part after some error. */
|
|
parse_part_revert:
|
|
rb->snprintf(buffer, sizeof(buffer), "ERR %u @line %u", errcode, num);
|
|
rb->splash(2*HZ, buffer);
|
|
++bad_parts;
|
|
mem_reset();
|
|
}
|
|
|
|
#undef CHECK
|
|
|
|
static void step_back(void)
|
|
{
|
|
beating = false;
|
|
beat = 0;
|
|
if(bar)
|
|
{
|
|
/* Endless parts only know position 0 to step to. */
|
|
if(part->bars) --bar;
|
|
else bar = 0;
|
|
}
|
|
else if(part->prev)
|
|
{
|
|
part = part->prev;
|
|
/* This will jump to bar 0 for endless parts. */
|
|
bar = positive(part->bars)-1;
|
|
tweak_volume(part->volume);
|
|
}
|
|
/* Always calculate period for acceleration. */
|
|
calc_period();
|
|
minitick = period;
|
|
}
|
|
|
|
static void step_forw(void)
|
|
{
|
|
beating = false;
|
|
/* Stepping forward in endless part always goes to the next one, if any. */
|
|
if(part->bars == 0 || bar+1 == part->bars)
|
|
{
|
|
if(part->next)
|
|
{ /* Advanced one part. */
|
|
part = part->next;
|
|
bar = 0;
|
|
beat = 0;
|
|
tweak_volume(part->volume);
|
|
}
|
|
}
|
|
else ++bar;
|
|
/* Always calculate period for acceleration. */
|
|
calc_period();
|
|
minitick = period;
|
|
}
|
|
|
|
static void tap(void)
|
|
{
|
|
struct part *ps = part;
|
|
|
|
/* Each tap resets the position. */
|
|
beat = 0;
|
|
bar = 0;
|
|
|
|
if(tap_count == 0 || tap_time < tap_count)
|
|
tap_time = 0;
|
|
else
|
|
{
|
|
if(tap_time > 0)
|
|
{
|
|
/* Could use fixed point math and rounding, even. */
|
|
ps->bpm = 60*timerfreq_div*tap_count/tap_time;
|
|
|
|
if(ps->bpm > 400) ps->bpm = 400;
|
|
}
|
|
tap_timeout = (tap_count+2)*tap_time/tap_count;
|
|
}
|
|
|
|
tap_count++;
|
|
minitick = 0; /* sync tock to tapping */
|
|
reset_tap = false;
|
|
play_ticktock();
|
|
}
|
|
|
|
enum plugin_status plugin_start(const void* file)
|
|
{
|
|
int button;
|
|
static int last_button = BUTTON_NONE;
|
|
bool common_action;
|
|
|
|
atexit(cleanup);
|
|
|
|
mem_init();
|
|
|
|
if(MET_IS_PLAYING) MET_PLAY_STOP; /* stop audio IS */
|
|
|
|
prepare_buffers();
|
|
#if INPUT_SRC_CAPS != 0
|
|
/* Select playback */
|
|
rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
|
|
rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
|
|
#endif
|
|
rb->pcm_set_frequency(SAMPR_44);
|
|
|
|
if(file)
|
|
{
|
|
parts = 0;
|
|
bad_parts = 0;
|
|
fd = rb->open(file, O_RDONLY);
|
|
if(fd >= 0)
|
|
{
|
|
unsigned int linenum = 0;
|
|
/* Crazyness, allocating line buffer depending on free memory. */
|
|
linebuf_size = mem_end - mem_free_region > 32*1024
|
|
? 1024
|
|
: ( mem_end - mem_free_region > 16*1024
|
|
? 256
|
|
: 128 );
|
|
if(!(linebuf = mem_allocate(linebuf_size))) return PLUGIN_ERROR;
|
|
/* I'm assuming that read_line always terminates. */
|
|
while(rb->read_line(fd, linebuf, linebuf_size) > 0)
|
|
{
|
|
parse_part(linebuf, ++linenum);
|
|
}
|
|
}
|
|
rb->close(fd);
|
|
if(bad_parts)
|
|
{
|
|
rb->snprintf(buffer, sizeof(buffer), "%u bad parts", bad_parts);
|
|
rb->splash(2*HZ, buffer);
|
|
}
|
|
if(!parts)
|
|
{
|
|
rb->splash(2*HZ, "Got no parts. Bye!");
|
|
return PLUGIN_OK;
|
|
}
|
|
}
|
|
|
|
/* If no parts given, start in simple metronome mode. */
|
|
if(!parts)
|
|
{ /* Just checking the early bailout here. */
|
|
struct part *ps = mem_allocate(sizeof(struct part));
|
|
if(!ps) return PLUGIN_ERROR;
|
|
part_init(ps);
|
|
part_add(ps);
|
|
track_mode = false;
|
|
}
|
|
else track_mode = true;
|
|
|
|
part = part_list;
|
|
tweak_volume(part->volume);
|
|
calc_period();
|
|
draw_display();
|
|
|
|
/* main loop */
|
|
while(true)
|
|
{
|
|
reset_tap = true;
|
|
button = pluginlib_getaction( TIMEOUT_NOBLOCK, plugin_contexts
|
|
, PLA_ARRAY_COUNT );
|
|
if(sound_trigger)
|
|
{
|
|
sound_trigger = false;
|
|
play_ticktock(); /* Draws display before playback. */
|
|
}
|
|
|
|
common_action = false;
|
|
if(track_mode)
|
|
{
|
|
switch(button)
|
|
{
|
|
case METRONOME_START:
|
|
if(sound_paused) metronome_unpause();
|
|
else metronome_pause();
|
|
break;
|
|
case METRONOME_PAUSE:
|
|
if(!sound_paused) metronome_pause();
|
|
break;
|
|
case METRONOME_LEFT:
|
|
case METRONOME_LEFT_REP:
|
|
step_back();
|
|
trigger_display(0);
|
|
break;
|
|
case METRONOME_RIGHT:
|
|
case METRONOME_RIGHT_REP:
|
|
step_forw();
|
|
trigger_display(0);
|
|
break;
|
|
default:
|
|
common_action = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch(button)
|
|
{
|
|
case METRONOME_PAUSE:
|
|
if(!sound_paused) metronome_pause();
|
|
break;
|
|
case METRONOME_PLAY:
|
|
if(sound_paused) metronome_unpause();
|
|
break;
|
|
case METRONOME_TAP:
|
|
if(last_button != METRONOME_PLAY)
|
|
{
|
|
if(sound_paused) metronome_unpause();
|
|
tap();
|
|
}
|
|
break;
|
|
case METRONOME_LEFT:
|
|
bpm_step_counter = 0;
|
|
/* fallthrough */
|
|
case METRONOME_LEFT_REP:
|
|
change_bpm(-1);
|
|
break;
|
|
case METRONOME_RIGHT:
|
|
bpm_step_counter = 0;
|
|
/* fallthrough */
|
|
case METRONOME_RIGHT_REP:
|
|
change_bpm(1);
|
|
break;
|
|
#ifdef MET_SYNC
|
|
case METRONOME_SYNC:
|
|
minitick = period;
|
|
break;
|
|
#endif
|
|
default:
|
|
common_action = true;
|
|
}
|
|
}
|
|
|
|
if(common_action)
|
|
switch(button)
|
|
{
|
|
case METRONOME_QUIT:
|
|
/* get out of here */
|
|
return PLUGIN_OK;
|
|
case METRONOME_VOL_UP:
|
|
case METRONOME_VOL_UP_REP:
|
|
change_volume(1);
|
|
trigger_display(0);
|
|
break;
|
|
case METRONOME_VOL_DOWN:
|
|
case METRONOME_VOL_DOWN_REP:
|
|
change_volume(-1);
|
|
trigger_display(0);
|
|
break;
|
|
default:
|
|
exit_on_usb(button);
|
|
reset_tap = false;
|
|
break;
|
|
}
|
|
|
|
if(button)
|
|
last_button = button;
|
|
if(reset_tap)
|
|
tap_count = 0;
|
|
/* If there was some action, display drawing is still needed.
|
|
This _might_ disturb audio on slow machines, but
|
|
then, you could just stop pressing buttons, then;-) */
|
|
if(display_trigger)
|
|
{
|
|
display_trigger = false;
|
|
draw_display();
|
|
}
|
|
|
|
/* This determines the accuracy of the metronome with SWCODEC ... the
|
|
scheduler decides when we are allowed to play. */
|
|
rb->yield();
|
|
}
|
|
}
|