You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

468 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCETICE project - Copyright 2009 by Lucio Asnaghi.
  4. JUCETICE is based around the JUCE library - "Jules' Utility Class Extensions"
  5. Copyright 2007 by Julian Storer.
  6. ------------------------------------------------------------------------------
  7. JUCE and JUCETICE can be redistributed and/or modified under the terms of
  8. the GNU General Public License, as published by the Free Software Foundation;
  9. either version 2 of the License, or (at your option) any later version.
  10. JUCE and JUCETICE are distributed in the hope that they will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with JUCE and JUCETICE; if not, visit www.gnu.org/licenses or write to
  16. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  17. Boston, MA 02111-1307 USA
  18. ==============================================================================
  19. */
  20. BEGIN_JUCE_NAMESPACE
  21. //==============================================================================
  22. enum TuningMapSection
  23. {
  24. SEC_None = 0,
  25. SEC_Unknown = 1,
  26. SEC_Tuning = 2,
  27. SEC_ExactTuning = 3
  28. };
  29. extern const String g_equal_tuning;
  30. //==============================================================================
  31. TuningMap::TuningMap()
  32. {
  33. reset(); // Provide a standard tuning
  34. MemoryInputStream mis((const char*)g_equal_tuning.toUTF8(), g_equal_tuning.length(), false);
  35. readFromStream(mis);
  36. }
  37. TuningMap::~TuningMap()
  38. {
  39. }
  40. //==============================================================================
  41. void TuningMap::reset()
  42. {
  43. m_dblBaseFreq = 8.1757989156437073336; // Means A = 440Hz
  44. for ( int i = 0 ; i < 128 ; ++i )
  45. m_dblTunes[i] = 100 * i;
  46. }
  47. //==============================================================================
  48. bool TuningMap::writeToStream (OutputStream& ofs, bool bSaveBaseFreq)
  49. {
  50. int i;
  51. char endl = '\n';
  52. ofs << ";" << endl;
  53. ofs << "; AnaMark / VAZ 1.5 Plus-compatible tuning map file" << endl;
  54. ofs << ";" << endl;
  55. ofs << ";" << endl;
  56. ofs << "; 1. VAZ-section with quantized tunings" << endl;
  57. ofs << ";" << endl;
  58. ofs << "[Tuning]" << endl;
  59. for ( i = 0 ; i < 128 ; ++i )
  60. ofs << "note " << i << "=" << (int)(m_dblTunes[i]) << endl;
  61. ofs << ";" << endl;
  62. ofs << "; 2. AnaMark-specific section with exact tunings" << endl;
  63. ofs << ";" << endl;
  64. ofs << "[Exact Tuning]" << endl;
  65. // ofs.precision(10);
  66. if ( bSaveBaseFreq )
  67. ofs << "basefreq = " << m_dblBaseFreq << endl;
  68. for ( i = 0 ; i < 128 ; ++i )
  69. ofs << "note " << i << "=" << m_dblTunes[i] << endl;
  70. // Always returns true, because currently there's no error processing
  71. return true;
  72. }
  73. //==============================================================================
  74. bool TuningMap::readFromStream (InputStream& ifs)
  75. {
  76. char szLine[512];
  77. long lTunes[128]; // For temporary use, unit: Cents
  78. bool bTuningFound = false;
  79. bool bExactTuningFound = false;
  80. long lET_LastNoteFound = -1;
  81. long lLineCount = 0;
  82. TuningMapSection secCurr = SEC_None;
  83. // Initialize data
  84. // Important, because notes not listed in the tuning file
  85. // should always have standard tuning.
  86. reset();
  87. for ( int i = 0 ; i < 128 ; ++i )
  88. lTunes[i] = (long)m_dblTunes[i];
  89. while ( !ifs.isExhausted() )
  90. {
  91. // Increase Line counter to make it easier detecting errors, if
  92. // a more detailed output is wanted.
  93. ++lLineCount;
  94. // Read line, until '\n', '\r' or '\0' is reached
  95. // Thus it is able to read WIN/DOS-style as well as UNIX-style files
  96. // By the way: Skip empty lines or multiple line-end-characters
  97. // Is not case sensitive, so all chars are converted to lower ones
  98. int nCurrPos = 0;
  99. do
  100. {
  101. while ( (!ifs.isExhausted()) && (nCurrPos < 510) )
  102. {
  103. char ch = '\0';
  104. ifs.read(&ch, 1);
  105. if ( (ch == '\0') || (ch == '\r') || (ch == '\n') )
  106. break;
  107. szLine[nCurrPos++] = (char) tolower(ch);
  108. }
  109. } while ( (!ifs.isExhausted()) && (nCurrPos == 0) );
  110. if ( nCurrPos >= 510 )
  111. {
  112. // xTRACE( "Line too long - line " << lLineCount );
  113. return false; // Line too long
  114. }
  115. szLine[nCurrPos] = '\0';
  116. // Skip leading spaces/tabs
  117. const char * szCurr = szLine;
  118. while ( (*szCurr != '\0') && ((*szCurr == ' ') || (*szCurr == '\t')) )
  119. ++szCurr;
  120. // Skip empty lines
  121. if ( szCurr[0] == '\0' )
  122. continue;
  123. // Skip comment lines
  124. if ( szCurr[0] == ';' )
  125. continue;
  126. // Skip trailing spaces/tabs
  127. char * szLast = (char *) &szCurr[strlen(szCurr)-1];
  128. while ( (szLast > szCurr) && ((*szLast == ' ') || (*szLast == '\t')) )
  129. --szLast;
  130. *(szLast+1) = '\0';
  131. // Check for new section
  132. if ( szCurr[0] == '[' )
  133. {
  134. if ( szCurr[strlen(szCurr)-1] != ']' )
  135. {
  136. // xTRACE( "Syntax error: Section-tag must be the only string in the line! - line " << lLineCount );
  137. return false; // error in section-tag! Must be the only one string in the line!
  138. }
  139. // Known section found?
  140. secCurr = SEC_Unknown;
  141. if ( strcmp(&szCurr[1], "tuning]") == 0 )
  142. {
  143. secCurr = SEC_Tuning;
  144. bTuningFound = true;
  145. }
  146. if ( strcmp(&szCurr[1], "exact tuning]") == 0 )
  147. {
  148. secCurr = SEC_ExactTuning;
  149. bExactTuningFound = true;
  150. }
  151. // Now process next line
  152. continue;
  153. }
  154. // Skip all lines which are in none or in an unknown section
  155. if ( (secCurr == SEC_None) || (secCurr == SEC_Unknown) )
  156. continue;
  157. // Separate parameter name and value
  158. const char * szParam = szCurr;
  159. const char * szValue = strchr(szCurr, '=');
  160. if ( szValue == 0 )
  161. {
  162. // xTRACE( "Syntax error: '=' missing! - line " << lLineCount );
  163. return false; // definitely an error: '=' missing!
  164. }
  165. ++szValue; // Set the pointer to the first char behind the '='
  166. // Now skip trailing spaces/tabs of parameter name:
  167. szLast = (char *) &szValue[-2];
  168. while ( (szLast > szParam) && ((*szLast == ' ') || (*szLast == '\t')) )
  169. --szLast;
  170. *(szLast+1) = '\0';
  171. // Now skip leading spaces/tabs of value:
  172. while ( (*szValue != '\0') && ((*szValue == ' ') || (*szValue == '\t')) )
  173. ++szValue;
  174. // Now process the different sections:
  175. switch ( secCurr )
  176. {
  177. case SEC_Tuning:
  178. {
  179. // Check for note-tag
  180. if ( memcmp(szParam, "note", 4) != 0 )
  181. continue; // note-tag not found, ignore line in case that it's
  182. // an option of a later version
  183. // If you want, you can return here false and process it as an error
  184. // Get MIDI-Note number
  185. int lNoteIndex = atol(&szParam[4]);
  186. // Check for correct range [0;127] and ignore it, if it's out
  187. // of range.
  188. if ( (lNoteIndex < 0) || (lNoteIndex > 127) )
  189. continue;
  190. lTunes[lNoteIndex] = atol(szValue);
  191. }
  192. break;
  193. case SEC_ExactTuning:
  194. {
  195. // Check for note-tag
  196. if ( memcmp(szParam, "note", 4) == 0 )
  197. {
  198. // note-tag found
  199. // Get MIDI-Note number
  200. int lNoteIndex = atol(&szParam[4]);
  201. // Check for correct range [0;127] and ignore it, if it's out
  202. // of range.
  203. if ( (lNoteIndex < 0) || (lNoteIndex > 127) )
  204. continue;
  205. m_dblTunes[lNoteIndex] = atof(szValue);
  206. if ( lET_LastNoteFound < lNoteIndex )
  207. lET_LastNoteFound = lNoteIndex;
  208. // O.K. -> Process next line
  209. continue;
  210. }
  211. // Check for basefreq parameter
  212. if ( strcmp(szParam, "basefreq") == 0 )
  213. {
  214. // basefreq found
  215. m_dblBaseFreq = atof(szValue);
  216. // O.K. -> Process next line
  217. continue;
  218. }
  219. // No known parameter found, so skip it
  220. continue;
  221. }
  222. break;
  223. default: // This part of the code should never be reached!
  224. // assert(false);
  225. // xTRACE( "Compilation error! Section not coded! - line " << lLineCount );
  226. return false;
  227. }
  228. }
  229. if ( (!bTuningFound) && (!bExactTuningFound) )
  230. {
  231. // xTRACE( "No tuning data found!" );
  232. return false; // No tuning data found at all should be worth an error...
  233. }
  234. if ( !bExactTuningFound )
  235. {
  236. // There are no exact tuning values, so map the quantized
  237. // values to the exact ones:
  238. for ( int i = 0 ; i < 128 ; ++i )
  239. m_dblTunes[i] = (double) lTunes[i];
  240. }
  241. else
  242. {
  243. // [Exact Tuning] section found, so ignore the values found
  244. // in the [Tuning] section and do the "auto expand":
  245. if ( (lET_LastNoteFound >= 0) && (lET_LastNoteFound < 127) )
  246. {
  247. // Now loop the given data (auto expand):
  248. int H = lET_LastNoteFound; // Highest MIDI note number
  249. double P = m_dblTunes[H]; // Period length
  250. for ( int i = H ; i < 128 ; ++i )
  251. m_dblTunes[i] = m_dblTunes[i-H] + P;
  252. }
  253. }
  254. return true; // Everything nice!
  255. }
  256. //==============================================================================
  257. bool TuningMap::setBaseFreq (double dblBaseFreq)
  258. {
  259. // jassert( dblBaseFreq > 0 );
  260. // First make sure, that the base frequency is in the valid range
  261. if ( dblBaseFreq > 0 )
  262. {
  263. m_dblBaseFreq = dblBaseFreq;
  264. return true;
  265. }
  266. else
  267. {
  268. return false;
  269. }
  270. }
  271. bool TuningMap::setRelativeTune (int nNoteIndex, double dblTune)
  272. {
  273. // jassert( (nNoteIndex >= 0) && (nNoteIndex <= 127) );
  274. // First make sure, that the note index is in the valid range
  275. if ( (nNoteIndex >= 0) && (nNoteIndex <= 127) )
  276. {
  277. m_dblTunes[nNoteIndex] = dblTune;
  278. return true;
  279. }
  280. else
  281. {
  282. return false;
  283. }
  284. }
  285. //==============================================================================
  286. const String g_equal_tuning =
  287. "; VAZ Plus 1.5 tuning map file \
  288. ; Equal Temperament\
  289. [Tuning]\
  290. note 0=0\
  291. note 1=100\
  292. note 2=200\
  293. note 3=300\
  294. note 4=400\
  295. note 5=500\
  296. note 6=600\
  297. note 7=700\
  298. note 8=800\
  299. note 9=900\
  300. note 10=1000\
  301. note 11=1100\
  302. note 12=1200\
  303. note 13=1300\
  304. note 14=1400\
  305. note 15=1500\
  306. note 16=1600\
  307. note 17=1700\
  308. note 18=1800\
  309. note 19=1900\
  310. note 20=2000\
  311. note 21=2100\
  312. note 22=2200\
  313. note 23=2300\
  314. note 24=2400\
  315. note 25=2500\
  316. note 26=2600\
  317. note 27=2700\
  318. note 28=2800\
  319. note 29=2900\
  320. note 30=3000\
  321. note 31=3100\
  322. note 32=3200\
  323. note 33=3300\
  324. note 34=3400\
  325. note 35=3500\
  326. note 36=3600\
  327. note 37=3700\
  328. note 38=3800\
  329. note 39=3900\
  330. note 40=4000\
  331. note 41=4100\
  332. note 42=4200\
  333. note 43=4300\
  334. note 44=4400\
  335. note 45=4500\
  336. note 46=4600\
  337. note 47=4700\
  338. note 48=4800\
  339. note 49=4900\
  340. note 50=5000\
  341. note 51=5100\
  342. note 52=5200\
  343. note 53=5300\
  344. note 54=5400\
  345. note 55=5500\
  346. note 56=5600\
  347. note 57=5700\
  348. note 58=5800\
  349. note 59=5900\
  350. note 60=6000\
  351. note 61=6100\
  352. note 62=6200\
  353. note 63=6300\
  354. note 64=6400\
  355. note 65=6500\
  356. note 66=6600\
  357. note 67=6700\
  358. note 68=6800\
  359. note 69=6900\
  360. note 70=7000\
  361. note 71=7100\
  362. note 72=7200\
  363. note 73=7300\
  364. note 74=7400\
  365. note 75=7500\
  366. note 76=7600\
  367. note 77=7700\
  368. note 78=7800\
  369. note 79=7900\
  370. note 80=8000\
  371. note 81=8100\
  372. note 82=8200\
  373. note 83=8300\
  374. note 84=8400\
  375. note 85=8500\
  376. note 86=8600\
  377. note 87=8700\
  378. note 88=8800\
  379. note 89=8900\
  380. note 90=9000\
  381. note 91=9100\
  382. note 92=9200\
  383. note 93=9300\
  384. note 94=9400\
  385. note 95=9500\
  386. note 96=9600\
  387. note 97=9700\
  388. note 98=9800\
  389. note 99=9900\
  390. note 100=10000\
  391. note 101=10100\
  392. note 102=10200\
  393. note 103=10300\
  394. note 104=10400\
  395. note 105=10500\
  396. note 106=10600\
  397. note 107=10700\
  398. note 108=10800\
  399. note 109=10900\
  400. note 110=11000\
  401. note 111=11100\
  402. note 112=11200\
  403. note 113=11300\
  404. note 114=11400\
  405. note 115=11500\
  406. note 116=11600\
  407. note 117=11700\
  408. note 118=11800\
  409. note 119=11900\
  410. note 120=12000\
  411. note 121=12100\
  412. note 122=12200\
  413. note 123=12300\
  414. note 124=12400\
  415. note 125=12500\
  416. note 126=12600\
  417. note 127=12700\
  418. ";
  419. END_JUCE_NAMESPACE