diff options
Diffstat (limited to 'src/devices/sound/vgm_visualizer.cpp')
-rw-r--r-- | src/devices/sound/vgm_visualizer.cpp | 303 |
1 files changed, 204 insertions, 99 deletions
diff --git a/src/devices/sound/vgm_visualizer.cpp b/src/devices/sound/vgm_visualizer.cpp index cc8041f6d36..e62ffe07785 100644 --- a/src/devices/sound/vgm_visualizer.cpp +++ b/src/devices/sound/vgm_visualizer.cpp @@ -11,7 +11,7 @@ ***************************************************************************/ #include "emu.h" -#include "sound/vgm_visualizer.h" +#include "vgm_visualizer.h" #include "wdlfft/fft.h" @@ -36,12 +36,12 @@ DEFINE_DEVICE_TYPE(VGMVIZ, vgmviz_device, "vgmviz", "VGM Visualizer") /*static*/ const bool vgmviz_device::NEEDS_FFT[VIZ_COUNT] = { - false, // VIZ_WAVEFORM - true, // VIZ_WATERFALL - true, // VIZ_RAWSPEC - true, // VIZ_BARSPEC4 - true, // VIZ_BARSPEC8 - true // VIZ_BARSPEC16 + false, // VIZ_WAVEFORM + true, // VIZ_WATERFALL + true, // VIZ_RAWSPEC + true, // VIZ_BARSPEC4 + true, // VIZ_BARSPEC8 + true // VIZ_BARSPEC16 }; //************************************************************************** @@ -78,6 +78,7 @@ void vgmviz_device::device_start() { WDL_fft_init(); fill_window(); + m_bitmap.resize(SCREEN_WIDTH, SCREEN_HEIGHT); } @@ -87,7 +88,7 @@ void vgmviz_device::device_start() void vgmviz_device::fill_window() { - float window_pos_delta = (3.14159265358979f * 2) / FFT_LENGTH; + float window_pos_delta = (M_PI * 2) / FFT_LENGTH; float power = 0; for (int i = 0; i < (FFT_LENGTH / 2) + 1; i++) { @@ -157,7 +158,7 @@ void vgmviz_device::apply_fft() void vgmviz_device::apply_waterfall() { - int total_bars = FFT_LENGTH / 2; + const 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] }; for (int bar = 0; bar < std::min<int>(total_bars, SCREEN_HEIGHT); bar++) { @@ -166,8 +167,8 @@ void vgmviz_device::apply_waterfall() continue; } int permuted = WDL_fft_permute(FFT_LENGTH / 2, bar); - float val = bins[0][permuted].re + bins[1][permuted].re; - int level = int(logf(val * 32768.0f) * 31.0f); + float val = std::max<float>(bins[0][permuted].re, bins[1][permuted].re); + 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_length++; @@ -253,6 +254,8 @@ void vgmviz_device::device_reset() } m_viz_mode = VIZ_WAVEFORM; + m_clear_pending = true; + m_bitmap.fill(0); m_history_length = 0; } @@ -270,6 +273,8 @@ void vgmviz_device::cycle_viz_mode() { m_viz_mode = VIZ_WAVEFORM; } + m_bitmap.fill(0); + m_clear_pending = true; } @@ -278,42 +283,39 @@ void vgmviz_device::cycle_viz_mode() // audio stream and process as necessary //------------------------------------------------- -void vgmviz_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +void vgmviz_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) { - // clear output buffers - for (int output = 0; output < m_outputs; output++) - std::fill_n(outputs[output], samples, 0); + // call the normal interface to actually mix + device_mixer_interface::sound_stream_update(stream, inputs, outputs); - m_current_rate = stream.sample_rate(); - - // loop over samples - const u8 *outmap = &m_outputmap[0]; - - // for each input, add it to the appropriate output - for (int pos = 0; pos < samples; pos++) + // now consume the outputs + for (int pos = 0; pos < outputs[0].samples(); pos++) { - for (int inp = 0; inp < m_auto_allocated_inputs; inp++) - { - outputs[outmap[inp]][pos] += inputs[inp][pos]; - } - - for (int i = 0; i < m_outputs; i++) + for (int i = 0; i < outputs.size(); i++) { - const float sample = (float)(int16_t)outputs[i][pos] / 65336.0f; + // Original code took 16-bit sample / 65536.0 instead of 32768.0, so multiply by 0.5 here but is it necessary? + const float sample = outputs[i].get(pos) * 0.5f; m_audio_buf[m_audio_fill_index][i][m_audio_count[m_audio_fill_index]] = sample + 0.5f; } switch (m_viz_mode) { default: - update_waveform(outputs); + update_waveform(); break; case VIZ_WATERFALL: - case VIZ_RAWSPEC: - case VIZ_BARSPEC4: - case VIZ_BARSPEC8: - case VIZ_BARSPEC16: - update_fft(outputs); + case VIZ_RAW_SPEC: + case VIZ_BAR_SPEC4: + case VIZ_BAR_SPEC8: + 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(); break; } } @@ -324,7 +326,7 @@ void vgmviz_device::sound_stream_update(sound_stream &stream, stream_sample_t ** // update_waveform - perform a wave-style update //------------------------------------------------- -void vgmviz_device::update_waveform(stream_sample_t **outputs) +void vgmviz_device::update_waveform() { m_history_length++; m_audio_count[m_audio_fill_index]++; @@ -343,7 +345,7 @@ void vgmviz_device::update_waveform(stream_sample_t **outputs) // update_fft - keep the FFT up-to-date //------------------------------------------------- -void vgmviz_device::update_fft(stream_sample_t **outputs) +void vgmviz_device::update_fft() { m_audio_count[m_audio_fill_index]++; if (m_audio_count[m_audio_fill_index] >= FFT_LENGTH) @@ -483,98 +485,201 @@ void vgmviz_device::device_add_mconfig(machine_config &config) // 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) { - bitmap.fill(0, cliprect); - switch (m_viz_mode) { default: + bitmap.fill(0, cliprect); draw_waveform(bitmap); break; case VIZ_WATERFALL: + bitmap.fill(0, cliprect); draw_waterfall(bitmap); break; - case VIZ_RAWSPEC: - case VIZ_BARSPEC4: - case VIZ_BARSPEC8: - case VIZ_BARSPEC16: - draw_spectrogram(bitmap); + case VIZ_RAW_SPEC: + draw_spectrogram<1, SPEC_VIZ_BAR>(bitmap); + break; + case VIZ_TOP_SPEC: + 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; } 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_idx = (512 + FFT_LENGTH / 2); + const int black_index = 512 + FFT_LENGTH / 2; + + if (m_clear_pending || SpecMode == SPEC_VIZ_BAR) + { + bitmap.fill(0); + m_clear_pending = false; + } - /* - find_levels(); + pen_t const *const pal = m_palette->pens(); - int chan_x = 0; - for (int chan = 0; chan < 2; chan++) + int width = SCREEN_WIDTH; + switch (SpecMode) { - int level = int(m_curr_levels[chan] * 255.0f); - int peak = int(m_curr_peaks[chan] * 255.0f); - for (int y = 0; y < 512; y++) + case SPEC_VIZ_PILLAR: + for (int y = 2; y < SCREEN_HEIGHT; y++) { - int bar_y = 255 - (y >> 1); - for (int x = 0; x < 7; x++) + uint32_t const *const src = &m_bitmap.pix(y); + uint32_t *const dst = &bitmap.pix(y - 2); + for (int x = SCREEN_WIDTH - 1; x >= 1; x--) { - uint32_t *line = &bitmap.pix32(y + 256); - bool lit = bar_y <= level || bar_y == peak; - line[chan_x + x] = pal[lit ? bar_y : black_idx]; + dst[x] = src[x - 1]; } } - chan_x += 8; - m_curr_peaks[chan] *= 0.99f; - } - */ - - int bar_size = 1; - switch (m_viz_mode) - { - default: - bar_size = 1; + width = int(SCREEN_WIDTH * 0.75f); break; - case VIZ_BARSPEC4: - bar_size = 4; - break; - case VIZ_BARSPEC8: - bar_size = 8; + case SPEC_VIZ_TOP: + for (int y = 1; y < SCREEN_HEIGHT; y++) + { + std::copy_n(&m_bitmap.pix(y), SCREEN_WIDTH, &bitmap.pix(y - 1)); + } break; - case VIZ_BARSPEC16: - bar_size = 16; + default: break; } - int total_bars = FFT_LENGTH / 2; + const 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] }; - 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; - 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); - 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; - uint32_t *line = &bitmap.pix32(y); - bool lit = bar_y <= level; - const int x_limit = bar_size == 1 ? 1 : bar_size - 1; - for (int x = 0; x < x_limit; x++) + case SPEC_VIZ_BAR: + level = int(raw_level * 95.0f); + if (level <= 767) + { + pal_index = 255 - level / 3; + } + for (int y = 0; y < SCREEN_HEIGHT; y++) + { + const int bar_y = SCREEN_HEIGHT - y; + uint32_t *const line = &bitmap.pix(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) { - line[(bar - 2) + x] = pal[lit ? (256 + bar) : black_idx]; + pal_index = 255 - (int)(level * 0.75f); } + + for (int y = 0; y < SCREEN_HEIGHT; y++) + { + const int bar_y = SCREEN_HEIGHT - y; + uint32_t *const line = &bitmap.pix(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.pix(SCREEN_HEIGHT - 1, x) = 0; + bitmap.pix(SCREEN_HEIGHT - 2, x) = 0; + } + for (int y = 0; y < SCREEN_HEIGHT; y++) + std::copy_n(&bitmap.pix(y), SCREEN_WIDTH, &m_bitmap.pix(y)); + 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.pix(SCREEN_HEIGHT - 1, (bar - 2) + x) = level > 0 ? pal[256 + pal_index] : pal[black_index]; + } + + for (int y = 0; y < SCREEN_HEIGHT; y++) + std::copy_n(&bitmap.pix(y), SCREEN_WIDTH, &m_bitmap.pix(y)); + break; } } } @@ -592,7 +697,7 @@ void vgmviz_device::draw_waterfall(bitmap_rgb32 &bitmap) const int v0_index = (int)v0h; const int v1_index = (int)v1h; const float interp = v0h - (float)v0_index; - uint32_t* line = &bitmap.pix32(y); + uint32_t* line = &bitmap.pix(y); for (int x = 0; x < SCREEN_WIDTH; x++) { if (m_waterfall_length < SCREEN_WIDTH) @@ -628,8 +733,8 @@ void vgmviz_device::draw_waveform(bitmap_rgb32 &bitmap) for (int x = 0; x < SCREEN_WIDTH; x++) { - bitmap.pix32(CHANNEL_CENTER, x) = MED_GRAY; - bitmap.pix32(CHANNEL_HEIGHT + 1 + CHANNEL_CENTER, x) = MED_GRAY; + bitmap.pix(CHANNEL_CENTER, x) = MED_GRAY; + bitmap.pix(CHANNEL_HEIGHT + 1 + CHANNEL_CENTER, x) = MED_GRAY; const float raw_l = m_audio_buf[1 - m_audio_fill_index][0][((int)m_history_length + 1 + x) % FFT_LENGTH]; const int sample_l = (int)((raw_l - 0.5f) * (CHANNEL_HEIGHT - 1)); @@ -638,7 +743,7 @@ void vgmviz_device::draw_waveform(bitmap_rgb32 &bitmap) int y = endy_l - sample_l; do { - bitmap.pix32(y, x) = LEFT_COLOR; + bitmap.pix(y, x) = LEFT_COLOR; y += dy_l; } while(y != endy_l); @@ -649,11 +754,11 @@ void vgmviz_device::draw_waveform(bitmap_rgb32 &bitmap) y = endy_r - sample_r; do { - bitmap.pix32(y, x) = RIGHT_COLOR; + bitmap.pix(y, x) = RIGHT_COLOR; y += dy_r; } while(y != endy_r); - bitmap.pix32(CHANNEL_HEIGHT, x) = WHITE; - bitmap.pix32(CHANNEL_HEIGHT + 1, x) = WHITE; + bitmap.pix(CHANNEL_HEIGHT, x) = WHITE; + bitmap.pix(CHANNEL_HEIGHT + 1, x) = WHITE; } } |