Added visualizer to the GBS player, various GBS UI improvements
This commit is contained in:
parent
77384a5f6a
commit
6ddfcc9725
@ -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;
|
||||
|
@ -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";
|
||||
|
@ -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>
|
||||
|
@ -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
14
Cocoa/GBVisualizerView.h
Normal 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
95
Cocoa/GBVisualizerView.m
Normal 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
|
Loading…
Reference in New Issue
Block a user