-
Notifications
You must be signed in to change notification settings - Fork 0
/
Tirakat.cpp
5277 lines (4348 loc) · 189 KB
/
Tirakat.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Tirakat.cpp : This file contains the 'main' function. Program execution begins and ends there.
// Author : Ukhem Fahmi Thoriqul Haq
// Made : 31 - 03 - 2024
//
// MAYBE NEXT BIG TODO: ADD VISUALIZATION ?
// 1. FFT (FREQ DOMAIN) SIGNAL STYLE - DONE
// 2. TIME DOMAIN SIGNAL STYLE - DONE
// 3. ADD Functionality to can add multiple music at one time - DONE
// 4. IMITATE THIS THINGS:
// 1. https://www.youtube.com/watch?v=SZzehktUeko. and go to 50 seconds positions
// 2. https://www.youtube.com/watch?v=LqUuMqfW1PE
// 3. maybe use drawspline, check if it can do good in 64 data.
// siapkan 5 array
// ambil spline taruh di array0, ambil sample
// jika telah 0.1s pindahkan ke array1, dan ambil array baru masukkan ke array0,
// jika telah 0.1s pidahkan ke array sebelumnya,
// jika telah 0.1s pidahkan ke array sebelumnya,
// jika telah 0.1s pidahkan ke array sebelumnya,
// jika telah 0.1s pidahkan ke array sebelumnya,
// jadi dalam 1 detik ada 10 sample string. dan terus berganti dengan sample string spline yang baru.
// string0 paling terang dan tebal, dan string4 paling redup dan tipis. coba dulu di raylib. jika jelek baru pikirkan custom shadernya nanti.
// dan waktu pengambilan sample mungkin diubah ke 0.05s.
// Mungkin dibuat dengan membuat class pada tiap kali waktunya sample dan memiliki method untuk menghitung waktu hidupnya
// dan menginterpolasi thickness dan alpha dengan inverse nilai waktu lifetime-nya.2
// 5. Coba untuk capture dan buat seperti cascade milik muzkaw tetapi tidak diagonal, melainkan kebelakang dan ditengah semakin kecil, naik dan kecilkan misal 95%, dan naik lagi 95%
// Hingga membuat effect semakin jauh semakin kecil. mungkin bagus.
// Masukkan ke sebuah struct mungkin, berisi ukuran rectangle dan posisi, data pointer spline, lalu posisi y dari spline, dan ukuran yang makin mengecil, coef yang makin jauh makin kecil dan selalu di kali 0.95, coef *= 0.95F
// sehingga ketinggian dan lebar tinggal menyesuaikan coef. dan juga tebal, dan alpha untuk splines bisa menggunakan coef tersebut. coef bisa digunakan untuk posisi rectangle.
// 0000a -> 000ab -> 00abc -> 0abcd -> abcde -> bcdef -> cdefg -> defgh -> dst. jika jumlah struct == batas, maka buang data paling tua, dan tambahkan data baru.
//
// Perlu buat rectangle untuk tempat draw spline, mungkin 20 - 30 rect dengan posisi makin kecil makin jauh. masukkan ke dalam vector, atau array.
// Jadi tinggal buat loop untuk para rectangle tadi, untuk titik spline tinggal ikuti bar_h tetapi dengan rect masing masing. yang urutan rect tetap, urutan data spline yang berubah seperti data diatas.
// yang paling awal yang paling jauh, jadi jika ada data baru, buang data awal, dan append data baru. mungkin pake linked list lebih cepat dibanding vector? entahlah, nanti saja itu, untuk optimization.
//
// 5. Extract Musical Note from Audio FFT
// https://www.youtube.com/watch?v=rj9NOiFLxWA&t=369s, i think this good for FFT Output mode 1.
//
// 7. library to write MIDI using code : Midifile by sapp.org :
// https://midifile.sapp.org/class/MidiFile/
// https://github.com/craigsapp/midifile -> MIDI file writing example
//
// 8. Dear ImGui Best tutorial to use in Visual Studio 2022:
// Part 1: https://www.youtube.com/watch?v=SP6Djf6ku1E
// Part 2: https://www.youtube.com/watch?v=HivfFkhpLjE
//
// 9. Add Label on button if hover functionality
//
// 10. Maybe consider to use JUCE? maybe not, maybe in the next project, maybe for MusMiBot?
// SMALL THINGS TODO:
// 1. FFT RESPON BUAT LEBIH BAIK:
// - Jika data baru lebih tinggi dari data hasil moving average:
// - maka update data terbaru, bukan data hasil moving average terbaru.
// - Jika data baru lebih rendah dari data hasil moving average:
// - tidak perlu di gambar. // aku tidak tahu mana yang lebih baik.
// - tidak perlu update.
// Agar menghasilkan sinyal gambar yang lebih responsive saat ada respon sinyal.
// Tetapi tidak berpindah naik dan turun berdasarkan data terbaru, melainkan turun seperti gravitasi dengan moving average.
//
// 2. Seringkali FFT tidak tampil, mungkin attach terjadi ketika music belum siap, jadi perlu while loop dulu sampai siap lalu lanjut ke attach music.
//
// 3. Tambah Splash Screen
//
// 4. Tambah notification format file couldn't load.
//
// 5. Edge case: jika ada simbol tidak umum di judul, biasanya tidak akan bisa load. perbaiki, atau tambah notifikasi perlu mengubah judul.
//
// MAXIMUM MIDI FREQ -> G#9/Ab9 = 13289.75Hz
// Features now:
// 1. Drag & Drop single or multiple musics.
// 2. Music format support : MP3, WAV, FLAC, & OGG.
// 3. Play Pause music by mouse and KEY_SPACE.
// 4. Volume control by mouse hold and drag, mouse wheel, press or hold down KEY_UP KEY_DOWN.
// 5. Mute Unmute by KEY_M.
// 6. Display playlists scrollable and can be re-arrange by mouse hold and drag.
// 7. Display time of play and duration of musics.
// 8. Music seek time in progress bar by mouse hold and drag, and press or hold down KEY_RIGHT KEY_LEFT.
// 9. Display time domain signal of music as progress bar, make it easy to jump and seek to the wanted time.
// 10.Display freq domain signal visualization (fft) in many modes and algorithms: Classic, Galaxy, Landscape.
// 11.2 mode of Playlist, repeat and loop.
// 12.Fullscreen display, by mouse and KEY_F.
// 13.Lock Time Domain Display, if you want it, by mouse and KEY_L.
//
// For next release try to:
// 1. Make Spectrogram.
// 2. Make custom title bar maybe.
// 3. Convert Image to Spectrogram, Spectrogram to audio? then i can see in my tirakat spectrogram mode.
//
// buat sanbox untuk mengubah image ke grayscale. input dengan drag and drop, masukkan kiri panel, lalu sebelah kanan hasil grayscale, dan bisa disave. buat dengan texture.
#include <iostream>
#include <filesystem>
#include <memory>
#include <fstream>
#include <sstream>
#include <vector>
#include <deque>
#include <array>
#include <string>
#include <cassert>
#include <algorithm>
#include <iomanip>
#include <cmath>
#include <mutex>
#include <chrono>
#include <thread>
#include <raylib.h>
#include <rlgl.h>
#include <fftw3.h>
#include <SFML/Audio.hpp>
#define FONT_LOC_Roboto_Slab {"resources/Fonts/Roboto_Slab/static/RobotoSlab-Regular.ttf"}
#define FONT_LOC_Roboto_Mono {"resources/Fonts/Roboto_Mono/static/RobotoMono-SemiBold.ttf"}
#define FONT_LOC_Source_Sans_BOLD {"resources/Fonts/Source_Sans_3/static/SourceSans3-Bold.ttf"}
#define FONT_LOC_Source_Sans_SEMIBOLD {"resources/Fonts/Source_Sans_3/static/SourceSans3-SemiBold.ttf"}
#define FONT_LOC_Source_Sans_REG {"resources/Fonts/Source_Sans_3/static/SourceSans3-Regular.ttf"}
#define FONT_LOC_Sofia_Sans_Condensed_BOLD {"resources/Fonts/Sofia_Sans_Condensed/static/SofiaSansCondensed-Bold.ttf"}
//#define FONT_LOC_Sofia_Sans_Condensed_REG {"resources/Fonts/Sofia_Sans_Condensed/static/SofiaSansCondensed-Regular.ttf"}
#define FONT_LOC_Sofia_Sans_Condensed_REG {"resources/Fonts/Sofia_Sans_Condensed/static/SofiaSansCondensed-Medium.ttf"}
#define ICON_APP_LOC {"resources/Icons/Tirakat-V4.png"}
#define ICON_PLAYPAUSE_LOC {"resources/Icons/PlayPause.png"}
#define ICON_FULLSCREEN_LOC {"resources/Icons/Fullscreen.png"}
#define ICON_VOLUME_LOC {"resources/Icons/Volume.png"}
#define ICON_SETTING_LOC {"resources/Icons/Setting.png"}
#define ICON_X_LOC {"resources/Icons/X.png"}
#define ICON_DELETE_LOC {"resources/Icons/Trash.png"}
#define ICON_MODE_LOC {"resources/Icons/Mode.png"}
#define ICON_POINTER_LOC {"resources/Icons/Pointer.png"}
#define ICON_LOCK_LOC {"resources/Icons/Lock.png"}
#define ICON_TOGGLE_LOC {"resources/Icons/Toggle.png"}
#define ICON_DOWNLOAD_LOC {"resources/Icons/Download.png"}
#define HUD_TIMER_SECS 1.5F
#define PANEL_LEFT_WIDTH 275.0F
#define PANEL_DURATION_HEIGHT 40.0F
#define PANEL_DURATION_WIDTH PANEL_LEFT_WIDTH
#define PANEL_BOTTOM 50.0F
#define PANEL_MEDIA_HEIGHT PANEL_BOTTOM
#define PANEL_MEIDA_WIDTH PANEL_LEFT_WIDTH
#define PANEL_PROGRESS_HEIGHT PANEL_BOTTOM
#define PANEL_PROGRESS_HEIGHT_FULLSCREEN_OFFSCREEN 5.0F
#define PANEL_LINE_THICK 2.0F // 4.0F
#define BASE_COLOR Color{ 10, 10, 10, 255 }
#define PANEL_COLOR Color{ 30, 30, 30, 255 }
#define PANEL_LEFT_COLOR Color{ 40, 40, 40, 255 }
#define PANEL_LINE_COLOR Color{ 30, 30, 30, 180 }
#define PANEL_PROGRESS_BASE_COLOR Color{ 25, 25, 25, 255 }
#define PANEL_PROGRESS_COLOR DARKGRAY
#define CONTENT_COLOR Color{ 60, 60, 63, 255 }
#define CONTENT_CHOOSE_COLOR Color{ 150, 150, 153, 255 }
#define CONTENT_OPTION_COLOR Color{ 190, 76, 45, 255 }
#define CONTENT_REARRANGE_COLOR Color{ 12, 82, 162, 255 }
#define BLUE_BUTTON_COLOR Color{ 58, 76, 131, 255 }
#define POPUP_CARD_COLOR Color{ 112, 141, 225, 255 }
#define POPUP_X_COLOR Color{ 190, 60, 50, 255 }
#define POPUP_BODY_COLOR Color{ 203, 209, 216, 255 }
#define POPUP_APPLY_COLOR Color{ 75, 109, 214, 255 }
#define POPUP_CANCEL_COLOR Color{ 142, 149, 178, 255 }
#define TARGET_DONE_COLOR Color{ 80, 180, 120, 255 }
#define KEY_TOGGLE_PLAY KEY_SPACE
#define KEY_TOGGLE_MUTE KEY_M
#define KEY_FULLSCREEN KEY_F
#define KEY_VISUAL_MODE KEY_V
#define KEY_LOCK_TIME_DOMAIN KEY_L
#define MIN_FREQ 10.0F
#define MAX_FREQ 24000.0F
//#define MAX_FREQ 22050.F
#define MAX_GRADIENT_COLORS 5
const int N{ 480 * 2 };
struct TirakatColorPalette {
Color PanelColorBase{};
Color PanelColorFunctionality{};
Color PanelColorPlaylistBase{};
Color PanelColorBaseProgress{};
Color PanelColorProgress{};
Color PanelColorLine{};
Color PlaylistColor{};
Color PlaylistColorHover{};
Color PlaylistColorChoosen{};
Color PlaylistColorOption{};
Color PlaylistColorRearranging{};
};
TirakatColorPalette ColorPalette1{
BASE_COLOR,
PANEL_COLOR,
PANEL_LEFT_COLOR,
PANEL_PROGRESS_BASE_COLOR,
PANEL_PROGRESS_COLOR,
PANEL_LINE_COLOR,
CONTENT_COLOR,
DARKGRAY,
CONTENT_CHOOSE_COLOR,
CONTENT_OPTION_COLOR,
CONTENT_REARRANGE_COLOR
};
TirakatColorPalette ColorPalette2{
Color{ 10, 12, 13, 255 },
Color{ 30, 32, 35, 255 },
Color{ 40, 42, 45, 255 },
Color{ 25, 27, 30, 255 },
Color{ 80, 82, 85, 255 },
Color{ 23, 25, 28, 255 },
Color{ 60, 62, 66, 255 },
Color{ 79, 81, 85, 225 },
Color{ 150, 152, 155, 255 },
Color{ 190, 76, 45, 255 },
Color{ 12, 82, 142, 255 }
};
TirakatColorPalette ColorPalette3_OBS{
Color{ 10, 12, 15, 255 },
Color{ 30, 32, 41, 255 },
Color{ 43, 45, 54, 255 },
Color{ 24, 26, 37, 255 },
Color{ 70, 72, 75, 255 },
Color{ 20, 21, 25, 255 },
Color{ 60, 62, 71, 255 },
Color{ 85, 88, 95, 225 },
Color{ 150, 152, 155, 255 },
Color{ 190, 76, 45, 255 },
Color{ 12, 82, 142, 255 }
};
TirakatColorPalette ColorPaletteUsed{};
//TirakatColorPalette ColorPaletteUsed{ ColorPalette1 };
//TirakatColorPalette ColorPaletteUsed{ ColorPalette2 };
//TirakatColorPalette ColorPaletteUsed{ ColorPalette3_OBS };
enum Page {
PAGE_DRAG_DROP,
PAGE_MAIN
};
enum Drag {
DRAG_MUSIC_PROGRESS,
DRAG_VOLUME,
DRAG_SCROLLBAR,
DRAG_RELEASE
};
enum Toggle {
OFF,
ON
};
enum MODE {
MODE_NATURAL = 1,
MODE_EXPONENTIAL,
MODE_MULTI_PEAK,
MODE_MAX_PEAK
};
enum VisualModes {
WAVE,
CLASSIC,
GALAXY,
LANDSCAPE,
SPECTROGRAM
};
enum TrimString {
EASY,
BOLD
};
enum PopupTrayLOG {
FAILED,
SUCCESS,
DELETE,
};
struct VisualMode {
std::string title{};
std::string shortcut{};
bool enable{};
};
VisualMode visualM1{ "Wave" , "V + 1", ON };
VisualMode visualM2{ "Classic" , "V + 2", ON };
VisualMode visualM3{ "Galaxy" , "V + 3", ON };
VisualMode visualM4{ "Landscape" , "V + 4", ON };
VisualMode visualM5{ "Spectogram" , "V + 5", ON };
struct Notification {
std::string g_info{};
float g_info_timer{};
};
struct DragDropPopup {
std::string name{};
int info{};
float time{ 0.0F };
float alpha{ 1.0F };
float slide_up{ 0.0F };
DragDropPopup(const std::string& name, const int info) : name(name), info(info) {}
void updateTime() {
time += GetFrameTime();
}
void updateAlpha() {
float threshold = 5.0F;
if (time > threshold) {
alpha -= 0.01F;
if (alpha < 0.0F) alpha = 0.0F;
}
}
void updateSlideUP() {
if (slide_up < 1.0F) slide_up += 0.05F;
}
void updateAll() {
updateTime();
updateAlpha();
updateSlideUP();
}
void resetSlideUp() {
slide_up = 0.0F;
}
float getSlideUp() { return slide_up; }
int getInfo() { return info; }
bool isExpired() const {
return alpha <= 0.0F;
}
};
struct Plug {
int page{};
int play{};
int dragging{};
bool music_playing{};
uint8_t music_channel{ 2 };
bool reset_time{};
bool icon_pp_index{};
bool volume_mute{ false };
float last_volume{};
size_t icon_fullscreen_index{};
bool fullscreen{ false };
bool popup_on = false;
std::string popup_title{};
bool mouse_onscreen{ true };
bool repeat{ ON };
int mouse_cursor{};
bool glow{ false };
int mode{ MODE_NATURAL };
bool moving_save{ false };
Shader circle{};
Shader bubble{};
int option_status{ OFF };
size_t option_music_order{};
bool visual_mode_expand{ OFF };
std::vector<VisualMode> visualmode{ visualM1, visualM2, visualM3, visualM4, visualM5 };
//size_t visual_mode_active{ SPECTROGRAM };
size_t visual_mode_active{ WAVE };
bool toggle_windowed_wave{ true };
Notification notification{};
float mouse_onscreen_timer{ HUD_TIMER_SECS };
bool visual_time_domain_lock{ ON };
size_t icon_lock_index{};
//const int spectrogram_h{ static_cast<int>((1 << 9)) };
const int spectrogram_h{ static_cast<int>((N / 2)) };
//const int spectrogram_w{ (1 << 9) * 16 / 9 };
//const int spectrogram_w{ 860 }; // for 99 FPS
//const int spectrogram_w{ 640 };
const int spectrogram_w{ 700 }; // for 75 FPS
//const int spectrogram_w{ 630 }; // for 75 FPS
Image spectrogram_image{};
Texture2D SPECTROGRAM_TEXTURE{};
const int spectrogram_zone_out_w{ 255 };
Image spectrogram_zone_out_image{};
Texture2D SPECTROGRAM_ZONE_OUT_TEXTURE{};
Image spectrogram_zone_in_image{};
Texture2D SPECTROGRAM_ZONE_IN_TEXTURE{};
std::deque<DragDropPopup> DragDropPopupTray{};
bool drawMiniWave{ ON };
bool spectrogramDownloading{ OFF };
bool spectrogramDownloaded{ OFF };
std::string spectrogramOutputFolder{ "resources/Spectrogram/" };
};
Plug tirakat{};
static Plug* p = &tirakat;
struct Data {
std::string path{};
std::string name{};
int target{};
int counter{};
int duration{};
bool downloaded{};
};
struct ScreenSize {
float w{};
float h{};
};
// Special cause mistake i made
struct SpecialToolTip {
bool enable{};
Rectangle rect{};
};
SpecialToolTip special_btn_delete{};
SpecialToolTip special_btn_setting{};
struct Frame {
float left{};
float right{};
};
//const int N{ 1 << 10 };
//fftw_complex* fftw_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * N);
//fftw_complex* fftw_out = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * N);
std::vector<fftw_complex> fftw_in(N);
std::vector<fftw_complex> fftw_out(N);
std::vector<float> wave_live(N / 2, 0.0F);
std::mutex wave_mutex;
const int BUCKETS{ 1 << 6 };
//const int BUCKETS{ 80 };
std::array<float, BUCKETS> Spectrum{};
std::array<float, BUCKETS + 1> Freq_Bin{};
const int SMOOTHING_BUFFER_SIZE{ 15 };
std::array<std::array<float, SMOOTHING_BUFFER_SIZE>, BUCKETS> prevAmplitude{};
std::array<float, BUCKETS> smoothedAmplitude{};
std::array<float, BUCKETS> out_smear{};
float maxAmplitude = 0.0F;
struct PeakInfo {
int frequency_index{};
float amplitude{};
};
std::array<PeakInfo, BUCKETS> Peak{};
void callback(void* bufferData, unsigned int frames) {
// Cast buffer data to float array
float* samples = (float*)bufferData;
// Lock the mutex to safely update the global buffer
std::lock_guard<std::mutex> lock(wave_mutex);
// Process the samples
for (unsigned int i = 0; i < frames; i++) {
float leftSample = samples[2 * i]; // Left channel sample
float rightSample = samples[2 * i + 1]; // Right channel sample
// You can choose to store only one channel or process both
wave_live[i] = (leftSample + rightSample) / 2.0f; // Example: average of both channels
fftw_in[i][0] = (leftSample + rightSample) / 2.0f;
}
if (IsKeyPressed(KEY_D)) {
std::cout << "frame size: " << frames << std::endl;
}
}
// Using std::vector, for now this not activated
void cleanup() {
//if (fftw_in != nullptr) {
// fftw_free(fftw_in);
//}
//if (fftw_out != nullptr) {
// fftw_free(fftw_out);
//}
}
void dc_offset(fftw_complex in[]) {
double dc_offset = 0.0F;
for (size_t i = 0; i < N; i++) {
dc_offset += in[i][0];
}
dc_offset = dc_offset / (float)N;
for (size_t i = 0; i < N; i++) {
in[i][0] -= dc_offset;
}
}
// Low-pass filter (simple moving average filter)
void low_pass_filter(fftw_complex in[], size_t n) {
std::vector<double> filtered(n, 0.0);
int window_size = 5; // Adjust as needed
for (size_t i = 0; i < n; i++) {
for (size_t j = 0; j < window_size && i >= j; j++) {
filtered[i] += in[i - j][0]; // Directly access the real part
}
filtered[i] /= window_size;
}
for (size_t i = 0; i < n; i++) {
in[i][0] = filtered[i]; // Update the real part
}
}
// Basic FIR Low-Pass Filter
void fir_low_pass_filter(fftw_complex in[], size_t n, double cutoff) {
// Define filter coefficients for a basic FIR filter
// Here, we create a simple low-pass filter using sinc function
const int filter_size = 21; // Number of filter coefficients
std::vector<double> h(filter_size);
double fc = cutoff / (0.5 * N); // Normalize cutoff frequency by Nyquist frequency
for (size_t i = 0; i < filter_size; i++) {
if (i == (filter_size - 1) / 2) {
h[i] = 2.0 * fc;
}
else {
h[i] = sin(2.0 * PI * fc * (i - (filter_size - 1) / 2)) / (PI * (i - (filter_size - 1) / 2));
// Apply Hamming window
h[i] *= 0.54 - 0.46 * cos(2.0 * PI * i / (filter_size - 1));
}
}
std::vector<double> filtered(n, 0.0);
for (size_t i = 0; i < n; i++) {
for (size_t j = 0; j < filter_size; j++) {
if (i >= j) {
filtered[i] += h[j] * in[i - j][0]; // Apply filter to the real part
}
}
}
for (size_t i = 0; i < n; i++) {
in[i][0] = filtered[i]; // Update the real part
}
}
void hann_window(fftw_complex in[], size_t N) {
for (size_t i = 0; i < N; i++) {
float w = 0.5F * (1.0F - cosf(2.0F * PI * i / (N - 1)));
in[i][0] *= w;
}
}
void hann_window(float in[], size_t N) {
for (size_t i = 0; i < N; i++) {
float w = 0.5F * (1.0F - cosf(2.0F * PI * i / (N - 1)));
in[i] *= w;
}
}
void hamming_window(fftw_complex in[], size_t N) {
for (size_t i = 0; i < N; i++) {
float w = 0.54F - 0.46F * cos(2 * PI * i / (N - 1));
in[i][0] *= w;
}
}
void gaussian_window(fftw_complex in[], size_t N) {
float sigma = 0.5F;
float center = (N - 1) / 2.0F; // Center of the window
for (size_t i = 0; i < N; i++) {
float exponent = -0.5F * std::powf((i - center) / (sigma * center), 2);
float w = std::exp(exponent);
in[i][0] *= w;
}
}
void fftw_calculation(fftw_complex in[], fftw_complex out[], size_t N) {
assert(N > 0);
//fftw_plan plan{ fftw_plan_dft_1d(static_cast<int>(N), in, out, FFTW_FORWARD, FFTW_ESTIMATE) };
fftw_plan plan{ fftw_plan_dft_1d(static_cast<int>(N), in, out, FFTW_FORWARD, FFTW_MEASURE) };
//plan = fftw_plan_dft_1d(static_cast<int>(N), in, out, FFTW_FORWARD, FFTW_ESTIMATE);
fftw_execute(plan);
fftw_destroy_plan(plan);
}
float natural_scale(float amplitude, float Fit_factor) {
return amplitude * Fit_factor;
}
float exponential_scale(float amplitude, float Fit_factor) {
//return std::log10(amplitude * Fit_factor) * Fit_factor;
return std::log10(amplitude) * Fit_factor;
//return sqrtf(std::log10(amplitude) * Fit_factor);
}
float multi_peak_scale(float amplitude, int i, float Fit_factor, const std::array<PeakInfo, BUCKETS>& Peak) {
if (Peak.at(i).frequency_index >= 0) {
if (i < 5) return amplitude / Peak.at(i).amplitude * Fit_factor * 1.3F; // untuk membuat drum bass pada awal bins lebih naik.
else return amplitude / Peak.at(i).amplitude * Fit_factor;
//else return sqrtf(amplitude / Peak.at(i).amplitude * Fit_factor) * 0.7F;
//else return (amplitude / Peak.at(i).amplitude * Fit_factor) * (amplitude / Peak.at(i).amplitude * Fit_factor);
}
else {
return amplitude * Fit_factor;
}
}
float max_peak_scale(float amplitude, float Global_Peak, float Fit_factor) {
return amplitude / Global_Peak * Fit_factor;
}
float calculateMovingAverage(std::array<float, SMOOTHING_BUFFER_SIZE>& arr, int size) {
float sum{};
for (int i = 0; i < size; i++) {
sum += arr.at(i);
}
return sum / size;
}
float min_frequency = MIN_FREQ;
float max_frequency = MAX_FREQ;
float bin_width = (max_frequency - min_frequency) / BUCKETS;
float log_f_min = std::log10(min_frequency);
float log_f_max = std::log10(max_frequency);
float delta_log = (log_f_max - log_f_min) / BUCKETS;
void make_bins() {
std::cout << std::fixed << std::setprecision(2);
for (size_t i = 0; i <= BUCKETS; i++) {
Freq_Bin.at(i) = min_frequency + i * bin_width;
//Freq_Bin.at(i) = std::powf(10, log_f_min + i * delta_log);
//std::cout << Freq_Bin[i] << std::endl;
}
}
float normalization(float val, float min_val, float max_val) {
if (min_val == max_val) return val;
return (val - min_val) / (max_val - min_val);
}
float millisecondsToSeconds(int milliseconds) {
return static_cast<float>(milliseconds) / 1000;
}
void Tooltip(const Rectangle& boundary, const Font& font, const ScreenSize& screen, const std::string& information) {
float rect_h{ 35.0F };
float rect_w{};
float font_size = rect_h * 0.8F;
float font_space = 0.5F;
float space = 10;
Vector2 text_measure = MeasureTextEx(font, information.c_str(), font_size, font_space);
rect_w = text_measure.x + (space * 3.F);
float center = boundary.x + boundary.width / 2;
Rectangle tip_panel{
0,
boundary.y - space - rect_h,
rect_w,
rect_h
};
tip_panel.x = center - (tip_panel.width / 2);
if (tip_panel.x < space) tip_panel.x = space;
else if (tip_panel.x + tip_panel.width > screen.w - space) {
tip_panel.x = screen.w - (tip_panel.width + space);
}
else if (tip_panel.y < space) tip_panel.y = boundary.y + boundary.height + space;
Vector2 text_coor{
tip_panel.x + (tip_panel.width - text_measure.x) / 2,
tip_panel.y + (tip_panel.height - text_measure.y) / 2,
};
Color color = { 20, 20, 20, 240 };
//Color color = { 50, 50, 50, 240 };
DrawRectangleRounded(tip_panel, 0.25F, 10, color);
DrawRectangleRoundedLines(tip_panel, 0.25F, 10, 3.0F, Fade(DARKGRAY, 0.2F));
DrawTextEx(font, information.c_str(), text_coor, font_size, font_space, RAYWHITE);
}
void NotificationTool(const Rectangle& base_boundary, const Font& font, const std::string& info, float& info_timer, float dt) {
float rect_h{ 40.0F };
float rect_w{};
float font_size{ rect_h * 0.8F };
float font_space{ 0.5F };
float space{ 10 };
Vector2 text_measure = MeasureTextEx(font, info.c_str(), font_size, font_space);
float center = base_boundary.x + base_boundary.width / 2;
rect_w = text_measure.x + (space * 3.F);
Rectangle notification_panel{
0,
base_boundary.y + 100,
rect_w,
rect_h
};
notification_panel.x = center - (notification_panel.width / 2);
Vector2 text_coor{
notification_panel.x + (notification_panel.width - text_measure.x) / 2,
notification_panel.y + (notification_panel.height - text_measure.y) / 2,
};
Color color = { 50, 50, 50, 255 };
if (info_timer > 0) {
float alpha = 1.0F;
if (info_timer < 1) alpha = (info_timer * info_timer);
DrawRectangleRounded(notification_panel, 0.25F, 10, Fade(color, alpha * 0.9F));
DrawTextEx(font, info.c_str(), text_coor, font_size, font_space, Fade(RAYWHITE, alpha));
}
info_timer -= dt;
}
Color GrayscaleColor(float normalizedValue) {
Color startColor = { 20, 20, 25, 255 }; // Black
Color endColor = { 255, 245, 245, 255 }; // White
float value = normalizedValue;
//float value = normalizedValue * normalizedValue;
//float value = normalizedValue * normalizedValue * normalizedValue;
//float value = sqrtf(normalizedValue);
// Interpolate between the start and end color based on the normalized value
Color resultColor;
resultColor.r = static_cast<unsigned char>(startColor.r + value * (endColor.r - startColor.r));
resultColor.g = static_cast<unsigned char>(startColor.g + value * (endColor.g - startColor.g));
resultColor.b = static_cast<unsigned char>(startColor.b + value * (endColor.b - startColor.b));
//resultColor.a = startColor.a + normalizedValue * (endColor.a - startColor.a);
resultColor.a = 200;
return resultColor;
}
Color SpectrogramColor(float normalizedValue) {
// Clamp value to 0-1 range
normalizedValue = std::clamp(normalizedValue, 0.0f, 1.0f);
// Initialize color channels
float red = 0.0f, green = 0.0f, blue = 0.0f, alpha = 240.0F;
if (normalizedValue > 0.8f) {
// White
red = 255.0f;
green = 255.0f;
blue = 255.0f;
alpha = 255.0f;
}
else if (normalizedValue > 0.65f) {
// ALMOST WHITE
red = 245.0f;
green = 235.0f;
blue = 235.0f;
alpha = 255.0f;
}
else if (normalizedValue > 0.55f) {
// ALMOST WHITE
red = 235.0f;
green = 225.0f;
blue = 225.0f;
alpha = 255.0f;
}
else if (normalizedValue > 0.45f) {
// Yellow
red = 225.0f;
green = 235.0f;
blue = 140.0f;
alpha = 200.0f;
}
//else if (normalizedValue > 0.35f) {
// // Orange
// red = 218.0f;
// green = 165.0f;
// blue = 90.0f;
// alpha = 150.0F;
//}
else if (normalizedValue > 0.35f) {
// Orange
red = 180.0f;
green = 160.0f;
blue = 130.0f;
alpha = 180.0F;
}
else if (normalizedValue > 0.25f) {
// Pink
red = 110.0f;
green = 110.0f;
blue = 135.0f;
alpha = 180.0F;
}
else if (normalizedValue > 0.15f) {
// Light Purple
red = 70.0f;
green = 60.0f;
blue = 110.0f;
alpha = 180.0F;
}
else if (normalizedValue > 0.1f) {
// Dark Purple
red = 45.0f;
green = 40.0f;
blue = 80.0f;
alpha = 150.0F;
}
else if (normalizedValue > 0.05f) {
// Dark Purple 35, 22, 59
red = 35.0f;
green = 20.0f;
blue = 70.0f;
alpha = 120.0F;
}
else {
// Zero Value
red = 15;
green = 15;
blue = 15;
alpha = 100;
}
// Return the color as raylib Color
return Color{ (unsigned char)red, (unsigned char)green, (unsigned char)blue, (unsigned char)alpha };
}
// Define gradient colors
Color gradientColors[MAX_GRADIENT_COLORS] = {
Color { 0, 0, 255, 255}, // Blue
Color { 0, 255, 255, 255}, // Cyan
Color { 0, 255, 0, 255}, // Green
Color {255, 255, 0, 255}, // Yellow
Color {255, 0, 0, 255} // Red
};
// Function to linearly interpolate between two colors
Color ColorLerp(Color color1, Color color2, float amount) {
return Color {
(unsigned char)((color1.r * (1 - amount)) + (color2.r * amount)),
(unsigned char)((color1.g * (1 - amount)) + (color2.g * amount)),
(unsigned char)((color1.b * (1 - amount)) + (color2.b * amount)),
(unsigned char)((color1.a * (1 - amount)) + (color2.a * amount))
};
}
// Function to map a value between 0 and 1 to a color in the gradient
Color getColorFromValue(float value) {
if (value <= 0) return gradientColors[0];
if (value >= 1) return gradientColors[MAX_GRADIENT_COLORS - 1];
int colorIndex = static_cast<int>(value) * (MAX_GRADIENT_COLORS - 1);
float colorPercentage = (value * (MAX_GRADIENT_COLORS - 1)) - colorIndex;
Color color1 = gradientColors[colorIndex];
Color color2 = gradientColors[colorIndex + 1];
return ColorLerp(color1, color2, colorPercentage);
}
Color getColorFromAmplitude(float normalizedAmplitude) {
// Map normalized amplitude to brightness (0 to 255)
unsigned char brightness = (unsigned char)(normalizedAmplitude * 255);
return Color { brightness, brightness, brightness, 255 }; // RGB components set to brightness
}
void InitFile(const std::filesystem::path& filename);
void FileCheck(const std::filesystem::path& filename);
void FileZeroDataCheck(const std::filesystem::path& filename);
int GetDuration(const char* c_file_path);
void ReloadVector();
void DrawTitleMP3(Rectangle& panel_main);
void DrawCounter(Rectangle& panel_main);
void DrawDuration(Rectangle& panel_duration);
void DrawPlayPause(const Rectangle& play_rect, const Rectangle& hover_panel);
void ApplyInputReset(std::string& input, bool& popup_on, std::string& name);
bool Save();
void LoadMP3();
void DrawMainPage(ScreenSize screen, int& retFlag);
void DrawPopUpReset(Rectangle& panel_main);
void DrawVisualTimeDomainProgress(Rectangle& panel, float progress_w);
void DrawMedia(Rectangle& panel_media);
void DrawVolume(Rectangle& panel_playpause, float button_panel, Rectangle& panel_media);
void DrawMusicList(Rectangle& panel_left, int& retFlag);
std::string TrimDisplayString(std::string& cpp_text, float text_width_limit, float font_size, float font_space, int TrimString);
void DeleteMusic(int& retFlag, size_t order);
void ResetVisualizerParameter();
void DrawMainDisplay(Rectangle& panel_main);
void DrawLockButton(Rectangle& panel_main, float dt);
void DrawVisualModeButton(Rectangle& panel_main, float dt);
void DrawFullscreenButton(Rectangle& panel_main, float dt);
void DrawMusicPlayModeButton(Rectangle& panel_main, float dt);
void DrawMusicProgress(Rectangle& panel_progress, float& music_volume);
void DrawLinePreviewJumpMusic(Rectangle& panel_progress, float progress_ratio);
bool is_Draw_Icons();
void DrawDragDropPage(ScreenSize screen);
bool Check_StartUp_Page();
void InitializedSpectrogram();
void DrawSplashScreen();
void DrawDragDropPopupTray();
//void InitializedSpectrogram(std::unique_ptr<Color[], std::default_delete<Color[]>>& spectrogram_data);
void InitializedSpectrogramZoneOut();
static std::vector<float> ExtractMusicData(std::string& filename) {
std::vector<float> audio_data{};
// Load the entire audio for processing (modify for large files)
if (1)
{
// SFML
auto start = std::chrono::high_resolution_clock::now();
sf::SoundBuffer soundBuffer{};
soundBuffer.loadFromFile(filename);
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Load time: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << std::endl;
std::cout << soundBuffer.getSampleRate() << std::endl;
sf::Uint64 total_samples = soundBuffer.getSampleCount();
audio_data.reserve(total_samples);
const sf::Int16* samples = soundBuffer.getSamples();
for (size_t i = 0; i < total_samples; i++) {
// Convert and push back all samples (no downsampling, only normalization)
float sample = static_cast<float>(samples[i]) / 32768.0F; // assuming 16-bit signed integer.
audio_data.push_back(sample);
}
int total_frames = (int)audio_data.size();
}
else
{
if (1)
{
// RAYLIB WAVE LOAD
auto start = std::chrono::high_resolution_clock::now();
Wave wave = LoadWave(filename.c_str());