4 ** Copyright (C) 1994,95,96 Bernardo Innocenti
6 ** Sample editor handling functions.
9 #include <intuition/intuition.h>
10 #include <intuition/gadgetclass.h>
11 #include <libraries/gadtools.h>
12 #include <graphics/rpattr.h>
13 #include <graphics/gfxmacros.h>
15 #include <clib/exec_protos.h>
16 #include <clib/intuition_protos.h>
17 #include <clib/graphics_protos.h>
18 #include <clib/gadtools_protos.h>
19 #include <clib/xmodule_protos.h>
21 #include <pragmas/exec_sysbase_pragmas.h>
22 #include <pragmas/intuition_pragmas.h>
23 #include <pragmas/gadtools_pragmas.h>
24 #include <pragmas/graphics_pragmas.h>
25 #include <pragmas/xmodule_pragmas.h>
27 #include "XModulePriv.h"
62 /* Sample rendering modes */
71 /* Local functions prototypes */
73 static void SampleRender (struct WinUserData *wud);
74 static LONG SampBoxSetup (struct Gadget *g);
75 static void HandleSampleIDCMP (void);
76 static void DrawRange (void);
77 static void UpdateRange (WORD newend);
78 static void UpdateRangeInfo (void);
80 static void DrawPixelGraph (struct RastPort *rp, BYTE *samp,
81 UWORD xmin, UWORD xmax, UWORD ycoord, UWORD height, UWORD step);
82 static void DrawLineGraph (struct RastPort *rp, BYTE *samp,
83 UWORD xmin, UWORD xmax, UWORD ycoord, UWORD height, UWORD step);
84 static void DrawFilledGraph (struct RastPort *rp, BYTE *samp,
85 UWORD xmin, UWORD xmax, UWORD ycoord, UWORD height, UWORD step);
87 static void SampScrollClicked (void);
88 static void SampZoomInClicked (void);
89 static void SampRangeAllClicked (void);
90 static void SampShowRangeClicked (void);
91 static void SampZoomOutClicked (void);
92 static void SampClearRangeClicked (void);
93 static void SampShowAllClicked (void);
94 static void SampPlayRClicked (void);
95 static void SampPlayDClicked (void);
96 static void SampPlayLClicked (void);
97 static void SampPlayAClicked (void);
98 static void DisplayStartClicked (void);
99 static void DisplayEndClicked (void);
100 static void DisplayLenClicked (void);
101 static void RangeStartClicked (void);
102 static void RangeEndClicked (void);
103 static void RangeLenClicked (void);
104 static void RepStartClicked (void);
105 static void RepLenClicked (void);
106 static void RepEndClicked (void);
107 static void SampBoxClicked (void);
109 static void SampleMiCut (void);
110 static void SampleMiCopy (void);
111 static void SampleMiPaste (void);
112 static void SampleMiPoints (void);
113 static void SampleMiLines (void);
114 static void SampleMiFilled (void);
117 static struct IBox SampBox; /* Sample Box Coordinates */
118 static WORD RangeStartX, RangeEndX, RangePole;
119 static WORD LoopPole1, LoopPole2;
120 static LONG DisplayStart, DisplayEnd;
124 static struct Gadget *SampleGadgets[Sample_CNT];
126 static struct NewMenu SampleNewMenu[] = {
127 NM_TITLE, (STRPTR)MSG_EDIT_MEN, NULL, 0, NULL, NULL,
128 NM_ITEM, (STRPTR)MSG_CUT_MEN, (STRPTR)"X", 0, 0L, (APTR)SampleMiCut,
129 NM_ITEM, (STRPTR)MSG_COPY_MEN, (STRPTR)"C", 0, 0L, (APTR)SampleMiCopy,
130 NM_ITEM, (STRPTR)MSG_PASTE_MEN, (STRPTR)"V", 0, 0L, (APTR)SampleMiPaste,
131 NM_TITLE, (STRPTR)MSG_RENDER_MEN, NULL, 0, NULL, NULL,
132 NM_ITEM, (STRPTR)MSG_POINTS_MEN, NULL, CHECKIT, 6L, (APTR)SampleMiPoints,
133 NM_ITEM, (STRPTR)MSG_LINES_MEN, NULL, CHECKIT|CHECKED, 5L, (APTR)SampleMiLines,
134 NM_ITEM, (STRPTR)MSG_FILLED_MEN, NULL, CHECKIT, 3L, (APTR)SampleMiFilled,
135 NM_END, NULL, NULL, 0, 0L, NULL
138 static UWORD SampleGTypes[] = {
163 struct NewGadget SampleNGad[] = {
164 4, 154, 623, 8, NULL, NULL, GD_SampScroll, 0, NULL, (APTR)SampScrollClicked,
165 4, 1, 105, 12, (UBYTE *)"Zoom _In", NULL, GD_SampZoomIn, PLACETEXT_IN, NULL, (APTR)SampZoomInClicked,
166 112, 1, 105, 12, (UBYTE *)"Range _All", NULL, GD_SampRangeAll, PLACETEXT_IN, NULL, (APTR)SampRangeAllClicked,
167 112, 27, 105, 12, (UBYTE *)"Show Range", NULL, GD_SampShowRange, PLACETEXT_IN, NULL, (APTR)SampShowRangeClicked,
168 4, 14, 105, 12, (UBYTE *)"Zoom _Out", NULL, GD_SampZoomOut, PLACETEXT_IN, NULL, (APTR)SampZoomOutClicked,
169 112, 14, 105, 12, (UBYTE *)"_Clear Range", NULL, GD_SampClearRange, PLACETEXT_IN, NULL, (APTR)SampClearRangeClicked,
170 4, 27, 105, 12, (UBYTE *)"Show All", NULL, GD_SampShowAll, PLACETEXT_IN, NULL, (APTR)SampShowAllClicked,
171 112, 40, 105, 12, (UBYTE *)"Sample Size", NULL, GD_SampSize, PLACETEXT_LEFT, NULL, NULL,
172 220, 27, 105, 12, (UBYTE *)"Play Range", NULL, GD_SampPlayR, PLACETEXT_IN, NULL, (APTR)SampPlayRClicked,
173 220, 1, 105, 12, (UBYTE *)"Play Display", NULL, GD_SampPlayD, PLACETEXT_IN, NULL, (APTR)SampPlayDClicked,
174 220, 40, 105, 12, (UBYTE *)"Loop Play", NULL, GD_SampPlayL, PLACETEXT_IN, NULL, (APTR)SampPlayLClicked,
175 220, 14, 105, 12, (UBYTE *)"Play All", NULL, GD_SampPlayA, PLACETEXT_IN, NULL, (APTR)SampPlayAClicked,
176 414, 11, 69, 13, (UBYTE *)"_Display", NULL, GD_DisplayStart, PLACETEXT_LEFT, NULL, (APTR)DisplayStartClicked,
177 486, 11, 69, 13, NULL, NULL, GD_DisplayEnd, 0, NULL, (APTR)DisplayEndClicked,
178 558, 11, 69, 13, NULL, NULL, GD_DisplayLen, 0, NULL, (APTR)DisplayLenClicked,
179 414, 25, 69, 13, (UBYTE *)"_Range", NULL, GD_RangeStart, PLACETEXT_LEFT, NULL, (APTR)RangeStartClicked,
180 486, 25, 69, 13, NULL, NULL, GD_RangeEnd, 0, NULL, (APTR)RangeEndClicked,
181 558, 25, 69, 13, NULL, NULL, GD_RangeLen, 0, NULL, (APTR)RangeLenClicked,
182 414, 39, 69, 13, (UBYTE *)"R_epeat", NULL, GD_RepStart, PLACETEXT_LEFT, NULL, (APTR)RepStartClicked,
183 486, 39, 69, 13, NULL, NULL, GD_RepEnd, 0, NULL, (APTR)RepEndClicked,
184 558, 39, 69, 13, NULL, NULL, GD_RepLen, 0, NULL, (APTR)RepLenClicked,
185 0, 0, 0, 0, NULL, NULL, GD_SampBox, 0, NULL, (APTR)SampBoxClicked
188 static static ULONG SampleGTags[] = {
189 PGA_Freedom, LORIENT_HORIZ, GA_RelVerify, TRUE, TAG_DONE,
196 GTNM_Border, TRUE, TAG_DONE,
201 GTIN_MaxChars, 6, TAG_DONE,
202 GTIN_MaxChars, 6, TAG_DONE,
203 GTIN_MaxChars, 6, TAG_DONE,
204 GTIN_MaxChars, 6, TAG_DONE,
205 GTIN_MaxChars, 6, TAG_DONE,
206 GTIN_MaxChars, 6, TAG_DONE,
207 GTIN_MaxChars, 6, TAG_DONE,
208 GTIN_MaxChars, 6, TAG_DONE,
209 GTIN_MaxChars, 6, TAG_DONE,
210 XMGAD_SetupFunc, (ULONG)SampBoxSetup, TAG_DONE
214 static struct IntuiText SampleIText[] = {
215 1, 0, JAM1,445, 5, NULL, (UBYTE *)"Start", NULL,
216 1, 0, JAM1,516, 5, NULL, (UBYTE *)"End", NULL,
217 1, 0, JAM1,589, 5, NULL, (UBYTE *)"Length", NULL };
219 #define Sample_TNUM 3
223 struct WinUserData SampleWUD =
242 WFLG_DRAGBAR|WFLG_DEPTHGADGET|WFLG_CLOSEGADGET|WFLG_ACTIVATE,
243 SCROLLERIDCMP|ARROWIDCMP|BUTTONIDCMP|INTEGERIDCMP|IDCMP_MENUPICK|IDCMP_CLOSEWINDOW|IDCMP_REFRESHWINDOW|IDCMP_MOUSEMOVE,
244 (STRPTR)MSG_SAMPLE_TITLE,
252 static void SampleRender (struct WinUserData *wud)
254 /* DrawBevelBox (wud->Win->RPort, SampBox.Left - 2, SampBox.Top - 1,
255 SampBox.Width + 4, SampBox.Height + 2,
256 GT_VisualInfo, VisualInfo,
260 RenderWindowTexts (wud, SampleIText, Sample_TNUM);
268 GLOBALCALL LONG OpenSampleWindow (void)
273 DisplayStart = RangeStartX = RangeEndX = 0;
275 if (si = xmLockActiveSong (SM_SHARED))
277 DisplayEnd = si->Instr[si->CurrentInst]->Length - 1;
278 ReleaseSemaphore (&si->Lock);
283 if (DisplayEnd < 0) DisplayEnd = 0;
284 LoopPole1 = LoopPole2 = 0;
286 win = MyOpenWindow (&SampleWUD);
295 GLOBALCALL void CloseSampleWindow (void)
297 MyCloseWindow (SampleWUD.Win);
302 static LONG SampBoxSetup (struct Gadget *g)
305 SampBox.Left = OffX; // + ComputeX (&SampleWUD, 4) + 2;
306 SampBox.Top = OffY; // + ComputeY (&SampleWUD, 53) + 1;
307 SampBox.Width = 500;//ComputeX (&SampleWUD, 622) - 4;
308 SampBox.Height = 300;//ComputeY (&SampleWUD, 100) - 2;
310 g->LeftEdge = SampBox.Left;
311 g->TopEdge = SampBox.Top;
312 g->Width = SampBox.Width;
313 g->Height = SampBox.Height;
315 g->Flags |= GFLG_GADGHNONE;
316 g->Activation |= GACT_IMMEDIATE | GACT_FOLLOWMOUSE | GACT_RELVERIFY;
317 g->GadgetType |= GTYP_BOOLGADGET; /* Preserve GadTools special flags */
324 static void HandleSampleIDCMP (void)
326 WORD mousex; //, mousey;
328 if (IntuiMsg.Class != IDCMP_MOUSEMOVE)
331 mousex = IntuiMsg.MouseX - SampBox.Left;
332 // mousey = IntuiMsg.MouseY - SampBox.Top;
334 /* Clip mouse position */
335 if (mousex < 0) mousex = 0;
336 // if (mousey < 0) mousey = 0;
337 if (mousex >= SampBox.Width) mousex = SampBox.Width - 1;
338 // if (mousey >= SampBox.Height) mousey = SampBox.Height - 1;
340 if (mousex == RangeEndX) /* Do not redraw until mouse position has changed */
343 UpdateRange (mousex);
347 /********************/
348 /* Sample Functions */
349 /********************/
351 static void DrawPixelGraph (struct RastPort *rp, BYTE *samp,
352 UWORD xmin, UWORD xmax, UWORD ycoord, UWORD height, UWORD step)
357 for (i = 0, x = xmin ; x < xmax ; i += step, x++)
358 WritePixel (rp, x, (samp[i]*height) >> 8 + ycoord);
363 static void DrawLineGraph (struct RastPort *rp, BYTE *samp,
364 UWORD xmin, UWORD xmax, UWORD ycoord, UWORD height, UWORD step)
369 Move (rp, xmin, ycoord);
371 for (i = 0, x = xmin ; x < xmax ; i += step, x++)
372 Draw (rp, x, (samp[i]*height)/256 + ycoord);
377 static void DrawFilledGraph (struct RastPort *rp, BYTE *samp,
378 UWORD xmin, UWORD xmax, UWORD ycoord, UWORD height, UWORD step)
383 for (i = 0, x = xmin ; x < xmax ; i += step, x++)
385 Move (rp, x, ycoord);
386 Draw (rp, x, (samp[i]*height)/256 + ycoord);
392 GLOBALCALL void UpdateSample (void)
394 /* You call this function when the selected instrument has changed. */
400 if (si = xmLockActiveSong (SM_SHARED))
402 DisplayStart = RangeStartX = RangeEndX = DisplayEnd = 0;
405 DisplayEnd = si->Instr[si->CurrentInst]->Length - 1;
407 if (DisplayEnd < 0) DisplayEnd = 0;
412 ReleaseSemaphore (&si->Lock);
419 GLOBALCALL void UpdateSampleMenu (void)
423 struct Menu *menu = SampleWUD.Win->MenuStrip;
424 struct MenuItem *item = menu->NextMenu->FirstItem;
426 ClearMenuStrip (SampleWUD.Win);
429 /* Clear checkmarks */
431 item->Flags &= ~CHECKED;
432 item->NextItem->Flags &= ~CHECKED;
433 item->NextItem->NextItem->Flags &= ~CHECKED;
438 switch (GuiSwitches.SampDrawMode)
441 item->Flags |= CHECKED;
445 item->NextItem->Flags |= CHECKED;
449 item->NextItem->NextItem->Flags |= CHECKED;
453 ResetMenuStrip (SampleWUD.Win, menu);
459 GLOBALCALL void UpdateSampInfo (void)
463 if (!SampleWUD.Win) return;
465 if (si = xmLockActiveSong (SM_SHARED))
467 struct Instrument *instr = si->Instr[si->CurrentInst];
470 repend = instr->Repeat + instr->Replen - 1;
471 if (repend < 0) repend = 0;
473 SetGadgets (&SampleWUD,
474 GD_RepStart, instr->Repeat,
476 GD_RepLen, instr->Replen,
477 GD_SampSize, instr->Length,
478 GD_DisplayStart, DisplayStart,
479 GD_DisplayEnd, DisplayEnd,
480 GD_DisplayLen, DisplayEnd ? (DisplayEnd - DisplayStart + 1) : 0,
485 struct RastPort *rp = SampleWUD.Win->RPort;
487 SetDrMd (rp, COMPLEMENT);
488 SetDrPt (rp, 0xFF00);
490 /* Delete previous loop */
493 Move (rp, LoopPole1, SampBox.Top);
494 Draw (rp, LoopPole1, SampBox.Top + SampBox.Height);
499 Move (rp, LoopPole2, SampBox.Top);
500 Draw (rp, LoopPole2, SampBox.Top + SampBox.Height);
504 if (instr->Replen) /* Draw new loop */
506 if (DisplayStart <= instr->Repeat && instr->Repeat <= DisplayEnd)
508 LoopPole1 = SampBox.Left + ((SampBox.Width * (instr->Repeat - DisplayStart)) / (DisplayEnd - DisplayStart + 1));
510 Move (rp, LoopPole1, SampBox.Top);
511 Draw (rp, LoopPole1, SampBox.Top + SampBox.Height - 1);
514 if (DisplayStart <= repend && repend <= DisplayEnd+1)
516 LoopPole2 = SampBox.Left + ((SampBox.Width * (repend - DisplayStart)) / (DisplayEnd - DisplayStart + 1));
518 Move (rp, LoopPole2, SampBox.Top);
519 Draw (rp, LoopPole2, SampBox.Top + SampBox.Height - 1);
523 SetDrPt (rp, 0xFFFF);
526 ReleaseSemaphore (&si->Lock);
532 static void UpdateRangeInfo (void)
534 WORD rs = DisplayStart + RangeStartX,
535 re = DisplayStart + RangeEndX;
537 if (!SampleWUD.Win) return;
539 SetGadgets (&SampleWUD,
542 GD_RangeLen, abs(re - rs),
548 GLOBALCALL void UpdateSampGraph (void)
554 UWORD step, xmin, xmax, height, ycoord;
556 if (!SampleWUD.Win) return;
559 if (si = xmLockActiveSong (SM_SHARED))
561 rp = SampleWUD.Win->RPort;
562 samp = si->Instr[si->CurrentInst]->Sample;
565 xmax = xmin + SampBox.Width;
566 height = SampBox.Height;
567 ycoord = SampBox.Top + height/2;
568 step = si->Instr[si->CurrentInst]->Length / SampBox.Width;
570 /* This helps with samples smaller than the graphic x size */
571 if (step == 0) step = 1;
573 /* Clear instrument rectangle */
576 SetAPen (rp, DrawInfo->dri_Pens[BACKGROUNDPEN]);
577 RectFill (rp, SampBox.Left, SampBox.Top,
578 SampBox.Left + SampBox.Width - 1, SampBox.Top + SampBox.Height - 1);
579 LoopPole1 = LoopPole2 = 0;
582 if (GfxBase->lib_Version >= 39) /* Optimized drawing with V39 */
585 RPTAG_MaxPen, &maxpen,
587 SetMaxPen (rp, max(DrawInfo->dri_Pens[TEXTPEN], DrawInfo->dri_Pens[FILLPEN]));
591 SetAPen (rp, DrawInfo->dri_Pens[TEXTPEN]);
592 Move (rp, xmin, ycoord);
593 Draw (rp, xmax-1, ycoord);
595 /* Draw sample graphic */
599 switch (GuiSwitches.SampDrawMode)
602 DrawPixelGraph (rp, samp, xmin, xmax, ycoord, height, step);
606 DrawLineGraph (rp, samp, xmin, xmax, ycoord, height, step);
610 DrawFilledGraph (rp, samp, xmin, xmax, ycoord, height, step);
615 DrawRange(); /* Redraw range */
617 /* Restore MaxPen if appropriate */
618 if (GfxBase->lib_Version >= 39) SetMaxPen (rp, maxpen);
620 ReleaseSemaphore (&si->Lock);
626 static void DrawRange (void)
630 struct RastPort *rp = SampleWUD.Win->RPort;
632 if (RangeStartX > RangeEndX)
643 /* Optimized drawing for V39 */
645 if (GfxBase->lib_Version >= 39)
648 RPTAG_MaxPen, &maxpen,
650 SetMaxPen (rp, max(DrawInfo->dri_Pens[TEXTPEN], DrawInfo->dri_Pens[FILLPEN]));
653 SetDrMd (rp, COMPLEMENT);
654 RectFill (rp, SampBox.Left + xmin, SampBox.Top,
655 SampBox.Left + xmax, SampBox.Top + SampBox.Height - 1);
657 /* Restore MaxPen if appropriate */
658 if (GfxBase->lib_Version >= 39) SetMaxPen (rp, maxpen);
662 static void UpdateRange (WORD newend)
664 /* Optimized range offset drawing */
666 WORD pole = RangeStartX; /* The fixed end of the range */
668 RangeStartX = RangeEndX;
671 if (RangeEndX < pole && RangeStartX <= pole) /* Range _left_ of pole */
673 if (RangeStartX > RangeEndX) RangeStartX--; /* Grow range */
674 else if (RangeStartX < RangeEndX) RangeEndX--; /* Reduce range */
675 DrawRange(); /* Draw/clear offset area */
678 else if (RangeEndX > pole && RangeStartX >= pole) /* Range _right_ of pole */
680 if (RangeStartX < RangeEndX) RangeStartX++; /* Grow range */
681 else if (RangeStartX > RangeEndX) RangeEndX++; /* Reduce range */
682 DrawRange(); /* Draw/clear offset area */
684 else /* Mouse has crossed the pole: it must be redrawn */
687 RangeStartX = RangeEndX = pole;
702 static void SampScrollClicked (void)
708 static void SampZoomInClicked (void)
714 static void SampRangeAllClicked (void)
716 DrawRange(); /* Delete previous range */
718 RangeEndX = SampBox.Width;
725 static void SampShowRangeClicked (void)
731 static void SampZoomOutClicked (void)
737 static void SampClearRangeClicked (void)
739 if (RangeStartX == RangeEndX) return;
742 RangeStartX = RangeEndX = 0;
749 static void SampShowAllClicked (void)
755 if (si = xmLockActiveSong (SM_SHARED))
757 DisplayEnd = si->Instr[si->CurrentInst]->Length - 1;
758 ReleaseSemaphore (&si->Lock);
768 static void SampPlayRClicked (void)
772 static void SampPlayDClicked (void)
776 static void SampPlayLClicked (void)
780 static void SampPlayAClicked (void)
784 if (si = xmLockActiveSong (SM_SHARED))
786 struct Instrument *instr = si->Instr[si->CurrentInst];
788 PlaySample (instr->Sample, instr->Length, instr->Volume, 0x1AC); /**/
789 ReleaseSemaphore (&si->Lock);
793 static void DisplayStartClicked (void)
797 static void DisplayEndClicked (void)
801 static void DisplayLenClicked (void)
805 static void RangeStartClicked (void)
809 static void RangeEndClicked (void)
813 static void RangeLenClicked (void)
819 static void RepStartClicked (void)
823 if (si = xmLockActiveSong (SM_SHARED))
825 struct Instrument *instr = si->Instr[si->CurrentInst];
827 instr->Repeat = GetNumber (SampleGadgets[GD_RepStart]);
829 if (instr->Repeat & 1) instr->Repeat--;
831 if (instr->Repeat >= instr->Length)
832 instr->Repeat = instr->Length - 2;
834 if (((LONG)instr->Repeat) < 0)
837 if (instr->Repeat + instr->Replen > instr->Length - 2)
838 instr->Replen = instr->Length - instr->Repeat;
840 if (((LONG)instr->Replen) < 0)
843 ReleaseSemaphore (&si->Lock);
851 static void RepLenClicked (void)
855 if (si = xmLockActiveSong (SM_SHARED))
857 struct Instrument *instr = si->Instr[si->CurrentInst];
859 instr->Replen = GetNumber (SampleGadgets[GD_RepLen]);
861 if (instr->Replen & 1) instr->Replen++;
863 if (instr->Replen + instr->Repeat >= instr->Length)
864 instr->Replen = instr->Length - instr->Repeat;
866 if (((LONG)instr->Replen) < 0)
869 ReleaseSemaphore (&si->Lock);
877 static void RepEndClicked (void)
881 if (si = xmLockActiveSong (SM_SHARED))
883 struct Instrument *instr = si->Instr[si->CurrentInst];
885 instr->Replen = GetNumber (SampleGadgets[GD_RepEnd]) - instr->Repeat;
887 if (instr->Replen & 1) instr->Replen++;
889 if (instr->Replen + instr->Repeat >= instr->Length)
890 instr->Replen = instr->Length - instr->Repeat;
892 if (((LONG)instr->Replen) < 0) instr->Replen = 0;
894 ReleaseSemaphore (&si->Lock);
902 static void SampBoxClicked (void)
904 if (IntuiMsg.Class == IDCMP_GADGETDOWN)
906 DrawRange(); /* Clear old range */
907 RangePole = RangeStartX = RangeEndX = IntuiMsg.MouseX - SampBox.Left;
908 DrawRange(); /* Draw pole */
911 //SetPointer (SampleWUD.Win, BlockPointer, 16, 16, -8, -7);
913 else if (IntuiMsg.Class == IDCMP_GADGETUP)
914 ; //ClearPointer (SampleWUD.Win);
922 static void SampleMiCut (void)
924 /* routine when (sub)item "Cut" is selected. */
927 static void SampleMiCopy (void)
929 /* routine when (sub)item "Copy" is selected. */
932 static void SampleMiPaste (void)
934 /* routine when (sub)item "Paste" is selected. */
937 static void SampleMiPoints (void)
939 GuiSwitches.SampDrawMode = SAMP_PIXEL;
943 static void SampleMiLines (void)
945 GuiSwitches.SampDrawMode = SAMP_LINE;
949 static void SampleMiFilled (void)
951 GuiSwitches.SampDrawMode = SAMP_FILLED;