1066 lines
25 KiB
C
1066 lines
25 KiB
C
|
// MIT License
|
||
|
|
||
|
// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com
|
||
|
|
||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
// of this software and associated documentation files (the "Software"), to deal
|
||
|
// in the Software without restriction, including without limitation the rights
|
||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
// copies of the Software, and to permit persons to whom the Software is
|
||
|
// furnished to do so, subject to the following conditions:
|
||
|
|
||
|
// The above copyright notice and this permission notice shall be included in all
|
||
|
// copies or substantial portions of the Software.
|
||
|
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
|
// SOFTWARE.
|
||
|
|
||
|
#include "sfx.h"
|
||
|
#include "history.h"
|
||
|
|
||
|
#define CANVAS_SIZE 6
|
||
|
#define CANVAS_COLS (SFX_TICKS)
|
||
|
#define CANVAS_ROWS (16)
|
||
|
#define CANVAS_WIDTH (CANVAS_COLS * CANVAS_SIZE)
|
||
|
#define CANVAS_HEIGHT (CANVAS_ROWS * CANVAS_SIZE)
|
||
|
|
||
|
#define PIANO_BUTTON_WIDTH 9
|
||
|
#define PIANO_BUTTON_HEIGHT 16
|
||
|
#define PIANO_WHITE_BUTTONS 7
|
||
|
#define PIANO_WIDTH ((PIANO_BUTTON_WIDTH+1)*PIANO_WHITE_BUTTONS)
|
||
|
#define PIANO_HEIGHT (PIANO_BUTTON_HEIGHT)
|
||
|
|
||
|
#define DEFAULT_CHANNEL 0
|
||
|
|
||
|
static void drawSwitch(Sfx* sfx, s32 x, s32 y, const char* label, s32 value, void(*set)(Sfx*, s32))
|
||
|
{
|
||
|
static const u8 LeftArrow[] =
|
||
|
{
|
||
|
0b00010000,
|
||
|
0b00110000,
|
||
|
0b01110000,
|
||
|
0b00110000,
|
||
|
0b00010000,
|
||
|
0b00000000,
|
||
|
0b00000000,
|
||
|
0b00000000,
|
||
|
};
|
||
|
|
||
|
static const u8 RightArrow[] =
|
||
|
{
|
||
|
0b01000000,
|
||
|
0b01100000,
|
||
|
0b01110000,
|
||
|
0b01100000,
|
||
|
0b01000000,
|
||
|
0b00000000,
|
||
|
0b00000000,
|
||
|
0b00000000,
|
||
|
};
|
||
|
|
||
|
sfx->tic->api.text(sfx->tic, label, x, y, systemColor(tic_color_white));
|
||
|
|
||
|
{
|
||
|
x += (s32)strlen(label)*TIC_FONT_WIDTH;
|
||
|
|
||
|
SDL_Rect rect = {x, y, TIC_FONT_WIDTH, TIC_FONT_HEIGHT};
|
||
|
|
||
|
if(checkMousePos(&rect))
|
||
|
{
|
||
|
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||
|
|
||
|
if(checkMouseClick(&rect, SDL_BUTTON_LEFT))
|
||
|
set(sfx, -1);
|
||
|
}
|
||
|
|
||
|
drawBitIcon(rect.x, rect.y, LeftArrow, systemColor(tic_color_dark_gray));
|
||
|
}
|
||
|
|
||
|
{
|
||
|
char val[] = "99";
|
||
|
sprintf(val, "%02i", value);
|
||
|
sfx->tic->api.fixed_text(sfx->tic, val, x += TIC_FONT_WIDTH, y, systemColor(tic_color_white));
|
||
|
}
|
||
|
|
||
|
{
|
||
|
x += 2*TIC_FONT_WIDTH;
|
||
|
|
||
|
SDL_Rect rect = {x, y, TIC_FONT_WIDTH, TIC_FONT_HEIGHT};
|
||
|
|
||
|
if(checkMousePos(&rect))
|
||
|
{
|
||
|
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||
|
|
||
|
if(checkMouseClick(&rect, SDL_BUTTON_LEFT))
|
||
|
set(sfx, +1);
|
||
|
}
|
||
|
|
||
|
drawBitIcon(rect.x, rect.y, RightArrow, systemColor(tic_color_dark_gray));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static tic_sound_effect* getEffect(Sfx* sfx)
|
||
|
{
|
||
|
return sfx->tic->cart.sound.sfx.data + sfx->index;
|
||
|
}
|
||
|
|
||
|
static void setIndex(Sfx* sfx, s32 delta)
|
||
|
{
|
||
|
sfx->index += delta;
|
||
|
}
|
||
|
|
||
|
static void setSpeed(Sfx* sfx, s32 delta)
|
||
|
{
|
||
|
tic_sound_effect* effect = getEffect(sfx);
|
||
|
|
||
|
effect->speed += delta;
|
||
|
|
||
|
history_add(sfx->history.envelope);
|
||
|
}
|
||
|
|
||
|
static void drawTopPanel(Sfx* sfx, s32 x, s32 y)
|
||
|
{
|
||
|
const s32 Gap = 8*TIC_FONT_WIDTH;
|
||
|
|
||
|
drawSwitch(sfx, x, y, "IDX", sfx->index, setIndex);
|
||
|
|
||
|
tic_sound_effect* effect = getEffect(sfx);
|
||
|
|
||
|
drawSwitch(sfx, x += Gap, y, "SPD", effect->speed, setSpeed);
|
||
|
}
|
||
|
|
||
|
static void setLoopStart(Sfx* sfx, s32 delta)
|
||
|
{
|
||
|
tic_sound_effect* effect = getEffect(sfx);
|
||
|
tic_sound_loop* loop = effect->loops + sfx->canvasTab;
|
||
|
|
||
|
loop->start += delta;
|
||
|
|
||
|
history_add(sfx->history.envelope);
|
||
|
}
|
||
|
|
||
|
static void setLoopSize(Sfx* sfx, s32 delta)
|
||
|
{
|
||
|
tic_sound_effect* effect = getEffect(sfx);
|
||
|
tic_sound_loop* loop = effect->loops + sfx->canvasTab;
|
||
|
|
||
|
loop->size += delta;
|
||
|
|
||
|
history_add(sfx->history.envelope);
|
||
|
}
|
||
|
|
||
|
static void drawLoopPanel(Sfx* sfx, s32 x, s32 y)
|
||
|
{
|
||
|
sfx->tic->api.text(sfx->tic, "LOOP:", x, y, systemColor(tic_color_dark_gray));
|
||
|
|
||
|
enum {Gap = 2};
|
||
|
|
||
|
tic_sound_effect* effect = getEffect(sfx);
|
||
|
tic_sound_loop* loop = effect->loops + sfx->canvasTab;
|
||
|
|
||
|
drawSwitch(sfx, x, y += Gap + TIC_FONT_HEIGHT, "", loop->size, setLoopSize);
|
||
|
drawSwitch(sfx, x, y += Gap + TIC_FONT_HEIGHT, "", loop->start, setLoopStart);
|
||
|
}
|
||
|
|
||
|
static tic_waveform* getWaveformById(Sfx* sfx, s32 i)
|
||
|
{
|
||
|
return &sfx->tic->cart.sound.sfx.waveform.envelopes[i];
|
||
|
}
|
||
|
|
||
|
static tic_waveform* getWaveform(Sfx* sfx)
|
||
|
{
|
||
|
return getWaveformById(sfx, sfx->waveform.index);
|
||
|
}
|
||
|
|
||
|
static void drawWaveButtons(Sfx* sfx, s32 x, s32 y)
|
||
|
{
|
||
|
static const u8 EmptyIcon[] =
|
||
|
{
|
||
|
0b01110000,
|
||
|
0b10001000,
|
||
|
0b10001000,
|
||
|
0b10001000,
|
||
|
0b01110000,
|
||
|
0b00000000,
|
||
|
0b00000000,
|
||
|
0b00000000,
|
||
|
};
|
||
|
|
||
|
static const u8 FullIcon[] =
|
||
|
{
|
||
|
0b01110000,
|
||
|
0b11111000,
|
||
|
0b11111000,
|
||
|
0b11111000,
|
||
|
0b01110000,
|
||
|
0b00000000,
|
||
|
0b00000000,
|
||
|
0b00000000,
|
||
|
};
|
||
|
|
||
|
enum {Scale = 4, Width = 10, Height = 5, Gap = 1, Count = ENVELOPES_COUNT, Rows = Count, HGap = 2};
|
||
|
|
||
|
for(s32 i = 0; i < Count; i++)
|
||
|
{
|
||
|
SDL_Rect rect = {x, y + (Count - i - 1)*(Height+Gap), Width, Height};
|
||
|
|
||
|
bool over = false;
|
||
|
|
||
|
if(checkMousePos(&rect))
|
||
|
{
|
||
|
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||
|
over = true;
|
||
|
|
||
|
if(checkMouseClick(&rect, SDL_BUTTON_LEFT))
|
||
|
{
|
||
|
sfx->waveform.index = i;
|
||
|
sfx->tab = SFX_WAVEFORM_TAB;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool active = false;
|
||
|
if(sfx->play.active)
|
||
|
{
|
||
|
tic_sfx_pos pos = sfx->tic->api.sfx_pos(sfx->tic, DEFAULT_CHANNEL);
|
||
|
|
||
|
if(pos.wave >= 0 && getEffect(sfx)->data[pos.wave].wave == i)
|
||
|
active = true;
|
||
|
}
|
||
|
|
||
|
sfx->tic->api.rect(sfx->tic, rect.x, rect.y, rect.w, rect.h,
|
||
|
active ? systemColor(tic_color_red) : over ? systemColor(tic_color_gray) : systemColor(tic_color_dark_gray));
|
||
|
|
||
|
{
|
||
|
enum{Size = 5};
|
||
|
SDL_Rect iconRect = {x+Width+HGap, y + (Count - i - 1)*(Height+Gap), Size, Size};
|
||
|
|
||
|
bool over = false;
|
||
|
if(checkMousePos(&iconRect))
|
||
|
{
|
||
|
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||
|
over = true;
|
||
|
|
||
|
if(checkMouseClick(&iconRect, SDL_BUTTON_LEFT))
|
||
|
{
|
||
|
tic_sound_effect* effect = getEffect(sfx);
|
||
|
for(s32 c = 0; c < SFX_TICKS; c++)
|
||
|
effect->data[c].wave = i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
drawBitIcon(iconRect.x, iconRect.y, EmptyIcon, systemColor(over ? tic_color_gray : tic_color_dark_gray));
|
||
|
}
|
||
|
|
||
|
{
|
||
|
tic_waveform* wave = getWaveformById(sfx, i);
|
||
|
|
||
|
for(s32 i = 0; i < ENVELOPE_VALUES/Scale; i++)
|
||
|
{
|
||
|
s32 value = tic_tool_peek4(wave->data, i*Scale)/Scale;
|
||
|
sfx->tic->api.pixel(sfx->tic, rect.x + i+1, rect.y + Height - value - 2, systemColor(tic_color_white));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// draw full icon
|
||
|
{
|
||
|
tic_sound_effect* effect = getEffect(sfx);
|
||
|
u8 start = effect->data[0].wave;
|
||
|
bool full = true;
|
||
|
for(s32 c = 1; c < SFX_TICKS; c++)
|
||
|
if(effect->data[c].wave != start)
|
||
|
{
|
||
|
full = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if(full)
|
||
|
drawBitIcon(x+Width+HGap, y + (Count - start - 1)*(Height+Gap), FullIcon, systemColor(tic_color_white));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void drawCanvasTabs(Sfx* sfx, s32 x, s32 y)
|
||
|
{
|
||
|
static const char* Labels[] = {"WAVE", "VOLUME", "ARPEGG", "PITCH"};
|
||
|
|
||
|
enum {Height = TIC_FONT_HEIGHT+2};
|
||
|
|
||
|
for(s32 i = 0, sy = y; i < COUNT_OF(Labels); sy += Height, i++)
|
||
|
{
|
||
|
s32 size = sfx->tic->api.text(sfx->tic, Labels[i], 0, -TIC_FONT_HEIGHT, systemColor(tic_color_black));
|
||
|
|
||
|
SDL_Rect rect = {x - size, sy, size, TIC_FONT_HEIGHT};
|
||
|
|
||
|
if(checkMousePos(&rect))
|
||
|
{
|
||
|
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||
|
|
||
|
if(checkMouseClick(&rect, SDL_BUTTON_LEFT))
|
||
|
{
|
||
|
sfx->canvasTab = i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sfx->tic->api.text(sfx->tic, Labels[i], rect.x, rect.y, i == sfx->canvasTab ? systemColor(tic_color_white) : systemColor(tic_color_dark_gray));
|
||
|
}
|
||
|
|
||
|
tic_sound_effect* effect = getEffect(sfx);
|
||
|
|
||
|
switch(sfx->canvasTab)
|
||
|
{
|
||
|
case SFX_PITCH_TAB:
|
||
|
{
|
||
|
static const char Label[] = "x16";
|
||
|
enum{Width = (sizeof Label - 1) * TIC_FONT_WIDTH};
|
||
|
SDL_Rect rect = {(x - Width)/2, y + Height * 6, Width, TIC_FONT_HEIGHT};
|
||
|
|
||
|
if(checkMousePos(&rect))
|
||
|
{
|
||
|
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||
|
|
||
|
if(checkMouseClick(&rect, SDL_BUTTON_LEFT))
|
||
|
effect->pitch16x++;
|
||
|
}
|
||
|
|
||
|
sfx->tic->api.fixed_text(sfx->tic, Label, rect.x, rect.y, systemColor(effect->pitch16x ? tic_color_white : tic_color_dark_gray));
|
||
|
}
|
||
|
break;
|
||
|
case SFX_ARPEGGIO_TAB:
|
||
|
{
|
||
|
static const char Label[] = "DOWN";
|
||
|
enum{Width = (sizeof Label - 1) * TIC_FONT_WIDTH};
|
||
|
SDL_Rect rect = {(x - Width)/2, y + Height * 6, Width, TIC_FONT_HEIGHT};
|
||
|
|
||
|
if(checkMousePos(&rect))
|
||
|
{
|
||
|
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||
|
|
||
|
if(checkMouseClick(&rect, SDL_BUTTON_LEFT))
|
||
|
effect->reverse++;
|
||
|
}
|
||
|
|
||
|
sfx->tic->api.text(sfx->tic, Label, rect.x, rect.y, systemColor(effect->reverse ? tic_color_white : tic_color_dark_gray));
|
||
|
}
|
||
|
break;
|
||
|
default: break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void drawCanvas(Sfx* sfx, s32 x, s32 y)
|
||
|
{
|
||
|
sfx->tic->api.rect(sfx->tic, x, y, CANVAS_WIDTH, CANVAS_HEIGHT, systemColor(tic_color_dark_red));
|
||
|
|
||
|
for(s32 i = 0; i < CANVAS_HEIGHT; i += CANVAS_SIZE)
|
||
|
sfx->tic->api.line(sfx->tic, x, y + i, x + CANVAS_WIDTH, y + i, TIC_COLOR_BG);
|
||
|
|
||
|
for(s32 i = 0; i < CANVAS_WIDTH; i += CANVAS_SIZE)
|
||
|
sfx->tic->api.line(sfx->tic, x + i, y, x + i, y + CANVAS_HEIGHT, TIC_COLOR_BG);
|
||
|
|
||
|
{
|
||
|
tic_sfx_pos pos = sfx->tic->api.sfx_pos(sfx->tic, DEFAULT_CHANNEL);
|
||
|
s32 tickIndex = *(pos.data + sfx->canvasTab);
|
||
|
|
||
|
if(tickIndex >= 0)
|
||
|
sfx->tic->api.rect(sfx->tic, x + tickIndex * CANVAS_SIZE, y, CANVAS_SIZE + 1, CANVAS_HEIGHT + 1, systemColor(tic_color_white));
|
||
|
}
|
||
|
|
||
|
SDL_Rect rect = {x, y, CANVAS_WIDTH, CANVAS_HEIGHT};
|
||
|
|
||
|
tic_sound_effect* effect = getEffect(sfx);
|
||
|
|
||
|
if(checkMousePos(&rect))
|
||
|
{
|
||
|
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||
|
|
||
|
s32 mx = getMouseX() - x;
|
||
|
s32 my = getMouseY() - y;
|
||
|
|
||
|
mx -= mx % CANVAS_SIZE;
|
||
|
my -= my % CANVAS_SIZE;
|
||
|
|
||
|
if(checkMouseDown(&rect, SDL_BUTTON_LEFT))
|
||
|
{
|
||
|
mx /= CANVAS_SIZE;
|
||
|
my /= CANVAS_SIZE;
|
||
|
|
||
|
switch(sfx->canvasTab)
|
||
|
{
|
||
|
case SFX_VOLUME_TAB: effect->data[mx].volume = my; break;
|
||
|
case SFX_ARPEGGIO_TAB: effect->data[mx].arpeggio = CANVAS_ROWS - my - 1; break;
|
||
|
case SFX_PITCH_TAB: effect->data[mx].pitch = CANVAS_ROWS / 2 - my - 1; break;
|
||
|
case SFX_WAVE_TAB: effect->data[mx].wave = CANVAS_ROWS - my - 1; break;
|
||
|
default: break;
|
||
|
}
|
||
|
|
||
|
history_add(sfx->history.envelope);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for(s32 i = 0; i < CANVAS_COLS; i++)
|
||
|
{
|
||
|
switch(sfx->canvasTab)
|
||
|
{
|
||
|
case SFX_VOLUME_TAB:
|
||
|
{
|
||
|
for(s32 j = 1, start = CANVAS_HEIGHT - CANVAS_SIZE; j <= CANVAS_ROWS - effect->data[i].volume; j++, start -= CANVAS_SIZE)
|
||
|
sfx->tic->api.rect(sfx->tic, x + i * CANVAS_SIZE + 1, y + 1 + start, CANVAS_SIZE-1, CANVAS_SIZE-1, systemColor(tic_color_red));
|
||
|
}
|
||
|
break;
|
||
|
case SFX_ARPEGGIO_TAB:
|
||
|
{
|
||
|
sfx->tic->api.rect(sfx->tic, x + i * CANVAS_SIZE + 1,
|
||
|
y + 1 + (CANVAS_HEIGHT - (effect->data[i].arpeggio+1)*CANVAS_SIZE), CANVAS_SIZE-1, CANVAS_SIZE-1, systemColor(tic_color_red));
|
||
|
}
|
||
|
break;
|
||
|
case SFX_PITCH_TAB:
|
||
|
{
|
||
|
for(s32 j = SDL_min(0, effect->data[i].pitch); j <= SDL_max(0, effect->data[i].pitch); j++)
|
||
|
sfx->tic->api.rect(sfx->tic, x + i * CANVAS_SIZE + 1, y + 1 + (CANVAS_HEIGHT/2 - (j+1)*CANVAS_SIZE),
|
||
|
CANVAS_SIZE-1, CANVAS_SIZE-1, systemColor(tic_color_red));
|
||
|
}
|
||
|
break;
|
||
|
case SFX_WAVE_TAB:
|
||
|
{
|
||
|
sfx->tic->api.rect(sfx->tic, x + i * CANVAS_SIZE + 1,
|
||
|
y + 1 + (CANVAS_HEIGHT - (effect->data[i].wave+1)*CANVAS_SIZE), CANVAS_SIZE-1, CANVAS_SIZE-1, systemColor(tic_color_red));
|
||
|
}
|
||
|
break;
|
||
|
default: break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
{
|
||
|
tic_sound_loop* loop = effect->loops + sfx->canvasTab;
|
||
|
if(loop->start > 0 || loop->size > 0)
|
||
|
{
|
||
|
for(s32 i = 0; i < loop->size; i++)
|
||
|
sfx->tic->api.rect(sfx->tic, x + (loop->start+i) * CANVAS_SIZE+1, y + CANVAS_HEIGHT - 2, CANVAS_SIZE-1, 2, systemColor(tic_color_yellow));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void drawPiano(Sfx* sfx, s32 x, s32 y)
|
||
|
{
|
||
|
tic_sound_effect* effect = getEffect(sfx);
|
||
|
|
||
|
static const s32 ButtonIndixes[] = {0, 2, 4, 5, 7, 9, 11, 1, 3, -1, 6, 8, 10};
|
||
|
|
||
|
SDL_Rect buttons[COUNT_OF(ButtonIndixes)];
|
||
|
|
||
|
for(s32 i = 0; i < COUNT_OF(buttons); i++)
|
||
|
{
|
||
|
buttons[i] = i < PIANO_WHITE_BUTTONS
|
||
|
? (SDL_Rect){x + (PIANO_BUTTON_WIDTH+1)*i, y, PIANO_BUTTON_WIDTH + 1, PIANO_BUTTON_HEIGHT}
|
||
|
: (SDL_Rect){x + (7 + 3) * (i - PIANO_WHITE_BUTTONS) + 6, y, 7, 8};
|
||
|
}
|
||
|
|
||
|
SDL_Rect rect = {x, y, PIANO_WIDTH, PIANO_HEIGHT};
|
||
|
|
||
|
if(checkMousePos(&rect))
|
||
|
{
|
||
|
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||
|
|
||
|
static const char* Tooltips[] = {"C [z]", "C# [s]", "D [x]", "D# [d]", "E [c]", "F [v]", "F# [g]", "G [b]", "G# [h]", "A [n]", "A# [j]", "B [m]" };
|
||
|
|
||
|
for(s32 i = COUNT_OF(buttons) - 1; i >= 0; i--)
|
||
|
{
|
||
|
SDL_Rect* rect = buttons + i;
|
||
|
|
||
|
if(checkMousePos(rect))
|
||
|
if(ButtonIndixes[i] >= 0)
|
||
|
{
|
||
|
showTooltip(Tooltips[ButtonIndixes[i]]);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(checkMouseDown(&rect, SDL_BUTTON_LEFT))
|
||
|
{
|
||
|
for(s32 i = COUNT_OF(buttons) - 1; i >= 0; i--)
|
||
|
{
|
||
|
SDL_Rect* rect = buttons + i;
|
||
|
s32 index = ButtonIndixes[i];
|
||
|
|
||
|
if(index >= 0)
|
||
|
{
|
||
|
if(checkMousePos(rect))
|
||
|
{
|
||
|
effect->note = index;
|
||
|
sfx->play.active = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for(s32 i = 0; i < COUNT_OF(buttons); i++)
|
||
|
{
|
||
|
SDL_Rect* rect = buttons + i;
|
||
|
bool white = i < PIANO_WHITE_BUTTONS;
|
||
|
s32 index = ButtonIndixes[i];
|
||
|
|
||
|
if(index >= 0)
|
||
|
sfx->tic->api.rect(sfx->tic, rect->x, rect->y, rect->w - (white ? 1 : 0), rect->h,
|
||
|
systemColor(sfx->play.active && effect->note == index ? tic_color_red : white ? tic_color_white : tic_color_black));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void drawOctavePanel(Sfx* sfx, s32 x, s32 y)
|
||
|
{
|
||
|
tic_sound_effect* effect = getEffect(sfx);
|
||
|
|
||
|
static const char Label[] = "OCT";
|
||
|
sfx->tic->api.text(sfx->tic, Label, x, y, systemColor(tic_color_white));
|
||
|
|
||
|
x += sizeof(Label)*TIC_FONT_WIDTH;
|
||
|
|
||
|
enum {Gap = 5};
|
||
|
|
||
|
for(s32 i = 0; i < OCTAVES; i++)
|
||
|
{
|
||
|
SDL_Rect rect = {x + i * (TIC_FONT_WIDTH + Gap), y, TIC_FONT_WIDTH, TIC_FONT_HEIGHT};
|
||
|
|
||
|
if(checkMousePos(&rect))
|
||
|
{
|
||
|
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||
|
|
||
|
if(checkMouseClick(&rect, SDL_BUTTON_LEFT))
|
||
|
{
|
||
|
effect->octave = i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sfx->tic->api.draw_char(sfx->tic, i + '1', rect.x, rect.y, systemColor(i == effect->octave ? tic_color_white : tic_color_dark_gray));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void playSound(Sfx* sfx)
|
||
|
{
|
||
|
if(sfx->play.active)
|
||
|
{
|
||
|
tic_sound_effect* effect = getEffect(sfx);
|
||
|
|
||
|
if(sfx->play.note != effect->note)
|
||
|
{
|
||
|
sfx->play.note = effect->note;
|
||
|
sfx->tic->api.sfx_stop(sfx->tic, DEFAULT_CHANNEL);
|
||
|
sfx->tic->api.sfx(sfx->tic, sfx->index, effect->note, effect->octave, -1, DEFAULT_CHANNEL);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sfx->play.note = -1;
|
||
|
sfx->tic->api.sfx_stop(sfx->tic, DEFAULT_CHANNEL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void undo(Sfx* sfx)
|
||
|
{
|
||
|
history_undo(sfx->history.envelope);
|
||
|
}
|
||
|
|
||
|
static void redo(Sfx* sfx)
|
||
|
{
|
||
|
history_redo(sfx->history.envelope);
|
||
|
}
|
||
|
|
||
|
static void undoWave(Sfx* sfx)
|
||
|
{
|
||
|
history_undo(sfx->history.waveform);
|
||
|
}
|
||
|
|
||
|
static void redoWave(Sfx* sfx)
|
||
|
{
|
||
|
history_redo(sfx->history.waveform);
|
||
|
}
|
||
|
|
||
|
static void copyToClipboard(Sfx* sfx)
|
||
|
{
|
||
|
tic_sound_effect* effect = getEffect(sfx);
|
||
|
toClipboard(effect, sizeof(tic_sound_effect), true);
|
||
|
}
|
||
|
|
||
|
static void copyWaveToClipboard(Sfx* sfx)
|
||
|
{
|
||
|
tic_waveform* wave = getWaveform(sfx);
|
||
|
toClipboard(wave, sizeof(tic_waveform), true);
|
||
|
}
|
||
|
|
||
|
static void resetSfx(Sfx* sfx)
|
||
|
{
|
||
|
tic_sound_effect* effect = getEffect(sfx);
|
||
|
memset(effect, 0, sizeof(tic_sound_effect));
|
||
|
|
||
|
history_add(sfx->history.envelope);
|
||
|
}
|
||
|
|
||
|
static void resetWave(Sfx* sfx)
|
||
|
{
|
||
|
tic_waveform* wave = getWaveform(sfx);
|
||
|
memset(wave, 0, sizeof(tic_waveform));
|
||
|
|
||
|
history_add(sfx->history.waveform);
|
||
|
}
|
||
|
|
||
|
static void cutToClipboard(Sfx* sfx)
|
||
|
{
|
||
|
copyToClipboard(sfx);
|
||
|
resetSfx(sfx);
|
||
|
}
|
||
|
|
||
|
static void cutWaveToClipboard(Sfx* sfx)
|
||
|
{
|
||
|
copyWaveToClipboard(sfx);
|
||
|
resetWave(sfx);
|
||
|
}
|
||
|
|
||
|
static void copyFromClipboard(Sfx* sfx)
|
||
|
{
|
||
|
tic_sound_effect* effect = getEffect(sfx);
|
||
|
|
||
|
if(fromClipboard(effect, sizeof(tic_sound_effect), true))
|
||
|
history_add(sfx->history.envelope);
|
||
|
}
|
||
|
|
||
|
static void copyWaveFromClipboard(Sfx* sfx)
|
||
|
{
|
||
|
tic_waveform* wave = getWaveform(sfx);
|
||
|
|
||
|
if(fromClipboard(wave, sizeof(tic_waveform), true))
|
||
|
history_add(sfx->history.waveform);
|
||
|
}
|
||
|
|
||
|
static void processKeyboard(Sfx* sfx)
|
||
|
{
|
||
|
s32 keyboardButton = -1;
|
||
|
|
||
|
static const s32 Scancodes[] =
|
||
|
{
|
||
|
SDL_SCANCODE_Z,
|
||
|
SDL_SCANCODE_S,
|
||
|
SDL_SCANCODE_X,
|
||
|
SDL_SCANCODE_D,
|
||
|
SDL_SCANCODE_C,
|
||
|
SDL_SCANCODE_V,
|
||
|
SDL_SCANCODE_G,
|
||
|
SDL_SCANCODE_B,
|
||
|
SDL_SCANCODE_H,
|
||
|
SDL_SCANCODE_N,
|
||
|
SDL_SCANCODE_J,
|
||
|
SDL_SCANCODE_M,
|
||
|
};
|
||
|
|
||
|
SDL_Keymod keymod = SDL_GetModState();
|
||
|
|
||
|
if(keymod & TIC_MOD_CTRL)
|
||
|
{
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for(int i = 0; i < COUNT_OF(Scancodes); i++)
|
||
|
if(getKeyboard()[Scancodes[i]])
|
||
|
keyboardButton = i;
|
||
|
}
|
||
|
|
||
|
tic_sound_effect* effect = getEffect(sfx);
|
||
|
|
||
|
if(keyboardButton >= 0)
|
||
|
{
|
||
|
effect->note = keyboardButton;
|
||
|
sfx->play.active = true;
|
||
|
}
|
||
|
|
||
|
if(getKeyboard()[SDL_SCANCODE_SPACE])
|
||
|
sfx->play.active = true;
|
||
|
}
|
||
|
|
||
|
static void processKeydown(Sfx* sfx, SDL_Keycode keycode)
|
||
|
{
|
||
|
switch(getClipboardEvent(keycode))
|
||
|
{
|
||
|
case TIC_CLIPBOARD_CUT: cutToClipboard(sfx); break;
|
||
|
case TIC_CLIPBOARD_COPY: copyToClipboard(sfx); break;
|
||
|
case TIC_CLIPBOARD_PASTE: copyFromClipboard(sfx); break;
|
||
|
default: break;
|
||
|
}
|
||
|
|
||
|
SDL_Keymod keymod = SDL_GetModState();
|
||
|
|
||
|
if(keymod & TIC_MOD_CTRL)
|
||
|
{
|
||
|
switch(keycode)
|
||
|
{
|
||
|
case SDLK_z: undo(sfx); break;
|
||
|
case SDLK_y: redo(sfx); break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch(keycode)
|
||
|
{
|
||
|
case SDLK_TAB: sfx->tab = SFX_WAVEFORM_TAB; break;
|
||
|
case SDLK_LEFT: sfx->index--; break;
|
||
|
case SDLK_RIGHT: sfx->index++; break;
|
||
|
case SDLK_DELETE: resetSfx(sfx); break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void processWaveformKeydown(Sfx* sfx, SDL_Keycode keycode)
|
||
|
{
|
||
|
switch(getClipboardEvent(keycode))
|
||
|
{
|
||
|
case TIC_CLIPBOARD_CUT: cutWaveToClipboard(sfx); break;
|
||
|
case TIC_CLIPBOARD_COPY: copyWaveToClipboard(sfx); break;
|
||
|
case TIC_CLIPBOARD_PASTE: copyWaveFromClipboard(sfx); break;
|
||
|
default: break;
|
||
|
}
|
||
|
|
||
|
SDL_Keymod keymod = SDL_GetModState();
|
||
|
|
||
|
if(keymod & TIC_MOD_CTRL)
|
||
|
{
|
||
|
switch(keycode)
|
||
|
{
|
||
|
case SDLK_z: undoWave(sfx); break;
|
||
|
case SDLK_y: redoWave(sfx); break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch(keycode)
|
||
|
{
|
||
|
case SDLK_TAB: sfx->tab = SFX_ENVELOPES_TAB; break;
|
||
|
case SDLK_LEFT: sfx->waveform.index--; break;
|
||
|
case SDLK_RIGHT: sfx->waveform.index++; break;
|
||
|
case SDLK_DELETE: resetWave(sfx); break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void drawModeTabs(Sfx* sfx)
|
||
|
{
|
||
|
static const u8 Icons[] =
|
||
|
{
|
||
|
0b00000000,
|
||
|
0b00110000,
|
||
|
0b01001001,
|
||
|
0b01001001,
|
||
|
0b01001001,
|
||
|
0b00000110,
|
||
|
0b00000000,
|
||
|
0b00000000,
|
||
|
|
||
|
0b00000000,
|
||
|
0b01000000,
|
||
|
0b01000100,
|
||
|
0b01010100,
|
||
|
0b01010101,
|
||
|
0b01010101,
|
||
|
0b00000000,
|
||
|
0b00000000,
|
||
|
};
|
||
|
|
||
|
enum { Width = 9, Height = 7, Rows = 8, Count = sizeof Icons / Rows };
|
||
|
|
||
|
for (s32 i = 0; i < Count; i++)
|
||
|
{
|
||
|
SDL_Rect rect = { TIC80_WIDTH - Width * (Count - i), 0, Width, Height };
|
||
|
|
||
|
bool over = false;
|
||
|
|
||
|
static const s32 Tabs[] = { SFX_WAVEFORM_TAB, SFX_ENVELOPES_TAB };
|
||
|
|
||
|
if (checkMousePos(&rect))
|
||
|
{
|
||
|
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||
|
over = true;
|
||
|
|
||
|
static const char* Tooltips[] = { "WAVEFORMS [tab]", "ENVELOPES [tab]" };
|
||
|
showTooltip(Tooltips[i]);
|
||
|
|
||
|
if (checkMouseClick(&rect, SDL_BUTTON_LEFT))
|
||
|
sfx->tab = Tabs[i];
|
||
|
}
|
||
|
|
||
|
if (sfx->tab == Tabs[i])
|
||
|
sfx->tic->api.rect(sfx->tic, rect.x, rect.y, rect.w, rect.h, systemColor(tic_color_black));
|
||
|
|
||
|
drawBitIcon(rect.x, rect.y, Icons + i*Rows, systemColor(sfx->tab == Tabs[i] ? tic_color_white : over ? tic_color_dark_gray : tic_color_light_blue));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void drawSfxToolbar(Sfx* sfx)
|
||
|
{
|
||
|
sfx->tic->api.rect(sfx->tic, 0, 0, TIC80_WIDTH, TOOLBAR_SIZE-1, systemColor(tic_color_white));
|
||
|
|
||
|
enum{Width = 3 * TIC_FONT_WIDTH};
|
||
|
s32 x = TIC80_WIDTH - Width - TIC_SPRITESIZE*3;
|
||
|
s32 y = 1;
|
||
|
|
||
|
SDL_Rect rect = {x, y, Width, TIC_FONT_HEIGHT};
|
||
|
bool over = false;
|
||
|
|
||
|
if(checkMousePos(&rect))
|
||
|
{
|
||
|
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||
|
over = true;
|
||
|
|
||
|
showTooltip("PLAY SFX [space]");
|
||
|
|
||
|
if(checkMouseDown(&rect, SDL_BUTTON_LEFT))
|
||
|
{
|
||
|
sfx->play.active = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tic_sound_effect* effect = getEffect(sfx);
|
||
|
|
||
|
{
|
||
|
static const char* Notes[] = SFX_NOTES;
|
||
|
char buf[] = "C#4";
|
||
|
sprintf(buf, "%s%i", Notes[effect->note], effect->octave+1);
|
||
|
|
||
|
sfx->tic->api.fixed_text(sfx->tic, buf, x, y, systemColor(over ? tic_color_dark_gray : tic_color_light_blue));
|
||
|
}
|
||
|
|
||
|
drawModeTabs(sfx);
|
||
|
}
|
||
|
|
||
|
static void envelopesTick(Sfx* sfx)
|
||
|
{
|
||
|
SDL_Event* event = NULL;
|
||
|
while ((event = pollEvent()))
|
||
|
{
|
||
|
switch(event->type)
|
||
|
{
|
||
|
case SDL_KEYDOWN:
|
||
|
processKeydown(sfx, event->key.keysym.sym);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
processKeyboard(sfx);
|
||
|
|
||
|
sfx->tic->api.clear(sfx->tic, TIC_COLOR_BG);
|
||
|
|
||
|
enum{ Gap = 3, Start = 40};
|
||
|
drawPiano(sfx, Start, TIC80_HEIGHT - PIANO_HEIGHT - Gap);
|
||
|
|
||
|
drawSfxToolbar(sfx);
|
||
|
drawToolbar(sfx->tic, TIC_COLOR_BG, false);
|
||
|
|
||
|
drawTopPanel(sfx, Start, TOOLBAR_SIZE + Gap - 1);
|
||
|
drawCanvasTabs(sfx, Start-Gap, TOOLBAR_SIZE + Gap + TIC_FONT_HEIGHT+1);
|
||
|
if(sfx->canvasTab == SFX_WAVE_TAB)
|
||
|
drawWaveButtons(sfx, Start + CANVAS_WIDTH + Gap-1, TOOLBAR_SIZE + Gap + TIC_FONT_HEIGHT+1);
|
||
|
|
||
|
drawLoopPanel(sfx, Gap, TOOLBAR_SIZE + Gap + TIC_FONT_HEIGHT+91);
|
||
|
drawCanvas(sfx, Start-1, TOOLBAR_SIZE + Gap + TIC_FONT_HEIGHT);
|
||
|
drawOctavePanel(sfx, Start + Gap + PIANO_WIDTH + Gap-1, TIC80_HEIGHT - TIC_FONT_HEIGHT - (PIANO_HEIGHT - TIC_FONT_HEIGHT)/2 - Gap);
|
||
|
}
|
||
|
|
||
|
static void drawWaveformBar(Sfx* sfx, s32 x, s32 y)
|
||
|
{
|
||
|
enum
|
||
|
{
|
||
|
Border = 2,
|
||
|
Scale = 2,
|
||
|
Width = ENVELOPE_VALUES/Scale + Border,
|
||
|
Height = CANVAS_HEIGHT/CANVAS_SIZE/Scale + Border,
|
||
|
Gap = 3,
|
||
|
Rows = 2,
|
||
|
Cols = ENVELOPES_COUNT/Rows,
|
||
|
};
|
||
|
|
||
|
for(s32 i = 0; i < ENVELOPES_COUNT; i++)
|
||
|
{
|
||
|
SDL_Rect rect = {x + (i%Cols)*(Width+Gap), y + (i/Cols)*(Height+Gap), Width, Height};
|
||
|
|
||
|
if(checkMousePos(&rect))
|
||
|
{
|
||
|
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||
|
|
||
|
if(checkMouseClick(&rect, SDL_BUTTON_LEFT))
|
||
|
sfx->waveform.index = i;
|
||
|
}
|
||
|
|
||
|
bool active = false;
|
||
|
if(sfx->play.active)
|
||
|
{
|
||
|
tic_sfx_pos pos = sfx->tic->api.sfx_pos(sfx->tic, DEFAULT_CHANNEL);
|
||
|
|
||
|
if(pos.wave >= 0 && getEffect(sfx)->data[pos.wave].wave == i)
|
||
|
active = true;
|
||
|
}
|
||
|
|
||
|
sfx->tic->api.rect(sfx->tic, rect.x, rect.y, rect.w, rect.h, systemColor(active ? tic_color_red : tic_color_white));
|
||
|
|
||
|
if(sfx->waveform.index == i)
|
||
|
sfx->tic->api.rect_border(sfx->tic, rect.x-2, rect.y-2, rect.w+4, rect.h+4, systemColor(tic_color_white));
|
||
|
|
||
|
{
|
||
|
tic_waveform* wave = getWaveformById(sfx, i);
|
||
|
|
||
|
for(s32 i = 0; i < ENVELOPE_VALUES/Scale; i++)
|
||
|
{
|
||
|
s32 value = tic_tool_peek4(wave->data, i*Scale)/Scale;
|
||
|
sfx->tic->api.pixel(sfx->tic, rect.x + i+1, rect.y + Height - value - 2, systemColor(tic_color_black));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void drawWaveformCanvas(Sfx* sfx, s32 x, s32 y)
|
||
|
{
|
||
|
enum {Rows = CANVAS_ROWS, Width = ENVELOPE_VALUES * CANVAS_SIZE, Height = CANVAS_HEIGHT};
|
||
|
|
||
|
SDL_Rect rect = {x, y, Width, Height};
|
||
|
|
||
|
sfx->tic->api.rect(sfx->tic, rect.x, rect.y, rect.w, rect.h, systemColor(tic_color_dark_red));
|
||
|
|
||
|
for(s32 i = 0; i < Height; i += CANVAS_SIZE)
|
||
|
sfx->tic->api.line(sfx->tic, rect.x, rect.y + i, rect.x + Width, rect.y + i, TIC_COLOR_BG);
|
||
|
|
||
|
for(s32 i = 0; i < Width; i += CANVAS_SIZE)
|
||
|
sfx->tic->api.line(sfx->tic, rect.x + i, rect.y, rect.x + i, rect.y + Width, TIC_COLOR_BG);
|
||
|
|
||
|
if(checkMousePos(&rect))
|
||
|
{
|
||
|
setCursor(SDL_SYSTEM_CURSOR_HAND);
|
||
|
|
||
|
if(checkMouseDown(&rect, SDL_BUTTON_LEFT))
|
||
|
{
|
||
|
s32 mx = getMouseX() - x;
|
||
|
s32 my = getMouseY() - y;
|
||
|
|
||
|
mx -= mx % CANVAS_SIZE;
|
||
|
my -= my % CANVAS_SIZE;
|
||
|
|
||
|
mx /= CANVAS_SIZE;
|
||
|
my /= CANVAS_SIZE;
|
||
|
|
||
|
tic_waveform* wave = getWaveform(sfx);
|
||
|
|
||
|
tic_tool_poke4(wave->data, mx, Rows - my - 1);
|
||
|
|
||
|
history_add(sfx->history.waveform);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tic_waveform* wave = getWaveform(sfx);
|
||
|
|
||
|
for(s32 i = 0; i < ENVELOPE_VALUES; i++)
|
||
|
{
|
||
|
s32 value = tic_tool_peek4(wave->data, i);
|
||
|
sfx->tic->api.rect(sfx->tic, x + i * CANVAS_SIZE + 1,
|
||
|
y + 1 + (Height - (value+1)*CANVAS_SIZE), CANVAS_SIZE-1, CANVAS_SIZE-1, systemColor(tic_color_red));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void waveformTick(Sfx* sfx)
|
||
|
{
|
||
|
SDL_Event* event = NULL;
|
||
|
while ((event = pollEvent()))
|
||
|
{
|
||
|
switch(event->type)
|
||
|
{
|
||
|
case SDL_KEYDOWN:
|
||
|
processWaveformKeydown(sfx, event->key.keysym.sym);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
processKeyboard(sfx);
|
||
|
|
||
|
sfx->tic->api.clear(sfx->tic, TIC_COLOR_BG);
|
||
|
|
||
|
drawSfxToolbar(sfx);
|
||
|
drawToolbar(sfx->tic, TIC_COLOR_BG, false);
|
||
|
|
||
|
drawWaveformCanvas(sfx, 23, 11);
|
||
|
drawWaveformBar(sfx, 36, 110);
|
||
|
}
|
||
|
|
||
|
static void tick(Sfx* sfx)
|
||
|
{
|
||
|
sfx->play.active = false;
|
||
|
|
||
|
switch(sfx->tab)
|
||
|
{
|
||
|
case SFX_WAVEFORM_TAB: waveformTick(sfx); break;
|
||
|
case SFX_ENVELOPES_TAB: envelopesTick(sfx); break;
|
||
|
}
|
||
|
|
||
|
playSound(sfx);
|
||
|
}
|
||
|
|
||
|
static void onStudioEnvelopeEvent(Sfx* sfx, StudioEvent event)
|
||
|
{
|
||
|
switch(event)
|
||
|
{
|
||
|
case TIC_TOOLBAR_CUT: cutToClipboard(sfx); break;
|
||
|
case TIC_TOOLBAR_COPY: copyToClipboard(sfx); break;
|
||
|
case TIC_TOOLBAR_PASTE: copyFromClipboard(sfx); break;
|
||
|
case TIC_TOOLBAR_UNDO: undo(sfx); break;
|
||
|
case TIC_TOOLBAR_REDO: redo(sfx); break;
|
||
|
default: break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void onStudioWaveformEvent(Sfx* sfx, StudioEvent event)
|
||
|
{
|
||
|
switch(event)
|
||
|
{
|
||
|
case TIC_TOOLBAR_CUT: cutWaveToClipboard(sfx); break;
|
||
|
case TIC_TOOLBAR_COPY: copyWaveToClipboard(sfx); break;
|
||
|
case TIC_TOOLBAR_PASTE: copyWaveFromClipboard(sfx); break;
|
||
|
case TIC_TOOLBAR_UNDO: undoWave(sfx); break;
|
||
|
case TIC_TOOLBAR_REDO: redoWave(sfx); break;
|
||
|
default: break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void onStudioEvent(Sfx* sfx, StudioEvent event)
|
||
|
{
|
||
|
switch(sfx->tab)
|
||
|
{
|
||
|
case SFX_WAVEFORM_TAB: onStudioWaveformEvent(sfx, event); break;
|
||
|
case SFX_ENVELOPES_TAB: onStudioEnvelopeEvent(sfx, event); break;
|
||
|
default: break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void initSfx(Sfx* sfx, tic_mem* tic)
|
||
|
{
|
||
|
if(sfx->history.envelope) history_delete(sfx->history.envelope);
|
||
|
if(sfx->history.waveform) history_delete(sfx->history.waveform);
|
||
|
|
||
|
*sfx = (Sfx)
|
||
|
{
|
||
|
.tic = tic,
|
||
|
.tick = tick,
|
||
|
.index = 0,
|
||
|
.play =
|
||
|
{
|
||
|
.note = -1,
|
||
|
.active = false,
|
||
|
},
|
||
|
.waveform =
|
||
|
{
|
||
|
.index = 0,
|
||
|
},
|
||
|
.canvasTab = SFX_WAVE_TAB,
|
||
|
.tab = SFX_ENVELOPES_TAB,
|
||
|
.history =
|
||
|
{
|
||
|
.envelope = history_create(&tic->cart.sound.sfx.data, sizeof tic->cart.sound.sfx.data),
|
||
|
.waveform = history_create(&tic->cart.sound.sfx.waveform, sizeof tic->cart.sound.sfx.waveform),
|
||
|
},
|
||
|
.event = onStudioEvent,
|
||
|
};
|
||
|
}
|