Added visualizer to the GBS player, various GBS UI improvements

This commit is contained in:
Lior Halphon 2021-04-10 16:10:10 +03:00
parent 77384a5f6a
commit 6ddfcc9725
6 changed files with 167 additions and 28 deletions

View File

@ -2,6 +2,7 @@
#include "GBView.h"
#include "GBImageView.h"
#include "GBSplitView.h"
#include "GBVisualizerView.h"
@class GBCheatWindowController;
@ -47,6 +48,7 @@
@property (strong) IBOutlet NSButton *gbsPlayPauseButton;
@property (strong) IBOutlet NSButton *gbsRewindButton;
@property (strong) IBOutlet NSSegmentedControl *gbsNextPrevButton;
@property (strong) IBOutlet GBVisualizerView *gbsVisualizer;
-(uint8_t) readMemory:(uint16_t) addr;
-(void) writeMemory:(uint16_t) addr value:(uint8_t)value;

View File

@ -318,6 +318,11 @@ static void infraredStateChanged(GB_gameboy_t *gb, bool on)
- (void) vblank
{
if (_gbsVisualizer) {
dispatch_async(dispatch_get_main_queue(), ^{
[_gbsVisualizer setNeedsDisplay:YES];
});
}
[self.view flip];
if (borderModeChanged) {
dispatch_sync(dispatch_get_main_queue(), ^{
@ -344,6 +349,9 @@ static void infraredStateChanged(GB_gameboy_t *gb, bool on)
- (void)gotNewSample:(GB_sample_t *)sample
{
if (_gbsVisualizer) {
[_gbsVisualizer addSample:sample];
}
[audioLock lock];
if (self.audioClient.isPlaying) {
if (audioBufferPosition == audioBufferSize) {
@ -837,6 +845,9 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
- (IBAction)changeGBSTrack:(id)sender
{
if (!running) {
[self start];
}
[self performAtomicBlock:^{
GB_gbs_switch_track(&gb, self.gbsTracks.indexOfSelectedItem);
}];
@ -878,6 +889,8 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
[_mainWindow standardWindowButton:NSWindowZoomButton].enabled = false;
});
[_mainWindow.contentView addSubview:self.gbsPlayerView];
_mainWindow.movableByWindowBackground = true;
[_mainWindow setContentBorderThickness:24 forEdge:NSRectEdgeMinY];
self.gbsTitle.stringValue = [NSString stringWithCString:info->title encoding:NSISOLatin1StringEncoding] ?: @"GBS Player";
self.gbsAuthor.stringValue = [NSString stringWithCString:info->author encoding:NSISOLatin1StringEncoding] ?: @"Unknown Composer";

View File

@ -16,44 +16,36 @@
<outlet property="gbsRewindButton" destination="0yD-Sp-Ilo" id="FgR-xd-JW5"/>
<outlet property="gbsTitle" destination="H3v-X3-48q" id="DCl-wL-oy8"/>
<outlet property="gbsTracks" destination="I1T-VS-Vse" id="Vk4-GP-RjB"/>
<outlet property="gbsVisualizer" destination="Q3o-bK-DIN" id="1YC-C5-Je6"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="c22-O7-iKe">
<rect key="frame" x="0.0" y="0.0" width="332" height="142"/>
<rect key="frame" x="0.0" y="0.0" width="332" height="221"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="H3v-X3-48q">
<rect key="frame" x="19.5" y="103" width="296" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" selectable="YES" alignment="center" title="Title" id="BwZ-Zj-sP6">
<rect key="frame" x="18" y="192" width="296" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Title" id="BwZ-Zj-sP6">
<font key="font" metaFont="systemBold" size="16"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="gaD-ZH-Beh">
<rect key="frame" x="19.5" y="79" width="296" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" selectable="YES" alignment="center" title="Author" id="IgT-r1-T38">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="2dl-dH-E3J">
<rect key="frame" x="18.5" y="24" width="296" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" selectable="YES" title="Copyright" id="nM9-oF-OV9">
<rect key="frame" x="18" y="166" width="296" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Author" id="IgT-r1-T38">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qxJ-pH-d0y">
<rect key="frame" x="61.5" y="48" width="39" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<rect key="frame" x="61.5" y="127" width="39" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="Play" imagePosition="only" alignment="center" alternateImage="Pause" state="on" borderStyle="border" focusRingType="none" inset="2" id="3ZK-br-UrS">
<behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
@ -63,8 +55,8 @@
</connections>
</button>
<button focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0yD-Sp-Ilo">
<rect key="frame" x="19.5" y="48" width="38" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<rect key="frame" x="19.5" y="127" width="38" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="Rewind" imagePosition="only" alignment="center" state="on" borderStyle="border" focusRingType="none" inset="2" id="ZIF-TP-Fqn">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@ -74,8 +66,8 @@
</connections>
</button>
<popUpButton focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="I1T-VS-Vse">
<rect key="frame" x="105.5" y="48" width="131" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<rect key="frame" x="106" y="127" width="131" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="border" focusRingType="none" imageScaling="proportionallyDown" inset="2" id="YJh-dI-A5D">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
@ -86,8 +78,8 @@
</connections>
</popUpButton>
<segmentedControl verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="SRS-M5-VVL">
<rect key="frame" x="240.5" y="48" width="72" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<rect key="frame" x="240.5" y="127" width="72" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<segmentedCell key="cell" borderStyle="border" alignment="left" style="texturedRounded" trackingMode="momentary" id="cmq-I8-cFL">
<font key="font" metaFont="system"/>
<segments>
@ -99,8 +91,31 @@
<action selector="gbsNextPrevPushed:" target="-2" id="roN-Iy-tDQ"/>
</connections>
</segmentedControl>
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="b9A-cd-ias">
<rect key="frame" x="0.0" y="117" width="332" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</box>
<customView appearanceType="darkAqua" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tRy-Gw-IaG" customClass="GBOptionalVisualEffectView">
<rect key="frame" x="0.0" y="24" width="332" height="95"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Q3o-bK-DIN" customClass="GBVisualizerView">
<rect key="frame" x="0.0" y="0.0" width="332" height="95"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</customView>
</subviews>
<point key="canvasLocation" x="67" y="253"/>
</customView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="2dl-dH-E3J">
<rect key="frame" x="18" y="5" width="296" height="14"/>
<autoresizingMask key="autoresizingMask" flexibleMinY="YES"/>
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" alignment="center" title="Copyright" id="nM9-oF-OV9">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<point key="canvasLocation" x="67" y="292.5"/>
</customView>
</objects>
<resources>

View File

@ -123,7 +123,7 @@ static const vector_float2 rect[] =
command_queue = [device newCommandQueue];
}
- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size
- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size
{
output_resolution = (vector_float2){size.width, size.height};
dispatch_async(dispatch_get_main_queue(), ^{
@ -131,7 +131,7 @@ static const vector_float2 rect[] =
});
}
- (void)drawInMTKView:(nonnull MTKView *)view
- (void)drawInMTKView:(MTKView *)view
{
if (!(view.window.occlusionState & NSWindowOcclusionStateVisible)) return;
if (!self.gb) return;

14
Cocoa/GBVisualizerView.h Normal file
View File

@ -0,0 +1,14 @@
//
// GBVisualizerView.h
// SameBoySDL
//
// Created by Lior Halphon on 7/4/21.
// Copyright © 2021 Lior Halphon. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#include <Core/gb.h>
@interface GBVisualizerView : NSView
- (void)addSample:(GB_sample_t *)sample;
@end

95
Cocoa/GBVisualizerView.m Normal file
View File

@ -0,0 +1,95 @@
//
// GBVisualizerView.m
// SameBoySDL
//
// Created by Lior Halphon on 7/4/21.
// Copyright © 2021 Lior Halphon. All rights reserved.
//
#import "GBVisualizerView.h"
#include <Core/gb.h>
#define SAMPLE_COUNT 1024
static NSColor *color_to_effect_color(typeof(GB_PALETTE_DMG.colors[0]) color)
{
if (@available(macOS 10.10, *)) {
double tint = MAX(color.r, MAX(color.g, color.b)) + 64;
return [NSColor colorWithRed:color.r / tint
green:color.g / tint
blue:color.b / tint
alpha:tint/(255 + 64)];
}
return [NSColor colorWithRed:color.r / 255.0
green:color.g / 255.0
blue:color.b / 255.0
alpha:1.0];
}
@implementation GBVisualizerView
{
GB_sample_t _samples[SAMPLE_COUNT];
size_t _position;
}
- (void)drawRect:(NSRect)dirtyRect
{
const GB_palette_t *palette;
switch ([[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorPalette"]) {
case 1:
palette = &GB_PALETTE_DMG;
break;
case 2:
palette = &GB_PALETTE_MGB;
break;
case 3:
palette = &GB_PALETTE_GBL;
break;
default:
palette = &GB_PALETTE_GREY;
break;
}
NSSize size = self.bounds.size;
[color_to_effect_color(palette->colors[0]) setFill];
NSRectFill(self.bounds);
NSBezierPath *line = [NSBezierPath bezierPath];
[line moveToPoint:NSMakePoint(0, size.height / 2)];
for (unsigned i = 0; i < SAMPLE_COUNT; i++) {
GB_sample_t *sample = _samples + ((i + _position) % SAMPLE_COUNT);
double volume = ((signed)sample->left + (signed)sample->right) / 32768.0;
[line lineToPoint:NSMakePoint(size.width * (i + 0.5) / SAMPLE_COUNT,
(volume + 1) * size.height / 2)];
}
[line lineToPoint:NSMakePoint(size.width, size.height / 2)];
[line setLineWidth:1.0];
[color_to_effect_color(palette->colors[2]) setFill];
[line fill];
[color_to_effect_color(palette->colors[1]) setFill];
NSRectFill(NSMakeRect(0, size.height / 2 - 0.5, size.width, 1));
[color_to_effect_color(palette->colors[3]) setStroke];
[line stroke];
[super drawRect:dirtyRect];
}
- (void)addSample:(GB_sample_t *)sample
{
_samples[_position++] = *sample;
if (_position == SAMPLE_COUNT) {
_position = 0;
}
}
@end