728x90
반응형
728x170
■ 마이크 입력 스펙트로그램을 표시하는 방법을 보여준다.
▶ ArgoColorMap.cs
using System;
namespace TestLibrary
{
/// <summary>
/// 아르고 색상 맵
/// </summary>
public class ArgoColorMap : IColorMap
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// RGB 배열
/// </summary>
private readonly int[] rgbArray =
{
00000000, 00000004, 00000264, 00000267, 00000527, 00000530, 00000789, 00066328,
00066588, 00066591, 00066849, 00132388, 00132647, 00132650, 00132908, 00198447,
00198706, 00198708, 00264503, 00264505, 00330299, 00330557, 00330560, 00396354,
00396612, 00462150, 00462408, 00527946, 00528204, 00593998, 00594000, 00659794,
00660052, 00725590, 00791383, 00791641, 00857435, 00857437, 00923230, 00989024,
00989026, 01054819, 01120613, 01120870, 01186408, 01252201, 01252459, 01318252,
01384046, 01384047, 01449841, 01515634, 01515892, 01581429, 01647222, 01713016,
01713273, 01779066, 01844860, 01910397, 01976190, 01976447, 02042241, 02108034,
02173827, 02239364, 02239621, 02305415, 02371208, 02437001, 02502794, 02568587,
02568844, 02634381, 02700174, 02765968, 02831761, 02897554, 02963347, 03029140,
03029397, 03095190, 03160983, 03226520, 03292313, 03358106, 03423899, 03489692,
03555485, 03621278, 03687071, 03752864, 03818656, 03884449, 03950242, 04016035,
04081828, 04147621, 04147878, 04213671, 04279464, 04345256, 04411049, 04476842,
04542635, 04608428, 04739757, 04805550, 04871342, 04937135, 05002928, 05068721,
05134514, 05200306, 05266099, 05331892, 05397685, 05463477, 05529270, 05595063,
05660856, 05726648, 05792441, 05858234, 05924027, 05989819, 06121148, 06186941,
06252734, 06318526, 06384319, 06450112, 06515904, 06581953, 06647746, 06779074,
06844867, 06910660, 06976452, 07042245, 07108038, 07173830, 07239623, 07370952,
07436744, 07502793, 07568586, 07634378, 07700171, 07765964, 07897292, 07963085,
08028877, 08094670, 08160719, 08226511, 08357840, 08423632, 08489425, 08555218,
08621010, 08752339, 08818387, 08884180, 08949973, 09015765, 09147094, 09212886,
09278679, 09344727, 09410520, 09541849, 09607641, 09673434, 09739226, 09870555,
09936603, 10002396, 10068188, 10133981, 10265309, 10331102, 10397150, 10462943,
10594272, 10660064, 10725857, 10791905, 10923234, 10989026, 11054819, 11120611,
11251940, 11317988, 11383781, 11515109, 11580902, 11646694, 11712743, 11844071,
11909864, 11975656, 12106985, 12173033, 12238826, 12304618, 12435947, 12501995,
12567787, 12699116, 12764908, 12830701, 12962285, 13028078, 13093870, 13159663,
13291247, 13357040, 13422832, 13554161, 13619953, 13686001, 13817330, 13883122,
13948915, 14080499, 14146292, 14212084, 14343412, 14409461, 14475253, 14606582,
14672374, 14738423, 14869751, 14935543, 15066872, 15132920, 15198713, 15330041,
15396090, 15461882, 15593210, 15659003, 15725051, 15856380, 15922172, 16053500,
16119549, 16185341, 16316670, 16382718, 16448510, 16579839, 16645631, 16777215,
};
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region RGB 구하기 - GetRGB(value)
/// <summary>
/// RGB 구하기
/// </summary>
/// <param name="value">값</param>
/// <returns>RGB 튜플</returns>
public (byte r, byte g, byte b) GetRGB(byte value)
{
byte[] byteArray = BitConverter.GetBytes(this.rgbArray[value]);
return (byteArray[2], byteArray[1], byteArray[0]);
}
#endregion
}
}
▶ BlueColorMap.cs
using System;
namespace TestLibrary
{
/// <summary>
/// 청색 색상 맵
/// </summary>
public class BlueColorMap : IColorMap
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// ARGB 배열
/// </summary>
private readonly int[] argbArray =
{
-16767403, -16767402, -16767144, -16766887, -16701093, -16700835, -16700578, -16634784,
-16634527, -16634269, -16568476, -16568218, -16568216, -16567959, -16502165, -16501908,
-16501650, -16435856, -16435599, -16435341, -16369548, -16369290, -16369033, -16303495,
-16303238, -16302980, -16237186, -16236929, -16236671, -16236414, -16170620, -16170363,
-16170105, -16104312, -16104054, -16103797, -16038259, -16038002, -16037745, -15971951,
-15971694, -15971436, -15905643, -15905385, -15905128, -15839335, -15839077, -15838820,
-15773027, -15772769, -15772512, -15706718, -15706717, -15706460, -15706203, -15640409,
-15640152, -15574359, -15574101, -15573844, -15508051, -15507794, -15507537, -15441743,
-15441486, -15441229, -15375436, -15375179, -15309386, -15309128, -15308871, -15243078,
-15242821, -15177284, -15177027, -15111234, -15110977, -15045184, -15044927, -14979134,
-14978877, -14913084, -14912827, -14847034, -14781241, -14780984, -14715191, -14715190,
-14649397, -14583605, -14517812, -14517555, -14451762, -14385969, -14320176, -14319920,
-14254383, -14188590, -14122797, -14057005, -13991212, -13925419, -13859626, -13859626,
-13793833, -13662505, -13596712, -13530919, -13465383, -13399590, -13333797, -13268005,
-13202212, -13136676, -13005347, -12939555, -12873762, -12808226, -12676897, -12611105,
-12545312, -12414240, -12348447, -12282655, -12151327, -12085790, -12019998, -11888669,
-11822877, -11757341, -11626012, -11560220, -11429148, -11363355, -11232027, -11166235,
-11035162, -10969370, -10903578, -10772505, -10706713, -10575385, -10509592, -10378520,
-10312728, -10181400, -10115863, -09984535, -09918743, -09787414, -09721878, -09590550,
-09524758, -09393429, -09327893, -09262101, -09130773, -09065237, -08933908, -08868116,
-08736788, -08671252, -08605459, -08474131, -08408339, -08277267, -08211475, -08145682,
-08014354, -07948818, -07817490, -07751698, -07685905, -07554833, -07489041, -07357713,
-07291921, -07226128, -07095056, -07029264, -06897936, -06832144, -06766351, -06635279,
-06569487, -06503695, -06372367, -06306575, -06175502, -06109710, -06043918, -05912590,
-05846798, -05781261, -05649933, -05584141, -05518349, -05387021, -05321485, -05190156,
-05124364, -05058572, -04927244, -04861452, -04730123, -04664587, -04598795, -04467467,
-04401675, -04335883, -04204554, -04139018, -04007690, -03941898, -03876106, -03744777,
-03678985, -03547657, -03481865, -03416329, -03285000, -03219208, -03087880, -03022088,
-02956296, -02824968, -02759175, -02628103, -02562311, -02430983, -02365191, -02299398,
-02168070, -02102278, -01970950, -01905158, -01839621, -01708293, -01642501, -01511173,
-01445381, -01314052, -01248260, -01182468, -01051140, -00985604, -00854275, -00788483,
-00657155, -00591363, -00525571, -00394242, -00328450, -00197122, -00131330, -00000001,
};
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region RGB 구하기 - GetRGB(value)
/// <summary>
/// RGB 구하기
/// </summary>
/// <param name="value">값</param>
/// <returns>RGB 튜플</returns>
public (byte r, byte g, byte b) GetRGB(byte value)
{
byte[] byteArray = BitConverter.GetBytes(this.argbArray[value]);
return (byteArray[2], byteArray[1], byteArray[0]);
}
#endregion
}
}
▶ GrayscaleColorMap.cs
namespace TestLibrary
{
/// <summary>
/// 회색조 색상 맵
/// </summary>
public class GrayscaleColorMap : IColorMap
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region RGB 구하기 - GetRGB(value)
/// <summary>
/// RGB 구하기
/// </summary>
/// <param name="value">값</param>
/// <returns>RGB</returns>
public (byte r, byte g, byte b) GetRGB(byte value)
{
return (value, value, value);
}
#endregion
}
}
▶ GrayscaleReversedColorMap.cs
namespace TestLibrary
{
/// <summary>
/// 회색조 반전 색상 맵
/// </summary>
public class GrayscaleReversedColorMap : IColorMap
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region RGB 구하기 - GetRGB(value)
/// <summary>
/// RGB 구하기
/// </summary>
/// <param name="value">값</param>
/// <returns>RGB</returns>
public (byte r, byte g, byte b) GetRGB(byte value)
{
value = (byte)(255 - value);
return (value, value, value);
}
#endregion
}
}
▶ GreenColorMap.cs
using System;
namespace TestLibrary
{
/// <summary>
/// 녹색 색상 맵
/// </summary>
public class GreenColorMap : IColorMap
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// ARGB 배열
/// </summary>
private readonly int[] argbArray =
{
-16761088, -16760832, -16760575, -16760318, -16760061, -16759804, -16759547, -16759290,
-16759033, -16758776, -16758519, -16758006, -16757749, -16757492, -16757235, -16756979,
-16756722, -16756465, -16756208, -16755951, -16755694, -16755437, -16755180, -16754667,
-16754410, -16688617, -16688360, -16688104, -16687847, -16687590, -16687333, -16687076,
-16621283, -16621026, -16620769, -16620512, -16620256, -16554463, -16554206, -16553949,
-16553692, -16487899, -16487642, -16487385, -16421336, -16421080, -16420823, -16355030,
-16354773, -16288980, -16288723, -16222930, -16222930, -16222673, -16156880, -16156623,
-16090830, -16025038, -16024781, -15958988, -15958731, -15892938, -15827145, -15826889,
-15761096, -15695303, -15695046, -15629254, -15563461, -15497924, -15497667, -15431875,
-15366082, -15300289, -15234496, -15234240, -15168447, -15102654, -15037118, -14971325,
-14905532, -14839740, -14773947, -14708154, -14642618, -14576825, -14511033, -14445240,
-14379447, -14313655, -14248118, -14182326, -14116533, -14050741, -13985204, -13919412,
-13853619, -13722291, -13656498, -13590962, -13525169, -13459377, -13328048, -13262512,
-13196720, -13130927, -13065391, -12934063, -12868270, -12802478, -12671406, -12605613,
-12539821, -12408749, -12342957, -12277164, -12146092, -12080300, -12014508, -11883435,
-11817643, -11686315, -11620779, -11554986, -11423914, -11358122, -11226794, -11161258,
-11029929, -10964137, -10833065, -10767273, -10636201, -10570408, -10439080, -10373544,
-10242216, -10176680, -10045351, -09914023, -09848487, -09717159, -09651623, -09520294,
-09389222, -09323430, -09192102, -09061029, -08995237, -08864165, -08798372, -08667300,
-08535972, -08404643, -08339107, -08207779, -08076706, -08010914, -07879842, -07748513,
-07682977, -07551648, -07420576, -07289248, -07223711, -07092383, -06961054, -06895518,
-06764189, -06633116, -06501788, -06436251, -06304923, -06173850, -06108057, -05976984,
-05845656, -05714583, -05648790, -05517717, -05386389, -05320596, -05189523, -05123730,
-04992657, -04861328, -04795791, -04664462, -04598925, -04467596, -04336523, -04270730,
-04139400, -04073863, -03942534, -03876997, -03745667, -03680130, -03614337, -03483007,
-03417470, -03286140, -03220603, -03154809, -03023479, -02957942, -02892148, -02826610,
-02760817, -02629487, -02563949, -02498155, -02432617, -02366823, -02301029, -02235491,
-02169697, -02103903, -02038365, -01972571, -01906777, -01841239, -01775444, -01709650,
-01644112, -01578318, -01512523, -01446985, -01381191, -01315396, -01249858, -01249599,
-01183805, -01118266, -01052472, -00986678, -00986675, -00920880, -00855086, -00789547,
-00723753, -00723494, -00657956, -00592161, -00526367, -00526108, -00460569, -00394775,
-00394516, -00328977, -00263183, -00197388, -00197385, -00131591, -00065796, -00000001,
};
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region RGB 구하기 - GetRGB(value)
/// <summary>
/// RGB 구하기
/// </summary>
/// <param name="value">값</param>
/// <returns>RGB</returns>
public (byte r, byte g, byte b) GetRGB(byte value)
{
byte[] byteArray = BitConverter.GetBytes(this.argbArray[value]);
return (byteArray[2], byteArray[1], byteArray[0]);
}
#endregion
}
}
▶ InfernoColorMap.cs
using System;
namespace TestLibrary
{
/// <summary>
/// 인페르노 색상 맵
/// </summary>
public class InfernoColorMap : IColorMap
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// RGB 배열
/// </summary>
private readonly int[] rgbArray =
{
00000003, 00000004, 00000006, 00065543, 00065801, 00065803, 00131342, 00131600,
00197138, 00262932, 00262934, 00328728, 00394267, 00460061, 00525855, 00591393,
00657187, 00722726, 00854056, 00919594, 00985389, 01050927, 01182258, 01247796,
01313590, 01444665, 01510203, 01641278, 01706816, 01838147, 01903685, 02034759,
02100298, 02231116, 02362190, 02493264, 02558802, 02689876, 02820694, 02951768,
03017306, 03148380, 03279197, 03410271, 03475808, 03606881, 03737954, 03869028,
03934565, 04065638, 04196710, 04262247, 04393576, 04524649, 04590185, 04721514,
04852586, 04918379, 05049451, 05180780, 05246316, 05377644, 05443181, 05574509,
05705581, 05771373, 05902701, 05968238, 06099566, 06230638, 06296430, 06427758,
06493294, 06624622, 06690158, 06821486, 06952814, 07018350, 07149678, 07215214,
07346542, 07477613, 07543405, 07674733, 07740269, 07871597, 08002669, 08068460,
08199532, 08265324, 08396651, 08462187, 08593515, 08724586, 08790378, 08921450,
08987241, 09118313, 09249641, 09315432, 09446504, 09512295, 09643367, 09774694,
09840230, 09971557, 10037348, 10168420, 10234211, 10365283, 10496610, 10562401,
10693473, 10759264, 10890335, 10956127, 11087454, 11218525, 11284316, 11415643,
11481435, 11612506, 11678297, 11809624, 11875159, 12006486, 12072278, 12203605,
12269396, 12400467, 12466258, 12532049, 12663376, 12729167, 12860494, 12926285,
13057612, 13123147, 13188938, 13320265, 13386056, 13451847, 13583430, 13649220,
13715011, 13780802, 13912129, 13977920, 14043711, 14109502, 14241085, 14306875,
14372666, 14438457, 14504504, 14570295, 14636086, 14702132, 14833459, 14899250,
14965297, 15031088, 15096878, 15097389, 15163180, 15229227, 15295018, 15361064,
15426855, 15492902, 15558693, 15559203, 15625250, 15691041, 15757087, 15757342,
15823389, 15889436, 15889690, 15955737, 15956248, 16022038, 16088085, 16088596,
16154642, 16154897, 16220944, 16221454, 16287501, 16287756, 16288267, 16354313,
16354824, 16355336, 16421127, 16421638, 16422150, 16422662, 16488710, 16489222,
16489734, 16489991, 16490503, 16491016, 16491530, 16492043, 16492557, 16493070,
16493584, 16494098, 16494612, 16494870, 16495384, 16495898, 16496412, 16496926,
16431905, 16432419, 16432933, 16433448, 16368426, 16368940, 16369455, 16304433,
16304948, 16305463, 16240442, 16240956, 16175935, 16176450, 16111429, 16111944,
16046923, 16047183, 15982162, 15982678, 15983193, 15918173, 15918688, 15853668,
15853928, 15854444, 15854960, 15855220, 15855737, 15856253, 15922049, 15922309,
15988361, 16054157, 16119953, 16186005, 16251801, 16383133, 16448928, 16580260,
};
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region RGB 구하기 - GetRGB(value)
/// <summary>
/// RGB 구하기
/// </summary>
/// <param name="value">값</param>
/// <returns>RGB</returns>
public (byte r, byte g, byte b) GetRGB(byte value)
{
byte[] byteArray = BitConverter.GetBytes(this.rgbArray[value]);
return (byteArray[2], byteArray[1], byteArray[0]);
}
#endregion
}
}
▶ LoporaColorMap.cs
using System;
namespace TestLibrary
{
/// <summary>
/// 로포라 색상 맵
/// </summary>
public class LoporaColorMap : IColorMap
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// RGB 배열
/// </summary>
private readonly int[] rgbArray =
{
0000000000, 0000069696, 0000137036, 0000203860, 0000270426, 0000336991, 0000403300, 0000469608,
0000535915, 0000602222, 0000668273, 0000734580, 0000800631, 0000866681, 0000932987, 0000999037,
0001065088, 0001131137, 0001197187, 0001262981, 0001329031, 0001395080, 0001461130, 0001526924,
0001592973, 0001659023, 0001724816, 0001790865, 0001856659, 0001922708, 0001988501, 0002054550,
0002120344, 0002186393, 0002252186, 0002317979, 0002384028, 0002449821, 0002515614, 0002581663,
0002647456, 0002713249, 0002779042, 0002845091, 0002910884, 0002976677, 0003042470, 0003108263,
0003174056, 0003240105, 0003305898, 0003371690, 0003437483, 0003503276, 0003569069, 0003634862,
0003700654, 0003766447, 0003832240, 0003898033, 0003963825, 0004029618, 0004095411, 0004161204,
0004227252, 0004292789, 0004358582, 0004424374, 0004490167, 0004555960, 0004621752, 0004687545,
0004753338, 0004819130, 0004884923, 0004950716, 0005016508, 0005082301, 0005148093, 0005213886,
0005279679, 0005345215, 0005411008, 0005476800, 0005542593, 0005608386, 0005674178, 0005739971,
0005805763, 0005871300, 0005937092, 0006002885, 0006068677, 0006134470, 0006200263, 0006265799,
0006331592, 0006397384, 0006463177, 0006528969, 0006594506, 0006660298, 0006726091, 0006791883,
0006857676, 0006923212, 0006989005, 0007054797, 0007120590, 0007186126, 0007251918, 0007317711,
0007383503, 0007449040, 0007514832, 0007580625, 0007646417, 0007711954, 0007777746, 0007843539,
0007909331, 0007974867, 0008040660, 0008106452, 0008171989, 0008237781, 0008303574, 0008369366,
0008434902, 0008435159, 0008500951, 0008566488, 0008632280, 0008698072, 0008763609, 0008829401,
0008895194, 0008960986, 0009026522, 0009092315, 0009158107, 0009223644, 0009289436, 0009355228,
0009420765, 0009486557, 0009552350, 0009617886, 0009683678, 0009749471, 0009815007, 0009880799,
0009946336, 0010012128, 0010077921, 0010143457, 0010209249, 0010275042, 0010340578, 0010406370,
0010472163, 0010537699, 0010603491, 0010669028, 0010734820, 0010800612, 0010866149, 0010931941,
0010997734, 0011063270, 0011129062, 0011194599, 0011260391, 0011326183, 0011391720, 0011457512,
0011523048, 0011588841, 0011654633, 0011720169, 0011785962, 0011851498, 0011917290, 0011983082,
0012048619, 0012114411, 0012179947, 0012245740, 0012311532, 0012377068, 0012442861, 0012508397,
0012574189, 0012639726, 0012705518, 0012771310, 0012836847, 0012902639, 0012968175, 0013033967,
0013099504, 0013165296, 0013231088, 0013296625, 0013362417, 0013427953, 0013493746, 0013559282,
0013625074, 0013690610, 0013756403, 0013822195, 0013887731, 0013953524, 0014019060, 0014084852,
0014150388, 0014216181, 0014281717, 0014347509, 0014413046, 0014478838, 0014544374, 0014610166,
0014675959, 0014741495, 0014807287, 0014872823, 0014938616, 0015004152, 0015069944, 0015135481,
0015201273, 0015266809, 0015332601, 0015398138, 0015463930, 0015529466, 0015595258, 0015660795,
0015726587, 0015792123, 0015857915, 0015923452, 0015989244, 0016054780, 0016120572, 0016186109,
0016251901, 0016317437, 0016383229, 0016448766, 0016514558, 0016580350, 0016645887, 0016711679,
};
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region RGB 구하기 - GetRGB(value)
/// <summary>
/// RGB 구하기
/// </summary>
/// <param name="value">값</param>
/// <returns>RGB</returns>
public (byte r, byte g, byte b) GetRGB(byte value)
{
byte[] byteArray = BitConverter.GetBytes(this.rgbArray[value]);
return (byteArray[2], byteArray[1], byteArray[0]);
}
#endregion
}
}
▶ MagmaColorMap.cs
using System;
namespace TestLibrary
{
/// <summary>
/// 마그마 색상 맵
/// </summary>
public class MagmaColorMap : IColorMap
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// RGB 배열
/// </summary>
private readonly int[] rgbArray =
{
00000003, 00000004, 00000006, 00065543, 00065801, 00065803, 00131597, 00131599,
00197393, 00262931, 00263189, 00328727, 00394521, 00460059, 00525853, 00591647,
00657186, 00722980, 00788774, 00854568, 00920106, 00985900, 01051695, 01117233,
01183027, 01314101, 01379896, 01445434, 01511228, 01576767, 01708097, 01773636,
01839174, 01970249, 02036043, 02101581, 02232656, 02298194, 02429269, 02494807,
02625881, 02756956, 02822494, 02953312, 03084386, 03149925, 03280999, 03412072,
03477354, 03608428, 03739502, 03870575, 03936113, 04067186, 04198259, 04329332,
04394869, 04525942, 04657015, 04722808, 04853881, 04919417, 05050746, 05181819,
05247611, 05378684, 05444476, 05575549, 05706877, 05772670, 05903742, 05969534,
06100862, 06166399, 06297727, 06363263, 06494591, 06625920, 06691456, 06822784,
06888576, 07019648, 07085440, 07216769, 07282305, 07413633, 07544705, 07610497,
07741825, 07807361, 07938689, 08004225, 08135553, 08266881, 08332417, 08463745,
08529281, 08660609, 08726145, 08857473, 08988801, 09054337, 09185664, 09251200,
09382528, 09513600, 09579392, 09710464, 09776256, 09907327, 10038655, 10104191,
10235519, 10366590, 10432382, 10563454, 10694782, 10760317, 10891645, 10957181,
11088508, 11219836, 11285371, 11416699, 11547771, 11613562, 11744634, 11875961,
11941497, 12072824, 12138360, 12269687, 12401015, 12466550, 12597877, 12728949,
12794740, 12926068, 12991603, 13122930, 13254258, 13319793, 13451120, 13516912,
13648239, 13714030, 13845101, 13910893, 14042220, 14108011, 14239338, 14305129,
14436457, 14502248, 14568039, 14699366, 14765158, 14830949, 14962276, 15028323,
15094114, 15159906, 15225953, 15357280, 15423072, 15489119, 15554911, 15620958,
15621469, 15687261, 15753309, 15819100, 15885148, 15951196, 15951707, 16017499,
16083547, 16084059, 16150107, 16150619, 16216411, 16216924, 16282972, 16283484,
16349532, 16350045, 16350557, 16416606, 16416862, 16417375, 16483424, 16483936,
16484449, 16484962, 16551011, 16551523, 16552036, 16552549, 16552806, 16618855,
16619368, 16619881, 16620394, 16620907, 16621420, 16621934, 16622191, 16622704,
16688753, 16689267, 16689780, 16690293, 16690806, 16691064, 16691577, 16692091,
16692604, 16693117, 16693631, 16694144, 16694402, 16694915, 16695429, 16695942,
16696456, 16696969, 16697227, 16697741, 16698254, 16633232, 16633746, 16634259,
16634517, 16635031, 16635544, 16636058, 16636572, 16637085, 16637343, 16637857,
16638371, 16573349, 16573862, 16574120, 16574634, 16575148, 16575662, 16576176,
16576689, 16576947, 16577461, 16577975, 16512953, 16513467, 16513725, 16514239,
};
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region RGB 구하기 - GetRGB(value)
/// <summary>
/// RGB 구하기
/// </summary>
/// <param name="value">값</param>
/// <returns>RGB</returns>
public (byte r, byte g, byte b) GetRGB(byte value)
{
byte[] byteArray = BitConverter.GetBytes(this.rgbArray[value]);
return (byteArray[2], byteArray[1], byteArray[0]);
}
#endregion
}
}
▶ PlasmaColorMap.cs
using System;
namespace TestLibrary
{
/// <summary>
/// 플라스마 색상 맵
/// </summary>
public class PlasmaColorMap : IColorMap
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// RGB 배열
/// </summary>
private readonly int[] rgbArray =
{
00788358, 01050503, 01246857, 01377930, 01574539, 01771148, 01902221, 02033038,
02164111, 02295184, 02426257, 02557330, 02688403, 02819476, 02950292, 03081365,
03212438, 03343511, 03409048, 03540120, 03671193, 03802266, 03867546, 03998619,
04129692, 04195228, 04326301, 04457374, 04522910, 04653727, 04784799, 04850336,
04981409, 05112481, 05178018, 05308834, 05374371, 05505443, 05636515, 05702052,
05833124, 05898405, 06029477, 06160549, 06226086, 06357158, 06422694, 06553767,
06619303, 06750375, 06815911, 06946983, 07078056, 07143592, 07274664, 07340200,
07471272, 07536808, 07667880, 07733672, 07864744, 07930280, 08061608, 08127143,
08258471, 08324007, 08455335, 08520871, 08652198, 08717990, 08783782, 08914853,
08980645, 09111972, 09177764, 09309348, 09375139, 09440931, 09572258, 09638049,
09769377, 09835168, 09900960, 10032287, 10098078, 10164126, 10295453, 10361244,
10427035, 10492827, 10624154, 10689945, 10755736, 10821527, 10953111, 11018902,
11084693, 11150484, 11281811, 11347602, 11413393, 11479184, 11545231, 11611023,
11676814, 11808141, 11873932, 11939723, 12005514, 12071561, 12137352, 12203143,
12268934, 12334725, 12400516, 12466307, 12532098, 12598145, 12663936, 12729728,
12795519, 12861310, 12927101, 12992892, 13058683, 13124730, 13190521, 13256312,
13322103, 13387894, 13453685, 13519477, 13585268, 13651315, 13717106, 13717361,
13783152, 13848943, 13914734, 13980525, 14046573, 14112364, 14112619, 14178410,
14244201, 14309992, 14375783, 14441830, 14442086, 14507877, 14573668, 14639459,
14639714, 14705761, 14771552, 14837344, 14903135, 14903390, 14969437, 15035228,
15035483, 15101274, 15167066, 15233113, 15233368, 15299159, 15364950, 15365205,
15431252, 15497044, 15497299, 15563090, 15563601, 15629392, 15695183, 15695438,
15761485, 15761741, 15827532, 15893579, 15893834, 15959625, 15959880, 16025927,
16026183, 16091974, 16092485, 16158276, 16158531, 16159042, 16224833, 16225089,
16291136, 16291391, 16291902, 16357693, 16357948, 16423995, 16424250, 16424762,
16425017, 16491064, 16491319, 16491574, 16557621, 16557877, 16558388, 16558643,
16559154, 16559409, 16625457, 16625712, 16626223, 16626478, 16626989, 16627245,
16627756, 16628011, 16628523, 16628778, 16629289, 16629801, 16630056, 16630568,
16630823, 16631334, 16566054, 16566566, 16567077, 16567333, 16567845, 16502820,
16503076, 16503588, 16438564, 16438820, 16439332, 16374052, 16374564, 16309540,
16310052, 16244772, 16245285, 16180261, 16180517, 16115494, 16116006, 16050726,
15985702, 15986214, 15921190, 15921446, 15856422, 15791397, 15791651, 15726625,
};
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region RGB 구하기 - GetRGB(value)
/// <summary>
/// RGB 구하기
/// </summary>
/// <param name="value">값</param>
/// <returns>RGB</returns>
public (byte r, byte g, byte b) GetRGB(byte value)
{
byte[] byteArray = BitConverter.GetBytes(this.rgbArray[value]);
return (byteArray[2], byteArray[1], byteArray[0]);
}
#endregion
}
}
▶ TurboColorMap.cs
using System;
namespace TestLibrary
{
/// <summary>
/// 터보 색상 맵
/// </summary>
public class TurboColorMap : IColorMap
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// ARGB 배열
/// </summary>
private readonly int[] argbArray =
{
-13559489, -13493436, -13427382, -13361328, -13295018, -13228964, -13162911, -13096857,
-13030547, -12964493, -12898440, -12832130, -12766077, -12700023, -12633970, -12567660,
-12501607, -12435554, -12369245, -12303192, -12237139, -12171086, -12170313, -12104260,
-12038208, -12037436, -11971383, -11905331, -11904559, -11838507, -11837991, -11771940,
-11771168, -11770653, -11770138, -11703831, -11703316, -11702801, -11702287, -11701517,
-11701003, -11700489, -11765255, -11764742, -11764228, -11828995, -11828482, -11893506,
-11892737, -11957761, -12022785, -12022017, -12087042, -12152067, -12217092, -12347397,
-12412423, -12477448, -12542218, -12672780, -12737806, -12802577, -12933139, -12998166,
-13128729, -13193500, -13324063, -13389091, -13519654, -13584682, -13714989, -13780017,
-13910581, -13975609, -14106173, -14171201, -14301765, -14366793, -14431822, -14562386,
-14627414, -14692442, -14757471, -14822499, -14887527, -14952556, -14952048, -15017332,
-15082361, -15081853, -15147137, -15146629, -15146121, -15145869, -15145361, -15145109,
-15079065, -15078812, -15013024, -15012515, -14946726, -14880938, -14749356, -14683567,
-14617778, -14486453, -14355127, -14223801, -14092475, -13961405, -13764543, -13633217,
-13436355, -13239748, -13108422, -12911559, -12714952, -12518089, -12255946, -12059339,
-11862476, -11600333, -11403725, -11141582, -10879182, -10682575, -10420431, -10158287,
-09896143, -09633999, -09372111, -09175503, -08913359, -08651215, -08389327, -08127183,
-07865294, -07603150, -07341262, -07079117, -06817229, -06555341, -06293452, -06031308,
-05834955, -05573067, -05311178, -05114826, -04852937, -04656585, -04394952, -04198600,
-04002247, -03740358, -03544262, -03347910, -03217349, -03020997, -02824900, -02628804,
-02497987, -02301891, -02171331, -02040770, -01910210, -01779394, -01648834, -01518273,
-01387713, -01257153, -01126849, -01061824, -00931264, -00866240, -00801216, -00670656,
-00605888, -00540864, -00475840, -00411072, -00346048, -00346560, -00281792, -00282304,
-00217536, -00218048, -00153280, -00153793, -00154561, -00155073, -00155841, -00156354,
-00157122, -00157634, -00158403, -00224451, -00225219, -00291524, -00292036, -00358341,
-00424389, -00490694, -00491206, -00557511, -00623559, -00755400, -00821705, -00887754,
-00954058, -01085643, -01151948, -01283533, -01349837, -01481422, -01613263, -01679312,
-01811153, -01942738, -02074579, -02206164, -02337749, -02469590, -02601175, -02733016,
-02930137, -03061978, -03193563, -03390940, -03522526, -03654111, -03851488, -03983073,
-04180450, -04377572, -04509157, -04706534, -04838119, -05035497, -05232618, -05429739,
-05561580, -05758702, -05956079, -06153200, -06350322, -06482163, -06679284, -06876662,
-07073783, -07270904, -07468282, -07665403, -07862524, -08059902, -08257023, -08388608,
};
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region RGB 구하기 - GetRGB(value)
/// <summary>
/// RGB 구하기
/// </summary>
/// <param name="value">값</param>
/// <returns>RGB</returns>
public (byte r, byte g, byte b) GetRGB(byte value)
{
byte[] byteArray = BitConverter.GetBytes(this.argbArray[value]);
return (byteArray[2], byteArray[1], byteArray[0]);
}
#endregion
}
}
▶ ViridisColorMap.cs
using System;
namespace TestLibrary
{
/// <summary>
/// 비리디스 색상 맵
/// </summary>
public class ViridisColorMap : IColorMap
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// RGB 배열
/// </summary>
private readonly int[] rgbArray =
{
04456788, 04457045, 04457303, 04523352, 04523610, 04524123, 04589916, 04590430,
04590687, 04591201, 04656994, 04657507, 04657765, 04658278, 04658535, 04658793,
04659306, 04725099, 04725356, 04725870, 04726127, 04726384, 04726897, 04727154,
04727411, 04727668, 04662645, 04662902, 04663159, 04663416, 04663929, 04664186,
04664443, 04599164, 04599676, 04599933, 04600190, 04534911, 04535423, 04535680,
04535937, 04470657, 04471170, 04405891, 04406147, 04406404, 04341124, 04341381,
04341893, 04276614, 04276870, 04211591, 04211847, 04146567, 04147080, 04081800,
04082057, 04016777, 04017033, 04017289, 03952010, 03952266, 03887242, 03887498,
03822219, 03822475, 03757195, 03757451, 03692171, 03692428, 03627148, 03627404,
03562124, 03562380, 03497100, 03497356, 03432077, 03432333, 03367053, 03367309,
03302029, 03302285, 03237005, 03237261, 03237517, 03172237, 03172493, 03107213,
03107469, 03042190, 03042446, 03042702, 02977422, 02977678, 02912398, 02912654,
02912910, 02847630, 02847886, 02782606, 02782862, 02783118, 02717838, 02718094,
02652814, 02652814, 02653070, 02587790, 02588046, 02588302, 02523022, 02523278,
02523534, 02458254, 02458509, 02393229, 02393485, 02393741, 02328461, 02328717,
02328973, 02263437, 02263693, 02263949, 02198669, 02198924, 02199180, 02133900,
02134156, 02134412, 02069132, 02069387, 02069643, 02069899, 02070155, 02004874,
02005130, 02005386, 02005386, 02005641, 02005897, 02006153, 02006408, 02006664,
02006920, 02007175, 02072967, 02073222, 02073478, 02139269, 02139525, 02205317,
02205572, 02271108, 02336899, 02337154, 02402946, 02468737, 02534529, 02600320,
02666111, 02731903, 02797694, 02863485, 02929021, 03060348, 03126139, 03191930,
03323258, 03389049, 03520376, 03586167, 03717494, 03783030, 03914357, 04045684,
04111475, 04242802, 04374129, 04505200, 04570991, 04702318, 04833645, 04964972,
05096043, 05227369, 05358696, 05490023, 05621350, 05752421, 05883748, 06015074,
06211937, 06343008, 06474335, 06605661, 06802524, 06933595, 07064921, 07196248,
07392854, 07524181, 07655508, 07852114, 07983441, 08180303, 08311374, 08508236,
08639307, 08836169, 08967495, 09164102, 09295428, 09492035, 09623361, 09819967,
09951294, 10147900, 10344762, 10475832, 10672695, 10869301, 11000627, 11197234,
11394096, 11525166, 11722028, 11918635, 12049705, 12246567, 12443174, 12574500,
12771106, 12967713, 13099039, 13295646, 13492253, 13623580, 13820187, 13951258,
14148121, 14344728, 14475800, 14672664, 14803736, 15000344, 15197209, 15328281,
15524890, 15656219, 15852828, 15983902, 16180767, 16311841, 16442914, 16639780,
};
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region RGB 구하기 - GetRGB(value)
/// <summary>
/// RGB 구하기
/// </summary>
/// <param name="value">값</param>
/// <returns>RGB</returns>
public (byte r, byte g, byte b) GetRGB(byte value)
{
byte[] byteArray = BitConverter.GetBytes(this.rgbArray[value]);
return (byteArray[2], byteArray[1], byteArray[0]);
}
#endregion
}
}
▶ BitmapHelper.cs
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace TestLibrary
{
/// <summary>
/// 비트맵 헬퍼
/// </summary>
public static class BitmapHelper
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 비트맵 구하기 - GetBitmap(fftList, colorMap, intensity, db, dbScale, roll, rollOffset)
/// <summary>
/// 비트맵 구하기
/// </summary>
/// <param name="fftList">FFT 리스트</param>
/// <param name="colorMap">색상 맵</param>
/// <param name="intensity">강도</param>
/// <param name="db">dB 적용 여부</param>
/// <param name="dbScale">dB 스케일</param>
/// <param name="roll">롤 여부</param>
/// <param name="rollOffset">롤 오프셋</param>
/// <returns>비트맵</returns>
public static Bitmap GetBitmap
(
List<double[]> fftList,
ColorMap colorMap,
double intensity = 1,
bool db = false,
double dbScale = 1,
bool roll = false,
int rollOffset = 0
)
{
if(fftList.Count == 0)
{
throw new ArgumentException("This Spectrogram contains no FFTs (likely because no signal was added)");
}
int width = fftList.Count;
int height = fftList[0].Length;
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
colorMap.SetBitmapPalette(bitmap);
Rectangle lockRectangle = new Rectangle(0, 0, width, height);
BitmapData bitmapData = bitmap.LockBits(lockRectangle, ImageLockMode.ReadOnly, bitmap.PixelFormat);
int stride = bitmapData.Stride;
byte[] byteArray = new byte[bitmapData.Stride * bitmap.Height];
Parallel.For
(
0,
width,
column =>
{
int sourceColumn = column;
if(roll)
{
sourceColumn += width - rollOffset % width;
if(sourceColumn >= width)
{
sourceColumn -= width;
}
}
for(int row = 0; row < height; row++)
{
double value = fftList[sourceColumn][row];
if(db)
{
value = 20 * Math.Log10(value * dbScale + 1);
}
value *= intensity;
value = Math.Min(value, 255);
int bytePosition = (height - 1 - row) * stride + column;
byteArray[bytePosition] = (byte)value;
}
}
);
Marshal.Copy(byteArray, 0, bitmapData.Scan0, byteArray.Length);
bitmap.UnlockBits(bitmapData);
return bitmap;
}
#endregion
#region 수직 스케일 비트맵 구하기 - GetVerticalScaleBitmap(width, setting, hzOffset, tickSize, reduction)
/// <summary>
/// 수직 스케일 비트맵 구하기
/// </summary>
/// <param name="width">너비</param>
/// <param name="setting">설정</param>
/// <param name="hzOffset">HZ 오프셋</param>
/// <param name="tickSize">틱 크기</param>
/// <param name="reduction">축소</param>
/// <returns>수직 스케일 비트맵</returns>
public static Bitmap GetVerticalScaleBitmap(int width, Setting setting, int hzOffset = 0, int tickSize = 3, int reduction = 1)
{
double hzTick = 1;
int minimumSpacingPixel = 50;
double[] multiplierArray = { 2, 2.5, 2 };
int multiplier = 0;
while(true)
{
hzTick *= multiplierArray[multiplier++ % multiplierArray.Length];
double tickCount = setting.FrequencySpan / hzTick;
double pixelBetweenTicks = setting.Height / tickCount;
if(pixelBetweenTicks >= minimumSpacingPixel * reduction)
{
break;
}
}
Bitmap bitmap = new Bitmap(width, setting.Height / reduction, PixelFormat.Format32bppPArgb);
using(Graphics graphics = Graphics.FromImage(bitmap))
using(Pen pen = new Pen(Color.Black))
using(SolidBrush brush = new SolidBrush(Color.Black))
using(Font font = new Font("나눔고딕코딩", 12))
using(StringFormat stringFormat = new StringFormat() { LineAlignment = StringAlignment.Center })
{
graphics.Clear(Color.White);
List<double> frequencyList = new List<double>();
for(double frequency = setting.MinimumFrequency; frequency <= setting.MaximumFrequency; frequency += hzTick)
{
frequencyList.Add(frequency);
}
if(frequencyList.Count >= 2)
{
frequencyList.RemoveAt(0);
frequencyList.RemoveAt(frequencyList.Count - 1);
}
foreach(double frequency in frequencyList)
{
int y = setting.GetPixelY(frequency) / reduction;
graphics.DrawLine(pen, 0, y, tickSize, y);
graphics.DrawString($"{frequency + hzOffset:N0} Hz", font, brush, tickSize, y, stringFormat);
}
}
return bitmap;
}
#endregion
}
}
▶ ColorMap.cs
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
namespace TestLibrary
{
/// <summary>
/// 색상 맵
/// </summary>
public class ColorMap
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 아르고 색상 맵 - Argo
/// <summary>
/// 아르고 색상 맵
/// </summary>
public static ColorMap Argo => new ColorMap(new ArgoColorMap());
#endregion
#region 청색 색상 맵 - Blue
/// <summary>
/// 청색 색상 맵
/// </summary>
public static ColorMap Blue => new ColorMap(new BlueColorMap());
#endregion
#region 회색조 색상 맵 - Grayscale
/// <summary>
/// 회색조 색상 맵
/// </summary>
public static ColorMap Grayscale => new ColorMap(new GrayscaleColorMap());
#endregion
#region 회색조 반전 색상 맵 - GrayscaleReversed
/// <summary>
/// 회색조 반전 색상 맵
/// </summary>
public static ColorMap GrayscaleReversed => new ColorMap(new GrayscaleReversedColorMap());
#endregion
#region 녹색 색상 맵 - Green
/// <summary>
/// 녹색 색상 맵
/// </summary>
public static ColorMap Green => new ColorMap(new GreenColorMap());
#endregion
#region 인페르노 색상 맵 - Inferno
/// <summary>
/// 인페르노 색상 맵
/// </summary>
public static ColorMap Inferno => new ColorMap(new InfernoColorMap());
#endregion
#region 로포라 색상 맵 - Lopora
/// <summary>
/// 로포라 색상 맵
/// </summary>
public static ColorMap Lopora => new ColorMap(new LoporaColorMap());
#endregion
#region 마그마 색상 맵 - Magma
/// <summary>
/// 마그마 색상 맵
/// </summary>
public static ColorMap Magma => new ColorMap(new MagmaColorMap());
#endregion
#region 플라스마 색상 맵 - Plasma
/// <summary>
/// 플라스마 색상 맵
/// </summary>
public static ColorMap Plasma => new ColorMap(new PlasmaColorMap());
#endregion
#region 터보 색상 맵 - Turbo
/// <summary>
/// 터보 색상 맵
/// </summary>
public static ColorMap Turbo => new ColorMap(new TurboColorMap());
#endregion
#region 비리디스 색상 맵 - Viridis
/// <summary>
/// 비리디스 색상 맵
/// </summary>
public static ColorMap Viridis => new ColorMap(new ViridisColorMap());
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 색상 맵
/// </summary>
private readonly IColorMap colorMap;
/// <summary>
/// 명칭
/// </summary>
public readonly string Name;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - ColorMap(colorMap)
/// <summary>
/// 생성자
/// </summary>
/// <param name="colorMap">색상 맵</param>
public ColorMap(IColorMap colorMap)
{
this.colorMap = colorMap ?? new GrayscaleColorMap();
Name = this.colorMap.GetType().Name;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 색상 맵 배열 구하기 - GetColorMapArray()
/// <summary>
/// 색상 맵 배열 구하기
/// </summary>
/// <returns>색상 맵 배열</returns>
public static ColorMap[] GetColorMapArray() => typeof(ColorMap).GetProperties()
.Select(x => (ColorMap)x.GetValue(x.Name))
.ToArray();
#endregion
#region 색상 맵 명칭 배열 구하기 - GetColorMapNameArray()
/// <summary>
/// 색상 맵 명칭 배열 구하기
/// </summary>
/// <returns>색상 맵 명칭 배열</returns>
public static string[] GetColorMapNameArray()
{
return GetColorMapArray().Select(x => x.Name).ToArray();
}
#endregion
#region 색상 맵 구하기 - GetColorMap(colorMapName)
/// <summary>
/// 색상 맵 구하기
/// </summary>
/// <param name="colorMapName">색상 맵 명칭</param>
/// <returns>색상 맵</returns>
public static ColorMap GetColorMap(string colorMapName)
{
foreach(ColorMap colorMap in GetColorMapArray())
{
if(string.Equals(colorMap.Name, colorMapName, StringComparison.InvariantCultureIgnoreCase))
{
return colorMap;
}
}
throw new ArgumentException($"ColorMap does not exist : {colorMapName}");
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Public
#region 문자열 구하기 - ToString()
/// <summary>
/// 문자열 구하기
/// </summary>
/// <returns>문자열</returns>
public override string ToString()
{
return $"ColorMap {Name}";
}
#endregion
#region RGB 구하기 - GetRGB(value)
/// <summary>
/// RGB 구하기
/// </summary>
/// <param name="value">값</param>
/// <returns>RGB 튜플</returns>
public (byte r, byte g, byte b) GetRGB(byte value)
{
return this.colorMap.GetRGB(value);
}
#endregion
#region RGB 구하기 - GetRGB(fraction)
/// <summary>
/// RGB 구하기
/// </summary>
/// <param name="fraction">분수</param>
/// <returns>RGB 튜플</returns>
public (byte r, byte g, byte b) GetRGB(double fraction)
{
fraction = Math.Max(fraction, 0);
fraction = Math.Min(fraction, 1);
return this.colorMap.GetRGB((byte)(fraction * 255));
}
#endregion
#region 32비트 정수 구하기 - GetInt32(value)
/// <summary>
/// 32비트 정수 구하기
/// </summary>
/// <param name="value">값</param>
/// <returns>32비트 정수</returns>
public int GetInt32(byte value)
{
var (r, g, b) = GetRGB(value);
return 255 << 24 | r << 16 | g << 8 | b;
}
#endregion
#region 32비트 정수 구하기 - GetInt32(fraction)
/// <summary>
/// 32비트 정수 구하기
/// </summary>
/// <param name="fraction">분수</param>
/// <returns>32비트 정수</returns>
public int GetInt32(double fraction)
{
var (r, g, b) = GetRGB(fraction);
return 255 << 24 | r << 16 | g << 8 | b;
}
#endregion
#region 색상 구하기 - GetColor(value)
/// <summary>
/// 색상 구하기
/// </summary>
/// <param name="value">값</param>
/// <returns>색상</returns>
public Color GetColor(byte value)
{
return Color.FromArgb(GetInt32(value));
}
#endregion
#region 색상 구하기 - GetColor(fraction)
/// <summary>
/// 색상 구하기
/// </summary>
/// <param name="fraction">분수</param>
/// <returns>색상</returns>
public Color GetColor(double fraction)
{
return Color.FromArgb(GetInt32(fraction));
}
#endregion
#region 비트맵 팔레트 설정하기 - SetBitmapPalette(bitmap)
/// <summary>
/// 비트맵 팔레트 설정하기
/// </summary>
/// <param name="bitmap">비트맵</param>
public void SetBitmapPalette(Bitmap bitmap)
{
ColorPalette palette = bitmap.Palette;
for(int i = 0; i < 256; i++)
{
palette.Entries[i] = GetColor((byte)i);
}
bitmap.Palette = palette;
}
#endregion
}
}
▶ IColorMap.cs
namespace TestLibrary
{
/// <summary>
/// 색상 맵 인터페이스
/// </summary>
public interface IColorMap
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
#region RGB 구하기 - GetRGB(value)
/// <summary>
/// RGB 구하기
/// </summary>
/// <param name="value">값</param>
/// <returns>RGB 튜플</returns>
(byte r, byte g, byte b) GetRGB(byte value);
#endregion
}
}
▶ Setting.cs
using System;
using FftSharp;
using FftSharp.Windows;
namespace TestLibrary
{
/// <summary>
/// 설정
/// </summary>
public class Setting
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Public
#region Field
/// <summary>
/// 샘플 비율
/// </summary>
public readonly int SampleRate;
/// <summary>
/// FFT 크기
/// </summary>
public readonly int FFTSize;
/// <summary>
/// FFT 길이 (단위 : 초)
/// </summary>
public readonly double FFTLength;
/// <summary>
/// 주파수 나이퀴스트
/// </summary>
public readonly double FrequencyNyquist;
/// <summary>
/// 픽셀당 HZ
/// </summary>
public readonly double HZPerPixel;
/// <summary>
/// HZ당 픽셀
/// </summary>
public readonly double PixelPerHZ;
/// <summary>
/// FFT 인덱스 1
/// </summary>
public readonly int FFTIndex1;
/// <summary>
/// FFT 인덱스 2
/// </summary>
public readonly int FFTIndex2;
/// <summary>
/// 최소 주파수
/// </summary>
public readonly double MinimumFrequency;
/// <summary>
/// 최대 주파수
/// </summary>
public readonly double MaximumFrequency;
/// <summary>
/// 주파수 범위
/// </summary>
public readonly double FrequencySpan;
/// <summary>
/// 높이
/// </summary>
public readonly int Height;
/// <summary>
/// HZ 오프셋
/// </summary>
public int HZOffset;
/// <summary>
/// 윈도우 배열
/// </summary>
public readonly double[] WindowArray;
/// <summary>
/// 단계 크기
/// </summary>
public readonly int StepSize;
/// <summary>
/// 단계 길이 (단위 : 초)
/// </summary>
public readonly double StepLength;
/// <summary>
/// 단계 오버랩 (분수)
/// </summary>
public readonly double StepOverlapFraction;
/// <summary>
/// 단계 오버랩 (단위 : 초)
/// </summary>
public readonly double StepOverlap;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - Setting(sampleRate, fftSize, stepSize, minimumFrequency, maximumFrequency, hzOffset)
/// <summary>
/// 생성자
/// </summary>
/// <param name="sampleRate">샘플 비율</param>
/// <param name="fftSize">FFT 크기</param>
/// <param name="stepSize">단계 크기</param>
/// <param name="minimumFrequency">최소 주파수</param>
/// <param name="maximumFrequency">최대 주파수</param>
/// <param name="hzOffset">HZ 오프셋</param>
public Setting(int sampleRate, int fftSize, int stepSize, double minimumFrequency, double maximumFrequency, int hzOffset)
{
if(Transform.IsPowerOfTwo(fftSize) == false)
{
throw new ArgumentException("FFT size must be a power of 2");
}
SampleRate = sampleRate;
FFTSize = fftSize;
StepSize = stepSize;
FFTLength = (double)fftSize / sampleRate;
minimumFrequency = Math.Max(minimumFrequency, 0);
FrequencyNyquist = sampleRate / 2;
HZPerPixel = (double)sampleRate / fftSize;
PixelPerHZ = (double)fftSize / sampleRate;
FFTIndex1 = (minimumFrequency == 0) ? 0 : (int)(minimumFrequency / HZPerPixel);
FFTIndex2 = (maximumFrequency >= FrequencyNyquist) ? fftSize / 2 : (int)(maximumFrequency / HZPerPixel);
Height = FFTIndex2 - FFTIndex1;
MinimumFrequency = FFTIndex1 * HZPerPixel;
MaximumFrequency = FFTIndex2 * HZPerPixel;
FrequencySpan = MaximumFrequency - MinimumFrequency;
HZOffset = hzOffset;
StepLength = (double)StepSize / sampleRate;
WindowArray = new Hanning().Create(fftSize);
StepOverlap = FFTLength - StepLength;
StepOverlapFraction = StepOverlap / FFTLength;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 픽셀 Y 구하기 - GetPixelY(frequency)
/// <summary>
/// 픽셀 Y 구하기
/// </summary>
/// <param name="frequency">주파수</param>
/// <returns>픽셀 Y</returns>
public int GetPixelY(double frequency)
{
return (int)(Height - (frequency - MinimumFrequency + HZPerPixel) * PixelPerHZ - 1);
}
#endregion
}
}
▶ SpectrogramFileFormat.cs
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
using FftSharp;
namespace TestLibrary
{
/// <summary>
/// 스펙트로그램 파일 포맷
/// </summary>
public class SpectrogramFileFormat
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Public
#region Field
/// <summary>
/// 메이저 버전
/// </summary>
public readonly byte MajorVersion = 1;
/// <summary>
/// 마이너 버전
/// </summary>
public readonly byte MinorVersion = 1;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 파일 경로 - FilePath
/// <summary>
/// 파일 경로
/// </summary>
public string FilePath { get; private set; }
#endregion
#region 샘플 비율 - SampleRate
/// <summary>
/// 샘플 비율
/// </summary>
public int SampleRate { get; private set; }
#endregion
#region 단계 크기 - StepSize
/// <summary>
/// 단계 크기
/// </summary>
public int StepSize { get; private set; }
#endregion
#region 너비 - Width
/// <summary>
/// 너비
/// </summary>
public int Width { get; private set; }
#endregion
#region FFT 크기 - FFTSize
/// <summary>
/// FFT 크기
/// </summary>
public int FFTSize { get; private set; }
#endregion
#region FFT 첫번째 인덱스 - FFTFirstIndex
/// <summary>
/// FFT 첫번째 인덱스
/// </summary>
public int FFTFirstIndex { get; private set; }
#endregion
#region 높이 - Height
/// <summary>
/// 높이
/// </summary>
public int Height { get; private set; }
#endregion
#region HZ 오프셋 - HZOffset
/// <summary>
/// HZ 오프셋
/// </summary>
public int HZOffset { get; private set; }
#endregion
#region MEL BIN 카운트 - MELBINCount
/// <summary>
/// MEL BIN 카운트
/// </summary>
public int MELBINCount { get; private set; }
#endregion
#region 데시빌 카운트 - DecibelCount
/// <summary>
/// 데시빌 카운트
/// </summary>
public bool DecibelCount { get; private set; }
#endregion
#region MEL 여부 - IsMEL
/// <summary>
/// MEL 여부
/// </summary>
public bool IsMEL
{
get
{
return MELBINCount > 0;
}
}
#endregion
#region FFT 리스트 - FFTList
/// <summary>
/// FFT 리스트
/// </summary>
public List<double[]> FFTList { get; private set; }
#endregion
#region 이미지 너비 - ImageWidth
/// <summary>
/// 이미지 너비
/// </summary>
public int ImageWidth { get { return (FFTList is null) ? 0 : FFTList.Count; } }
#endregion
#region 이미지 높이 - ImageHeight
/// <summary>
/// 이미지 높이
/// </summary>
public int ImageHeight { get { return (FFTList is null) ? 0 : FFTList[0].Length; } }
#endregion
#region 시간 배열 - TimeArray
/// <summary>
/// 시간 배열
/// </summary>
public double[] TimeArray { get; private set; }
#endregion
#region 주파수 배열 - FrequencyArray
/// <summary>
/// 주파수 배열
/// </summary>
public double[] FrequencyArray { get; private set; }
#endregion
#region MEL 배열 - MELArray
/// <summary>
/// MEL 배열
/// </summary>
public double[] MELArray { get; private set; }
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - SpectrogramFileFormat()
/// <summary>
/// 생성자
/// </summary>
public SpectrogramFileFormat()
{
}
#endregion
#region 생성자 - SpectrogramFileFormat(filePath)
/// <summary>
/// 생성자
/// </summary>
/// <param name="filePath">파일 경로</param>
public SpectrogramFileFormat(string filePath)
{
Load(filePath);
CalculateTimes();
CalculateFrequencies();
}
#endregion
#region 생성자 - SpectrogramFileFormat(generator, melBinCount)
/// <summary>
/// 생성자
/// </summary>
/// <param name="generator">스펙트로그램 제너레이터</param>
/// <param name="melBinCount">MEL BIN 카운트</param>
public SpectrogramFileFormat(SpectrogramGenerator generator, int melBinCount = 0)
{
SampleRate = generator.SampleRate;
StepSize = generator.StepSize;
Width = generator.Width;
FFTSize = generator.FFTSize;
FFTFirstIndex = generator.NextColumnIndex;
Height = generator.Height;
HZOffset = generator.HZOffset;
MELBINCount = melBinCount;
FFTList = (melBinCount > 0) ? generator.GetMELFFTArrayList(melBinCount) : generator.GetFFTArrayList();
CalculateTimes();
CalculateFrequencies();
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 문자열 구하기 - ToString()
/// <summary>
/// 문자열 구하기
/// </summary>
/// <returns>문자열</returns>
public override string ToString()
{
return $"SFF {ImageWidth}X{ImageHeight}";
}
#endregion
#region 비트맵 구하기 - GetBitmap(colorMap, intensity, db)
/// <summary>
/// 비트맵 구하기
/// </summary>
/// <param name="colorMap">색상 맵</param>
/// <param name="intensity">강도</param>
/// <param name="db">데시빌 여부</param>
/// <returns>비트맵</returns>
public Bitmap GetBitmap(ColorMap colorMap = null, double intensity = 1, bool db = false)
{
colorMap = colorMap ?? ColorMap.Viridis;
return BitmapHelper.GetBitmap(FFTList, colorMap, intensity, db);
}
#endregion
#region 로드하기 - Load(filePath)
/// <summary>
/// 로드하기
/// </summary>
/// <param name="filePath">파일 경로</param>
public void Load(string filePath)
{
FilePath = Path.GetFullPath(filePath);
byte[] byteArray = File.ReadAllBytes(filePath);
int magicNumber = BitConverter.ToInt32(byteArray, 0);
if(magicNumber != 1179014099)
{
throw new InvalidDataException("not a valid SFF file");
}
SampleRate = BitConverter.ToInt32(byteArray, 42);
StepSize = BitConverter.ToInt32(byteArray, 46);
Width = BitConverter.ToInt32(byteArray, 50);
FFTSize = BitConverter.ToInt32(byteArray, 54);
FFTFirstIndex = BitConverter.ToInt32(byteArray, 58);
Height = BitConverter.ToInt32(byteArray, 62);
HZOffset = BitConverter.ToInt32(byteArray, 66);
MELBINCount = BitConverter.ToInt32(byteArray, 84);
byte valueCountPerPoint = byteArray[70];
bool isComplex = valueCountPerPoint == 2;
if(isComplex)
{
throw new NotImplementedException("complex data is not yet supported");
}
byte byteCountPerValue = byteArray[71];
DecibelCount = byteArray[72] == 1;
int firstDataByte = (int)BitConverter.ToUInt32(byteArray, 80);
MELBINCount = BitConverter.ToInt32(byteArray, 84);
int FFTHeight = BitConverter.ToInt32(byteArray, 88);
int FFTWidth = BitConverter.ToInt32(byteArray, 92);
FFTList = new List<double[]>();
int byteCountPerPoint = byteCountPerValue * valueCountPerPoint;
int byteCountPerColumn = FFTHeight * byteCountPerPoint;
for(int x = 0; x < FFTWidth; x++)
{
FFTList.Add(new double[FFTHeight]);
int columnOffset = byteCountPerColumn * x;
for(int y = 0; y < FFTHeight; y++)
{
int rowOffset = y * byteCountPerPoint;
int valueOffset = firstDataByte + columnOffset + rowOffset;
double value = BitConverter.ToDouble(byteArray, valueOffset);
FFTList[x][y] = value;
}
}
}
#endregion
#region 저장하기 - Save(filePath)
/// <summary>
/// 저장하기
/// </summary>
/// <param name="filePath">파일 경로</param>
public void Save(string filePath)
{
FilePath = Path.GetFullPath(filePath);
byte[] headerArray = new byte[256];
headerArray[0] = 211;
headerArray[1] = (byte)'S';
headerArray[2] = (byte)'F';
headerArray[3] = (byte)'F';
headerArray[4] = (byte)'\r';
headerArray[5] = (byte)'\n';
headerArray[6] = (byte)' ';
headerArray[7] = (byte)'\n';
int magicNumber = BitConverter.ToInt32(headerArray, 0);
if(magicNumber != 1179014099)
{
throw new InvalidDataException("magic number for SFF files is 1179014099");
}
string fileInfo = $"SFF {MajorVersion}.{MinorVersion}\r\n";
byte[] fileInfoByteArray = Encoding.UTF8.GetBytes(fileInfo);
if(fileInfoByteArray.Length > 32)
{
throw new InvalidDataException("file info cannot exceed 32 bytes");
}
Array.Copy(fileInfoByteArray, 0, headerArray, 8, fileInfoByteArray.Length);
headerArray[40] = MajorVersion;
headerArray[41] = MinorVersion;
Array.Copy(BitConverter.GetBytes(SampleRate), 0, headerArray, 42, 4);
Array.Copy(BitConverter.GetBytes(StepSize ), 0, headerArray, 46, 4);
Array.Copy(BitConverter.GetBytes(Width ), 0, headerArray, 50, 4);
Array.Copy(BitConverter.GetBytes(FFTSize ), 0, headerArray, 54, 4);
Array.Copy(BitConverter.GetBytes(FFTFirstIndex), 0, headerArray, 58, 4);
Array.Copy(BitConverter.GetBytes(Height ), 0, headerArray, 62, 4);
Array.Copy(BitConverter.GetBytes(HZOffset ), 0, headerArray, 66, 4);
byte valueCountPerPoint = 1;
byte byteCountPerValue = 8;
byte decibelUnitCount = 0;
byte dataExtraByte = 0;
headerArray[70] = valueCountPerPoint;
headerArray[71] = byteCountPerValue;
headerArray[72] = decibelUnitCount;
headerArray[73] = dataExtraByte;
Array.Copy(BitConverter.GetBytes(MELBINCount), 0, headerArray, 84, 4);
Array.Copy(BitConverter.GetBytes(ImageHeight), 0, headerArray, 88, 4);
Array.Copy(BitConverter.GetBytes(ImageWidth ), 0, headerArray, 92, 4);
int firstDataByte = headerArray.Length;
Array.Copy(BitConverter.GetBytes(firstDataByte), 0, headerArray, 80, 4);
int dataPointCount = ImageHeight * ImageWidth;
int byteCountPerPoint = byteCountPerValue * valueCountPerPoint;
byte[] fileByteArray = new byte[headerArray.Length + dataPointCount * byteCountPerPoint];
Array.Copy(headerArray, 0, fileByteArray, 0, headerArray.Length);
int byteCountPerColumn = ImageHeight * byteCountPerPoint;
for(int x = 0; x < ImageWidth; x++)
{
int columnOffset = byteCountPerColumn * x;
for(int y = 0; y < ImageHeight; y++)
{
int rowOffset = y * byteCountPerPoint;
int valueOffset = firstDataByte + columnOffset + rowOffset;
double value = FFTList[x][y];
Array.Copy(BitConverter.GetBytes(value), 0, fileByteArray, valueOffset, 8);
}
}
File.WriteAllBytes(filePath, fileByteArray);
}
#endregion
#region 픽셀 정보 구하기 - GetPixelInformation(x, y)
/// <summary>
/// 픽셀 정보 구하기
/// </summary>
/// <param name="x">X</param>
/// <param name="y">Y</param>
/// <returns>(시간(단위 : 초), 주파수(단위 : HZ), 진도) 튜플</returns>
public (double timeSecond, double frequencyHZ, double magnitude) GetPixelInformation(int x, int y)
{
double timeSecond = (double)x * StepSize / SampleRate;
double maximumFrequency = SampleRate / 2;
double maximumMEL = Transform.MelFromFreq(maximumFrequency);
double fraction = (ImageHeight - y) / (double)ImageHeight;
double frequency = IsMEL ? Transform.MelToFreq(fraction * maximumMEL) : fraction * maximumFrequency;
double magnitude = double.NaN;
try
{
magnitude = FFTList[x][ImageHeight - y - 1];
}
catch
{
}
return (timeSecond, frequency, magnitude);
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 시간 계산하기 - CalculateTimes()
/// <summary>
/// 시간 계산하기
/// </summary>
private void CalculateTimes()
{
TimeArray = new double[ImageWidth];
double step = (double)StepSize / SampleRate;
for(int i = 0; i < ImageWidth; i++)
{
TimeArray[i] = i * step;
}
}
#endregion
#region 주파수 계산하기 - CalculateFrequencies()
/// <summary>
/// 주파수 계산하기
/// </summary>
private void CalculateFrequencies()
{
FrequencyArray = new double[ImageHeight];
MELArray = new double[ImageHeight];
double maximumFrequency = SampleRate / 2;
double maximumMEL = Transform.MelFromFreq(maximumFrequency);
for(int y = 0; y < ImageHeight; y++)
{
double fraction = (ImageHeight - y) / (double)ImageHeight;
if(IsMEL)
{
MELArray[y] = fraction * maximumMEL;
FrequencyArray[y] = Transform.MelToFreq(MELArray[y]);
}
else
{
FrequencyArray[y] = fraction * maximumFrequency;
MELArray[y] = Transform.MelFromFreq(FrequencyArray[y]);
}
}
}
#endregion
}
}
▶ SpectrogramGenerator.cs
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Threading.Tasks;
using FftSharp;
namespace TestLibrary
{
/// <summary>
/// 스펙트로그램 제너레이터
/// </summary>
public class SpectrogramGenerator
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Public
#region Field
/// <summary>
/// 색상 맵
/// </summary>
public ColorMap Colormap = ColorMap.Viridis;
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 설정
/// </summary>
private readonly Setting Setting;
/// <summary>
/// FFT 배열 리스트
/// </summary>
private readonly List<double[]> FFTArrayList = new List<double[]>();
/// <summary>
/// 미처리 FFT 리스트
/// </summary>
private readonly List<double> UnprocessedFFTList;
/// <summary>
/// 롤 오프셋
/// </summary>
private int rollOffset = 0;
/// <summary>
/// 고정 너비
/// </summary>
private int fixedWidth = 0;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 너비 - Width
/// <summary>
/// 너비
/// </summary>
public int Width { get => FFTArrayList.Count; }
#endregion
#region 높이 - Height
/// <summary>
/// 높이
/// </summary>
public int Height { get => Setting.Height; }
#endregion
#region FFT 크기 - FFTSize
/// <summary>
/// FFT 크기
/// </summary>
public int FFTSize { get => Setting.FFTSize; }
#endregion
#region 픽셀당 HZ - HZPerPixel
/// <summary>
/// 픽셀당 HZ
/// </summary>
public double HZPerPixel { get => Setting.HZPerPixel; }
#endregion
#region 픽셀당 초 카운트 - SecondCountPerPixel
/// <summary>
/// 픽셀당 초 카운트
/// </summary>
public double SecondCountPerPixel { get => Setting.StepLength; }
#endregion
#region 처리할 FFT 카운트 - FFTCountToProcess
/// <summary>
/// 처리할 FFT 카운트
/// </summary>
public int FFTCountToProcess { get => (UnprocessedFFTList.Count - Setting.FFTSize) / Setting.StepSize; }
#endregion
#region 처리 완료 FFT 카운트 - FFTCountProcessed
/// <summary>
/// 처리 완료 FFT 카운트
/// </summary>
public int FFTCountProcessed { get; private set; }
#endregion
#region 다음 컬럼 인덱스 - NextColumnIndex
/// <summary>
/// 다음 컬럼 인덱스
/// </summary>
public int NextColumnIndex { get => (FFTCountProcessed + rollOffset) % Width; }
#endregion
#region HZ 오프셋 - HZOffset
/// <summary>
/// HZ 오프셋
/// </summary>
public int HZOffset
{
get => Setting.HZOffset;
set
{
Setting.HZOffset = value;
}
}
#endregion
#region 샘플 비율 - SampleRate
/// <summary>
/// 샘플 비율
/// </summary>
public int SampleRate { get => Setting.SampleRate; }
#endregion
#region 단계 크기 - StepSize
/// <summary>
/// 단계 크기
/// </summary>
public int StepSize { get => Setting.StepSize; }
#endregion
#region 최대 주파수 - MaximumFrequency
/// <summary>
/// 최대 주파수
/// </summary>
public double MaximumFrequency { get => Setting.MaximumFrequency; }
#endregion
#region 최소 주파수 - MinimumFrequency
/// <summary>
/// 최소 주파수
/// </summary>
public double MinimumFrequency { get => Setting.MinimumFrequency; }
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - SpectrogramGenerator(sampleRate, fftSize, stepSize, minimumFrequency, maximumFrequency, fixedWidth, hzOffset, initialAudioList)
/// <summary>
/// 생성자
/// </summary>
/// <param name="sampleRate">샘플 비율</param>
/// <param name="fftSize">FFT 크기</param>
/// <param name="stepSize">단계 크기</param>
/// <param name="minimumFrequency">최소 주파수</param>
/// <param name="maximumFrequency">최대 주파수</param>
/// <param name="fixedWidth">고정 너비</param>
/// <param name="hzOffset">HZ 오프셋</param>
/// <param name="initialAudioList">초기 오디오 리스트</param>
public SpectrogramGenerator
(
int sampleRate,
int fftSize,
int stepSize,
double minimumFrequency = 0,
double maximumFrequency = double.PositiveInfinity,
int? fixedWidth = null,
int hzOffset = 0,
List<double> initialAudioList = null
)
{
Setting = new Setting(sampleRate, fftSize, stepSize, minimumFrequency, maximumFrequency, hzOffset);
UnprocessedFFTList = initialAudioList ?? new List<double>();
if(fixedWidth.HasValue)
{
SetFixedWidth(fixedWidth.Value);
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 문자열 구하기 - ToString()
/// <summary>
/// 문자열 구하기
/// </summary>
/// <returns>문자열</returns>
public override string ToString()
{
double processedSampleCount = FFTArrayList.Count * Setting.StepSize + Setting.FFTSize;
double processedSecondCount = processedSampleCount / Setting.SampleRate;
string processedTime = (processedSecondCount < 60) ? $"{processedSecondCount:N2} 초" : $"{processedSecondCount / 60.0:N2} 분";
return $"스펙트로그램 ({Width}, {Height})" +
$"\n 수직 ({Height} 픽셀) : {Setting.MinimumFrequency:N0} - {Setting.MaximumFrequency:N0} HZ, " +
$"FFT 크기 : {Setting.FFTSize:N0} 샘플 수, {Setting.HZPerPixel:N2} HZ/픽셀" +
$"\n 수평 ({Width} 픽셀) : {processedTime}, 윈도우 : {Setting.FFTLength:N2} 초, " +
$"단계 : {Setting.StepLength:N2} 초, 오버랩 : {Setting.StepOverlapFraction * 100:N0}%";
}
#endregion
#region 윈도우 설정하기 - SetWindow(newWindowArray)
/// <summary>
/// 윈도우 설정하기
/// </summary>
/// <param name="newWindowArray">신규 윈도우 배열</param>
public void SetWindow(double[] newWindowArray)
{
if(newWindowArray.Length > Setting.FFTSize)
{
throw new ArgumentException("window length cannot exceed FFT size");
}
for(int i = 0; i < Setting.FFTSize; i++)
{
Setting.WindowArray[i] = 0;
}
int offset = (Setting.FFTSize - newWindowArray.Length) / 2;
Array.Copy(newWindowArray, 0, Setting.WindowArray, offset, newWindowArray.Length);
}
#endregion
#region 추가하기 - Add(audioEnumerable, process)
/// <summary>
/// 추가하기
/// </summary>
/// <param name="audioEnumerable">오디오 열거 가능형</param>
/// <param name="process">처리 여부</param>
public void Add(IEnumerable<double> audioEnumerable, bool process = true)
{
UnprocessedFFTList.AddRange(audioEnumerable);
if(process)
{
Process();
}
}
#endregion
#region 롤 리셋하기 - ResetRoll(offset)
/// <summary>
/// 롤 리셋하기
/// </summary>
/// <param name="offset">오프셋</param>
public void ResetRoll(int offset = 0)
{
this.rollOffset = -FFTCountProcessed + offset;
}
#endregion
#region 처리하기 - Process()
/// <summary>
/// 처리하기
/// </summary>
/// <returns>처리 결과</returns>
public double[][] Process()
{
if(FFTCountToProcess < 1)
{
return null;
}
int newFFTCount = FFTCountToProcess;
double[][] newFFTArray = new double[newFFTCount][];
Parallel.For
(
0,
newFFTCount,
newFFTIndex =>
{
Complex[] bufferArray = new Complex[Setting.FFTSize];
int sourceIndex = newFFTIndex * Setting.StepSize;
for(int i = 0; i < Setting.FFTSize; i++)
{
bufferArray[i].Real = UnprocessedFFTList[sourceIndex + i] * Setting.WindowArray[i];
}
Transform.FFT(bufferArray);
newFFTArray[newFFTIndex] = new double[Setting.Height];
for(int i = 0; i < Setting.Height; i++)
{
newFFTArray[newFFTIndex][i] = bufferArray[Setting.FFTIndex1 + i].Magnitude / Setting.FFTSize;
}
}
);
foreach(var newFFT in newFFTArray)
{
FFTArrayList.Add(newFFT);
}
FFTCountProcessed += newFFTArray.Length;
UnprocessedFFTList.RemoveRange(0, newFFTCount * Setting.StepSize);
PadOrTrimForFixedWidth();
return newFFTArray;
}
#endregion
#region MEL FFT 배열 리스트 구하기 - GetMELFFTArrayList(melBINCount)
/// <summary>
/// MEL FFT 배열 리스트 구하기
/// </summary>
/// <param name="melBINCount">MEL BIN 카운트</param>
/// <returns>MEL FFT 배열 리스트</returns>
public List<double[]> GetMELFFTArrayList(int melBINCount)
{
if(Setting.MinimumFrequency != 0)
{
throw new InvalidOperationException("cannot get Mel spectrogram unless minimum frequency is 0Hz");
}
List<double[]> melFFTArrayList = new List<double[]>();
foreach(double[] fftArray in FFTArrayList)
{
melFFTArrayList.Add(Transform.MelScale(fftArray, SampleRate, melBINCount));
}
return melFFTArrayList;
}
#endregion
#region 비트맵 구하기 - GetBitmap(intensity, db, dbScale, roll)
/// <summary>
/// 비트맵 구하기
/// </summary>
/// <param name="intensity">강도</param>
/// <param name="db">데시빌 여부</param>
/// <param name="dbScale">데시빌 스케일</param>
/// <param name="roll">롤 여부</param>
/// <returns>비트맵</returns>
public Bitmap GetBitmap(double intensity = 1, bool db = false, double dbScale = 1, bool roll = false) =>
BitmapHelper.GetBitmap(FFTArrayList, Colormap, intensity, db, dbScale, roll, NextColumnIndex);
#endregion
#region MEL 비트맵 구하기 - GetMELBitmap(melBINCount, intensity, db, dbScale, roll)
/// <summary>
/// MEL 비트맵 구하기
/// </summary>
/// <param name="melBINCount">MEL BIN 카운트</param>
/// <param name="intensity">강도</param>
/// <param name="db">데시빌 여부</param>
/// <param name="dbScale">데시빌 스케일</param>
/// <param name="roll">롤 여부</param>
/// <returns>MEL 비트맵</returns>
public Bitmap GetMELBitmap(int melBINCount = 25, double intensity = 1, bool db = false, double dbScale = 1, bool roll = false) =>
BitmapHelper.GetBitmap(GetMELFFTArrayList(melBINCount), Colormap, intensity, db, dbScale, roll, NextColumnIndex);
#endregion
#region 이미지 저장하기 - SaveImage(filePath, intensity, db, dbScale, roll)
/// <summary>
/// 이미지 저장하기
/// </summary>
/// <param name="filePath">파일 경로</param>
/// <param name="intensity">강도</param>
/// <param name="db">데시빌 여부</param>
/// <param name="dbScale">데시빌 스케일</param>
/// <param name="roll">롤 여부</param>
public void SaveImage(string filePath, double intensity = 1, bool db = false, double dbScale = 1, bool roll = false)
{
if(FFTArrayList.Count == 0)
{
throw new InvalidOperationException("Spectrogram contains no data. Use Add() to add signal data.");
}
string extension = Path.GetExtension(filePath).ToLower();
ImageFormat imageFormat;
if(extension == ".bmp")
{
imageFormat = ImageFormat.Bmp;
}
else if(extension == ".png")
{
imageFormat = ImageFormat.Png;
}
else if(extension == ".jpg" || extension == ".jpeg")
{
imageFormat = ImageFormat.Jpeg;
}
else if(extension == ".gif")
{
imageFormat = ImageFormat.Gif;
}
else
{
throw new ArgumentException("unknown file extension");
}
BitmapHelper.GetBitmap(FFTArrayList, Colormap, intensity, db, dbScale, roll, NextColumnIndex).Save(filePath, imageFormat);
}
#endregion
#region 최대 비트맵 구하기 - GetMaximumBitmap(intensity, db, dbScale, roll, reduction)
/// <summary>
/// 최대 비트맵 구하기
/// </summary>
/// <param name="intensity">강도</param>
/// <param name="db">데시빌 여부</param>
/// <param name="dbScale">데시빌 스케일</param>
/// <param name="roll">롤 여부</param>
/// <param name="reduction">축소</param>
/// <returns>최대 비트맵</returns>
public Bitmap GetMaximumBitmap(double intensity = 1, bool db = false, double dbScale = 1, bool roll = false, int reduction = 4)
{
List<double[]> fftArrayList = new List<double[]>();
for(int i = 0; i < FFTArrayList.Count; i++)
{
double[] valueArray1 = FFTArrayList[i];
double[] valueArray2 = new double[valueArray1.Length / reduction];
for(int j = 0; j < valueArray2.Length; j++)
{
for(int k = 0; k < reduction; k++)
{
valueArray2[j] = Math.Max(valueArray2[j], valueArray1[j * reduction + k]);
}
}
fftArrayList.Add(valueArray2);
}
return BitmapHelper.GetBitmap(fftArrayList, Colormap, intensity, db, dbScale, roll, NextColumnIndex);
}
#endregion
#region 데이터 저장하기 - SaveData(filePath, melBINCount)
/// <summary>
/// 데이터 저장하기
/// </summary>
/// <param name="filePath">파일 경로</param>
/// <param name="melBINCount">MEL BIN 카운트</param>
public void SaveData(string filePath, int melBINCount = 0)
{
if(!filePath.EndsWith(".sff", StringComparison.OrdinalIgnoreCase))
{
filePath += ".sff";
}
new SpectrogramFileFormat(this, melBINCount).Save(filePath);
}
#endregion
#region 고정 너비 설정하기 - SetFixedWidth(width)
/// <summary>
/// 고정 너비 설정하기
/// </summary>
/// <param name="width">너비</param>
public void SetFixedWidth(int width)
{
this.fixedWidth = width;
PadOrTrimForFixedWidth();
}
#endregion
#region 수직 스케일 비트맵 구하기 - GetVerticalScaleBitmap(width, hzOffset, tickSize, reduction)
/// <summary>
/// 수직 스케일 비트맵 구하기
/// </summary>
/// <param name="width">너비</param>
/// <param name="hzOffset">HZ 오프셋</param>
/// <param name="tickSize">틱 크기</param>
/// <param name="reduction">축소</param>
/// <returns>수직 스케일 비트맵</returns>
public Bitmap GetVerticalScaleBitmap(int width, int hzOffset = 0, int tickSize = 3, int reduction = 1)
{
return BitmapHelper.GetVerticalScaleBitmap(width, Setting, hzOffset, tickSize, reduction);
}
#endregion
#region 픽셀 Y 구하기 - GetPixelY(frequency, reduction)
/// <summary>
/// 픽셀 Y 구하기
/// </summary>
/// <param name="frequency">주파수</param>
/// <param name="reduction">축소</param>
/// <returns>픽셀 Y</returns>
public int GetPixelY(double frequency, int reduction = 1)
{
int pixelCountFromZeroHZ = (int)(Setting.PixelPerHZ * frequency / reduction);
int pixelCountFromMinimumFrequency = pixelCountFromZeroHZ - Setting.FFTIndex1 / reduction + 1;
int pixelRow = Setting.Height / reduction - 1 - pixelCountFromMinimumFrequency;
return pixelRow - 1;
}
#endregion
#region FFT 배열 리스트 구하기 - GetFFTArrayList()
/// <summary>
/// FFT 배열 리스트 구하기
/// </summary>
/// <returns>FFT 배열 리스트</returns>
public List<double[]> GetFFTArrayList()
{
return FFTArrayList;
}
#endregion
#region 피크 구하기 - GetPeak(latestFFT)
/// <summary>
/// 피크 구하기
/// </summary>
/// <param name="latestFFT">최신 FFT</param>
/// <returns>(주파수 (단위 : HZ), 진도) 튜플</returns>
public (double frequencyHZ, double magnitude) GetPeak(bool latestFFT = true)
{
if(FFTArrayList.Count == 0)
{
return (double.NaN, double.NaN);
}
if(latestFFT == false)
{
throw new NotImplementedException("peak of mean of all FFTs not yet supported");
}
double[] frequencyList = FFTArrayList[FFTArrayList.Count - 1];
int peakIndex = 0;
double peakMagnitude = 0;
for(int i = 0; i < frequencyList.Length; i++)
{
if(frequencyList[i] > peakMagnitude)
{
peakMagnitude = frequencyList[i];
peakIndex = i;
}
}
double maximumFrequency = SampleRate / 2;
double peakFrequencyFraction = peakIndex / (double)frequencyList.Length;
double peakFrequencyHZ = maximumFrequency * peakFrequencyFraction;
return (peakFrequencyHZ, peakMagnitude);
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 고정 너비용 제거 또는 채우기 - PadOrTrimForFixedWidth()
/// <summary>
/// 고정 너비용 제거 또는 채우기
/// </summary>
private void PadOrTrimForFixedWidth()
{
if(fixedWidth > 0)
{
int overhang = Width - fixedWidth;
if(overhang > 0)
{
FFTArrayList.RemoveRange(0, overhang);
}
while(FFTArrayList.Count < fixedWidth)
{
FFTArrayList.Insert(0, new double[Height]);
}
}
}
#endregion
}
}
▶ Tool.cs
using System;
using System.Linq;
namespace TestLibrary
{
/// <summary>
/// 도구
/// </summary>
public static class Tool
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 평균 FFT 배열 구하기 - GetMeanFFTArray(sff, db)
/// <summary>
/// 평균 FFT 배열 구하기
/// </summary>
/// <param name="sff">스펙트로그램 파일 포맷</param>
/// <param name="db">데시빌 여부</param>
/// <returns>평균 FFT 배열</returns>
public static double[] GetMeanFFTArray(SpectrogramFileFormat sff, bool db = false)
{
double[] meanArray = new double[sff.FFTList[0].Length];
foreach(double[] fftArray in sff.FFTList)
{
for(int y = 0; y < fftArray.Length; y++)
{
meanArray[y] += fftArray[y];
}
}
for(int i = 0; i < meanArray.Length; i++)
{
meanArray[i] /= sff.FFTList.Count();
}
if(db)
{
for(int i = 0; i < meanArray.Length; i++)
{
meanArray[i] = 20 * Math.Log10(meanArray[i]);
}
}
if(meanArray[meanArray.Length - 1] <= 0)
{
meanArray[meanArray.Length - 1] = meanArray[meanArray.Length - 2];
}
return meanArray;
}
#endregion
#region 평균 전력 배열 구하기 - GetMeanPower(sff, db)
/// <summary>
/// 평균 전력 배열 구하기
/// </summary>
/// <param name="sff">스펙트로그램 파일 포맷</param>
/// <param name="db">데시빌 여부</param>
/// <returns>평균 전력 배열</returns>
public static double[] GetMeanPower(SpectrogramFileFormat sff, bool db = false)
{
double[] powerArray = new double[sff.FFTList.Count];
for(int i = 0; i < sff.FFTList.Count; i++)
{
powerArray[i] = (double)sff.FFTList[i].Sum() / sff.FFTList[i].Length;
}
if(db)
{
for(int i = 0; i < powerArray.Length; i++)
{
powerArray[i] = 20 * Math.Log10(powerArray[i]);
}
}
return powerArray;
}
#endregion
#region 정점 주파수 구하기 - GetPeakFrequency(sff, firstFFTOnly)
/// <summary>
/// 정점 주파수 구하기
/// </summary>
/// <param name="sff">스펙트로그램 파일 포맷</param>
/// <param name="firstFFTOnly">첫번째 FFT만 여부</param>
/// <returns>정점 주파수</returns>
public static double GetPeakFrequency(SpectrogramFileFormat sff, bool firstFFTOnly = false)
{
double[] frequencyArray = firstFFTOnly ? sff.FFTList[0] : GetMeanFFTArray(sff, false);
int peakIndex = 0;
double peakPower = 0;
for(int i = 0; i < frequencyArray.Length; i++)
{
if(frequencyArray[i] > peakPower)
{
peakPower = frequencyArray[i];
peakIndex = i;
}
}
double maximumFrequency = sff.SampleRate / 2;
double fraction = peakIndex / (double)sff.ImageHeight;
if(sff.MELBINCount > 0)
{
double maximumMEL = FftSharp.Transform.MelFromFreq(maximumFrequency);
return FftSharp.Transform.MelToFreq(fraction * maximumMEL);
}
else
{
return fraction * maximumFrequency;
}
}
#endregion
#region 피아노 키 구하기 - GetPianoKey(frequency)
/// <summary>
/// 피아노 키 구하기
/// </summary>
/// <param name="frequency">주파수 (단위 : HZ)</param>
/// <returns>피아노 키</returns>
public static int GetPianoKey(double frequency)
{
double pianoKey = (39.86 * Math.Log10(frequency / 440)) + 49;
return (int)Math.Round(pianoKey);
}
#endregion
#region 미디 노트 구하기 - GetMIDINote(frequency)
/// <summary>
/// 미디 노트 구하기
/// </summary>
/// <param name="frequency">주파수 (단위 : HZ)</param>
/// <returns>미디 노트</returns>
public static int GetMIDINote(double frequency)
{
return GetPianoKey(frequency) + 20;
}
#endregion
}
}
▶ Listener.cs
using System;
using System.Collections.Generic;
using NAudio.Wave;
namespace TestProject
{
/// <summary>
/// 리스너
/// </summary>
public class Listener : IDisposable
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Public
#region Field
/// <summary>
/// 샘플 비율
/// </summary>
public readonly int SampleRate;
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 웨이브 입력 이벤트
/// </summary>
private readonly WaveInEvent waveInEvent;
/// <summary>
/// 오디오 리스트
/// </summary>
private readonly List<double> audioList = new List<double>();
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 진폭 분수 - AmplitudeFraction
/// <summary>
/// 진폭 분수
/// </summary>
public double AmplitudeFraction { get; private set; }
#endregion
#region 전체 샘플 카운트 - TotalSampleCount
/// <summary>
/// 전체 샘플 카운트
/// </summary>
public double TotalSampleCount { get; private set; }
#endregion
#region 전체 시간 (단위 : 초) - TotalTime
/// <summary>
/// 전체 시간 (단위 : 초)
/// </summary>
public double TotalTime
{
get
{
return (double)TotalSampleCount / SampleRate;
}
}
#endregion
#region 메모리 내 샘플 카운트 - SampleCountInMemory
/// <summary>
/// 메모리 내 샘플 카운트
/// </summary>
public int SampleCountInMemory
{
get
{
return this.audioList.Count;
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - Listener(deviceIndex, sampleRate)
/// <summary>
/// 생성자
/// </summary>
/// <param name="deviceIndex">장치 인덱스</param>
/// <param name="sampleRate">샘플 비율</param>
public Listener(int deviceIndex, int sampleRate)
{
SampleRate = sampleRate;
this.waveInEvent = new WaveInEvent
{
DeviceNumber = deviceIndex,
WaveFormat = new WaveFormat(sampleRate, bits : 16, channels : 1),
BufferMilliseconds = 20
};
this.waveInEvent.DataAvailable += waveInEvent_DataAvailable;
this.waveInEvent.StartRecording();
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 신규 오디오 배열 구하기 - GetNewAudioArray()
/// <summary>
/// 신규 오디오 배열 구하기
/// </summary>
/// <returns>신규 오디오 배열</returns>
public double[] GetNewAudioArray()
{
lock(this.audioList)
{
double[] valueArray = new double[this.audioList.Count];
for(int i = 0; i < valueArray.Length; i++)
{
valueArray[i] = this.audioList[i];
}
this.audioList.RemoveRange(0, valueArray.Length);
return valueArray;
}
}
#endregion
#region 리소스 해제하기 - Dispose()
/// <summary>
/// 리소스 해제하기
/// </summary>
public void Dispose()
{
this.waveInEvent?.StopRecording();
this.waveInEvent?.Dispose();
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 웨이브 입력 이벤트 데이터 이용 가능시 처리하기 - waveInEvent_DataAvailable(sender, e)
/// <summary>
/// 웨이브 입력 이벤트 데이터 이용 가능시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void waveInEvent_DataAvailable(object sender, WaveInEventArgs e)
{
int byteCountPerSample = this.waveInEvent.WaveFormat.BitsPerSample / 8;
int newSampleCount = e.BytesRecorded / byteCountPerSample;
double[] bufferArray = new double[newSampleCount];
double peak = 0d;
for(int i = 0; i < newSampleCount; i++)
{
bufferArray[i] = BitConverter.ToInt16(e.Buffer, i * byteCountPerSample);
peak = Math.Max(peak, bufferArray[i]);
}
lock(this.audioList)
{
this.audioList.AddRange(bufferArray);
}
AmplitudeFraction = peak / (1 << 15);
TotalSampleCount += newSampleCount;
}
#endregion
}
}
▶ MainForm.cs
using System;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using NAudio.Wave;
using TestLibrary;
namespace TestProject
{
/// <summary>
/// 메인 폼
/// </summary>
public partial class MainForm : Form
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 스펙트로그램 제너레이터
/// </summary>
private SpectrogramGenerator generator;
/// <summary>
/// 리스너
/// </summary>
private Listener listener;
/// <summary>
/// 색상 맵 배열
/// </summary>
private ColorMap[] colorMapArray;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MainForm()
/// <summary>
/// 생성자
/// </summary>
public MainForm()
{
InitializeComponent();
if(WaveIn.DeviceCount == 0)
{
MessageBox.Show
(
"No audio input devices found.\n\nThis program will now exit.",
"ERROR",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
Close();
}
this.deviceComboBox.Items.Clear();
for(int i = 0; i < WaveIn.DeviceCount; i++)
{
this.deviceComboBox.Items.Add(WaveIn.GetCapabilities(i).ProductName);
}
for(int i = 9; i < 16; i++)
{
this.fftSizeComboBox.Items.Add($"2^{i} ({1 << i:N0})");
}
this.colorMapArray = ColorMap.GetColorMapArray();
foreach(ColorMap colorMap in this.colorMapArray)
{
this.colorMapComboBox.Items.Add(colorMap.Name);
}
this.deviceComboBox.SelectedIndexChanged += deviceComboBox_SelectedIndexChanged;
this.fftSizeComboBox.SelectedIndexChanged += fftSizeComboBox_SelectedIndexChanged;
this.colorMapComboBox.SelectedIndexChanged += colorMapComboBox_SelectedIndexChanged;
this.rollCheckBox.CheckedChanged += rollCheckBox_CheckedChanged;
this.resetRollButton.Click += resetRollButton_Click;
this.timer.Tick += timer_Tick;
this.deviceComboBox.SelectedIndex = 0;
this.fftSizeComboBox.SelectedIndex = 1;
this.colorMapComboBox.SelectedIndex = this.colorMapComboBox.Items.IndexOf("LoporaColorMap");
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Private
//////////////////////////////////////////////////////////////////////////////// Event
#region 장치 콤보 박스 선택 인덱스 변경시 처리하기 - deviceComboBox_SelectedIndexChanged(sender, e)
/// <summary>
/// 장치 콤보 박스 선택 인덱스 변경시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void deviceComboBox_SelectedIndexChanged(object sender, EventArgs e) => StartListening();
#endregion
#region FFT 크기 콤보 박스 선택 인덱스 변경시 처리하기 - fftSizeComboBox_SelectedIndexChanged(sender, e)
/// <summary>
/// FFT 크기 콤보 박스 선택 인덱스 변경시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void fftSizeComboBox_SelectedIndexChanged(object sender, EventArgs e) => StartListening();
#endregion
#region 색상 맵 콤보 박스 선택 인덱스 변경시 처리하기 - colorMapComboBox_SelectedIndexChanged(sender, e)
/// <summary>
/// 색상 맵 콤보 박스 선택 인덱스 변경시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void colorMapComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
this.generator.Colormap = this.colorMapArray[this.colorMapComboBox.SelectedIndex];
}
#endregion
#region Roll 체크 박스 체크 변경시 처리하기 - rollCheckBox_CheckedChanged(sender, e)
/// <summary>
/// Roll 체크 박스 체크 변경시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void rollCheckBox_CheckedChanged(object sender, EventArgs e)
{
this.resetRollButton.Enabled = this.rollCheckBox.Checked;
}
#endregion
#region Roll 리셋하기 버튼 클릭시 처리하기 - resetRollButton_Click(sender, e)
/// <summary>
/// Roll 리셋하기 버튼 클릭시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void resetRollButton_Click(object sender, EventArgs e)
{
this.generator.ResetRoll();
}
#endregion
#region 타이머 틱 처리하기 - timer_Tick(sender, e)
/// <summary>
/// 타이머 틱 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void timer_Tick(object sender, EventArgs e)
{
double[] newAudioArray = this.listener.GetNewAudioArray();
this.generator.Add(newAudioArray, process : false);
double multiplier = this.brightnessTrackBar.Value / 20d;
if(this.generator.FFTCountToProcess > 0)
{
Stopwatch stopWatch = Stopwatch.StartNew();
this.generator.Process();
this.generator.SetFixedWidth(spectorgramPictureBox.Width);
Bitmap spectrogramBitmap = new Bitmap(this.generator.Width, this.generator.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using(Bitmap indexedSpectrogramBitmap = this.generator.GetBitmap(multiplier, this.dbCheckBox.Checked, roll : this.rollCheckBox.Checked))
using(Graphics graphics = Graphics.FromImage(spectrogramBitmap))
using(Pen pen = new Pen(Color.White))
{
graphics.DrawImage(indexedSpectrogramBitmap, 0, 0);
if(this.rollCheckBox.Checked)
{
graphics.DrawLine(pen, this.generator.NextColumnIndex, 0, this.generator.NextColumnIndex, this.spectorgramPictureBox.Height);
}
}
stopWatch.Stop();
this.spectorgramPictureBox.Image?.Dispose();
this.spectorgramPictureBox.Image = spectrogramBitmap;
this.renderTimeStatusLabel.Text = $"렌러딩 시간 : {stopWatch.ElapsedMilliseconds:D2} 밀리초";
this.peakFrequencyStatusLabel.Text = $"피크 (Hz): {this.generator.GetPeak().frequencyHZ:N0}";
}
this.timeStatusLabel.Text = $"시간 : {this.listener.TotalTime:N3} 초";
this.fftCountProcessedStatusLabel.Text = $"처리 FFT 수 : {this.generator.FFTCountProcessed:N0}";
this.amplitudeProgressBar.Value = (int)(this.listener.AmplitudeFraction * this.amplitudeProgressBar.Maximum);
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Function
#region 청취 시작하기 - StartListening()
/// <summary>
/// 청취 시작하기
/// </summary>
private void StartListening()
{
int sampleRate = 6000;
int fftSize = 1 << (9 + this.fftSizeComboBox.SelectedIndex);
int stepSize = fftSize / 20;
this.spectorgramPictureBox.Image?.Dispose();
this.spectorgramPictureBox.Image = null;
this.listener?.Dispose();
this.listener = new Listener(this.deviceComboBox.SelectedIndex, sampleRate);
this.generator = new SpectrogramGenerator(sampleRate, fftSize, stepSize);
this.spectorgramPictureBox.Height = this.generator.Height;
this.verticalScalePictureBox.Image?.Dispose();
this.verticalScalePictureBox.Image = this.generator.GetVerticalScaleBitmap(this.verticalScalePictureBox.Width);
this.verticalScalePictureBox.Height = this.generator.Height;
}
#endregion
}
}
728x90
반응형
그리드형(광고전용)
'C# > WinForm' 카테고리의 다른 글
[C#/WINFORM/.NET5] WasapiLoopbackCapture 클래스 : 사운드 카드 출력을 WAV 파일로 레코딩하기 (0) | 2021.12.28 |
---|---|
[C#/WINFORM/.NET5] WaveInEvent 클래스 : WAV 파일 레코딩하기 (0) | 2021.12.28 |
[C#/WINFORM/.NET5] WaveOutEvent 클래스 : Volume 속성을 사용해 볼륨 설정하기 (0) | 2021.12.26 |
[C#/WINFORM/.NET5] WaveOutEvent 클래스 : 오디오 파일 재생하기 (기능 개선) (0) | 2021.12.26 |
[C#/WINFORM/.NET5] WaveOutEvent 클래스 : 오디오 파일 재생하기 (0) | 2021.12.26 |
[C#/WINFORM] 사운드 필터링 사용하기 (0) | 2021.12.09 |
[C#/WINFORM] NativeWindow 클래스 : 마우스 캡처 변경시 이벤트 전달하기 (0) | 2021.12.06 |
[C#/WINFORM] 마우스 히트 테스트(Hit Test) 사용하기 (0) | 2021.12.06 |
[C#/WINFORM] 특정 영역에서 마우스 커서 설정하기 (0) | 2021.12.06 |
[C#/WINFORM] 마우스 드래그 대상 이미지 표시하기 (0) | 2021.12.06 |
댓글을 달아 주세요