Compare commits
18 Commits
main
...
refactorin
Author | SHA1 | Date |
---|---|---|
|
b0a6a52057 | |
|
ed75cf450d | |
|
eb58997fb4 | |
|
5d4a9c8c58 | |
|
c2cfc429ae | |
|
3b6ed078f4 | |
|
bb33b03436 | |
|
8f035375ab | |
|
05b642bdcc | |
|
64a2a96628 | |
|
7615d41c01 | |
|
421cddf267 | |
|
a59c0c4e08 | |
|
02c5579186 | |
|
c969501c52 | |
|
cba14e4782 | |
|
542b6e9996 | |
|
9e7c18930f |
|
@ -1,2 +1,7 @@
|
|||
*.csv
|
||||
.venv
|
||||
songs/
|
||||
pptx/code/
|
||||
pptx/data/
|
||||
pptx/*.png
|
||||
pptx/*.jpg
|
|
@ -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:
|
|
@ -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()
|
||||
'''
|
After Width: | Height: | Size: 97 KiB |
|
@ -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
|
After Width: | Height: | Size: 156 KiB |
After Width: | Height: | Size: 132 KiB |
After Width: | Height: | Size: 94 KiB |
After Width: | Height: | Size: 71 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 91 KiB |
|
@ -0,0 +1 @@
|
|||
pandoc presentation.md -t beamer -o presentation.pdf
|
|
@ -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
|
||||
{ width=50% }
|
||||
{ width=40% }
|
||||
|
||||
### Les beatmaps (niveaux)
|
||||
::: columns
|
||||
:::: column
|
||||
{ width=110% }
|
||||

|
||||
::::
|
||||
:::: column
|
||||
{ width=110% }
|
||||
::::
|
||||
|
||||
:::
|
||||
|
||||
### Exemples de cercles
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
### Exemples de sliders
|
||||
|
||||
{ width=50% }
|
||||
{ width=50% }
|
||||

|
||||

|
||||
|
||||
|
||||
### Formulation du problème
|
||||
|
||||
Nous nous proposons de **créer deux programmes** permettant **au mieux**, à partir d’un **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
|
||||
|
||||

|
||||
|
||||
### Schéma du processus
|
||||
|
||||

|
||||
|
||||
### Filtres physiques & Transformée de Fourier
|
||||
|
||||
::: columns
|
||||
:::: column
|
||||
{ width=90% }
|
||||
::::
|
||||
:::: column
|
||||

|
||||
::::
|
||||
:::
|
||||
|
||||
### Résultats
|
||||
|
||||
{ width=60% }
|
||||
|
||||
Résultat de l'extraction sur une musique (Bad Apple) pendant 15s
|
||||
|
||||
### Résultats
|
||||
|
||||

|
||||
|
||||
Résultat de l'extraction sur la même musique pendant 120s
|
||||
|
||||
### Résultats
|
||||
|
||||
#### Complexité
|
||||
|
||||
{ width=95% }
|
||||
|
||||
### Limites, saturation et améliorations
|
||||
|
||||

|
||||
|
||||
### Limites, saturation et améliorations
|
||||
|
||||

|
||||
|
||||
> les fréquences ne sont pas correctement détéctées ici (car trop d'harmoniques)
|
||||
|
||||
### Limites, saturation et améliorations
|
||||
|
||||

|
||||
|
||||
### Limites, saturation et améliorations
|
||||
|
||||

|
||||
|
||||
> 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
|
||||
|
||||
{ width=70% }
|
||||
|
||||
### quelques fonctions utiles
|
||||
|
||||

|
||||
|
||||
### `parse music` : extraction de la liste des amplitudes et du "sample rate"
|
||||
|
||||
::: columns
|
||||
:::: column
|
||||
{ width=80% }
|
||||
::::
|
||||
:::: column
|
||||

|
||||
::::
|
||||
:::
|
||||
|
||||
### `cleaning` : retirer les fréquences trop basses et hautes
|
||||
|
||||
{ width=60% }
|
||||
|
||||
### `get amp distribution` : usage de files de priorité pour extraire les maxima
|
||||
|
||||
{ width=50% }
|
||||
|
||||
### `get frequency distribution` : fréquence maximale pour chaque point
|
||||
|
||||
{ width=60% }
|
||||
|
||||
### `get sliders` : fusionner les notes trop proches en des 'sliders'
|
||||
|
||||
{ width=75% }
|
||||
|
||||
### `draw` : afficher les résultats
|
||||
|
||||
{ width=60% }
|
||||
|
||||
### quelques données de test
|
||||
|
||||
::: columns
|
||||
:::: column
|
||||
{ width=75% }
|
||||
::::
|
||||
:::: column
|
||||

|
||||
::::
|
||||
:::
|
||||
|
||||
### interface
|
||||
|
||||

|
|
@ -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}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
145
script (1).py
|
@ -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")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
872
sound_process.py
|
@ -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])
|
||||
'''
|