Guest User

Untitled

a guest
Nov 16th, 2024
82
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.91 KB | None | 0 0
  1. Chord thing, sorry for using an AI again for this as there could be some shoddy mistakes (still using my phone, gotta shower and eat food), I also like reading code from others that isn't mine but AIs don't easily impress me that much (too verbose, too simple, or doesn't think outside the box, can't correct certain mistakes that require visualization/hearing/runtime)…
  2. [code=javascript]
  3. //(16 Nov 2024 10:00) I asked o1 to write a chord progression interpreter.
  4. //I don't like using redundant spaces between tokens (but newlines and tab indentations are OK), it's better to be more concise and use ES6 tricks whenever possible.
  5. //(11:40) There were some issues with minor chords. Now it's “error: Cannot read properties of undefined (reading 'match')”…
  6. var PI=Math.PI,TAU=PI*2,
  7. noteNumbers={
  8. "C":0,
  9. "C#":1,"Db":1,
  10. "D":2,
  11. "D#":3,"Eb":3,
  12. "E":4,
  13. "F":5,
  14. "F#":6,"Gb":6,
  15. "G":7,
  16. "G#":8,"Ab":8,
  17. "A":9,
  18. "A#":10,"Bb":10,
  19. "B":11
  20. };
  21. //parse a chord string into root note, modifiers, and bass note
  22. function parseChordString(chordStr){
  23. /*
  24. //split the chord by slash to separate chord and bass note
  25. var parts=chordStr.split('/'),
  26. chordPart=parts[0],
  27. bassNote=parts[1]||null;
  28. */
  29. //check if the chord contains a bass note
  30. var bassNoteMatch=chordStr.match(/\/([A-G][#b]?)$/),
  31. bassNote=null;
  32. if(bassNoteMatch){
  33. bassNote=bassNoteMatch[1];
  34. chordStr=chordStr.slice(0,chordStr.length-bassNoteMatch[0].length); //remove bass note part from chordStr
  35. }
  36. //extract the root note and modifiers from the chordStr
  37. var match=chordStr.match(/^([A-G][#b]?)(.*)$/);
  38. if(!match)throw new Error("Invalid chord: "+chordStr);
  39. var rootNote=match[1],
  40. modifiers=match[2];
  41. return{
  42. rootNote:rootNote,
  43. modifiers:modifiers,
  44. bassNote:bassNote
  45. };
  46. }
  47. //function to parse chord modifiers into tokens
  48. function parseModifiers(modifiers){
  49. let tokens=[],
  50. str=modifiers;
  51. while(str.length>0){
  52. let matched=false,
  53. res;
  54. //match 'maj' with optional extension
  55. if(res=str.match(/^maj(7|9|11|13)?/)){
  56. tokens.push('maj');
  57. if(res[1])tokens.push(res[1]);
  58. str=str.substr(res[0].length);
  59. matched=true;
  60. }
  61. //match 'm' or 'min' with optional extension
  62. else if(res=str.match(/^m(in)?(7|9|11|13)?/)){
  63. tokens.push('min');
  64. if(res[2])tokens.push(res[2]);
  65. str=str.substr(res[0].length);
  66. matched=true;
  67. }
  68. //match 'dim' with optional '7'
  69. else if(res=str.match(/^dim(7)?/)){
  70. tokens.push('dim');
  71. if(res[1])tokens.push(res[1]);
  72. str=str.substr(res[0].length);
  73. matched=true;
  74. }
  75. //match 'aug'
  76. else if(res=str.match(/^aug/)){
  77. tokens.push('aug');
  78. str=str.substr(res[0].length);
  79. matched=true;
  80. }
  81. //match 'sus', 'sus2', 'sus4'
  82. else if(res=str.match(/^sus(2|4)?/)){
  83. tokens.push('sus'+(res[1]||''));
  84. str=str.substr(res[0].length);
  85. matched=true;
  86. }
  87. //match 'add' with optional alteration
  88. else if(res=str.match(/^add(#|b)?(\d+)/)){
  89. tokens.push('add');
  90. tokens.push((res[1]||'')+res[2]);
  91. str=str.substr(res[0].length);
  92. matched=true;
  93. }
  94. //match extensions like '13', '11', '9'
  95. else if(res=str.match(/^(13|11|9)/)){
  96. tokens.push(res[1]);
  97. str=str.substr(res[1].length);
  98. matched=true;
  99. }
  100. //match alterations like '#5', 'b9'
  101. else if(res=str.match(/^([#b])(\d+)/)){
  102. tokens.push(res[1]+res[2]);
  103. str=str.substr(res[0].length);
  104. matched=true;
  105. }
  106. //match '6/9'
  107. else if(res=str.match(/^6\/9/)){
  108. tokens.push('6/9');
  109. str=str.substr(4);
  110. matched=true;
  111. }
  112. //match '6', '7', '5'
  113. else if(res=str.match(/^(6|7|5)/)){
  114. tokens.push(res[1]);
  115. str=str.substr(1);
  116. matched=true;
  117. }
  118. //skip unrecognized characters
  119. else{
  120. str=str.substr(1);
  121. }
  122. }
  123. return tokens;
  124. }
  125. //helper function to get interval from scale degree
  126. var intervalsMap={1:0,2:2,3:4,4:5,5:7,6:9,7:11,9:14,11:17,13:21};
  127. function getIntervalFromDegree(degree){return intervalsMap[degree]||0;}
  128. //function to get chord intervals based on modifiers
  129. function getChordIntervals(modifiers){
  130. var tokens=parseModifiers(modifiers),
  131. intervals=[];
  132. //start with basic triad
  133. var chordQuality='major'; //default
  134. if(tokens.includes('maj')){
  135. chordQuality='major';
  136. }else if(tokens.includes('min')){
  137. chordQuality='minor';
  138. }else if(tokens.includes('dim')){
  139. chordQuality='diminished';
  140. }else if(tokens.includes('aug')){
  141. chordQuality='augmented';
  142. }
  143. //build the basic triad intervals
  144. if(chordQuality=='major'){
  145. intervals.push(0,4,7);
  146. }else if(chordQuality=='minor'){
  147. intervals.push(0,3,7);
  148. }else if(chordQuality=='diminished'){
  149. intervals.push(0,3,6);
  150. }else if(chordQuality=='augmented'){
  151. intervals.push(0,4,8);
  152. }
  153. //handle sevenths
  154. if(tokens.includes('maj') && tokens.includes('7')){
  155. intervals.push(11);
  156. }else if(tokens.includes('7')){
  157. intervals.push(10);
  158. }
  159. //handle extensions
  160. if(tokens.includes('9')){
  161. intervals.push(14);
  162. }
  163. if(tokens.includes('11')){
  164. intervals.push(17);
  165. }
  166. if(tokens.includes('13')){
  167. intervals.push(21);
  168. }
  169. //handle '6' and '6/9'
  170. if(tokens.includes('6/9')){
  171. intervals.push(9,14);
  172. }else if(tokens.includes('6')){
  173. intervals.push(9);
  174. }
  175. //handle added tones
  176. if(tokens.includes('add')){
  177. tokens.filter(t=>t.startsWith('#')||t.startsWith('b')||/^\d+$/.test(t)).forEach(t=>{
  178. var number=parseInt(t.replace('#','').replace('b','')),
  179. interval = getIntervalFromDegree(number);
  180. if(t.startsWith('#'))interval++;
  181. if(t.startsWith('b'))interval--;
  182. intervals.push(interval);
  183. });
  184. }
  185. //handle suspensions
  186. if(tokens.includes('sus2')){
  187. //replace the third with major 2nd
  188. var index=intervals.indexOf(chordQuality=='major'?4:3);
  189. if(index>=0)intervals[index]=2;
  190. }else if(tokens.includes('sus4')){
  191. //replace the third with perfect 4th
  192. var index=intervals.indexOf(chordQuality=='major'?4:3);
  193. if(index>=0)intervals[index]=5;
  194. }else if(tokens.includes('sus')){
  195. //replace the third with perfect 4th
  196. var index=intervals.indexOf(chordQuality=='major'?4:3);
  197. if(index>=0)intervals[index]=5;
  198. }
  199. //handle alterations
  200. tokens.forEach(t=>{
  201. if(t=='#5'){
  202. //replace the fifth with augmented fifth
  203. var index=intervals.indexOf(7);
  204. if(index>=0)intervals[index]=8;
  205. }else if(t=='b5'){
  206. //replace the fifth with diminished fifth
  207. var index=intervals.indexOf(7);
  208. if(index>=0)intervals[index]=6;
  209. }else if(t=='#9'){
  210. intervals.push(15); //sharp ninth
  211. }else if(t=='b9'){
  212. intervals.push(13); //flat ninth
  213. }else if(t=='#11'){
  214. intervals.push(18); //sharp eleventh
  215. }else if(t=='b13'){
  216. intervals.push(20); //flat thirteenth
  217. }
  218. });
  219. //remove duplicates and sort intervals
  220. intervals=intervals.filter((v,i,a)=>a.indexOf(v)===i);
  221. intervals.sort((a,b)=>a-b);
  222. return intervals;
  223. }
  224. function note(n,base=440,keys=12){return base*Math.pow(2,n/keys);}
  225. function sine(x){return Math.sin(TAU*x);}
  226. function pulse(x){return x%1<.5?-1:1;}
  227. //limit frequency within a given range
  228. function limitFrequency(freq,minFreq,maxFreq){
  229. while(freq<minFreq)freq*=2;
  230. while(freq>maxFreq)freq/=2;
  231. return freq;
  232. }
  233. var chordDuration=2, //seconds per chord
  234. //chords="Amaj7sus2/Ab Gmaj7sus2 C69#11 Bm11 Ab7#5b9 Dbm11 Cmaj7b5/6 Abm11 F#m11".split(" "),
  235. chords="Gb7 Gmmaj7 Emmaj9 Ab9addb13/C Gbaug Bmaj9 Abm11 Dbm11 Gb7/#11 Dbmaj9sus2 Eb13sus4 Dbm9".split(" "),
  236. //chords="Am7 Dm9 Em11 Fmaj7 G7 Cmaj9".split(" "),
  237. totalChords = chords.length;
  238. return function dsp(t){
  239. var out=0,
  240. chordIndex=Math.floor(t/chordDuration)%totalChords,
  241. chordTime=t%chordDuration,
  242. chordStr=chords[chordIndex],
  243. chord=parseChordString(chordStr),
  244. rootNote=chord.rootNote,
  245. modifiers=chord.modifiers,
  246. bassNote=chord.bassNote||chord.rootNote,
  247. intervals=getChordIntervals(modifiers),
  248. rootNoteNumber=noteNumbers[rootNote],
  249. //get the semitone offset from A4 (note number 9)
  250. semitoneOffset=rootNoteNumber-9,
  251. //calculate frequencies and sum the sine waves
  252. numNotes=intervals.length+1; //number of notes in the chord including bass
  253. for(var i=0;i<intervals.length;i++){
  254. var n=semitoneOffset+intervals[i], //total semitone offset from A4
  255. freq=limitFrequency(note(n),220,880);
  256. out+=sine(freq*t);
  257. }
  258. //add the bass note an octave lower
  259. if(bassNote){
  260. var bassNoteNumber=noteNumbers[bassNote],
  261. bassSemitoneOffset=bassNoteNumber-9-12, //bass an octave lower
  262. bassFreq=note(bassSemitoneOffset);
  263. out+=sine(bassFreq*t);
  264. }
  265. //normalize output
  266. out=out/numNotes;
  267. return out;
  268. }
  269. [/code]
  270. >[b]Error[/b]
  271. >Your post has too many lines.
  272. Fuck off.
Add Comment
Please, Sign In to add comment