-vgm_visualizer: Added more spectrogram visualization modes. [Ryan Holtz]

This commit is contained in:
Ryan Holtz 2020-04-08 19:38:04 +02:00
parent eb2b889654
commit 9c6f0781b0
2 changed files with 204 additions and 68 deletions

View File

@ -78,6 +78,7 @@ void vgmviz_device::device_start()
{ {
WDL_fft_init(); WDL_fft_init();
fill_window(); fill_window();
m_bitmap.resize(SCREEN_WIDTH, SCREEN_HEIGHT);
} }
@ -166,8 +167,8 @@ void vgmviz_device::apply_waterfall()
continue; continue;
} }
int permuted = WDL_fft_permute(FFT_LENGTH / 2, bar); int permuted = WDL_fft_permute(FFT_LENGTH / 2, bar);
float val = bins[0][permuted].re + bins[1][permuted].re; float val = std::max<float>(bins[0][permuted].re, bins[1][permuted].re);
int level = int(logf(val * 32768.0f) * 31.0f); int level = int(log10f(val * 32768.0f) * 95.0f);
m_waterfall_buf[m_waterfall_length % SCREEN_WIDTH][total_bars - bar] = (level < 0) ? 0 : (level > 255 ? 255 : level); m_waterfall_buf[m_waterfall_length % SCREEN_WIDTH][total_bars - bar] = (level < 0) ? 0 : (level > 255 ? 255 : level);
} }
m_waterfall_length++; m_waterfall_length++;
@ -253,6 +254,8 @@ void vgmviz_device::device_reset()
} }
m_viz_mode = VIZ_WAVEFORM; m_viz_mode = VIZ_WAVEFORM;
m_clear_pending = true;
m_bitmap.fill(0);
m_history_length = 0; m_history_length = 0;
} }
@ -270,6 +273,8 @@ void vgmviz_device::cycle_viz_mode()
{ {
m_viz_mode = VIZ_WAVEFORM; m_viz_mode = VIZ_WAVEFORM;
} }
m_bitmap.fill(0);
m_clear_pending = true;
} }
@ -309,10 +314,17 @@ void vgmviz_device::sound_stream_update(sound_stream &stream, stream_sample_t **
update_waveform(outputs); update_waveform(outputs);
break; break;
case VIZ_WATERFALL: case VIZ_WATERFALL:
case VIZ_RAWSPEC: case VIZ_RAW_SPEC:
case VIZ_BARSPEC4: case VIZ_BAR_SPEC4:
case VIZ_BARSPEC8: case VIZ_BAR_SPEC8:
case VIZ_BARSPEC16: case VIZ_BAR_SPEC16:
case VIZ_PILLAR_SPEC4:
case VIZ_PILLAR_SPEC8:
case VIZ_PILLAR_SPEC16:
case VIZ_TOP_SPEC:
case VIZ_TOP_SPEC4:
case VIZ_TOP_SPEC8:
case VIZ_TOP_SPEC16:
update_fft(outputs); update_fft(outputs);
break; break;
} }
@ -483,98 +495,204 @@ void vgmviz_device::device_add_mconfig(machine_config &config)
// screen_update - update vu meters // screen_update - update vu meters
//------------------------------------------------- //-------------------------------------------------
template void vgmviz_device::draw_spectrogram<1, vgmviz_device::SPEC_VIZ_BAR>(bitmap_rgb32 &bitmap);
template void vgmviz_device::draw_spectrogram<4, vgmviz_device::SPEC_VIZ_BAR>(bitmap_rgb32 &bitmap);
template void vgmviz_device::draw_spectrogram<8, vgmviz_device::SPEC_VIZ_BAR>(bitmap_rgb32 &bitmap);
template void vgmviz_device::draw_spectrogram<16, vgmviz_device::SPEC_VIZ_BAR>(bitmap_rgb32 &bitmap);
template void vgmviz_device::draw_spectrogram<4, vgmviz_device::SPEC_VIZ_TOP>(bitmap_rgb32 &bitmap);
template void vgmviz_device::draw_spectrogram<8, vgmviz_device::SPEC_VIZ_TOP>(bitmap_rgb32 &bitmap);
template void vgmviz_device::draw_spectrogram<16, vgmviz_device::SPEC_VIZ_TOP>(bitmap_rgb32 &bitmap);
template void vgmviz_device::draw_spectrogram<3, vgmviz_device::SPEC_VIZ_PILLAR>(bitmap_rgb32 &bitmap);
template void vgmviz_device::draw_spectrogram<6, vgmviz_device::SPEC_VIZ_PILLAR>(bitmap_rgb32 &bitmap);
template void vgmviz_device::draw_spectrogram<12, vgmviz_device::SPEC_VIZ_PILLAR>(bitmap_rgb32 &bitmap);
uint32_t vgmviz_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) uint32_t vgmviz_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{ {
bitmap.fill(0, cliprect);
switch (m_viz_mode) switch (m_viz_mode)
{ {
default: default:
bitmap.fill(0, cliprect);
draw_waveform(bitmap); draw_waveform(bitmap);
break; break;
case VIZ_WATERFALL: case VIZ_WATERFALL:
bitmap.fill(0, cliprect);
draw_waterfall(bitmap); draw_waterfall(bitmap);
break; break;
case VIZ_RAWSPEC: case VIZ_RAW_SPEC:
case VIZ_BARSPEC4: draw_spectrogram<1, SPEC_VIZ_BAR>(bitmap);
case VIZ_BARSPEC8: break;
case VIZ_BARSPEC16: case VIZ_TOP_SPEC:
draw_spectrogram(bitmap); draw_spectrogram<1, SPEC_VIZ_TOP>(bitmap);
break;
case VIZ_BAR_SPEC4:
draw_spectrogram<4, SPEC_VIZ_BAR>(bitmap);
break;
case VIZ_PILLAR_SPEC4:
draw_spectrogram<3, SPEC_VIZ_PILLAR>(bitmap);
break;
case VIZ_TOP_SPEC4:
draw_spectrogram<4, SPEC_VIZ_TOP>(bitmap);
break;
case VIZ_BAR_SPEC8:
draw_spectrogram<8, SPEC_VIZ_BAR>(bitmap);
break;
case VIZ_PILLAR_SPEC8:
draw_spectrogram<6, SPEC_VIZ_PILLAR>(bitmap);
break;
case VIZ_TOP_SPEC8:
draw_spectrogram<8, SPEC_VIZ_TOP>(bitmap);
break;
case VIZ_BAR_SPEC16:
draw_spectrogram<16, SPEC_VIZ_BAR>(bitmap);
break;
case VIZ_PILLAR_SPEC16:
draw_spectrogram<12, SPEC_VIZ_PILLAR>(bitmap);
break;
case VIZ_TOP_SPEC16:
draw_spectrogram<16, SPEC_VIZ_TOP>(bitmap);
break; break;
} }
return 0; return 0;
} }
void vgmviz_device::draw_spectrogram(bitmap_rgb32 &bitmap) template <int BarSize, int SpecMode> void vgmviz_device::draw_spectrogram(bitmap_rgb32 &bitmap)
{ {
const pen_t *pal = m_palette->pens(); const int black_index = 512 + FFT_LENGTH / 2;
const int black_idx = (512 + FFT_LENGTH / 2);
/* if (m_clear_pending || SpecMode == SPEC_VIZ_BAR)
find_levels();
int chan_x = 0;
for (int chan = 0; chan < 2; chan++)
{ {
int level = int(m_curr_levels[chan] * 255.0f); bitmap.fill(0);
int peak = int(m_curr_peaks[chan] * 255.0f); m_clear_pending = false;
for (int y = 0; y < 512; y++) }
const pen_t *pal = m_palette->pens();
int width = SCREEN_WIDTH;
switch (SpecMode)
{
case SPEC_VIZ_PILLAR:
for (int y = 2; y < SCREEN_HEIGHT; y++)
{ {
int bar_y = 255 - (y >> 1); uint32_t *src = &m_bitmap.pix32(y);
for (int x = 0; x < 7; x++) uint32_t *dst = &bitmap.pix32(y - 2);
for (int x = SCREEN_WIDTH - 1; x >= 1; x--)
{ {
uint32_t *line = &bitmap.pix32(y + 256); dst[x] = src[x - 1];
bool lit = bar_y <= level || bar_y == peak;
line[chan_x + x] = pal[lit ? bar_y : black_idx];
} }
} }
chan_x += 8; width = (int)(SCREEN_WIDTH * 0.75f);
m_curr_peaks[chan] *= 0.99f; break;
} case SPEC_VIZ_TOP:
*/ for (int y = 1; y < SCREEN_HEIGHT; y++)
{
int bar_size = 1; uint32_t *src = &m_bitmap.pix32(y);
switch (m_viz_mode) uint32_t *dst = &bitmap.pix32(y - 1);
{ for (int x = 0; x < SCREEN_WIDTH; x++)
{
dst[x] = src[x];
}
}
break;
default: default:
bar_size = 1;
break;
case VIZ_BARSPEC4:
bar_size = 4;
break;
case VIZ_BARSPEC8:
bar_size = 8;
break;
case VIZ_BARSPEC16:
bar_size = 16;
break; break;
} }
int total_bars = FFT_LENGTH / 2; int total_bars = FFT_LENGTH / 2;
WDL_FFT_COMPLEX *bins[2] = { (WDL_FFT_COMPLEX *)m_fft_buf[0], (WDL_FFT_COMPLEX *)m_fft_buf[1] }; WDL_FFT_COMPLEX *bins[2] = { (WDL_FFT_COMPLEX *)m_fft_buf[0], (WDL_FFT_COMPLEX *)m_fft_buf[1] };
for (int bar = 0; bar < total_bars && bar < SCREEN_WIDTH; bar += bar_size)
for (int bar = 2; bar < total_bars && bar < width; bar += BarSize)
{ {
if (bar < 2)
{
continue;
}
float max_val = 0.0f; float max_val = 0.0f;
for (int sub_bar = 0; sub_bar < bar_size && (bar + sub_bar) < total_bars; sub_bar++) for (int sub_bar = 0; sub_bar < BarSize && (bar + sub_bar) < total_bars; sub_bar++)
{ {
int permuted = WDL_fft_permute(FFT_LENGTH/2, bar + sub_bar); int permuted = WDL_fft_permute(FFT_LENGTH/2, bar + sub_bar);
max_val = std::max<float>((bins[0][permuted].re + bins[1][permuted].re) * 0.5f, max_val); const float max_channel = std::max<float>(bins[0][permuted].re, bins[1][permuted].re);
max_val = std::max<float>(max_channel, max_val);
} }
int level = int(logf(max_val * 32768.0f) * 96.0f);
for (int y = 0; y < SCREEN_HEIGHT; y++) float raw_level = logf(max_val * 32768.0f);
int level = 0;
int pal_index = 0;
switch (SpecMode)
{ {
int bar_y = SCREEN_HEIGHT - y; case SPEC_VIZ_BAR:
uint32_t *line = &bitmap.pix32(y); level = int(raw_level * 95.0f);
bool lit = bar_y <= level; if (level <= 767)
const int x_limit = bar_size == 1 ? 1 : bar_size - 1;
for (int x = 0; x < x_limit; x++)
{ {
line[(bar - 2) + x] = pal[lit ? (256 + bar) : black_idx]; pal_index = 255 - level / 3;
} }
for (int y = 0; y < SCREEN_HEIGHT; y++)
{
int bar_y = SCREEN_HEIGHT - y;
uint32_t *line = &bitmap.pix32(y);
for (int x = 0; x < BarSize; x++)
{
line[(bar - 2) + x] = bar_y <= level ? pal[256 + pal_index] : pal[black_index];
}
}
break;
case SPEC_VIZ_PILLAR:
level = int(raw_level * 59.0f);
if (level < 383)
{
pal_index = 255 - (int)(level * 0.75f);
}
for (int y = 0; y < SCREEN_HEIGHT; y++)
{
int bar_y = SCREEN_HEIGHT - y;
uint32_t *line = &bitmap.pix32(y);
line[0] = 0;
if (bar_y > level)
{
continue;
}
const uint32_t entry = pal[256 + pal_index];
for (int x = (bar_y < level) ? 0 : 1; x < (BarSize - 1); x++)
{
if (bar_y < (level - 1))
{
const uint8_t r = (uint8_t)(((entry >> 16) & 0xff) * 0.75f);
const uint8_t g = (uint8_t)(((entry >> 8) & 0xff) * 0.75f);
const uint8_t b = (uint8_t)(((entry >> 0) & 0xff) * 0.75f);
line[(bar - 2) + x] = 0xff000000 | (r << 16) | (g << 8) | b;
}
else
{
line[(bar - 2) + x] = pal[256 + pal_index];
}
}
const uint8_t r = (uint8_t)(((entry >> 16) & 0xff) * 0.5f);
const uint8_t g = (uint8_t)(((entry >> 8) & 0xff) * 0.5f);
const uint8_t b = (uint8_t)(((entry >> 0) & 0xff) * 0.5f);
line[(bar - 2) + (BarSize - 1)] = 0xff000000 | (r << 16) | (g << 8) | b;
if (bar_y < level)
{
line[(bar - 2) + (BarSize - 2)] = 0xff000000 | (r << 16) | (g << 8) | b;
}
}
for (int x = 0; x < SCREEN_WIDTH; x++)
{
bitmap.pix32(SCREEN_HEIGHT - 1, x) = 0;
bitmap.pix32(SCREEN_HEIGHT - 2, x) = 0;
}
memcpy(&m_bitmap.pix32(0), &bitmap.pix32(0), sizeof(uint32_t) * SCREEN_WIDTH * SCREEN_HEIGHT);
break;
case SPEC_VIZ_TOP:
level = int(raw_level * 63.0f);
if (level < 255)
{
pal_index = 255 - level;
}
for (int x = 0; x < BarSize; x++)
{
bitmap.pix32(SCREEN_HEIGHT - 1, (bar - 2) + x) = level > 0 ? pal[256 + pal_index] : pal[black_index];
}
memcpy(&m_bitmap.pix32(0), &bitmap.pix32(0), sizeof(uint32_t) * SCREEN_WIDTH * SCREEN_HEIGHT);
break;
} }
} }
} }

View File

@ -50,16 +50,31 @@ public:
void toggle_normalize(); void toggle_normalize();
protected: protected:
enum spec_viz_mode : int
{
SPEC_VIZ_BAR,
SPEC_VIZ_PILLAR,
SPEC_VIZ_TOP
};
enum viz_mode : int enum viz_mode : int
{ {
VIZ_WAVEFORM, VIZ_WAVEFORM,
VIZ_WATERFALL, VIZ_WATERFALL,
VIZ_SPEC_START, VIZ_SPEC_START,
VIZ_RAWSPEC = VIZ_SPEC_START, VIZ_RAW_SPEC = VIZ_SPEC_START,
VIZ_BARSPEC4, VIZ_TOP_SPEC,
VIZ_BARSPEC8, VIZ_BAR_SPEC4,
VIZ_BARSPEC16, VIZ_PILLAR_SPEC4,
VIZ_SPEC_END = VIZ_BARSPEC16, VIZ_TOP_SPEC4,
VIZ_BAR_SPEC8,
VIZ_PILLAR_SPEC8,
VIZ_TOP_SPEC8,
VIZ_BAR_SPEC16,
VIZ_PILLAR_SPEC16,
VIZ_TOP_SPEC16,
VIZ_SPEC_END = VIZ_TOP_SPEC16,
VIZ_COUNT VIZ_COUNT
}; };
@ -86,7 +101,7 @@ protected:
uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
void draw_waveform(bitmap_rgb32 &bitmap); void draw_waveform(bitmap_rgb32 &bitmap);
void draw_waterfall(bitmap_rgb32 &bitmap); void draw_waterfall(bitmap_rgb32 &bitmap);
void draw_spectrogram(bitmap_rgb32 &bitmap); template <int BarSize, int SpecMode> void draw_spectrogram(bitmap_rgb32 &bitmap);
void fill_window(); void fill_window();
void apply_window(uint32_t buf_index); void apply_window(uint32_t buf_index);
@ -114,6 +129,9 @@ protected:
float m_window[FFT_LENGTH]; float m_window[FFT_LENGTH];
bitmap_rgb32 m_bitmap;
bool m_clear_pending;
viz_mode m_viz_mode; viz_mode m_viz_mode;
static const bool NEEDS_FFT[VIZ_COUNT]; static const bool NEEDS_FFT[VIZ_COUNT];