Compare commits

...

18 Commits

44 changed files with 1561 additions and 1520 deletions

5
.gitignore vendored
View File

@ -1,2 +1,7 @@
*.csv
.venv
songs/
pptx/code/
pptx/data/
pptx/*.png
pptx/*.jpg

View File

@ -1,503 +0,0 @@
osu file format v14
[General]
AudioFilename: audio.wav
AudioLeadIn: 0
PreviewTime: 63278
Countdown: 0
SampleSet: Normal
StackLeniency: 0.5
Mode: 0
LetterboxInBreaks: 0
WidescreenStoryboard: 1
[Editor]
DistanceSpacing: 1.2
BeatDivisor: 4
GridSize: 32
TimelineZoom: 1.8
[Metadata]
Title:Shinzou o Sasageyo! [TV Size]
TitleUnicode:心臓を捧げよ! [TV Size]
Artist:Linked Horizon
ArtistUnicode:Linked Horizon
Creator:Monstrata
Version:Insane
Source:進撃の巨人
Tags:revo Attack on Titan shingeki no kyojin season 2 two dedicate all your hearts heart snk aot Haruto haruto_aizawa armin arlert levi erin jaeger mikasa ackerman survey corps opening one theme song sound
BeatmapID:1256136
BeatmapSetID:593620
[Difficulty]
HPDrainRate:5.5
CircleSize:4
OverallDifficulty:7.5
ApproachRate:9
SliderMultiplier:2.2
SliderTickRate:1
[Events]
//Background and Video events
0,0,"sasageyo.jpg",0,0
Video,-150,"sasageyo.flv"
//Break Periods
//Storyboard Layer 0 (Background)
//Storyboard Layer 1 (Fail)
//Storyboard Layer 2 (Pass)
//Storyboard Layer 3 (Foreground)
//Storyboard Sound Samples
[TimingPoints]
560,375,4,2,2,50,1,0
560,-200,4,2,2,50,0,0
3560,-133.333333333333,4,3,2,65,0,0
6560,-166.666666666667,4,3,3,55,0,0
17341,-166.666666666667,4,3,3,5,0,0
17435,-166.666666666667,4,3,2,55,0,0
17716,-166.666666666667,4,3,2,5,0,0
17810,-166.666666666667,4,3,2,50,0,0
18091,-166.666666666667,4,3,2,5,0,0
18185,-166.666666666667,4,3,2,60,0,0
18560,-153.846153846154,4,3,2,50,0,0
18747,-153.846153846154,4,3,3,60,0,0
29060,-133.333333333333,4,3,2,70,0,0
29341,-133.333333333333,4,3,2,5,0,0
29435,-133.333333333333,4,3,2,70,0,0
29716,-133.333333333333,4,3,2,5,0,0
29810,-133.333333333333,4,3,2,70,0,0
30560,-166.666666666667,4,3,2,55,0,0
32810,-166.666666666667,4,3,2,60,0,0
33185,-166.666666666667,4,3,2,60,0,0
33560,-166.666666666667,4,3,2,55,0,0
35810,-153.846153846154,4,3,2,60,0,0
36560,-133.333333333333,4,3,2,60,0,0
39091,-133.333333333333,4,3,2,5,0,0
39185,-133.333333333333,4,3,2,60,0,0
39466,-133.333333333333,4,3,2,5,0,0
39560,-133.333333333333,4,3,2,60,0,0
41060,-100,4,3,2,60,0,0
41435,-100,4,3,2,65,0,0
41810,-100,4,3,2,70,0,0
42185,-97.0873786407767,4,3,2,75,0,0
42560,-100,4,3,2,70,0,0
45466,-100,4,3,2,5,0,0
45560,-166.666666666667,4,3,2,50,0,0
48091,-166.666666666667,4,3,2,5,0,0
48185,-125,4,3,2,60,0,0
48560,-100,4,3,2,70,0,0
50060,-125,4,3,2,75,0,0
51560,-200,4,3,2,55,0,0
54372,-200,4,3,3,55,0,0
54560,-200,4,3,2,55,0,0
57560,-300,4,3,2,45,0,0
60185,-200,4,3,2,55,0,0
60560,-133.333333333333,4,3,2,65,0,0
62060,-117.647058823529,4,3,2,70,0,0
62435,-111.111111111111,4,3,2,70,0,0
62810,-105.263157894737,4,3,2,75,0,0
63560,-94.1176470588235,4,1,2,70,0,1
72560,-100,4,3,2,65,0,0
73778,-100,4,3,3,65,0,0
73966,-100,4,3,2,65,0,0
74060,-133.333333333333,4,3,2,70,0,1
74247,-133.333333333333,4,3,3,70,0,1
78372,-100,4,3,2,75,0,1
78653,-100,4,3,3,75,0,1
78747,-100,4,3,2,70,0,1
79028,-100,4,3,3,70,0,1
79122,-100,4,3,2,70,0,1
79403,-100,4,3,3,70,0,1
79497,-100,4,3,2,70,0,1
80060,-94.1176470588235,4,1,2,75,0,1
83528,-94.1176470588235,4,3,3,80,0,1
83622,-94.1176470588235,4,1,2,70,0,1
86060,-100,4,3,2,80,0,0
[Colours]
Combo1 : 222,184,135
Combo2 : 255,128,0
Combo3 : 0,128,192
Combo4 : 255,255,128
[HitObjects]
35,98,560,5,2,0:2:0:0:
105,141,747,2,0,L|162:133,1,55,0|0,0:0|0:0,0:0:0:0:
124,221,1122,2,0,L|178:213,1,55,2|0,0:0|0:0,0:0:0:0:
143,301,1497,2,0,L|197:293,1,55,2|0,0:0|0:0,0:0:0:0:
258,237,1872,1,2,0:0:0:0:
254,354,2060,5,2,0:1:0:0:
314,297,2247,2,0,L|368:304,1,55,2|0,0:0|0:0,0:0:0:0:
339,217,2622,2,0,L|393:224,1,55,2|0,0:0|0:0,0:0:0:0:
364,137,2997,2,0,L|418:144,1,55,2|0,0:1|0:0,0:0:0:0:
389,57,3372,1,2,0:0:0:0:
283,156,3560,5,4,0:2:0:0:
471,202,3747,2,0,L|478:93,1,82.5000031471253,8|0,0:0|1:0,0:0:0:0:
278,73,4122,2,0,L|283:155,1,82.5000031471253,4|0,0:2|0:0,0:0:0:0:
212,213,4403,1,8,0:0:0:0:
212,213,4497,2,0,L|206:295,1,82.5000031471253,4|4,1:2|2:3,0:0:0:0:
277,353,4778,1,8,3:3:0:0:
277,353,4872,6,0,L|359:347,1,82.5000031471253,4|0,0:2|2:3,0:0:0:0:
410,272,5153,1,8,0:0:0:0:
410,272,5247,2,0,P|415:232|404:193,1,82.5000031471254,4|4,0:2|2:3,0:0:0:0:
295,242,5528,1,0,2:3:0:0:
295,242,5622,2,0,L|359:237,1,41.2500015735627,4|0,0:2|0:0,0:0:0:0:
304,163,5810,5,0,1:0:0:0:
288,162,5903,1,8,0:0:0:0:
273,165,5997,1,4,0:2:0:0:
258,171,6091,1,8,0:0:0:0:
245,179,6185,1,0,0:0:0:0:
234,189,6278,1,0,1:0:0:0:
225,202,6372,1,4,0:3:0:0:
218,216,6466,1,8,0:0:0:0:
215,231,6560,5,4,0:1:0:0:
297,307,6747,2,0,L|295:242,1,65.9999979858399,2|2,0:1|0:0,0:0:0:0:
218,314,7122,2,0,L|220:379,1,65.9999979858399,2|2,0:2|0:0,0:0:0:0:
377,301,7497,2,0,L|374:235,1,65.9999979858399,2|2,0:2|0:0,0:0:0:0:
292,176,7872,1,2,0:0:0:0:
292,176,7966,1,2,0:0:0:0:
292,176,8060,6,0,L|294:241,2,65.9999979858399,10|0|2,0:0|0:0|0:0,0:0:0:0:
354,66,8622,2,0,L|356:131,1,65.9999979858399,2|2,0:2|0:0,0:0:0:0:
227,131,8997,1,6,0:0:0:0:
199,56,9185,1,10,0:0:0:0:
199,56,9278,1,10,0:0:0:0:
199,56,9372,1,2,0:0:0:0:
149,117,9560,5,6,0:0:0:0:
175,192,9747,2,0,P|203:208|235:211,1,65.9999979858399,2|2,0:2|0:0,0:0:0:0:
359,182,10122,2,0,P|331:165|299:163,1,65.9999979858399,2|2,0:2|0:0,0:0:0:0:
309,241,10497,2,0,P|337:257|369:260,1,65.9999979858399,2|2,0:2|0:0,0:0:0:0:
493,231,10872,1,2,0:0:0:0:
493,231,10966,1,2,0:0:0:0:
493,231,11060,6,0,P|464:214|432:211,2,65.9999979858399,6|0|2,0:0|0:0|0:0,0:0:0:0:
421,351,11622,2,0,P|438:323|441:291,1,65.9999979858399,2|2,0:2|0:0,0:0:0:0:
352,219,11997,1,10,0:0:0:0:
287,265,12185,1,4,0:2:0:0:
364,302,12372,1,2,0:0:0:0:
364,302,12466,1,2,0:0:0:0:
364,302,12560,5,4,0:2:0:0:
344,140,12747,2,0,L|350:59,1,65.9999979858399,0|2,0:0|0:0,0:0:0:0:
266,156,13122,2,0,L|271:91,1,65.9999979858399,2|10,0:2|0:0,0:0:0:0:
188,172,13497,2,0,L|192:106,1,65.9999979858399,2|2,0:2|0:0,0:0:0:0:
109,187,13872,1,2,0:0:0:0:
109,187,13966,1,2,0:0:0:0:
109,187,14060,6,0,L|113:121,2,65.9999979858399,6|2|2,0:0|0:0|0:0,0:0:0:0:
101,265,14622,2,0,P|69:255|46:233,1,65.9999979858399,2|2,0:2|0:1,0:0:0:0:
31,154,14997,1,10,0:0:0:0:
112,121,15185,2,0,L|108:187,1,65.9999979858399,2|2,0:2|0:1,0:0:0:0:
109,187,15466,1,2,0:0:0:0:
109,187,15560,5,10,0:0:0:0:
191,93,15747,2,0,L|187:159,1,65.9999979858399,2|2,0:2|0:0,0:0:0:0:
253,41,16122,1,10,0:0:0:0:
253,41,16216,1,10,0:0:0:0:
252,40,16310,2,0,P|267:70|268:103,1,65.9999979858399,2|0,0:1|0:0,0:0:0:0:
342,130,16685,2,0,P|327:159|326:192,1,65.9999979858399,2|2,3:0|0:0,0:0:0:0:
376,254,16966,1,2,0:0:0:0:
376,254,17060,6,0,P|430:269|490:240,1,98.9999969787599,4|0,0:2|0:0,0:0:0:0:
472,163,17435,2,0,P|457:217|486:277,1,98.9999969787599,8|0,0:2|0:0,0:0:0:0:
504,360,17810,2,0,P|450:345|390:374,1,98.9999969787599,10|0,1:2|0:2,0:0:0:0:
311,384,18185,2,0,P|326:330|297:270,1,98.9999969787599,12|8,0:2|0:0,0:0:0:0:
256,192,18560,5,4,0:1:0:0:
175,164,18747,2,0,P|172:196|203:261,1,71.4999967269899,2|2,0:1|0:0,0:0:0:0:
337,220,19122,2,0,P|340:188|309:123,1,71.4999967269899,2|2,0:2|0:0,0:0:0:0:
256,19,19497,1,2,0:2:0:0:
255,106,19685,1,2,0:0:0:0:
331,63,19872,1,2,0:2:0:0:
180,62,20060,5,2,0:2:0:0:
256,192,20247,2,0,L|350:201,1,71.4999967269899,0|2,0:0|0:0,0:0:0:0:
402,69,20622,2,0,L|330:62,1,71.4999967269899,2|2,0:2|0:0,0:0:0:0:
255,106,20997,1,0,0:0:0:0:
405,218,21185,2,0,L|411:146,1,71.4999967269899,12|2,0:2|0:0,0:0:0:0:
256,192,21560,5,12,0:2:0:0:
228,273,21747,2,0,P|260:276|325:245,1,71.4999967269899,2|2,0:2|0:0,0:0:0:0:
284,111,22122,2,0,P|252:108|187:139,1,71.4999967269899,2|2,0:2|0:0,0:0:0:0:
83,192,22497,1,0,0:0:0:0:
170,193,22685,1,2,0:2:0:0:
127,117,22872,1,0,0:0:0:0:
126,268,23060,5,2,0:0:0:0:
256,192,23247,2,0,P|294:186|349:216,1,71.4999967269899,2|2,0:2|0:0,0:0:0:0:
308,355,23622,2,0,P|272:358|239:346,1,71.4999967269899,2|2,0:2|0:0,0:0:0:0:
284,273,23997,1,2,0:0:0:0:
392,373,24185,1,4,0:2:0:0:
409,375,24278,1,0,1:0:0:0:
427,375,24372,1,0,1:0:0:0:
444,372,24466,1,0,1:0:0:0:
461,365,24560,5,12,0:2:0:0:
425,219,24747,2,0,L|418:291,1,71.4999967269899,2|2,0:2|0:0,0:0:0:0:
284,273,25122,2,0,L|350:245,1,71.4999967269899,2|2,0:1|0:0,0:0:0:0:
401,368,25497,2,0,L|343:325,1,71.4999967269899,2|2,0:2|0:0,0:0:0:0:
229,335,25872,1,0,1:0:0:0:
229,335,25966,1,0,1:0:0:0:
229,335,26060,6,0,L|286:377,1,71.4999967269899,12|8,0:2|0:0,0:0:0:0:
284,273,26435,1,2,0:0:0:0:
151,303,26622,2,0,P|145:339|154:373,1,71.4999967269899,2|2,0:1|0:0,0:0:0:0:
208,239,26997,2,0,P|212:199|197:156,1,71.4999967269899,2|2,0:2|0:0,0:0:0:0:
57,217,27372,1,2,0:0:0:0:
57,217,27466,1,2,0:0:0:0:
57,217,27560,6,0,L|128:209,1,71.4999967269899,4|8,0:2|0:0,0:0:0:0:
209,82,27935,1,2,0:0:0:0:
132,122,28122,1,2,0:1:0:0:
281,127,28310,2,0,P|319:120|345:100,1,71.4999967269899,10|0,0:2|0:0,0:0:0:0:
284,42,28685,1,2,0:0:0:0:
132,122,28872,1,8,0:0:0:0:
270,247,29060,6,0,P|288:187|281:126,1,123.750004720688,4|0,0:2|0:0,0:0:0:0:
183,57,29435,2,0,P|224:102|280:126,1,123.750004720688,4|0,1:3|0:0,0:0:0:0:
342,7,29810,6,0,P|336:47|347:86,1,82.5000031471254,10|2,0:2|0:1,0:0:0:0:
441,118,30091,1,8,0:2:0:0:
441,118,30185,2,0,P|447:77|436:38,1,82.5000031471254,4|0,0:2|2:0,0:0:0:0:
366,182,30560,5,4,0:1:0:0:
444,207,30747,1,0,0:0:0:0:
408,340,30935,1,8,0:0:0:0:
383,262,31122,1,0,0:0:0:0:
250,299,31310,1,0,0:0:0:0:
328,324,31497,1,2,0:0:0:0:
289,180,31685,2,0,L|310:243,1,65.9999979858399,8|0,0:0|0:0,0:0:0:0:
388,120,32060,6,0,L|367:183,1,65.9999979858399,2|0,0:2|0:0,0:0:0:0:
278,103,32435,1,8,0:0:0:0:
225,227,32622,1,2,0:0:0:0:
149,142,32810,2,0,L|215:148,1,65.9999979858399,4|0,0:2|0:0,0:0:0:0:
142,221,33185,2,0,L|76:215,1,65.9999979858399,12|0,0:2|0:0,0:0:0:0:
132,65,33560,5,4,0:2:0:0:
149,142,33747,1,0,0:0:0:0:
209,48,33935,1,8,0:0:0:0:
226,125,34123,1,2,0:0:0:0:
125,24,34310,5,2,0:2:0:0:
108,148,34497,1,2,0:0:0:0:
249,41,34684,1,8,0:0:0:0:
232,165,34872,1,2,0:0:0:0:
173,55,35060,5,4,0:3:0:0:
286,110,35247,1,2,0:0:0:0:
163,132,35435,1,8,0:0:0:0:
249,41,35622,1,2,0:0:0:0:
232,165,35810,2,0,P|211:193|176:209,1,71.4999967269899,4|0,0:2|0:0,0:0:0:0:
151,336,36185,1,12,0:0:0:0:
159,320,36278,1,2,0:1:0:0:
172,307,36372,1,2,0:2:0:0:
187,298,36466,1,2,0:2:0:0:
204,293,36560,5,4,0:2:0:0:
230,379,36747,1,2,0:0:0:0:
267,229,36935,1,8,0:0:0:0:
292,314,37122,1,2,0:0:0:0:
179,208,37310,1,2,0:2:0:0:
117,273,37497,1,2,0:0:0:0:
154,123,37685,1,8,0:0:0:0:
92,187,37872,1,2,0:0:0:0:
242,144,38060,5,4,0:3:0:0:
67,102,38247,1,2,0:0:0:0:
267,229,38435,1,8,0:0:0:0:
217,58,38622,1,2,0:0:0:0:
217,58,38716,1,2,0:0:0:0:
216,58,38810,6,0,P|273:36|333:50,1,123.750004720688,4|0,0:2|0:0,0:0:0:0:
341,167,39185,2,0,P|354:107|333:50,1,123.750004720688,12|0,0:2|0:0,0:0:0:0:
242,144,39560,5,4,0:2:0:0:
267,229,39747,1,2,0:0:0:0:
118,272,39935,1,8,0:0:0:0:
180,208,40122,1,2,0:0:0:0:
292,314,40310,1,2,0:2:0:0:
267,229,40497,1,2,0:0:0:0:
179,378,40685,1,8,0:0:0:0:
204,293,40872,1,2,0:1:0:0:
380,335,41060,6,0,P|426:298|396:263,1,110,12|0,1:2|0:0,0:0:0:0:
393,233,41341,1,4,0:0:0:0:
393,233,41435,2,0,P|439:196|409:161,1,110,8|0,1:2|0:0,0:0:0:0:
406,131,41716,1,0,0:0:0:0:
406,131,41810,2,0,P|426:97|414:17,1,110,12|2,1:2|0:1,0:0:0:0:
347,70,42091,1,2,0:1:0:0:
347,70,42185,2,0,L|399:251,1,169.949998573723,12|0,0:0|0:0,0:0:0:0:
335,298,42560,5,12,0:2:0:0:
184,118,42747,1,2,0:1:0:0:
193,353,42935,1,10,0:2:0:0:
271,131,43122,1,2,0:1:0:0:
39,174,43310,1,8,0:0:0:0:
248,284,43497,1,0,0:0:0:0:
248,284,43591,1,0,0:0:0:0:
248,284,43685,1,12,0:2:0:0:
238,49,43872,1,0,0:0:0:0:
238,49,43965,1,4,0:3:0:0:
238,49,44059,1,4,0:2:0:0:
161,270,44247,5,2,0:1:0:0:
325,62,44434,1,10,0:0:0:0:
126,187,44622,1,4,0:0:0:0:
335,298,44810,1,8,0:0:0:0:
335,298,44903,2,0,P|387:290|426:237,1,110,8|2,0:0|0:0,0:0:0:0:
346,189,45185,2,0,L|335:299,1,110,12|0,0:2|0:0,0:0:0:0:
263,226,45560,6,0,P|232:216|200:216,1,65.9999979858399,4|0,0:1|0:0,0:0:0:0:
117,299,45935,2,0,P|127:267|127:235,1,65.9999979858399,2|0,0:0|0:0,0:0:0:0:
44,152,46310,2,0,P|75:162|107:162,1,65.9999979858399,8|0,0:0|0:0,0:0:0:0:
190,79,46685,2,0,P|179:110|179:142,1,65.9999979858399,2|0,0:1|0:0,0:0:0:0:
297,145,47060,6,0,P|239:119|179:142,1,131.99999597168,4|0,0:0|0:0,0:0:0:0:
237,206,47622,1,2,0:0:0:0:
357,206,47810,2,0,L|387:136,1,65.9999979858399,12|0,0:2|0:0,0:0:0:0:
289,58,48185,6,0,L|298:150,1,88,2|0,0:1|0:0,0:0:0:0:
435,71,48560,6,0,P|365:32|289:57,1,165,12|0,0:2|0:0,0:0:0:0:
151,161,48935,2,0,P|227:185|297:147,1,165,4|0,0:0|1:0,0:0:0:0:
165,75,49310,1,10,0:0:0:0:
120,327,49497,1,0,0:0:0:0:
360,239,49685,1,8,0:0:0:0:
99,214,49872,1,0,0:0:0:0:
251,0,50060,6,0,L|246:103,1,88,12|0,1:2|0:0,0:0:0:0:
329,196,50341,1,0,0:0:0:0:
329,196,50435,2,0,L|333:108,1,88,8|0,1:3|0:0,0:0:0:0:
417,161,50716,1,0,0:0:0:0:
417,161,50810,6,0,L|488:150,1,44,4|0,1:2|1:0,0:0:0:0:
451,47,50997,2,0,L|407:53,1,44,8|0,1:3|1:0,0:0:0:0:
412,171,51185,2,0,L|483:160,1,44,2|0,1:0|1:0,0:0:0:0:
446,57,51372,2,0,L|402:63,1,44,8|0,1:3|1:3,0:0:0:0:
343,220,51560,6,0,P|395:255|452:246,1,110,4|0,0:1|0:0,0:0:0:0:
412,171,52122,1,2,0:0:0:0:
267,185,52310,2,0,P|262:247|298:292,1,110,8|2,0:0|0:1,0:0:0:0:
343,220,52872,1,2,0:0:0:0:
488,136,53060,6,0,P|492:198|456:243,1,110,2|0,0:2|0:0,0:0:0:0:
412,171,53622,1,2,0:0:0:0:
259,79,53810,2,0,P|254:141|290:186,1,110,12|2,0:0|0:1,0:0:0:0:
343,122,54372,1,2,0:0:0:0:
343,122,54466,1,8,0:0:0:0:
343,122,54560,6,0,P|390:109|458:144,1,110,4|0,0:2|0:0,0:0:0:0:
389,194,55122,1,2,0:0:0:0:
169,122,55310,2,0,P|122:109|54:144,1,110,8|2,0:0|0:1,0:0:0:0:
123,194,55872,1,2,0:0:0:0:
256,152,56060,6,0,L|256:32,1,110,0|2,0:0|0:1,0:0:0:0:
180,76,56622,1,2,0:0:0:0:
332,76,56810,2,0,L|332:146,1,55,12|0,0:0|0:0,0:0:0:0:
256,207,57185,2,0,L|256:152,1,55,12|2,0:2|0:2,0:0:0:0:
309,271,57560,6,0,L|399:271,1,73.3333333333333,4|0,0:1|0:0,0:0:0:0:
423,206,58122,1,2,0:2:0:0:
423,206,58310,2,0,P|447:207|474:201,1,36.6666666666667,0|0,0:0|0:0,0:0:0:0:
440,130,58685,2,0,P|441:105|435:79,1,36.6666666666667,0|2,0:0|0:2,0:0:0:0:
364,113,59060,6,0,P|337:111|283:136,1,73.3333333333333,2|0,0:0|0:0,0:0:0:0:
238,179,59622,1,0,0:0:0:0:
238,179,59810,2,0,P|251:204|252:224,1,36.6666666666667,2|0,0:2|0:0,0:0:0:0:
123,229,60185,6,0,L|178:224,1,55,8|2,0:0|2:0,0:0:0:0:
227,286,60466,1,2,0:1:0:0:
227,286,60560,6,0,L|313:294,1,82.5000031471254,12|0,0:2|0:0,0:0:0:0:
384,260,60841,1,0,0:0:0:0:
384,260,60935,2,0,L|376:342,1,82.5000031471254,2|0,0:1|1:0,0:0:0:0:
454,365,61216,1,2,0:1:0:0:
454,365,61310,2,0,L|460:292,1,41.2500015735627,12|0,0:2|0:0,0:0:0:0:
505,237,61497,2,0,L|464:241,1,41.2500015735627,0|0,1:0|0:0,0:0:0:0:
366,175,61685,2,0,L|408:179,1,41.2500015735627,8|0,1:3|0:0,0:0:0:0:
438,61,61872,2,0,L|442:103,1,41.2500015735627,2|0,0:1|0:0,0:0:0:0:
357,268,62060,6,0,L|366:175,1,93.500001783371,12|0,0:2|0:0,0:0:0:0:
220,120,62341,1,0,0:0:0:0:
220,120,62435,6,0,L|206:258,1,98.9999969787599,12|0,0:2|0:0,0:0:0:0:
284,241,62716,1,0,0:0:0:0:
284,241,62810,6,0,L|296:116,1,104.499997209549,12|0,0:2|0:0,0:0:0:0:
147,93,63091,1,0,0:0:0:0:
147,93,63185,6,0,L|134:224,1,104.499997209549,12|2,0:2|0:1,0:0:0:0:
210,219,63466,1,8,0:0:0:0:
210,219,63560,6,0,L|225:67,1,116.875002229214,4|2,0:1|0:1,0:0:0:0:
294,137,63841,1,0,0:0:0:0:
294,137,63935,2,0,L|371:137,1,58.4375011146069,2|0,0:2|0:0,0:0:0:0:
328,209,64122,2,0,L|378:238,1,58.4375011146069,8|0,0:3|0:0,0:0:0:0:
325,294,64310,6,0,P|303:363|259:384,1,116.875002229214,2|10,0:0|0:3,0:0:0:0:
191,305,64591,1,0,0:0:0:0:
191,305,64685,2,0,L|250:308,1,58.4375011146069,0|0,0:0|3:0,0:0:0:0:
258,232,64872,2,0,L|261:173,1,58.4375011146069,8|0,0:3|0:0,0:0:0:0:
185,200,65060,6,0,P|169:270|197:310,1,116.875002229214,12|8,0:2|0:3,0:0:0:0:
141,362,65341,1,0,0:0:0:0:
141,362,65435,2,0,L|82:364,1,58.4375011146069,0|0,0:0|3:0,0:0:0:0:
41,292,65622,2,0,L|100:290,1,58.4375011146069,8|0,0:3|0:0,0:0:0:0:
141,143,65810,2,0,L|82:140,1,58.4375011146069,10|0,0:2|0:0,0:0:0:0:
42,213,65997,2,0,L|100:215,1,58.4375011146069,8|0,0:3|0:0,0:0:0:0:
265,93,66185,5,4,0:2:0:0:
227,70,66278,1,0,3:0:0:0:
184,74,66372,1,8,0:3:0:0:
151,102,66466,1,0,0:0:0:0:
141,143,66560,6,0,L|290:169,1,116.875002229214,4|2,0:2|0:0,0:0:0:0:
294,228,66841,1,0,0:0:0:0:
294,228,66935,2,0,L|400:209,1,58.4375011146069,0|0,3:0|3:0,0:0:0:0:
386,134,67122,2,0,L|329:145,1,58.4375011146069,8|0,0:3|0:0,0:0:0:0:
369,290,67310,6,0,P|416:258|424:202,1,116.875002229214,12|2,0:2|0:0,0:0:0:0:
464,137,67591,1,0,0:0:0:0:
464,137,67685,2,0,L|521:147,1,58.4375011146069,0|0,3:0|3:0,0:0:0:0:
486,57,67872,2,0,L|429:68,1,58.4375011146069,8|0,0:3|0:0,0:0:0:0:
271,114,68060,6,0,L|423:140,1,116.875002229214,12|8,0:2|0:3,0:0:0:0:
387,5,68341,1,0,0:0:0:0:
387,5,68435,2,0,P|366:26|356:53,1,58.4375011146069,0|0,3:0|3:0,0:0:0:0:
427,196,68622,2,0,P|447:175|458:148,1,58.4375011146069,8|0,0:3|0:0,0:0:0:0:
271,114,68810,6,0,L|297:266,1,116.875002229214,12|0,0:2|0:0,0:0:0:0:
354,303,69091,1,0,0:0:0:0:
354,303,69185,2,0,L|365:246,1,58.4375011146069,12|0,0:2|0:0,0:0:0:0:
427,196,69372,2,0,L|456:246,1,58.4375011146069,8|8,0:3|0:3,0:0:0:0:
405,369,69560,6,0,P|350:387|296:363,1,116.875002229214,12|2,0:2|0:0,0:0:0:0:
202,297,69841,1,0,0:0:0:0:
202,297,69935,2,0,P|228:285|257:285,1,58.4375011146069,0|0,3:0|3:0,0:0:0:0:
214,374,70122,2,0,P|184:374|158:362,1,58.4375011146069,8|0,0:3|0:0,0:0:0:0:
142,210,70310,6,0,P|189:242|202:298,1,116.875002229214,12|10,0:2|0:3,0:0:0:0:
84,331,70591,1,0,0:0:0:0:
84,331,70685,2,0,P|100:306|124:289,1,58.4375011146069,0|0,3:0|3:0,0:0:0:0:
14,219,70872,2,0,P|43:220|70:233,1,58.4375011146069,8|2,0:3|0:3,0:0:0:0:
154,94,71060,6,0,L|137:255,1,116.875002229214,12|10,0:2|0:3,0:0:0:0:
289,216,71341,1,0,0:0:0:0:
289,216,71435,2,0,L|295:157,1,58.4375011146069,0|0,3:0|3:0,0:0:0:0:
222,138,71622,2,0,L|216:197,1,58.4375011146069,8|0,0:3|0:0,0:0:0:0:
340,277,71810,6,0,L|392:251,1,58.4375011146069,12|0,0:2|0:0,0:0:0:0:
347,132,71997,2,0,L|278:166,1,58.4375011146069,2|0,0:0|3:0,0:0:0:0:
211,323,72185,1,8,0:3:0:0:
247,345,72278,1,10,0:3:0:0:
291,343,72372,1,12,0:3:0:0:
325,318,72466,1,8,0:2:0:0:
340,277,72560,5,14,0:2:0:0:
340,277,73310,5,2,0:2:0:0:
283,212,73497,1,2,3:0:0:0:
294,187,73590,1,2,3:1:0:0:
290,161,73684,1,6,0:0:0:0:
270,142,73778,1,8,1:3:0:0:
243,138,73872,1,8,1:3:0:0:
220,151,73965,1,2,1:0:0:0:
208,175,74059,5,4,0:1:0:0:
340,277,74247,2,0,L|440:261,1,82.5000031471254,2|2,0:2|0:0,0:0:0:0:
424,98,74622,2,0,L|342:111,1,82.5000031471254,2|2,0:2|0:0,0:0:0:0:
341,194,74997,2,0,L|423:181,1,82.5000031471254,2|2,0:2|0:0,0:0:0:0:
425,15,75372,1,8,0:0:0:0:
273,149,75560,5,4,0:2:0:0:
495,225,75747,2,0,P|505:180|491:131,1,82.5000031471254,2|2,0:2|0:0,0:0:0:0:
276,69,76122,2,0,P|266:114|280:163,1,82.5000031471254,10|2,0:2|0:0,0:0:0:0:
349,111,76497,2,0,P|359:66|345:17,1,82.5000031471254,8|2,0:2|0:0,0:0:0:0:
201,25,76872,1,10,0:2:0:0:
248,264,77060,5,2,0:0:0:0:
430,101,77247,2,0,L|338:112,1,82.5000031471254,10|2,0:2|0:0,0:0:0:0:
166,274,77622,2,0,L|247:264,1,82.5000031471254,10|2,0:2|0:0,0:0:0:0:
212,55,77997,2,0,L|223:147,1,82.5000031471254,10|0,0:2|1:0,0:0:0:0:
387,345,78372,6,0,L|374:237,1,110,14|0,0:2|0:0,0:0:0:0:
469,285,78653,1,2,0:0:0:0:
469,285,78747,2,0,L|481:176,1,110,12|0,0:2|0:0,0:0:0:0:
397,130,79028,1,2,0:0:0:0:
397,130,79122,2,0,L|497:87,1,110,12|0,0:2|0:0,0:0:0:0:
311,83,79403,1,2,0:0:0:0:
311,83,79497,2,0,P|299:136|318:187,1,110,12|0,0:2|1:0,0:0:0:0:
378,280,79778,1,8,0:3:0:0:
378,280,79872,2,0,L|283:272,1,55,12|8,0:2|1:2,0:0:0:0:
287,346,80060,6,0,L|192:354,1,58.4375011146069,6|0,0:1|0:0,0:0:0:0:
152,347,80247,2,0,P|206:318|225:257,1,116.875002229214,10|0,0:2|0:0,0:0:0:0:
219,188,80528,1,0,0:0:0:0:
219,188,80622,6,0,P|165:217|146:278,1,116.875002229214,12|0,0:2|0:0,0:0:0:0:
79,303,80903,1,0,3:0:0:0:
79,303,80997,2,0,L|20:296,1,58.4375011146069,10|0,0:2|0:0,0:0:0:0:
78,226,81185,2,0,L|22:184,1,58.4375011146069,8|0,0:3|0:0,0:0:0:0:
115,158,81372,2,0,L|87:94,1,58.4375011146069,10|0,0:2|0:0,0:0:0:0:
187,59,81560,6,0,L|181:118,1,58.4375011146069,8|0,0:3|0:0,0:0:0:0:
219,188,81747,2,0,B|274:216|274:170|336:200,1,116.875002229214,10|2,0:2|0:3,0:0:0:0:
336,305,82028,1,0,0:0:0:0:
336,305,82122,2,0,P|314:252|332:197,1,116.875002229214,12|0,1:2|0:0,0:0:0:0:
389,249,82403,1,0,3:0:0:0:
389,249,82497,2,0,L|470:253,1,58.4375011146069,10|0,0:2|0:0,0:0:0:0:
399,174,82685,2,0,L|471:137,1,58.4375011146069,8|0,0:3|0:0,0:0:0:0:
371,105,82872,2,0,L|415:37,1,58.4375011146069,10|0,0:2|0:0,0:0:0:0:
315,0,83060,6,0,L|313:59,1,58.4375011146069,2|0,0:0|0:0,0:0:0:0:
208,55,83247,2,0,P|262:34|313:58,1,116.875002229214,10|2,0:2|0:3,0:0:0:0:
309,167,83528,1,8,1:3:0:0:
309,167,83622,2,0,P|255:187|204:164,1,116.875002229214,12|0,0:2|0:0,0:0:0:0:
132,135,83903,1,0,3:0:0:0:
132,135,83997,2,0,L|110:194,1,58.4375011146069,10|0,0:2|0:0,0:0:0:0:
35,197,84185,2,0,L|56:256,1,58.4375011146069,8|0,0:3|0:0,0:0:0:0:
16,318,84372,6,0,P|72:326|119:294,1,116.875002229214,14|0,0:2|0:0,0:0:0:0:
309,133,84747,2,0,P|255:112|202:134,1,116.875002229214,10|0,0:2|0:0,0:0:0:0:
392,294,85122,2,0,P|439:326|495:318,1,116.875002229214,14|0,0:2|0:0,0:0:0:0:
257,187,85497,1,10,0:3:0:0:
249,205,85590,1,0,0:0:0:0:
249,224,85684,1,0,0:0:0:0:
255,242,85778,1,0,0:0:0:0:
262,260,85872,1,8,0:3:0:0:
263,280,85965,1,0,0:0:0:0:
256,298,86059,5,12,1:2:0:0:

BIN
Zblit.wav Normal file

Binary file not shown.

BIN
audio.mp3

Binary file not shown.

BIN
audio.wav

Binary file not shown.

472
cleaned_sp.py Normal file
View File

@ -0,0 +1,472 @@
from math import *
import numpy as np
import scipy as scp
from scipy.io import wavfile
import matplotlib.pyplot as plt
import subprocess
import heapq
from pathlib import Path
from time import sleep
import datetime
def is_data_stereo(raw_global_data:list) -> bool:
"""
self-explainatory
"""
try:
assert(raw_global_data[0][0])
except IndexError:
return False
except AssertionError:
return True
return True
def dist_to_integer(x):
ent = np.floor(x)
if(ent < 0.5):
return ent
else:
return (1-ent)
def is_note_within(fr1, fr2):
if(fr1 > fr2):
return (fr1/fr2 <= NOTE_DIST or dist_to_integer(fr1/fr2) >= OCTAVE_DIST) # same tone or octave
else:
return (fr2/fr1 <= NOTE_DIST or dist_to_integer(fr2/fr1) >= OCTAVE_DIST)
def keep_highest(song_name, offset, songlen, segsize, count, output_name, minfreq=110, maxfreq=5000, ampthr=250, canPlot=True, writeO = True):
'''
INPUT : data relative to music + config about the analysis
OUTPUT :
* a list of timings : it contains floats (representing circles) and couple of floats (representing sliders) (e.g. [float, float])
* a list of amplitudes relative to timings
'''
# extracting data from cropped song
sample_rate, raw_song_data = wavfile.read(song_name)
blit = int(sample_rate*segsize) # Te
song_data = [0 for i in range(len(raw_song_data))]
id_start = int(offset*sample_rate)
id_end = min(len(raw_song_data), int((offset+songlen)*sample_rate))
a = 0
if(is_data_stereo(raw_song_data)):
print("Converting to mono...")
for x in range(id_start, id_end):
song_data[x] = raw_song_data[x][0]/2 + raw_song_data[x][1]/2
if(x % (int(len(raw_song_data)/100)) == 0):
print(a, "/ 100")
a += 1
else:
song_data = raw_song_data
print("\nSampleRate : ", sample_rate)
print("SegSize : ", blit)
# calculate the frequencies associated to the FFTs
pfreq = scp.fft.rfftfreq(blit, 1/sample_rate)
# left boundary of segment to crop
current_time = offset
# list of FFTs
fft_list = []
fft_list_untouched = []
# number of samples
k = 0
print("Retrieving freqs from", offset, "to", songlen+offset, "...")
while(current_time < songlen+offset-segsize):
# index corresponding to left boundary
left_id = int(current_time*sample_rate)
# index corresponding to right boundary
right_id = int((current_time+segsize)*sample_rate)
# calculate the fft, append it to fft_list
pff = scp.fft.rfft(song_data[int(current_time*sample_rate):int(sample_rate*(current_time+segsize))])
fft_list.append(pff)
fft_list_untouched.append([ee for ee in pff])
# just to avoid what causes 0.1 + 0.1 == 0.2 to be False
k += 1
current_time = offset + k*segsize
#print(current_time)
print("\n\nSegSize :", segsize, "\nFFT :", len(fft_list), "\nFFT[0] :", len(fft_list[0]), "\npfreq :", len(pfreq), "\n\n")
# -------------------------------------------- Clean song -------------------------------------------- #
pfreq_minid = 0
pfreq_maxid = len(pfreq) -1
while(pfreq[pfreq_minid] < minfreq):
for t in range(len(fft_list)):
fft_list[t][pfreq_minid] = 0+0j
pfreq_minid += 1
while(pfreq[pfreq_maxid] > maxfreq):
for t in range(len(fft_list)):
fft_list[t][pfreq_maxid] = 0+0j
pfreq_maxid -= 1
new_times = []
new_freqs = []
new_ampls = []
new_kept = []
# i = time, j = freq
for i in range(len(fft_list)):
#returns a list of couples [id, value]
elements = heapq.nlargest(count, enumerate(fft_list[i]), key=lambda x: x[1])
for idx in range(len(elements)):
if(elements[idx][0] < len(pfreq)):
new_times.append(offset + i*segsize)
new_freqs.append(pfreq[elements[idx][0]])
new_ampls.append(fft_list[i][elements[idx][0]])
# -------------------------------------------- Get amp distribution -------------------------------------------- #
new_new_amps = [0 for i in range(int(sample_rate*songlen))]
new_new_t = [offset + i/sample_rate for i in range(int(sample_rate*songlen))]
amp_ct = 0
incr_a = segsize*4
len_seg_a = int(sample_rate*incr_a)
count_a = len_seg_a//1000
left_0 = int(sample_rate*(amp_ct+offset))
while(amp_ct < songlen-segsize):
left = int(sample_rate*(amp_ct+offset))
right = int(sample_rate*(amp_ct+offset + incr_a))
#returns a list of couples [id, value]
elements = heapq.nlargest(count_a, enumerate([song_data[i] for i in range(left, right)]), key=lambda x: x[1])
amp_ct += incr_a
for idx in range(len(elements)):
try:
new_new_amps[elements[idx][0]+left-left_0] = song_data[left+elements[idx][0]]
except:
pass
mmxx = max(new_new_amps)
new_new_amps = [nnw*1000/mmxx for nnw in new_new_amps]
# localize peaks
left_id = 0
right_id = 0
a_ampl = 0
in_seg = False
time_d = 0.035
cur_t = 0
last_t = -10.0
locs = [] # amplitudes
loct = [] # times
for i in range(len(new_new_amps)):
if(new_new_amps[i] > 100):
if(not in_seg):
in_seg = True
left_id = i
right_id = i
a_ampl = max(a_ampl, new_new_amps[i])
cur_t = 0
else:
cur_t += 1/sample_rate
if(in_seg and cur_t >= time_d):
in_seg = False
delta_t = (right_id - left_id)/sample_rate
if(np.abs(left_id/sample_rate - last_t) >= 0.01): # these notes are less than 10ms apart !
last_t = right_id/sample_rate
if(delta_t < segsize*1.1):
locs.append(a_ampl)
loct.append((left_id + right_id)/(2*sample_rate) + offset)
else:
locs.append(a_ampl)
loct.append([left_id/sample_rate + offset, right_id/sample_rate + offset])
a_ampl = 0
# -------------------------------------------- Compute freqs -------------------------------------------- #
ssize_0 = segsize/3
locf = [] # frequencies
for k in range(len(locs)):
ktime = 0
ssize = ssize_0
if(type(loct[k]) == float): # circle
ktime = loct[k]
else: # slider
ktime = (loct[k][1]+loct[k][0])/2
ssize = max((loct[k][1]-loct[k][0])/2, ssize_0)
left_id = max(0, int((ktime-ssize/2)*sample_rate))
right_id = min(int((ktime+ssize/2)*sample_rate), len(song_data))
# calculate the fft
pff = scp.fft.rfft(song_data[left_id:right_id])
fmax = pfreq[0]
fampmax = 0
for i in range(1, len(pff)):
if(pfreq[i] > minfreq and pfreq[i] < maxfreq and fampmax < np.abs(pff[i])):
fmax = pfreq[i]
fampmax = np.abs(pff[i])
locf.append(fmax)
# -------------------------------------------- Merge -------------------------------------------- #
k = 0
while(k < len(locs)):
delta_t = 0
if(type(loct[k]) == float):
delta_t += loct[k]
else:
delta_t += (loct[k][0] + loct[k][1])/2
if(type(loct[k-1]) == float):
delta_t -= loct[k-1]
else:
delta_t -= (loct[k-1][0] + loct[k-1][1])/2
if(k > 0 and np.abs(delta_t) < segsize and np.abs(locs[k] - locs[k-1]) < 50 and is_note_within(locf[k], locf[k-1])):
loct[k-1] = [loct[k-1], loct[k]]
locs[k-1] = (locs[k-1] + locs[k])/2
loct[k] = -1
locs[k] = -1
locf[k] = -1
loct.remove(-1)
locs.remove(-1)
locf.remove(-1)
k += 1
# -------------------------------------------- Plot -------------------------------------------- #
if(canPlot):
plt_loct_all = []
plt_loct = []
plt_locs = []
plt_slidt = []
plt_slids = []
for i in range(len(loct)):
if(type(loct[i]) == float):
plt_loct_all.append(loct[i])
plt_loct.append(loct[i])
plt_locs.append(locs[i])
else:
plt_loct_all.append(loct[i][0])
plt_slidt.append(loct[i][0])
plt_slidt.append(loct[i][1])
plt_slids.append(locs[i])
plt_slids.append(locs[i])
plt.plot(new_new_t, new_new_amps, "y-", label="amplitude (ua)")
plt.plot(plt_loct, plt_locs, "ro", label="circles")
plt.plot(plt_slidt, plt_slids, "go", label="sliders")
plt.plot(plt_loct_all, locf, "mo", label="frequencies (Hz)")
plt.legend(loc="upper left")
'''plt.plot(new_times, new_freqs)
plt.plot(new_times, [elt*1000/mx for elt in new_ampls])
plt.plot(new_times, new_kept, "bo")'''
plt.grid()
plt.show()
# -------------------------------------------- Write -------------------------------------------- #
if(writeO):
f = open("result_bad_apple[90].txt", "w")
f.write("Song name : " + song_name + "\n")
f.write("Start : " + str(offset) + "\n")
f.write("End : " + str(offset+songlen) + "\n\n")
f.write("Hit Objects : \n")
for ct in loct:
f.write(str(ct))
f.write("\n")
f.close()
return (loct, locs)
def convert_to_wav(song_name:str, output_file="audio.wav") -> str:
"""
Converts the song to .wav, only if it's not already in wave format.
Currently relies on file extension.
Returns: the song_name that should be used afterwards.
"""
extension = Path(song_name).suffix
if(extension == ".mp3" or extension == ".ogg"):
print("Converting to .wav...")
subprocess.run(["ffmpeg", "-y", "-i", song_name, output_file], shell=False)
return output_file
return song_name
'''
# c-type
SONG_LEN = 7
OFFSET = 0.042
BPM = 149.3
SEGSIZE = 1/(BPM/60)
wavved_song = convert_to_wav("songs/ctype.mp3")
'''
'''
# tetris_2
SONG_LEN = 14
OFFSET = 0
BPM = 157
SEGSIZE = 1/(BPM/60)
wavved_song = convert_to_wav("songs/tetris_2.wav")
'''
'''
# test
SONG_LEN = 1
OFFSET = 0
BPM = 240
SEGSIZE = 1/(BPM/60)
'''
'''
# gmtn
SONG_LEN = 5
OFFSET = 1.652
BPM = 155
SEGSIZE = 1/(BPM/60)
wavved_song = convert_to_wav("songs/furioso melodia.mp3")
'''
'''
# E
SONG_LEN = 15
OFFSET = 2.641
BPM = 155
SEGSIZE = 1/(BPM/60)
wavved_song = convert_to_wav("songs/rushe.mp3")
'''
'''
# Tsubaki
SONG_LEN = 20
OFFSET = 35.659
BPM = 199
SEGSIZE = 1/(BPM/60)
wavved_song = convert_to_wav("songs/TSUBAKI.mp3")
'''
'''
# Owen 1/2
SONG_LEN = 20
OFFSET = 1.008
BPM = 157
SEGSIZE = 1/(BPM/60)
wavved_song = convert_to_wav("songs/owen(157.00024-1008).mp3")
'''
'''
# Owen 2/2
SONG_LEN = 7
OFFSET = 25.466
BPM = 157
SEGSIZE = 1/(BPM/60)
wavved_song = convert_to_wav("songs/owen(157.00024-1008).mp3")
'''
# death
SONG_LEN = 10
OFFSET = 21.750
BPM = 180
SEGSIZE = 1/(BPM/60)
wavved_song = convert_to_wav("songs/Night of Knights.mp3")
'''
# Bad apple
SONG_LEN = 15
OFFSET = 0.152
BPM = 138
SEGSIZE = 1/(BPM/60)
#wavved_song = convert_to_wav("songs/Bad apple (138-152).mp3")
wavved_song = convert_to_wav("songs/Bad apple (138-152)[filtered].wav")
'''
'''
# Freedom dive
SONG_LEN = 7
OFFSET = 1.058
BPM = 222.22
SEGSIZE = 1/(BPM/60)
#wavved_song = convert_to_wav("songs/Freedom Dive (222.22-1058).mp3")
wavved_song = convert_to_wav("songs/Freedom Dive (222.22-1058)[filtered].wav")
'''
'''
# Mevalogania
SONG_LEN = 7
OFFSET = 7.984
BPM = 240
SEGSIZE = 1/(BPM/60)
#wavved_song = convresult_bad_appleert_to_wav("songs/Megalovania(240-7984).mp3")
wavved_song = convert_to_wav("songs/Megalovania(240-7984)[filtered].wav")
'''
'''
SONG_LEN = 0 # length of the song, in seconds
OFFSET = 0 # offset of the 1st note (aka time offset of the first red bar), in seconds
BPM = 0 # BPM
wavved_song = convert_to_wav("insert_song_name_here.wav")
'''
# Do not touch
DIVIDER = 4 # note divider
SEGSIZE = 1/(BPM/60)
NOTE_DIST = (2**(1/4))
OCTAVE_DIST = 0.05
# keep_highest(song_name, offset, songlen, segsize, count, output_name, minfreq=110, maxfreq=5000, ampthr=250):
(loct, locs) = keep_highest(wavved_song, OFFSET, SONG_LEN, SEGSIZE/DIVIDER, 1, "Zblit.wav", minfreq=220, maxfreq=3000, ampthr=800)
'''
minfreq and maxfred are thresholds for frequency analysts (anything outside of [minfreq, maxfreq] will not be accounted for)
ampthr is a threshold for amplitude (arbitrary unit)
'''
''' you can deactivate this if you want (show timings points in terminal) '''
'''
import time
import random
loct2 = []
for k in loct:
if(type(k) == float):
loct2.append(k)
else:
loct2.append(k[0])
loct2.append(k[1])
for i in range(len(loct2)-1):
print("*"*(random.randint(10, 100)))
time.sleep(loct2[i+1]-loct2[i])
print("yipee")
'''
# complexity test
fl = open("complexity.txt", "w")
# f.write("Song name : " + song_name + "\n")
'''
deltat = []
compl = []
for end in range(2,120):
st = datetime.datetime.now()
(e, ee) = keep_highest(wavved_song, OFFSET, OFFSET+end/2, SEGSIZE/DIVIDER, 1, "Zblit.wav", minfreq=220, maxfreq=3000, ampthr=800, canPlot=False,writeO=False)
et = datetime.datetime.now()
dt = et.microsecond - st.microsecond + (et.second - st.second)*1000000 + (et.minute - st.minute)/60
if(dt>0):
deltat.append(end/2)
compl.append(dt)
plt.plot(deltat, compl, "y-")
plt.plot(deltat, compl, "ro")
plt.xlabel("size of the song")
plt.ylabel("time complexity (us)")
plt.grid()
plt.show()
fl.close()
'''

0
complexity.txt Normal file
View File

BIN
pptx/code/cleaning.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

183
pptx/code/code.py Normal file
View File

@ -0,0 +1,183 @@
# treat_as_circle
# -------------------
def treat_as_circle(ind, is_slider_head):
global timeM1
start = (ind == 0)
beat_phase = round((objects[ind].time.total_seconds()*1000 - offset*1000)/ms_per_beat % 1, 2)
xcoord, ycoord = objects[ind].position
xcoord, ycoord = normalize(xcoord, ycoord)
if start:
delta_time = 0
else:
delta_time = (objects[ind].time - timeM1).total_seconds()
red_tick = (beat_phase == 0.5)
blue_tick = (beat_phase == 0.75)
white_tick = (beat_phase == 1.0)
delta_beat = round((delta_time*1000)/ms_per_beat, 2) / 3
timeM1 = objects[ind].time
stream.append((start,delta_time,delta_beat,blue_tick,red_tick,white_tick,xcoord,ycoord,False,is_slider_head, stars))
# Création de "stream"
#-------------------------------
def extract_stream(map:sl.beatmap.Beatmap, fix_stars=-1):
if fix_stars == -1:
stars = map.stars() / 7
else:
stars = fix_stars / 7
objects = map.hit_objects()
stream = []
offset = map.timing_points[0].offset.total_seconds()
ms_per_beat = map.timing_points[0].ms_per_beat
timeM1 = timedelta(seconds=0)
n = len(objects)
for i in range(len(objects)):
if is_slider(objects[i]):
duration = objects[i].end_time - objects[i].time
positions = []
number_points,curve = len(objects[i].curve.points)
curve = objects[i].curve.points
delta = duration/float(number_points)
for j in range(number_points):
if j == 0:
treat_as_circle(i, True)
else:
start = False
xcoord, ycoord = normalize(curve[j][0], curve[j][1])
delta_time = delta.total_seconds()
beat_phase = round(((objects[i].time + j*delta).total_seconds()*1000 - offset*1000)/ms_per_beat % 1, 2)
delta_beat = round((delta_time*1000)/ms_per_beat, 2) / 3
red_tick = (beat_phase == 0.5)
blue_tick = (beat_phase == 0.75)
white_tick = (beat_phase == 1.0)
stream.append((start,delta_time,delta_beat,blue_tick,red_tick,white_tick,xcoord,ycoord,True,False, stars))
timeM1 = objects[i].end_time
else:
treat_as_circle(i, False)
return stream
#Construction de séquence 2
# --------------------------------------
def mirror_stream(stream):
return [(s[0], s[1], s[2], s[3], s[4], s[5], 1 - s[6], s[7], s[8], s[9], s[10]) for s in stream]
def build_sequences(maps:list):
sequences = []
for mapp in maps:
stream = extract_stream(mapp)
mirror = mirror_stream(stream)
for i in range(len(stream) - seq_len-1):
sequences.append((stream[i:i+seq_len], stream[i+seq_len+1]))
sequences.append((mirror[i:i+seq_len], mirror[i+seq_len+1]))
return sequences
#Création de map
# --------------------------------------
def reposition_objects(mapp: sl.Beatmap):
stream = extract_stream(mapp, fix_stars=5.5)
model.eval()
j = 0 # because stream separates different curve points, j keeps the true object index while str_i is the index corresponding to "fake" objects
str_i = 0
objects = mapp.hit_objects()
while str_i < len(stream):
with torch.no_grad():
if not is_slider(objects[j]):
in_sequence = torch.tensor(stream[max(0, str_i - 20):str_i+1], dtype=torch.float32).unsqueeze(0).to(cuda)
coords = model(in_sequence)
coords = coords.tolist()
x,y = coords[0][0], coords[0][1]
mapp.hit_objects()[j].position = sl.Position(x*512, y*384)
stream[str_i] = [stream[str_i][0], stream[str_i][1], stream[str_i][2], stream[str_i][3], stream[str_i][4], stream[str_i][5], x, y, stream[str_i][8], stream[str_i][9], stream[str_i][10]]
j += 1
str_i += 1
else:
for curvepoint_k in range(len(objects[j].curve.points)):
in_sequence = torch.tensor(stream[max(0, str_i - 16):str_i+1], dtype=torch.float32).unsqueeze(0).to(cuda)
coords = model(in_sequence) # Predict xcoord and ycoord
coords = coords.tolist()
x,y = coords[0][0], coords[0][1]
if curvepoint_k == 0:
mapp.hit_objects()[j].position = sl.Position(x*512, y*384)
mapp.hit_objects()[j].curve.points[curvepoint_k] = sl.Position(x*512, y*384)
stream[str_i] = [stream[str_i][0], stream[str_i][1], stream[str_i][2], stream[str_i][3], stream[str_i][4], stream[str_i][5], x, y, stream[str_i][8], stream[str_i][9], stream[str_i][10]]
str_i += 1
j += 1
return mapp
path = Path("/kaggle/input/survey1/4.osu")
map = reposition_objects(sl.Beatmap.from_path(path))
map.write_path("/kaggle/working/4.osu")
#TRAINING LOOP
# --------------------------------------
model = LSTM(input_size=num_features, hidden_size=hidden_size, output_size=2).to(cuda)
optimizer = Adam(model.parameters(), lr=0.003)
criterion = nn.L1Loss()
# boucle d'entraînement
for epoch in range(20):
model.train()
train_loss = 0
for x, y in train_dataloader:
optimizer.zero_grad()
preds = model(x)
loss = criterion(preds, y)
loss.backward()
optimizer.step()
train_loss += loss.item()
avg_train_loss = train_loss / len(train_dataloader)
model.eval()
val_loss = 0
with torch.no_grad():
for x_val, y_val in val_dataloader:
preds_val = model(x_val)
val_loss += criterion(preds_val, y_val).item()
avg_val_loss = val_loss / len(val_dataloader)
print(f'Epoch {epoch+1}: Train Loss = {avg_train_loss:.4f}, Val Loss = {avg_val_loss:.4f}')
#CREATION DATASET
# --------------------------------------
class SequenceDataset(torch.utils.data.Dataset):
def __init__(self, sequences):
self.sequences = sequences # liste de tuples
def __len__(self):
return len(self.sequences)
def __getitem__(self, idx):
x, y = self.sequences[idx]
x = torch.tensor(x, dtype=torch.float32).to(cuda) # x = entrée
y = torch.tensor([y[6], y[7]], dtype=torch.float32).to(cuda) # y = sortie ([xcoord, ycoord])
return x, y
path1 = Path("/kaggle/input/biggerset139")
path2 = Path("/kaggle/input/varietypack51")
maps = [sl.Beatmap.from_path(m) for m in (path1.ls() + path2.ls())]
sequences = build_sequences(maps)
train_sequences, val_sequences = train_test_split(sequences, test_size=0.2)
train_dataset = SequenceDataset(train_sequences)
val_dataset = SequenceDataset(val_sequences)
train_dataloader = DataLoader(train_dataset, batch_size=64, shuffle=True)
val_dataloader = DataLoader(val_dataset, batch_size=64, shuffle=False)
#DEFINITION LSTM/dataset
# --------------------------------------
hidden_size,num_features,num_outputs = 128,11,2
class LSTM(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super().__init__()
self.lstm = nn.LSTM(input_size, hidden_size, batch_first=True)
self.fc = nn.Linear(hidden_size, output_size)
def forward(self, x):
out, _ = self.lstm(x)
out = self.fc(out[:, -1, :])
out = torch.sigmoid(out)
return out

BIN
pptx/code/display.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

BIN
pptx/code/getDistr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
pptx/code/getFreqs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
pptx/code/getSliders.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

BIN
pptx/code/parsing_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

BIN
pptx/code/parsing_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

1
pptx/compilation.sh Normal file
View File

@ -0,0 +1 @@
pandoc presentation.md -t beamer -o presentation.pdf

178
pptx/presentation.md Normal file
View File

@ -0,0 +1,178 @@
---
title: "Construction automatique de niveaux pour le jeu de rythme osu! à partir de fichiers audio"
topic: "osu"
author: "Aboujaib Alexandre - 28173"
theme: "Copenhagen"
header-includes:
- \AtBeginDocument{\title[Construction automatique de niveaux pour osu!]{Construction automatique de niveaux pour le jeu de rythme osu! à partir de fichiers audio}}
---
# Presentation du problème
### Le jeu osu!
- Jeu de rythme pour PC
- Sorti le 16 septembre 2007, adapté d'un jeu pour DS
- Environ 500 000 joueurs actifs
- Se joue avec une souris/tablette et clavier
![logo](osuLogo.png){ width=50% }
![tablet](tablet.png){ width=40% }
### Les beatmaps (niveaux)
::: columns
:::: column
![Définition d'une beatmap](wikibeatmap.png){ width=110% }
![Beatmap en jeu](gameplay.jpg)
::::
:::: column
![Liste de nouvelles beatmaps](MapList.png){ width=110% }
::::
:::
### Exemples de cercles
![](exemplesEditeur.png)
![](rcMusicRepresentation.png)
![](rcDifficultySpike.png)
### Exemples de sliders
![](sliderSimple.png){ width=50% }
![](sliderWave.png){ width=50% }
![](rcOffScreen.png)
![](rcSliderPath.png)
### Formulation du problème
Nous nous proposons de **créer deux programmes** permettant **au mieux**, à partir dun **fichier
audio** donné, de **construire un niveau pour osu!**.
# Partie I : Analyse de musique
### Approche en deux temps
#### Spécifications
*Entrée :* un fichier audio (format quelconque)
*Sortie :* des données relatives à la musique permettant le placement des notes :
`(double | (double * double)) list`
#### Processus retenu ici
![](trame.png)
### Schéma du processus
![](process.png)
### Filtres physiques & Transformée de Fourier
::: columns
:::: column
![Un exemple de filtre](filtre_ex.png){ width=90% }
::::
:::: column
![Illustration de la FFT](fft.png)
::::
:::
### Résultats
![](dataBad.png){ width=60% }
Résultat de l'extraction sur une musique (Bad Apple) pendant 15s
### Résultats
![](dataBigBad.png)
Résultat de l'extraction sur la même musique pendant 120s
### Résultats
#### Complexité
![](complexity.png){ width=95% }
### Limites, saturation et améliorations
![](tetris2.png)
### Limites, saturation et améliorations
![](tetris2.png)
> les fréquences ne sont pas correctement détéctées ici (car trop d'harmoniques)
### Limites, saturation et améliorations
![](saturation.png)
### Limites, saturation et améliorations
![](saturation.png)
> une musique "dense" fait que la méthode d'extraction des amplitudes n'est pas précise
# Partie II : placement spatial
# Annexe : code Python
### librairies utilisées ici
![](libs.png){ width=70% }
### quelques fonctions utiles
![](aux.png)
### `parse music` : extraction de la liste des amplitudes et du "sample rate"
::: columns
:::: column
![](code/parsing_1.png){ width=80% }
::::
:::: column
![](code/parsing_2.png)
::::
:::
### `cleaning` : retirer les fréquences trop basses et hautes
![](code/cleaning.png){ width=60% }
### `get amp distribution` : usage de files de priorité pour extraire les maxima
![](code/getDistr.png){ width=50% }
### `get frequency distribution` : fréquence maximale pour chaque point
![](code/getFreqs.png){ width=60% }
### `get sliders` : fusionner les notes trop proches en des 'sliders'
![](code/getSliders.png){ width=75% }
### `draw` : afficher les résultats
![](code/display.png){ width=60% }
### quelques données de test
::: columns
:::: column
![](sample1.png){ width=75% }
::::
:::: column
![](sample2.png)
::::
:::
### interface
![](sample3.png)

BIN
pptx/presentation.pdf Normal file

Binary file not shown.

192
pptx/presentation.pdf495,73 Normal file
View File

@ -0,0 +1,192 @@
\hypertarget{presentation-du-probluxe8me}{%
\section{Presentation du problème}\label{presentation-du-probluxe8me}}
\hypertarget{pruxe9sentation-dune-beatmap}{%
\subsection{Présentation d'une
beatmap}\label{pruxe9sentation-dune-beatmap}}
\begin{frame}{Le Jeu osu!}
\protect\hypertarget{le-jeu-osu}{}
\begin{itemize}
\tightlist
\item
Jeu de rythme pour PC
\item
Sorti le 16 septembre 2007
\item
Environ 500 000 joueurs actifs
\item
Se joue avec une souris/tablette et clavier {[}osuLogo.png{]}
{[}tablet.png{]}
\end{itemize}
\end{frame}
\begin{frame}{les beatmaps}
\protect\hypertarget{les-beatmaps}{}
\begin{block}{3 types d'objets:}
\protect\hypertarget{types-dobjets}{}
\begin{itemize}
\item
cercles
\item
sliders
\item
spinners (ignorés)
\item
Lien musique \textless-\textgreater{} niveau
\item
Difficulté quantifiée par des ``étoiles''
\end{itemize}
\end{block}
\end{frame}
\begin{frame}{Formulation du problème}
\protect\hypertarget{formulation-du-probluxe8me}{}
Nous nous proposons de créer deux programmes permettant au mieux, à
partir d'un fichier audio donné, de construire un niveau pour osu!.
\end{frame}
\hypertarget{duxe9composition-du-probluxe8me}{%
\subsection{Décomposition du
problème}\label{duxe9composition-du-probluxe8me}}
\begin{frame}{Analyse de musique (Alexandre)}
\protect\hypertarget{analyse-de-musique-alexandre}{}
\begin{itemize}
\tightlist
\item
respecter les caractéristiques de la musique : décalage, tempo
\item
distinguer sons importants et accessoires
\end{itemize}
\end{frame}
\begin{frame}{Placement des objets}
\protect\hypertarget{placement-des-objets}{}
\begin{itemize}
\tightlist
\item
angles -\textgreater{} \emph{flow}
\item
doit complémenter la musique (espacement, difficulté)
\item
visuel: plaisant et lisible (-\textgreater{} motifs/patterns)
\end{itemize}
\end{frame}
\hypertarget{ruxe9duction-placement-pruxe9diction-de-suxe9quence}{%
\subsection{Réduction: placement \textless{} prédiction de
séquence}\label{ruxe9duction-placement-pruxe9diction-de-suxe9quence}}
\begin{frame}{Observation}
\protect\hypertarget{observation}{}
Le placement du prochain objet dépend:
\begin{itemize}
\tightlist
\item
de la musique
\item
des objets précédents
\end{itemize}
\textasciitilde{} prédiction de séquence (en économie par ex.)
\textbf{BUT} = encoder musique et objets pour prédire les prochains
objets
\end{frame}
\begin{frame}{1ère approche: glouton}
\protect\hypertarget{uxe8re-approche-glouton}{}
\begin{block}{Un principe simple\ldots{}}
\protect\hypertarget{un-principe-simple}{}
\begin{itemize}
\tightlist
\item
à partir des choix précédents -\textgreater{} faire le meilleur choix
\end{itemize}
\end{block}
\begin{block}{\ldots mais peu efficace en pratique.}
\protect\hypertarget{mais-peu-efficace-en-pratique.}{}
\begin{itemize}
\tightlist
\item
beaucoup d'heuristiques à coder -\textgreater{} compliqué sur un jeu
comme osu!
\end{itemize}
\end{block}
\end{frame}
\begin{frame}{Réseaux de neurones nécurrents}
\protect\hypertarget{ruxe9seaux-de-neurones-nuxe9currents}{}
{[}{]}
\end{frame}
\begin{frame}{Amélioration: LSTM}
\protect\hypertarget{amuxe9lioration-lstm}{}
{[}{]}
\end{frame}
\begin{frame}{Encodage précis des objets}
\protect\hypertarget{encodage-pruxe9cis-des-objets}{}
\begin{block}{Importance de l'encodage}
\protect\hypertarget{importance-de-lencodage}{}
Permet au modèle ``d'apprendre'' le plus efficacement possible
\end{block}
\begin{block}{Encodage retenu}
\protect\hypertarget{encodage-retenu}{}
10-uplet contenant: start, delta\_time (en ms), delta\_beat (float),
blue\_tick (bool), red\_tick (bool), white\_tick (bool), xcoord (float),
ycoord (float), is\_curve\_point, is\_slider\_start
Normalisation des données
\end{block}
\end{frame}
\begin{frame}{Protocole d'entraînement}
\protect\hypertarget{protocole-dentrauxeenement}{}
\begin{itemize}
\tightlist
\item
Echantillon de 188 niveaux, style
\end{itemize}
\end{frame}
\hypertarget{ruxe9sultats}{%
\subsection{Résultats}\label{ruxe9sultats}}
\begin{frame}{1ère observations}
\protect\hypertarget{uxe8re-observations}{}
\begin{block}{Choix de musiques d'essai :}
\protect\hypertarget{choix-de-musiques-dessai}{}
\begin{itemize}
\tightlist
\item
1 niveau contrôle = humain (Science)
\item
1 niveau de type ``stream'' (Entanglement)
\item
1 niveau de type ``jump'' (Rooftop)
\item
1 niveau hybride (Blade Dance)
\end{itemize}
Rythmes : humains
\end{block}
\end{frame}
\begin{frame}{Améliorations possibles?}
\protect\hypertarget{amuxe9liorations-possibles}{}
\begin{itemize}
\tightlist
\item
autres types de modèles
\item
plus d'entraînement (mais surapprentissage\ldots)
\item
meilleur lien musique \textless-\textgreater{} placement (ici mis de
côté\ldots)
\end{itemize}
\end{frame}

68
result_NoN.txt Normal file
View File

@ -0,0 +1,68 @@
Song name : audio.wav
Start : 21.75
End : 31.75
Hit Objects :
21.80657596371882
21.86517006802721
21.954943310657598
22.158684807256236
22.288934240362813
22.47361678004535
22.554920634920634
22.686349206349206
22.807539682539684
22.91013605442177
23.045102040816328
23.131043083900227
23.180090702947847
23.220328798185943
[23.287528344671202, 23.367142857142856]
[23.472698412698414, 23.55392290249433]
23.821655328798187
23.95299319727891
24.15141723356009
24.22986394557823
24.498219954648526
24.66047619047619
[24.78424036281179, 24.884920634920636]
25.170090702947846
25.489603174603175
25.57687074829932
25.655873015873016
25.812653061224488
[25.908027210884356, 25.956757369614515]
25.993038548752835
[26.150385487528347, 26.199002267573697]
26.305804988662132
26.492925170068027
26.652392290249434
26.81343537414966
27.013990929705216
27.16793650793651
27.28827664399093
27.49448979591837
27.838990929705215
27.88766439909297
27.953197278911563
28.156689342403627
28.287687074829932
28.502426303854875
28.686780045351476
28.83141723356009
29.16842403628118
29.285532879818593
[29.477437641723355, 29.53560090702948]
29.660725623582767
29.810306122448978
29.866689342403628
30.13770975056689
[30.484603174603173, 30.555702947845806]
30.623378684807257
30.807585034013606
30.956666666666667
31.155680272108846
31.289024943310658
31.38056689342404
31.4947052154195
31.62167800453515

63
result_bad_apple.txt Normal file
View File

@ -0,0 +1,63 @@
Song name : songs/Bad apple (138-152)[filtered].wav
Start : 0.152
End : 20.152
Hit Objects :
0.17748752834467119
0.6138367346938776
1.0485873015873015
1.4824761904761903
[1.5911723356009069, 1.6998571428571427]
[1.80856462585034, 1.916580498866213]
2.352963718820862
2.786183673469388
3.2216031746031746
3.4390068027210887
3.6557414965986395
4.092079365079365
4.525321995464853
4.960752834467121
5.0694263038548755
[5.178133786848073, 5.286818594104308]
5.394857142857143
5.8312176870748305
6.264437641723356
6.699879818594105
6.917260770975057
7.133995464852608
7.570333333333333
8.00357596371882
8.439006802721087
8.547680272108844
[8.65639909297052, 8.765083900226756]
8.873122448979592
9.309471655328798
9.742714285714285
10.178133786848072
10.395537414965986
10.612272108843536
11.049222222222221
11.481841269841269
11.917272108843537
12.025945578231292
12.134641723356008
12.243371882086167
12.35137641723356
12.788360544217687
13.223145124716552
13.655741496598639
14.090480725623582
14.526897959183673
14.960072562358276
15.394879818594104
15.829607709750567
16.264414965986397
16.69919954648526
17.13463038548753
17.352011337868483
17.568780045351474
18.00577551020408
18.44058276643991
18.874732426303854
19.30789569160998
19.744233560090706

68
result_bad_apple[90].txt Normal file
View File

@ -0,0 +1,68 @@
Song name : audio.wav
Start : 21.75
End : 31.75
Hit Objects :
21.80657596371882
21.86517006802721
21.954943310657598
22.158684807256236
22.288934240362813
22.47361678004535
22.554920634920634
22.686349206349206
22.807539682539684
22.91013605442177
23.045102040816328
23.131043083900227
23.180090702947847
23.220328798185943
[23.287528344671202, 23.367142857142856]
[23.472698412698414, 23.55392290249433]
23.821655328798187
23.95299319727891
24.15141723356009
24.22986394557823
24.498219954648526
24.66047619047619
[24.78424036281179, 24.884920634920636]
25.170090702947846
25.489603174603175
25.57687074829932
25.655873015873016
25.812653061224488
[25.908027210884356, 25.956757369614515]
25.993038548752835
[26.150385487528347, 26.199002267573697]
26.305804988662132
26.492925170068027
26.652392290249434
26.81343537414966
27.013990929705216
27.16793650793651
27.28827664399093
27.49448979591837
27.838990929705215
27.88766439909297
27.953197278911563
28.156689342403627
28.287687074829932
28.502426303854875
28.686780045351476
28.83141723356009
29.16842403628118
29.285532879818593
[29.477437641723355, 29.53560090702948]
29.660725623582767
29.810306122448978
29.866689342403628
30.13770975056689
[30.484603174603173, 30.555702947845806]
30.623378684807257
30.807585034013606
30.956666666666667
31.155680272108846
31.289024943310658
31.38056689342404
31.4947052154195
31.62167800453515

49
result_megalovania.txt Normal file
View File

@ -0,0 +1,49 @@
Song name : songs/Megalovania(240-7984)[filtered].wav
Start : 7.984
End : 14.984
Hit Objects :
[8.078591836734693, 8.132503401360545]
8.204578231292517
[8.288115646258504, 8.34134693877551]
8.527968253968254
[8.879102040816326, 8.922888888888888]
[9.129260770975057, 9.181766439909296]
9.37977097505669
9.423172335600906
9.549498866213153
9.689034013605442
9.8049410430839
9.898104308390023
10.072764172335601
10.276834467120182
10.341426303854876
10.512015873015873
10.899748299319729
11.129272108843537
11.183319727891156
[11.385530612244898, 11.428920634920635]
11.57258276643991
11.688943310657596
11.76148299319728
11.881256235827664
11.935269841269841
12.076947845804987
12.293716553287982
12.344793650793651
[12.503886621315193, 12.549975056689341]
[12.88151700680272, 12.925519274376416]
13.12928344671202
13.183308390022676
13.420326530612245
13.63055328798186
13.695836734693877
13.776233560090702
13.816392290249432
13.914839002267573
14.015201814058956
14.068489795918367
14.142446712018142
14.282956916099774
14.527979591836734
14.897560090702948

97
result_owen.txt Normal file
View File

@ -0,0 +1,97 @@
Song name : audio.wav
Start : 1.008
End : 21.008
Hit Objects :
[1.0518548752834467, 1.1318095238095238]
1.4385102040816327
1.8517641723356009
1.9118095238095238
2.1988276643990927
2.590641723356009
2.984496598639456
3.3595192743764173
3.403498866213152
3.7352448979591837
3.8218548752834467
4.10879365079365
4.192229024943311
4.494666666666667
4.894315192743765
4.968136054421769
5.245267573696145
5.644451247165533
5.823487528344671
6.041390022675737
6.4073197278911564
6.785743764172335
6.878181405895692
6.985267573696145
[7.173113378684807, 7.250256235827664]
7.5618208616780045
7.628351473922902
7.929609977324263
7.970482993197279
8.309609977324264
8.702648526077098
9.100709750566892
9.465505668934242
9.65797732426304
9.850721088435375
10.227104308390022
10.614780045351473
10.673158730158729
11.0098820861678
11.083691609977325
11.266696145124715
11.379507936507938
11.758986394557823
12.156390022675737
12.535868480725625
12.714349206349205
12.896265306122448
13.279984126984125
13.37145804988662
13.665267573696145
13.722784580498868
14.048702947845804
14.099213151927437
14.140097505668933
14.435789115646259
14.817399092970522
14.940732426303853
15.216299319727892
15.625063492063493
15.759995464852608
15.945392290249433
16.147569160997733
16.36139002267574
16.444802721088436
16.724621315192742
17.101503401360542
17.144802721088436
17.187897959183672
17.296344671201812
17.379405895691608
17.48303401360544
17.875551020408164
17.958532879818595
18.27968934240363
18.440879818594105
18.653986394557823
[18.829950113378683, 18.918034013605443]
19.008544217687074
19.084541950113376
19.306492063492062
19.388408163265304
19.464836734693876
19.77943990929705
20.020301587301585
20.167897959183673
20.33250113378685
20.402331065759636
20.478589569160995
20.53909977324263
20.824961451247166
20.869950113378685
20.934825396825396

61
result_owen_2.txt Normal file
View File

@ -0,0 +1,61 @@
Song name : audio.wav
Start : 25.466
End : 32.466
Hit Objects :
25.52553514739229
25.60707709750567
25.738539682539685
25.859526077097506
25.942394557823132
25.989775510204083
26.09740589569161
26.19691836734694
26.363222222222223
26.4916462585034
26.554990929705216
26.757621315192743
26.866702947845805
26.97207709750567
27.072337868480727
27.175580498866214
27.399310657596374
27.498664399092974
27.716861678004538
27.803505668934243
27.902190476190476
28.176839002267574
28.273913832199547
28.359049886621317
28.46615873015873
[28.562768707482995, 28.64804081632653]
[28.756884353741498, 28.83251927437642]
28.949106575963718
29.01647619047619
[29.084253968253968, 29.173789115646258]
29.319333333333333
29.41299546485261
29.484344671201814
29.559356009070296
29.73783673469388
29.926419501133786
29.987961451247166
30.09112471655329
30.19709977324263
30.301578231292517
30.378426303854877
30.481668934240364
[30.683539682539685, 30.760727891156463]
30.847587301587303
31.04754195011338
31.14048979591837
31.229185941043085
31.290421768707482
31.433086167800454
31.524730158730158
31.61157823129252
[31.72844897959184, 31.816873015873018]
31.988358276643993
32.095784580498865
32.20156689342404
32.29895918367347

124
result_tsubaki.txt Normal file
View File

@ -0,0 +1,124 @@
Song name : audio.wav
Start : 35.659
End : 55.659
Hit Objects :
35.79228798185941
35.91021315192744
36.04037188208617
36.15184580498866
36.23495238095238
36.32842176870748
36.406290249433106
36.75744671201814
36.95995238095238
37.2967664399093
37.449056689342406
37.5271746031746
37.65802494331066
37.84421541950113
[37.947548752834464, 37.990655328798184]
38.125190476190475
38.411800453514736
38.60766213151928
38.70639229024943
38.821278911564626
38.8689433106576
39.10621088435374
39.28780952380952
39.45389795918367
39.684714285714286
39.864
39.920814058956914
40.10293424036281
40.195848072562356
40.32245804988662
40.56655102040816
[40.74982766439909, 40.82168707482993]
41.003024943310656
41.14491836734694
41.392820861678004
41.57685714285714
[41.72762811791383, 41.76821768707483]
41.81646031746032
41.87975963718821
42.062174603174604
42.31275283446712
42.4768231292517
42.53006575963719
42.63855782312925
42.81466893424036
42.990655328798184
43.133659863945574
43.20110884353741
43.258172335600904
[43.56973696145125, 43.611335600907026]
43.7077641723356
44.05415873015873
44.18567800453515
44.2824126984127
44.647401360544215
44.76270748299319
45.070519274376416
45.406018140589566
45.518750566893424
45.694918367346936
45.8018231292517
46.045814058956914
46.25060997732426
46.47650566893424
46.5686485260771
[46.845791383219954, 46.91973696145125]
47.06896598639456
47.17215192743764
47.29363718820861
47.33420408163265
47.43748072562358
47.647129251700676
47.71998639455782
47.972854875283446
48.054034013605445
[48.258807256235826, 48.31286621315193]
48.353263038548754
48.48960090702948
48.910995464852604
[49.1158253968254, 49.16668707482993]
49.26697052154195
49.64987301587301
49.74669841269841
49.86911337868481
50.1181723356009
50.40005442176871
50.459453514739224
50.65728798185941
50.71109750566893
[50.84771882086168, 50.893149659863944]
51.33664172335601
51.39431746031746
51.636709750566894
51.688761904761904
51.743988662131514
52.00931746031746
52.11760544217687
52.15932879818594
52.20068934240363
52.25446485260771
52.47726530612245
52.55314965986395
52.8780589569161
53.04224263038549
53.08240136054422
53.177356009070294
53.29026984126984
53.49267346938775
[53.62920408163265, 53.668081632653056]
53.77542857142857
[54.10584807256235, 54.16563265306122]
54.327820861678006
[54.431188208616774, 54.479566893424035]
54.691052154195006
54.992820861678
55.19167573696145
55.29097278911564
55.553512471655324
55.60739002267574

View File

@ -1,145 +0,0 @@
import numpy as np
import scipy as scp
import heapq
def retrieve_dominant_freqs(song_name, offset, songlen, segsize):
# returns a list with peak frequencies alongside the sample rate
# /!\ song_name is specified to be a list, NOT a list of couples (aka song is mono)
# segsize is in seconds
# remove high_pitched/low-pitched frequencies
minfreq = 110
maxfreq = 440*8
# cutting the song to only keep the one we're interested in
subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(songlen+offset), "-i", song_name, "crop.wav"], shell=False)
# extracting data from cropped song
sample_rate, song_data = wavfile.read("crop.wav")
blit = int(sample_rate*segsize) # Te
# remove the copy of the song
subprocess.run(["rm", "crop.wav"], shell=False)
# calculate the frequencies associated to the FFTs
pfreq = scipy.fft.rfftfreq(blit, 1/sample_rate)
# left boundary of segment to crop
current_time = offset
# list of FFTs
fft_list = []
# number of samples
k = 0
while(current_time <= songlen+offset):
# index corresponding to left boundary
left_id = int(current_time*sample_rate)
# index corresponding to right boundary
right_id = int((current_time+segsize)*sample_rate)
# calculate the fft, append it to fft_list
pff = scp.fft.rfft(global_data[left:right])
fft_list.append(pff)
# just to avoid what causes 0.1 + 0.1 == 0.2 to be False
k += 1
current_time = offset + k*segsize
# spacing between samples (time)
fe = segsize/sample_rate
# list that will contain the maximum frequencies/amplitudes for all FFTs
maxlist = []
maxamps = []
# find all maximums
for i in range(len(fft_list)):
current_max = -1
current_fmax = 0
for j in range(len(fft_list[i])):
if(pfreq[j] < maxfreq & pfreq[j] >= minfreq & np.abs(fft_list[i][j]) > current_max):
current_max = np.abs(fft_list[i][j])
current_fmax = pfreq[j]
maxlist.append(current_fmax)
maxamps.append(current_max)
# gg
# maxlist[i] corresponds to time (offset + i*segsize)
return (maxlist, maxamps, segsize)
def retrieve_dominant_amps(song_name, offset, songlen, segsize, percent):
# returns a list with the percent% peak amplitudes alongside the sample rate
# /!\ song_name is specified to be a list, NOT a list of couples (aka song is mono)
# segsize is in seconds
# cutting the song to only keep the one we're interested in
subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(songlen+offset), "-i", song_name, "crop.wav"], shell=False)
# extracting data from cropped song
sample_rate, song_data = wavfile.read("crop.wav")
blit = int(sample_rate*segsize) # Te
# remove the copy of the song
subprocess.run(["rm", "crop.wav"], shell=False)
# which notes will be voided
is_locked = [False for i in range(len(song_data))]
x = int((len(song_data)*threshold)//100)
print("Retreiving the", int(x), "/", len(song_data), "highest values")
elements = heapq.nlargest(int(x), enumerate(song_data), key=lambda x: x[1])
#returns a list of couples [id, value]
for idx in range(len(elements)):
is_locked[elements[idx][0]] = True
for r in range(len(song_data)):
if(is_locked[r] == False):
song_data[r] = 0
# now we need to reduce song_data so that it matches the length of the previous function's return
res = []
k = 0
current_time = offset
while(current_time <= songlen+offset):
# index corresponding to left boundary
left_id = int(current_time*sample_rate)
# index corresponding to right boundary
right_id = int((current_time+segsize)*sample_rate)
# merge the segment into one value
cmax = 0
for i in range(left_id, right_id):
if(i < len(song_data) & cmax < song_data[i]):
cmax = song_data[i]
res.append(cmax)
k += 1
current_time = current_time + k*segsize
# gg
# res[i] corresponds to time (offset + i*segsize)
return res
print("done")

BIN
songs/Galaxy Collapse.mp3 Normal file

Binary file not shown.

BIN
songs/Night of Knights.mp3 Normal file

Binary file not shown.

BIN
songs/TSUBAKI.mp3 Normal file

Binary file not shown.

BIN
songs/audio.wav Normal file

Binary file not shown.

BIN
songs/crop.wav Normal file

Binary file not shown.

BIN
songs/ctype.mp3 Normal file

Binary file not shown.

BIN
tetris_4.wav → songs/furioso melodia.mp3 Executable file → Normal file

Binary file not shown.

BIN
songs/no.mp3 Normal file

Binary file not shown.

BIN
songs/rushe.mp3 Normal file

Binary file not shown.

BIN
songs/tetris_2.wav Executable file

Binary file not shown.

View File

@ -1,872 +0,0 @@
from math import *
import numpy as np
from scipy.io import wavfile
from scipy import signal
import matplotlib.pyplot as plt
import subprocess
import wave as wv
import struct
import librosa
import heapq
import scipy
import os
import random
from pathlib import Path
from time import sleep
from datetime import timedelta
import debug
print("Starting...\n")
def filter_n_percent_serial(song_name, offset, n_iter, step, threshold):
"""
song_name : string
offset : int
n_iter : int (number of turns)
step : int (length of each small segment)
threshold : int (is in ]0, 100])
filter data associated with song_name to keep only the highest threshold% values
"""
subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(offset+step*n_iter), "-i", song_name, "crop.wav"], shell=False)
sample_rate, global_data = wavfile.read('crop.wav')
subprocess.run(["clear"], shell=False)
subprocess.run(["rm", "crop.wav"], shell=False)
for i in range(n_iter):
print(i, "/", n_iter)
#print(i * step)
song_data = global_data[int(i*step*sample_rate):int((i+1)*step*sample_rate)]
if(len(song_data) != 0):
mx = max(song_data)
is_locked = [False for i in range(len(song_data))]
x = int((len(song_data)*threshold)//100)
#print("X = ", x)
#print("Retreiving the", int(x), "/", len(song_data), "highest values")
elements = heapq.nlargest(int(x), enumerate(song_data), key=lambda x: x[1])
#print("Done")
for idx in range(len(elements)):
is_locked[elements[idx][0]] = True
for r in range(len(song_data)):
if(is_locked[r] == False):
global_data[r+int(i*step*sample_rate)] = 0
return global_data
def write_to_file_thr(sample_rate, song_data, offset, threshold, filename):
# write data to output file
file = open(filename, 'w')
file.writelines('time,amplitude\n')
mx = max(song_data)
print("writing to output...")
for i in range(len(song_data)):
if(i%(len(song_data)//50) == 0):
print(i, "/", len(song_data))
if(song_data[i]/mx > threshold):
file.writelines(str(np.round(offset + i/sample_rate, 3)))
file.writelines(',')
file.writelines(str(np.round(song_data[i], 0)))
file.writelines('\n')
def round_t(id, sample_rate, bpm, div, offset, k0):
k = k0
t = offset + k/(bpm*div)
while(t < id/sample_rate):
t = offset + k/(bpm*div)
k += 1
if(np.abs(t - id/sample_rate) < np.abs((t - 1/(bpm*div)) - id/sample_rate)):
return t
return (t - 1/(bpm*div), 0)
def compress(Zxx):
res = []
def get_freq(song_name, times, width=1000, display=False):
"""
for a given list of times (in seconds), returns the corresponding peak frequencies
"""
subprocess.run(["ffmpeg", "-ss", str(0), "-t", str(max(np.array(times))), "-i", song_name, "crop.wav"], shell=False)
sample_rate, global_data = wavfile.read(song_name)
#blit = int(sample_rate*step)
subprocess.run(["clear"], shell=False)
subprocess.run(["rm", "crop.wav"], shell=False)
pfreq = scipy.fft.rfftfreq(2*width, 1/sample_rate)
frequencies = [0 for s in range(len(times))]
print(len(pfreq))
for s in range(len(times)):
left = max(0, int(times[s]*44100)-width)
right = min(len(global_data), int(times[s]*44100)+width)
pff = scipy.fft.rfft(global_data[left:right])
#print(len(pff), len(pfreq))
mx = max(np.abs(pff))
for id in range(len(pff)):
if frequencies[s] == 0 and np.abs(pff[id]) == mx:
frequencies[s] = pfreq[id]
if(display):
plt.plot(times, frequencies)
plt.grid()
plt.xlabel("Time (s)")
plt.ylabel("Dominant frequency (Hz)")
plt.title("Dominant frequencies at peaks")
plt.show()
return frequencies
def is_data_stereo(raw_global_data:list) -> bool:
"""
raw_global_data : list
"""
try:
assert(raw_global_data[0][0])
except IndexError:
return False
except AssertionError:
return True
return True
def void_freq(song_name, offset, songlen, increment, minfreq, maxfreq, upperthr, ampthr, ampfreq, ampval, leniency, write, linear, output_file="trimmed.wav"):
"""
song_name : string
offset : int
songlen : int (length of the part that will be filtered, starting from offset)
increment : float (technical parameter)
minfreq and maxfreq : every frequency in [minfreq, maxfreq] will be voided
upperthr : every frequency above upperthr will be voided
ampthr : every frequency with amplitude under MAX/ampthr (aka amplitudes under (100/ampthr)% of the max will be voided
ampfreq, leniency (if linear is false), linear : technical parameters
ampval : int
- if linear is false, then this willbe the maximum amplification possible
- if linear is true, this is the multiplier (Amp <- Amp * (ampval * frequency + leniency))
write : bool (should be set to True)
output_file : technical
"""
fft_list = []
times = []
current_time = offset
k = 0
subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(songlen+offset), "-i", song_name, "crop.wav"], shell=False)
sample_rate, raw_global_data = wavfile.read("crop.wav")
blit = int(sample_rate*increment)
global_data = [0 for i in range(len(raw_global_data))]
#subprocess.run(["clear"])
subprocess.run(["rm", "crop.wav"], shell=False)
a = 0
if(is_data_stereo(raw_global_data)):
print("Converting to mono...")
for x in range(len(raw_global_data)):
global_data[x] = raw_global_data[x][0]/2 + raw_global_data[x][1]/2
if(x % (int(len(raw_global_data)/100)) == 0):
print(a, "/ 100")
a += 1
else:
global_data = raw_global_data
#print("Blit :", blit)
pfreq = scipy.fft.rfftfreq(blit, 1/sample_rate)
#print(len(pfreq))
while(current_time <= songlen):
pff = scipy.fft.rfft(global_data[k*blit:(k+1)*blit])
fft_list.append(pff)
times.append(k*increment)
k += 1
current_time = offset + k*increment
print("FFT :", len(fft_list), "\nFFT[0] :", len(fft_list[0]), "\npfreq :", len(pfreq))
print("Finding global max...")
if(linear == False):
for i in range(len(fft_list)):
for j in range(len(fft_list[i])):
fft_list[i][j] *= (1 + ampval/max(1, np.abs(pfreq[j] - ampfreq)))
else:
for i in range(len(fft_list)):
for j in range(len(fft_list[i])):
fft_list[i][j] *= (ampval*pfreq[j] + leniency)
print("Trimming...")
for i in range(len(fft_list)):
lmax = 0
for j in range(len(fft_list[i])):
if(np.abs(fft_list[i][j]) > lmax):
lmax = np.abs(fft_list[i][j])
for j in range(len(fft_list[i])):
if((pfreq[j] >= minfreq and pfreq[j] < maxfreq) or pfreq[j] > upperthr):
fft_list[i][j] = 0+0j
if(np.abs(fft_list[i][j]) < lmax/ampthr):
fft_list[i][j] = 0+0j
if(write):
res = []
print("Converting...")
for i in range(len(fft_list)):
ift = scipy.fft.irfft(fft_list[i], n=blit)
for k in ift:
res.append(k)
#print(type(res[0]))
mx = 0
for j in range(len(res)):
if(res[j] > mx):
mx = res[j]
for i in range(len(res)):
res[i] = np.int16(32767*res[i]/mx)
res = np.array(res)
wavfile.write(output_file, 44100, res)
#plt.plot(np.abs(pfreq[:len(fft_list[0])]), np.abs(fft_list[0]))
#plt.grid()
#plt.show()
print("Done")
def convert_tuple(data, times):
"""
Takes data and converts it to a list of tuples (amplitude, datetimes)
"""
return [(times[i], data[i]) for i in range(len(data))]
def get_songlen(filename):
"""
retrieves the length of the song in seconds
"""
sample_rate, global_data = wavfile.read(filename)
print("LEN :", len(global_data)/sample_rate)
return (len(global_data)/sample_rate)
def convert_to_wav(song_name:str, output_file="audio.wav") -> str:
"""
Converts the song to .wav, only if it's not already in wave format.
Currently relies on file extension.
Returns: the song_name that should be used afterwards.
"""
extension = Path(song_name).suffix
match extension:
case ".mp3" | ".ogg":
print("Converting to .wav...")
subprocess.run(["ffmpeg", "-y", "-i", song_name, output_file], shell=False)
return output_file
return song_name
def process_song(filename, bpm, offset0=0, div_len_factor=1, n_iter_2=-1, threshold=0.5, divisor=4):
"""
filename : string (name of the song)
offset : int [+] (song mapping will start from this time in seconds, default is 0)
bpm : int [+]
div_len_factor : float [+] (the length multiplier of each segment, default is 1)
n_iter : int [+*] (the number of iterations, default is -1 (maps the whole music))
threshold : int [0, 100] (used by the filter function to only keep the largest threshold% of timing points, default is 0.5)
divisor : int [+] (beat divisor used to snap the notes, default is 4)
"""
filename = convert_to_wav(filename)
offset = offset0/1000
div_len = div_len_factor*60/bpm-0.01
n_iter = n_iter_2
song_len = get_songlen(filename)
if(n_iter == -1):
n_iter = int((song_len-offset/1000)/div_len)-1
filtered_name = f"{filename}_trimmed.wav"
void_freq(filename, offset, min(song_len, offset+div_len*(n_iter+1)+0.01), 4*60/bpm, minfreq=0, maxfreq=220, upperthr=5000, ampthr=60, ampfreq = 1200, ampval = 5.0, leniency = 0.005, write=True, linear=False, output_file=filtered_name)
datares = filter_n_percent_serial(filtered_name, offset, n_iter, div_len, threshold)
#snapped_data = amplitude
#times in ms
(snapped_data, times) = debug.snap3(datares, mintime=50, initial_plot=True, after_plot=True)
#frequencies=get_freq(filtered_name, offset, div_len, div_len*n_iter, snapped_data, True)
frequencies = get_freq(filtered_name, times, display=True)
Path(f"{filename}_trimmed.wav").unlink()
return snapped_data, times, frequencies
'''
datares = debug.snap2(datares, 44100, bpm, first_offset=offset, div=divisor, show=True, adjust=True)
frequencies = get_freq(filtered_name, offset, div_len, div_len*n_iter, datares, True)
Path(f"{filename}_trimmed.wav").unlink()
return convert_tuple(datares, frequencies)
'''
def main():
aa, bb, cc = process_song("tetris_4.wav", 160, n_iter_2=48)
#print(data)
print("Program finished with return 0")
if __name__ == "__main__":
main()
''' -------------------------------------------------------------------- '''
''' -----------------------| Feuilles mortes |-------------------------- '''
''' -------------------------------------------------------------------- '''
'''
def smooth(data, thr, mergeThr, show):
mx = max(data)
for i in range(len(data)-mergeThr):
if(data[i]/mx > thr):
for k in range(1, mergeThr):
data[i+k] = 0
if(show):
t = [j/1000 for j in range(len(data))]
plt.plot(t, data)
plt.xlabel("Time (not scaled to origin)")
plt.ylabel("Amplitude")
plt.grid()
plt.show()
return data
if(False):
#t, f, Zxx = fct("no.wav", 0, 0.032, 10, 5000, False)
#t, f, Zxx = fct("worlds_end_3.wav", 150.889, 0.032, 170.889, 3000, False)
#t, f, Zxx = fct("deltamax.wav", 9.992, 0.032, 114.318, 3000, False)
#t, f, Zxx = fct("deltamax.wav", 9.992, 0.032, 20, 3000, False)
#t, f, Zxx = fct("da^9.wav", 8.463, 0.032, 20, 5000, False)
t, f, Zxx = fct("13. Cosmic Mind.wav", 0, 0.032, 20, 5000, False)
#t, f, Zxx = fct("Furioso Melodia 44100.wav", 4, 0.032, 8, 3000, False)
#t, f, Zxx = fct("changing.wav", 0, 0.05, 3.9, 5000, False)
#fct("worlds_end_3.wav", 75, (60/178)/4, 75+2, 2500)
plot_max(t, f, Zxx, True)
if(False):
#(t, data) = peaks("worlds_end_3.wav", 0, 300, False, 0.92)
(t, data) = peaks("worlds_end_3.wav", 74.582, 6, False, 0.9)
#(t, data) = peaks("da^9.wav", 8.463, 301.924 - 8.463, False, 0.95)
#(t, data) = peaks("deltamax.wav", 8.463, 30101.924 - 8.463, False, 0.92)
da = find_bpm(t, 44100, data, 100, 200, 1, 10)
print("BPM data is", da)'''
#data = [-1 for i in range(int(x))]
#ids = [-1 for i in range(int(x))]
'''
data = []
ids = []
for k in range(int(x)):
data.append(int(7*mx/10))
ids.append(-1)
# structure there is [[index, value]...]
i = 0
calc = 0
while(i < len(song_data)):
if(i%10 == 0):
print(i, "/", len(song_data))
if(data[int(x)-1] < song_data[i]):
calc += 1
#print("\n \n \n \n \n")
data[int(x)-1] = song_data[i]
ids[int(x)-1] = i
k = int(x)-1
#while(k < int(x) & data[0] > data[k]):
while(k > 0 and data[k-1] <= data[k]):
data[k], data[k-1] = data[k-1], data[k]
ids[k], ids[k-1] = ids[k-1], ids[k]
k -= 1
#print(data[int(x)-1], calc, "/", x)
i += skip
i += 1
for s in range(int(x)-1):
if(data[s] < data[s+1]):
print("Nope", s)
assert(0)
'''
'''
def fct(song_name, offset, increment, songlen, maxfreq, display):
to_cut = 20000//maxfreq
global_Zxx = np.array([])
global_f = np.array([])
global_t = np.array([])
current_time = offset
k = 0
while(current_time <= songlen):
subprocess.run(["ffmpeg", "-ss", str(current_time), "-t", str(increment), "-i", song_name, "crop.wav"], shell=False)
sample_rate, audio_data = wavfile.read('crop.wav')
size = audio_data.size
#subprocess.run(["clear"])
subprocess.run(["rm", "crop.wav"], shell=False)
# do stuff here
#f, t, Zxx = signal.stft(audio_data, sample_rate, nperseg=1000)
f, t, Zxx = signal.spectrogram(audio_data, fs=sample_rate, nfft=size)
leng = len(f)
f, Zxx = f[:leng//to_cut], Zxx[:leng//to_cut]
#print(len(Zxx))
#print(len(Zxx[0]))
for i in range(len(Zxx)):
for j in range(len(Zxx[i])):
Zxx[i][j] *= 1127*np.log(1+f[i]/700)
t = np.array([current_time + x for x in t])
if(k == 0):
global_f = f
global_t = t
global_Zxx = Zxx
else:
global_Zxx = np.concatenate((global_Zxx, Zxx), axis=1)
global_t = np.concatenate((global_t, t))
#print(len(global_t))
k += 1
current_time = offset + k*increment
print("Completion rate : ", np.round(100*(current_time-offset)/(songlen-offset), 4), "%")
if(display):
plt.pcolormesh(global_t, global_f, np.abs(global_Zxx), shading='gouraud')
# print(len(global_Zxx), len(global_Zxx[0]))
# 88 192 = 2500
# 70 192 = 2000
plt.title('STFT Magnitude')
plt.ylabel('Frequency [Hz]')
plt.xlabel('Time [sec]')
plt.show()
return global_t, global_f, np.abs(global_Zxx)
def write_to_file(t, flist, maxlist, filename):
file = open(filename, 'w')
file.writelines('time,frequency,maxvalue\n')
for i in range(len(t)):
file.writelines(str(np.round(t[i], 3)))
file.writelines(',')
file.writelines(str(np.round(flist[i], 1)))
file.writelines(',')
file.writelines(str(np.round(maxlist[i], 0)))
file.writelines('\n')
#close(file)
def plot_max(time, freq, Zxx, save):
fres = [0 for x in range(len(time))]
maxres = [0 for x in range(len(time))]
for t in range(len(time)):
#subprocess.run(["clear"])
print(t, "/", len(time))
for f in range(len(Zxx)):
if(maxres[t] < Zxx[f][t]):
maxres[t] = Zxx[f][t]
fres[t] = freq[f]
if(save):
write_to_file(time, fres, maxres, 'output.csv')
''''''
plt.plot(time, fres, 'r')
plt.grid()
plt.xlabel("Time")
plt.ylabel("Maximum frequencies")
plt.plot(time, maxres, 'g')
plt.grid()
plt.xlabel("Time")
plt.ylabel("Maximun values")
plt.show()''''''
fig, (ax1, ax2) = plt.subplots(2)
fig.suptitle('Top : time and frequencies\nBottom : time and max values')
ax1.plot(time, fres)
ax2.plot(time, maxres)
plt.show()
def extract_peaks(song_data, sample_rate, offset, display, threshold):
mx = max(song_data)
for i in range(len(song_data)):
#subprocess.run(["clear"])
print(i, "/", len(song_data))
if(song_data[i]/mx < threshold):
song_data[i] = 0
t = [offset + i/sample_rate for i in range(len(song_data))]
if(display):
plt.plot(t, song_data, 'b+')
plt.grid()
plt.xlabel("t")
plt.ylabel("amp")
plt.show()
return (t, song_data)
def get_local_max(song_data, center, width):
mx = 0
for o in range(-width, width+1):
togo = min(len(song_data)-1, center+o)
togo = max(0, togo)
if(mx < song_data[togo]):
mx = song_data[togo]
return mx
def extract_peaks_v2(song_data, sample_rate, offset, display, threshold, seglen):
mx = 0
for i in range(len(song_data)):
if (i%seglen == 0):
print("----")
mx = get_local_max(song_data, i+seglen//2, seglen//2)
#subprocess.run(["clear"])
print(i, "/", len(song_data))
if(song_data[i]/mx < threshold):
song_data[i] = 0
t = [offset + i/sample_rate for i in range(len(song_data))]
if(display):
plt.plot(t, song_data, 'b+')
plt.grid()
plt.xlabel("t")
plt.ylabel("amp")
plt.show()
return (t, song_data)
def peaks(song_name, offset, length, display, thr):
subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(length), "-i", song_name, "crop.wav"], shell=False)
sample_rate, audio_data = wavfile.read('crop.wav')
#subprocess.run(["clear"])
subprocess.run(["rm", "crop.wav"], shell=False)
#return extract_peaks(audio_data, sample_rate, offset, display, thr)
return extract_peaks_v2(audio_data, sample_rate, offset, display, thr, 44100*2)
def find_bpm(sample_rate, data, minbpm, maxbpm, step, width):
optimal = minbpm
optimal_acc = 0
accuracy = 0
bpmlst = []
scores = []
for beat in range(minbpm, maxbpm+step, step):
loopturn = 0
print("testing", beat)
accuracy = 0
current = 0
while(current+width < len(data)):
loopturn += 1
for o in range(-width, width+1):
accuracy += data[current + o]
#current = (loopturn*sample_rate)//beat
current += (sample_rate)//beat
#accuracy = accuracy/loopturn
#accuracy *= (1+(maxbpm-beat)/minbpm)
if optimal_acc < accuracy:
optimal_acc = accuracy
optimal = beat
bpmlst.append(beat)
scores.append(accuracy)
if(False):
plt.plot(bpmlst, scores)
plt.xlabel("BPM")
plt.ylabel("Score")
plt.grid()
plt.show()
return (optimal, optimal_acc)
'''
'''
def void_freq(song_name, offset, songlen, increment, lthr, gthr):
to_cut = 20000//2500
global_Zxx = np.array([])
global_f = np.array([])
global_t = np.array([])
current_time = offset
k = 0
sample_rate, global_data = wavfile.read(song_name)
blit = int(sample_rate*increment)
print("Blit :", blit)
while(current_time <= songlen):
#subprocess.run(["ffmpeg", "-ss", str(current_time), "-t", str(increment), "-i", song_name, "crop.wav"])
#sample_rate, audio_data = wavfile.read('crop.wav')
audio_data = global_data[int(k*blit):int((k+1)*blit)]
size = audio_data.size
#subprocess.run(["clear"])
#subprocess.run(["rm", "crop.wav"])
# do stuff here
#f, t, Zxx = signal.stft(audio_data, sample_rate, nperseg=1000)
f, t, Zxx = signal.spectrogram(audio_data, fs=sample_rate, nfft=size)
leng = len(f)
f, Zxx = f[:leng//to_cut], Zxx[:leng//to_cut]
for i in range(len(Zxx)):
for j in range(len(Zxx[i])):
#Zxx[i][j] *= 1127*np.log(1+f[i]/700)
Zxx[i][j] *= 1000
t = np.array([current_time + x for x in t])
if(k == 0):
global_f = f
global_t = t
global_Zxx = Zxx
else:
global_Zxx = np.concatenate((global_Zxx, Zxx), axis=1)
global_t = np.concatenate((global_t, t))
#print(len(global_t))
k += 1
current_time = offset + k*increment
print("Completion rate : ", np.round(100*(current_time-offset)/(songlen-offset), 4), "%")
print("Finding global max...")
gmax = 0
for i in range(len(global_Zxx)):
for j in range(len(global_Zxx[i])):
if(global_Zxx[i][j] > gmax):
gmax = global_Zxx[i][j]
print("Trimming...")
for j in range(len(global_Zxx[0])):
lmax = 0
for i in range(len(global_Zxx)):
if(global_Zxx[i][j] > lmax):
lmax = global_Zxx[i][j]
for i in range(len(global_Zxx)):
val = global_Zxx[i][j]
if(val/lmax <= lthr/100):
global_Zxx[i][j] = 0
elif(val/gmax <= gthr/100):
global_Zxx[i][j] = 0
if(False):
print("Plotting...")
plt.pcolormesh(global_t, global_f, np.abs(global_Zxx), shading='gouraud')
# print(len(global_Zxx), len(global_Zxx[0]))
print("XLEN :", len(global_Zxx), "\nYLEN :", len(global_Zxx[0]))
plt.title('STFT Magnitude')
plt.ylabel('Frequency [Hz]')
plt.xlabel('Time [sec]')
plt.show()
if(True):
print("Converting...")
audio_signal = librosa.griffinlim(global_Zxx)
#scipy.io.wavfile.write('trimmed.wav', sample_rate, np.array(audio_signal, dtype=np.int16))
wavfile.write('test.wav', sample_rate, np.array(audio_signal, dtype=np.int16))
print("Done")
def find_bpm_2(sample_rate, data, threshold, maxbpm, show):
mx = np.max(data)
min_spacing = (60*sample_rate)/maxbpm
k = 0
while(k < len(data) and data[k]/mx < threshold):
k += 1
k += 1
spacing = []
current = 1
progress = 0
while(k < len(data)):
if(k%(len(data)/100) == 0):
print(progress, "%")
progress += 1
if(data[k]/mx >= threshold and current > min_spacing):
spacing.append(current)
current = 0
else:
current += 1
k += 1
for x in range(len(spacing)):
spacing[x] = 60/(spacing[x]/sample_rate)
digits = [i for i in range(len(spacing))]
if(show):
plt.plot(digits, spacing)
plt.xlabel("N")
plt.ylabel("BPM")
plt.grid()
plt.show()
beat = np.mean(spacing)
error = np.std(spacing)
return (np.round(beat, 3), np.round(error, 3))
def to_ms(song_data, sample_rate, offset):
# converts audio data to have exactly 1 sample per millisecond (aka set sample_rate to 1000)
new_data = []
spacing = int(sample_rate * 0.001)
mx = max(song_data)
i = 0
while(i < len(song_data)):
avg = 0
for k in range(spacing):
if(i+spacing < len(song_data)):
avg += song_data[i+spacing]
avg = avg / spacing
new_data.append(avg)
i += spacing
if(False): # pls dont kill me thx
t = [offset + j/1000 for j in range(len(new_data))]
plt.plot(t, new_data)
plt.xlabel("Time")
plt.ylabel("Amplitude")
plt.grid()
plt.show()
return (new_data, len(new_data))
def filter_n_percent(song_name, offset, length, threshold, reduce, show):
# threshold is in ]0, 100]
# filter data associated with song_name to keep only the highest threshold% values
subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(length), "-i", song_name, "crop.wav"], shell=False)
sample_rate, song_data = wavfile.read('crop.wav')
subprocess.run(["clear"], shell=False)
subprocess.run(["rm", "crop.wav"], shell=False)
if(reduce):
(song_data,e) = to_ms(song_data, 44100, 1)
sample_rate = 1000
mx = max(song_data)
is_locked = [False for i in range(len(song_data))]
x = int((len(song_data)*threshold)//100)
#print("X = ", x)
print("Retreiving the", int(x), "/", len(song_data), "highest values")
elements = heapq.nlargest(int(x), enumerate(song_data), key=lambda x: x[1])
print("Done")
for idx in range(len(elements)):
is_locked[elements[idx][0]] = True
for r in range(len(song_data)):
if(is_locked[r] == False):
song_data[r] = 0
if(show):
#print("EEEEE")
t = [offset + j/sample_rate for j in range(len(song_data))]
plt.plot(t, song_data)
plt.xlabel("Time")
plt.ylabel("Amplitude")
plt.grid()
plt.show()
return song_data
def get_tpts(data, sample_rate, thr):
res = []
for i in range(len(data)):
if(data[i] > thr):
res.append(i/sample_rate)
for i in res:
print(i)
return res
def test_sample(timelist):
for i in range(1,len(timelist)):
#os.system('play -n synth %s sin %s' % (0.05, 440))
for k in range(random.randint(1, 10)):
print("E", end="")
print("F")
sleep(timelist[i]-timelist[i-1])
'''