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.

466 lines
13KB

  1. #!/usr/bin/python2.5
  2. #
  3. # Copyright 2014 Olivier Gillet.
  4. #
  5. # Author: Olivier Gillet (ol.gillet@gmail.com)
  6. #
  7. # Permission is hereby granted, free of charge, to any person obtaining a copy
  8. # of this software and associated documentation files (the "Software"), to deal
  9. # in the Software without restriction, including without limitation the rights
  10. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. # copies of the Software, and to permit persons to whom the Software is
  12. # furnished to do so, subject to the following conditions:
  13. #
  14. # The above copyright notice and this permission notice shall be included in
  15. # all copies or substantial portions of the Software.
  16. #
  17. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. # THE SOFTWARE.
  24. #
  25. # See http://creativecommons.org/licenses/MIT/ for more information.
  26. #
  27. # -----------------------------------------------------------------------------
  28. #
  29. # Lookup table definitions.
  30. import numpy
  31. """----------------------------------------------------------------------------
  32. LFO and portamento increments.
  33. ----------------------------------------------------------------------------"""
  34. lookup_tables_32 = []
  35. sample_rate = 4000
  36. min_frequency = 1.0 / 2.0 # Hertz
  37. max_frequency = 16.0 # Hertz
  38. excursion = 1 << 32
  39. num_values = 128
  40. min_increment = excursion * min_frequency / sample_rate
  41. max_increment = excursion * max_frequency / sample_rate
  42. rates = numpy.linspace(numpy.log(min_increment),
  43. numpy.log(max_increment), num_values)
  44. lookup_tables_32.append(
  45. ('lfo_increments', numpy.exp(rates).astype(int))
  46. )
  47. # Create lookup table for portamento.
  48. max_time = 6.0 # seconds
  49. min_time = 3.0 / sample_rate
  50. gamma = 0.25
  51. min_increment = excursion / (max_time * sample_rate)
  52. max_increment = excursion / (min_time * sample_rate)
  53. rates = numpy.linspace(numpy.power(max_increment, -gamma),
  54. numpy.power(min_increment, -gamma), num_values)
  55. values = numpy.power(rates, -1/gamma).astype(int)
  56. lookup_tables_32.append(
  57. ('portamento_increments', values)
  58. )
  59. sample_rate = 48000
  60. # Create table for pitch.
  61. a4_midi = 69
  62. a4_pitch = 440.0
  63. highest_octave = 116
  64. notes = numpy.arange(
  65. highest_octave * 128.0,
  66. (highest_octave + 12) * 128.0 + 16,
  67. 16)
  68. pitches = a4_pitch * 2 ** ((notes - a4_midi * 128) / (128 * 12))
  69. increments = excursion / sample_rate * pitches
  70. lookup_tables_32.append(
  71. ('oscillator_increments', increments.astype(int)))
  72. """----------------------------------------------------------------------------
  73. Envelope curves
  74. -----------------------------------------------------------------------------"""
  75. lookup_tables = []
  76. lookup_tables_signed = []
  77. env_linear = numpy.arange(0, 257.0) / 256.0
  78. env_linear[-1] = env_linear[-2]
  79. env_expo = 1.0 - numpy.exp(-4 * env_linear)
  80. lookup_tables.append(('env_expo', env_expo / env_expo.max() * 65535.0))
  81. """----------------------------------------------------------------------------
  82. Arpeggiator patterns
  83. ----------------------------------------------------------------------------"""
  84. def XoxTo16BitInt(pattern):
  85. uint16 = 0
  86. i = 0
  87. for char in pattern:
  88. if char == 'o':
  89. uint16 += (2 ** i)
  90. i += 1
  91. elif char == '-':
  92. i += 1
  93. assert i == 16
  94. return uint16
  95. def ConvertPatterns(patterns):
  96. return [XoxTo16BitInt(pattern) for pattern in patterns]
  97. lookup_tables.append(
  98. ('arpeggiator_patterns', ConvertPatterns([
  99. 'o-o- o-o- o-o- o-o-',
  100. 'o-o- oooo o-o- oooo',
  101. 'o-o- oo-o o-o- oo-o',
  102. 'o-o- o-oo o-o- o-oo',
  103. 'o-o- o-o- oo-o -o-o',
  104. 'o-o- o-o- o--o o-o-',
  105. 'o-o- o--o o-o- o--o',
  106. 'o--o ---- o--o ----',
  107. 'o--o --o- -o-- o--o',
  108. 'o--o --o- -o-- o-o-',
  109. 'o--o --o- o--o --o-',
  110. 'o--o o--- o-o- o-oo',
  111. 'oo-o -oo- oo-o -oo-',
  112. 'oo-o o-o- oo-o o-o-',
  113. 'ooo- ooo- ooo- ooo-',
  114. 'ooo- oo-o o-oo -oo-',
  115. 'ooo- o-o- ooo- o-o-',
  116. 'oooo -oo- oooo -oo-',
  117. 'oooo o-oo -oo- ooo-',
  118. 'o--- o--- o--o -o-o',
  119. 'o--- --oo oooo -oo-',
  120. 'o--- ---- o--- o-oo'])))
  121. """----------------------------------------------------------------------------
  122. Euclidean patterns
  123. ----------------------------------------------------------------------------"""
  124. def Flatten(l):
  125. if hasattr(l, 'pop'):
  126. for item in l:
  127. for j in Flatten(item):
  128. yield j
  129. else:
  130. yield l
  131. def EuclideanPattern(k, n):
  132. pattern = [[1]] * k + [[0]] * (n - k)
  133. while k:
  134. cut = min(k, len(pattern) - k)
  135. k, pattern = cut, [pattern[i] + pattern[k + i] for i in xrange(cut)] + \
  136. pattern[cut:k] + pattern[k + cut:]
  137. return pattern
  138. table = []
  139. for num_steps in xrange(1, 33):
  140. for num_notes in xrange(32):
  141. num_notes = min(num_notes, num_steps)
  142. bitmask = 0
  143. for i, bit in enumerate(Flatten(EuclideanPattern(num_notes, num_steps))):
  144. if bit:
  145. bitmask |= (1 << i)
  146. table.append(bitmask)
  147. lookup_tables_32 += [('euclidean', table)]
  148. """----------------------------------------------------------------------------
  149. Just intonation tuning table
  150. ----------------------------------------------------------------------------"""
  151. intervals = [
  152. (1, 1),
  153. (9, 8),
  154. (5, 4),
  155. (4, 3),
  156. (3, 2),
  157. (5, 3),
  158. (15, 8),
  159. (256, 243),
  160. (16, 15),
  161. (10, 9),
  162. (32, 27),
  163. (6, 5),
  164. (81, 64),
  165. (45, 32),
  166. (1024, 729),
  167. (64, 45),
  168. (729, 512),
  169. (128, 81),
  170. (8, 5),
  171. (27, 16),
  172. (16, 9),
  173. (9, 5),
  174. (243, 128),
  175. # (7, 4),
  176. # (7, 5),
  177. # (7, 6),
  178. # (10, 7),
  179. # (11, 7),
  180. # (15, 14),
  181. # (21, 11)
  182. ]
  183. consonant_intervals = []
  184. for interval in intervals:
  185. p, q = interval
  186. midi_ratio = int(numpy.round(1536 * numpy.log2(float(p) / q)))
  187. # The larger the number in the numerator and denominator of the fraction
  188. # representing the interval, the more dissonant it is.
  189. consonance_score = (numpy.log2(p * q) ** 2)
  190. consonant_intervals.append((midi_ratio, consonance_score))
  191. consonance_table = [0] * 1536
  192. for i in xrange(1536):
  193. nearest = numpy.argmin(
  194. [min(abs(i - p), abs(i - p - 1536)) for (p, _) in consonant_intervals])
  195. index, consonance_score = consonant_intervals[nearest]
  196. consonance_score += min((i - index) ** 2, (i - index - 1536) ** 2)
  197. consonance_table[i] = consonance_score
  198. lookup_tables.append(('consonance', consonance_table))
  199. """----------------------------------------------------------------------------
  200. List of 22 shrutis with different notation schemes.
  201. The most common notation scheme is in the 3th column.
  202. ----------------------------------------------------------------------------"""
  203. shrutis = [
  204. # Swara ref 1, Swara ref 2, Swara, Swara (carnatic, common), Just, Ratio
  205. ('S', 'sa', 's', 'C', 1),
  206. ('r1', 'ra', 'r1', 'pC#', 256.0/243.0),
  207. ('r2', 'ri', 'r2', 'C#', 16.0/15.0),
  208. ('R1', 'ru', 'r3', '?', 10.0/9.0),
  209. ('R2', 're', 'r4', 'D', 9.0/8.0),
  210. ('g1', 'ga', 'g1', 'pD#', 32.0/27.0),
  211. ('g2', 'gi', 'g2', 'D#', 6.0/5.0),
  212. ('G1', 'gu', 'g3', 'E', 5.0/4.0),
  213. ('G2', 'ge', 'g4', 'pE', 81.0/64.0),
  214. ('m1', 'ma', 'm1', 'F', 4.0/3.0),
  215. ('m2', 'mi', 'm2', '?', 27.0/20.0),
  216. ('M1', 'mu', 'm3', 'F#', 45.0/32.0),
  217. ('M2', 'me', 'm4', 'pF#', 729.0/512.0),
  218. ('P', 'pa', 'p', 'G', 3.0/2.0),
  219. ('d1', 'dha', 'd1', 'pG#', 128.0/81.0),
  220. ('d2', 'dhi', 'd2', 'G#', 8.0/5.0),
  221. ('D1', 'dhu', 'd3', 'A', 5.0/3.0),
  222. ('D2', 'dhe', 'd4', 'pA', 27.0/16.0),
  223. ('n1', 'na', 'n1', 'A#', 16.0/9.0),
  224. ('n2', 'ni', 'n2', '?', 9.0/5.0),
  225. ('N1', 'nu', 'n3', 'B', 15.0/8.0),
  226. ('N2', 'ne', 'n4', 'pB', 243.0/128.0),
  227. ('!', '!', '!', '!', 100),
  228. ]
  229. def DecodeShrutiChart(line):
  230. values = [
  231. 's',
  232. 'r1',
  233. 'r2',
  234. 'r3',
  235. 'r4',
  236. 'g1',
  237. 'g2',
  238. 'g3',
  239. 'g4',
  240. 'm1',
  241. 'm2',
  242. 'm3',
  243. 'm4',
  244. 'p',
  245. 'd1',
  246. 'd2',
  247. 'd3',
  248. 'd4',
  249. 'n1',
  250. 'n2',
  251. 'n3',
  252. 'n4'
  253. ]
  254. decoded = []
  255. for i, x in enumerate(line):
  256. if x != '-':
  257. decoded.append(values[i])
  258. return ' '.join(decoded)
  259. """----------------------------------------------------------------------------
  260. A recommended key on the keyboard for each of the swara.
  261. From:
  262. http://commons.wikimedia.org/wiki/Melakarta_ragams_(svg)
  263. ----------------------------------------------------------------------------"""
  264. recommended_keys = {
  265. 's': 0,
  266. 'r1': 1,
  267. 'r2': 1,
  268. 'r3': 2,
  269. 'r4': 2,
  270. 'g1': 3,
  271. 'g2': 3,
  272. 'g3': 4,
  273. 'g4': 4,
  274. 'm1': 5,
  275. 'm2': 6,
  276. 'm3': 6,
  277. 'm4': 6,
  278. 'p': 7,
  279. 'd1': 8,
  280. 'd2': 8,
  281. 'd3': 9,
  282. 'd4': 9,
  283. 'n1': 10,
  284. 'n2': 10,
  285. 'n3': 11,
  286. 'n4': 11
  287. }
  288. shruti_dictionary = {}
  289. for entry in shrutis:
  290. for name in entry[:-1]:
  291. shruti_dictionary[name] = entry[-1]
  292. def Compute(scale):
  293. """Translate a list of 12 note/swaras names into pitch corrections."""
  294. values = [shruti_dictionary.get(x) for x in scale.split(' ')]
  295. while 100 in values:
  296. for i, v in enumerate(values):
  297. if v == 100:
  298. values[i] = values[i- 1]
  299. equal = 2 ** (numpy.arange(12.0) / 12.0)
  300. shifts = numpy.round((numpy.log2(values / equal) * 12 * 128)).astype(int)
  301. silences = numpy.where(shifts > 127)
  302. if len(silences[0]):
  303. shifts[silences[0]] = 32767
  304. return shifts
  305. def LayoutRaga(raga, silence_other_notes=False):
  306. """Find a good assignments of swaras to keys for a raga."""
  307. raga = raga.lower()
  308. scale = numpy.zeros((12,))
  309. mapping = ['' for i in range(12)]
  310. for swara in raga.split(' '):
  311. key = recommended_keys.get(swara)
  312. mapping[key] = swara
  313. # Fill unassigned notes
  314. for i, n in enumerate(mapping):
  315. if n == '':
  316. if silence_other_notes:
  317. mapping[i] = '!'
  318. else:
  319. candidates = []
  320. for swara, key in recommended_keys.items():
  321. if key == i:
  322. candidates.append(swara)
  323. for candidate in candidates:
  324. if candidate[0] != mapping[i - 1]:
  325. mapping[i] = candidate
  326. break
  327. else:
  328. mapping[i] = candidates[0]
  329. scale = [shruti_dictionary.get(swara) for swara in mapping]
  330. return Compute(' '.join(mapping))
  331. altered_e_b = [0, 0, 0, 0, -64, 0, 0, 0, 0, 0, 0, -64]
  332. altered_e = [0, 0, 0, 0, -64, 0, 0, 0, 0, 0, 0, 0]
  333. altered_e_a = [0, 0, 0, 0, -64, 0, 0, 0, 0, -64, 0, 0]
  334. scales = [
  335. ('pythagorean', Compute('C pC# D pD# pE F pF# G pG# pA A# pB')),
  336. ('1/4 eb', numpy.array(altered_e_b, dtype=int)),
  337. ('1/4 e', numpy.array(altered_e, dtype=int)),
  338. ('1/4 ea', numpy.array(altered_e_a, dtype=int)),
  339. ('bhairav',
  340. LayoutRaga(DecodeShrutiChart('sr-----g-m---pd-----n-'), True)),
  341. ('gunakri',
  342. LayoutRaga(DecodeShrutiChart('s-r------m---p-d------'), True)),
  343. ('marwa',
  344. LayoutRaga(DecodeShrutiChart('s-r----g---m----d---n-'), True)),
  345. ('shree',
  346. LayoutRaga(DecodeShrutiChart('sr-----g---m-pd-----n-'), True)),
  347. ('purvi',
  348. LayoutRaga(DecodeShrutiChart('s-r----g---m-p-d----n-'), True)),
  349. ('bilawal',
  350. LayoutRaga(DecodeShrutiChart('s---r--g-m---p---d--n-'), True)),
  351. ('yaman',
  352. LayoutRaga(DecodeShrutiChart('s---r---g---mp---d---n'), True)),
  353. ('kafi',
  354. LayoutRaga(DecodeShrutiChart('s--r-g---m---p--d-n---'), True)),
  355. ('bhimpalasree',
  356. LayoutRaga(DecodeShrutiChart('s---r-g--m---p---d-n--'), True)),
  357. ('darbari',
  358. LayoutRaga(DecodeShrutiChart('s---rg---m---pd---n---'), True)),
  359. ('bageshree',
  360. LayoutRaga(DecodeShrutiChart('s--r-g---m---p--d-n---'), True)),
  361. ('rageshree',
  362. LayoutRaga(DecodeShrutiChart('s---r--g-m---p--d-n---'), True)),
  363. ('khamaj',
  364. LayoutRaga(DecodeShrutiChart('s---r--g-m---p---dn--n'), True)),
  365. ('mi\'mal',
  366. LayoutRaga(DecodeShrutiChart('s---rg---m---p--d-n-n-'), True)),
  367. ('parameshwari',
  368. LayoutRaga(DecodeShrutiChart('sr---g---m------d-n---'), True)),
  369. ('rangeshwari',
  370. LayoutRaga(DecodeShrutiChart('s---rg---m---p------n-'), True)),
  371. ('gangeshwari',
  372. LayoutRaga(DecodeShrutiChart('s------g-m---pd---n---'), True)),
  373. ('kameshwari',
  374. LayoutRaga(DecodeShrutiChart('s---r------m-p--d-n---'), True)),
  375. ('pa. kafi',
  376. LayoutRaga(DecodeShrutiChart('s---rg---m---p---dn---'), True)),
  377. ('natbhairav',
  378. LayoutRaga(DecodeShrutiChart('s---r--g-m---pd-----n-'), True)),
  379. ('m.kauns',
  380. LayoutRaga(DecodeShrutiChart('s---r---gm----d---n---'), True)),
  381. ('bairagi',
  382. LayoutRaga(DecodeShrutiChart('sr-------m---p----n---'), True)),
  383. ('b.todi',
  384. LayoutRaga(DecodeShrutiChart('sr---g-------p----n---'), True)),
  385. ('chandradeep',
  386. LayoutRaga(DecodeShrutiChart('s----g---m---p----n---'), True)),
  387. ('kaushik todi',
  388. LayoutRaga(DecodeShrutiChart('s----g---m-m--d-------'), True)),
  389. ('jogeshwari',
  390. LayoutRaga(DecodeShrutiChart('s----g-g-m------d-n---'), True)),
  391. ('rasia',
  392. LayoutRaga(DecodeShrutiChart('s---r---g---mp---d---n'), True)),
  393. ]
  394. for scale, values in scales:
  395. lookup_tables_signed.append(('scale_%s' % scale, values))