mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-18 01:30:56 +00:00
Merge pull request #774 from TomHarte/VolumeControl
Adds output volume control.
This commit is contained in:
commit
d3bac57d6a
@ -54,6 +54,12 @@ bool MultiSpeaker::get_is_stereo() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MultiSpeaker::set_output_volume(float volume) {
|
||||||
|
for(const auto &speaker: speakers_) {
|
||||||
|
speaker->set_output_volume(volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MultiSpeaker::set_delegate(Outputs::Speaker::Speaker::Delegate *delegate) {
|
void MultiSpeaker::set_delegate(Outputs::Speaker::Speaker::Delegate *delegate) {
|
||||||
delegate_ = delegate;
|
delegate_ = delegate;
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ class MultiSpeaker: public Outputs::Speaker::Speaker, Outputs::Speaker::Speaker:
|
|||||||
void set_computed_output_rate(float cycles_per_second, int buffer_size, bool stereo) override;
|
void set_computed_output_rate(float cycles_per_second, int buffer_size, bool stereo) override;
|
||||||
void set_delegate(Outputs::Speaker::Speaker::Delegate *delegate) override;
|
void set_delegate(Outputs::Speaker::Speaker::Delegate *delegate) override;
|
||||||
bool get_is_stereo() override;
|
bool get_is_stereo() override;
|
||||||
|
void set_output_volume(float) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void speaker_did_complete_samples(Speaker *speaker, const std::vector<int16_t> &buffer) final;
|
void speaker_did_complete_samples(Speaker *speaker, const std::vector<int16_t> &buffer) final;
|
||||||
|
@ -204,6 +204,7 @@
|
|||||||
4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; };
|
4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; };
|
||||||
4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */; };
|
4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */; };
|
||||||
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */; };
|
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */; };
|
||||||
|
4B50AF80242817F40099BBD7 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B50AF7F242817F40099BBD7 /* QuartzCore.framework */; };
|
||||||
4B54C0BC1F8D8E790050900F /* KeyboardMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */; };
|
4B54C0BC1F8D8E790050900F /* KeyboardMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */; };
|
||||||
4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BD1F8D8F450050900F /* Keyboard.cpp */; };
|
4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0BD1F8D8F450050900F /* Keyboard.cpp */; };
|
||||||
4B54C0C21F8D91CD0050900F /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0C11F8D91CD0050900F /* Keyboard.cpp */; };
|
4B54C0C21F8D91CD0050900F /* Keyboard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B54C0C11F8D91CD0050900F /* Keyboard.cpp */; };
|
||||||
@ -1104,6 +1105,7 @@
|
|||||||
4B4DC8271D2C2470003C5BF8 /* C1540.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = C1540.hpp; sourceTree = "<group>"; };
|
4B4DC8271D2C2470003C5BF8 /* C1540.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = C1540.hpp; sourceTree = "<group>"; };
|
||||||
4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SerialBus.cpp; sourceTree = "<group>"; };
|
4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SerialBus.cpp; sourceTree = "<group>"; };
|
||||||
4B4DC82A1D2C27A4003C5BF8 /* SerialBus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SerialBus.hpp; sourceTree = "<group>"; };
|
4B4DC82A1D2C27A4003C5BF8 /* SerialBus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SerialBus.hpp; sourceTree = "<group>"; };
|
||||||
|
4B50AF7F242817F40099BBD7 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
|
||||||
4B51F70920A521D700AFA2C1 /* Source.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Source.hpp; sourceTree = "<group>"; };
|
4B51F70920A521D700AFA2C1 /* Source.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Source.hpp; sourceTree = "<group>"; };
|
||||||
4B51F70A20A521D700AFA2C1 /* Observer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Observer.hpp; sourceTree = "<group>"; };
|
4B51F70A20A521D700AFA2C1 /* Observer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Observer.hpp; sourceTree = "<group>"; };
|
||||||
4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = KeyboardMachine.cpp; sourceTree = "<group>"; };
|
4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = KeyboardMachine.cpp; sourceTree = "<group>"; };
|
||||||
@ -1793,6 +1795,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
4B50AF80242817F40099BBD7 /* QuartzCore.framework in Frameworks */,
|
||||||
4BC76E6B1C98F43700E6EF73 /* Accelerate.framework in Frameworks */,
|
4BC76E6B1C98F43700E6EF73 /* Accelerate.framework in Frameworks */,
|
||||||
4B69FB461C4D950F00B5F0AA /* libz.tbd in Frameworks */,
|
4B69FB461C4D950F00B5F0AA /* libz.tbd in Frameworks */,
|
||||||
);
|
);
|
||||||
@ -1820,6 +1823,7 @@
|
|||||||
4B055A761FAE78210060FFFF /* Frameworks */ = {
|
4B055A761FAE78210060FFFF /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
4B50AF7F242817F40099BBD7 /* QuartzCore.framework */,
|
||||||
4B055AF01FAE9C080060FFFF /* OpenGL.framework */,
|
4B055AF01FAE9C080060FFFF /* OpenGL.framework */,
|
||||||
4B055A771FAE78210060FFFF /* SDL2.framework */,
|
4B055A771FAE78210060FFFF /* SDL2.framework */,
|
||||||
);
|
);
|
||||||
@ -5137,7 +5141,7 @@
|
|||||||
GCC_WARN_UNUSED_LABEL = YES;
|
GCC_WARN_UNUSED_LABEL = YES;
|
||||||
INFOPLIST_FILE = "Clock Signal/Info.plist";
|
INFOPLIST_FILE = "Clock Signal/Info.plist";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
MACOSX_DEPLOYMENT_TARGET = 10.12.2;
|
||||||
OTHER_CPLUSPLUSFLAGS = (
|
OTHER_CPLUSPLUSFLAGS = (
|
||||||
"$(OTHER_CFLAGS)",
|
"$(OTHER_CFLAGS)",
|
||||||
"-Wreorder",
|
"-Wreorder",
|
||||||
@ -5185,7 +5189,7 @@
|
|||||||
GCC_WARN_UNUSED_LABEL = YES;
|
GCC_WARN_UNUSED_LABEL = YES;
|
||||||
INFOPLIST_FILE = "Clock Signal/Info.plist";
|
INFOPLIST_FILE = "Clock Signal/Info.plist";
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
MACOSX_DEPLOYMENT_TARGET = 10.12.2;
|
||||||
OTHER_CPLUSPLUSFLAGS = (
|
OTHER_CPLUSPLUSFLAGS = (
|
||||||
"$(OTHER_CFLAGS)",
|
"$(OTHER_CFLAGS)",
|
||||||
"-Wreorder",
|
"-Wreorder",
|
||||||
|
@ -56,6 +56,10 @@
|
|||||||
argument = "/Users/thomasharte/Downloads/test-dsk-for-rw-and-50-60-hz/TEST-RW-60Hz.DSK"
|
argument = "/Users/thomasharte/Downloads/test-dsk-for-rw-and-50-60-hz/TEST-RW-60Hz.DSK"
|
||||||
isEnabled = "NO">
|
isEnabled = "NO">
|
||||||
</CommandLineArgument>
|
</CommandLineArgument>
|
||||||
|
<CommandLineArgument
|
||||||
|
argument = "--volume=0.001"
|
||||||
|
isEnabled = "YES">
|
||||||
|
</CommandLineArgument>
|
||||||
<CommandLineArgument
|
<CommandLineArgument
|
||||||
argument = "--new=amstradcpc"
|
argument = "--new=amstradcpc"
|
||||||
isEnabled = "NO">
|
isEnabled = "NO">
|
||||||
@ -70,7 +74,7 @@
|
|||||||
</CommandLineArgument>
|
</CommandLineArgument>
|
||||||
<CommandLineArgument
|
<CommandLineArgument
|
||||||
argument = "--output=CompositeMonochrome"
|
argument = "--output=CompositeMonochrome"
|
||||||
isEnabled = "YES">
|
isEnabled = "NO">
|
||||||
</CommandLineArgument>
|
</CommandLineArgument>
|
||||||
<CommandLineArgument
|
<CommandLineArgument
|
||||||
argument = "--logical-keyboard"
|
argument = "--logical-keyboard"
|
||||||
@ -94,11 +98,11 @@
|
|||||||
</CommandLineArgument>
|
</CommandLineArgument>
|
||||||
<CommandLineArgument
|
<CommandLineArgument
|
||||||
argument = "--help"
|
argument = "--help"
|
||||||
isEnabled = "YES">
|
isEnabled = "NO">
|
||||||
</CommandLineArgument>
|
</CommandLineArgument>
|
||||||
<CommandLineArgument
|
<CommandLineArgument
|
||||||
argument = "--model=cpc6128"
|
argument = "--model=cpc6128"
|
||||||
isEnabled = "YES">
|
isEnabled = "NO">
|
||||||
</CommandLineArgument>
|
</CommandLineArgument>
|
||||||
</CommandLineArguments>
|
</CommandLineArguments>
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15705" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15705"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
<customObject id="-2" userLabel="File's Owner" customClass="MachineDocument" customModule="Clock_Signal" customModuleProvider="target">
|
<customObject id="-2" userLabel="File's Owner" customClass="MachineDocument" customModule="Clock_Signal" customModuleProvider="target">
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="openGLView" destination="DEG-fq-cjd" id="Gxs-2u-n7B"/>
|
<outlet property="openGLView" destination="DEG-fq-cjd" id="Gxs-2u-n7B"/>
|
||||||
|
<outlet property="volumeView" destination="4ap-Gi-2AO" id="v4e-k6-Fqf"/>
|
||||||
<outlet property="window" destination="xOd-HO-29H" id="JIz-fz-R2o"/>
|
<outlet property="window" destination="xOd-HO-29H" id="JIz-fz-R2o"/>
|
||||||
</connections>
|
</connections>
|
||||||
</customObject>
|
</customObject>
|
||||||
@ -19,7 +20,7 @@
|
|||||||
<windowCollectionBehavior key="collectionBehavior" fullScreenPrimary="YES"/>
|
<windowCollectionBehavior key="collectionBehavior" fullScreenPrimary="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
<rect key="contentRect" x="80" y="250" width="600" height="450"/>
|
<rect key="contentRect" x="80" y="250" width="600" height="450"/>
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="900"/>
|
<rect key="screenRect" x="0.0" y="0.0" width="3840" height="2137"/>
|
||||||
<value key="minSize" type="size" width="228" height="171"/>
|
<value key="minSize" type="size" width="228" height="171"/>
|
||||||
<view key="contentView" id="gIp-Ho-8D9">
|
<view key="contentView" id="gIp-Ho-8D9">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="450"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="450"/>
|
||||||
@ -28,9 +29,57 @@
|
|||||||
<openGLView hidden="YES" wantsLayer="YES" useAuxiliaryDepthBufferStencil="NO" allowOffline="YES" wantsBestResolutionOpenGLSurface="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DEG-fq-cjd" customClass="CSOpenGLView">
|
<openGLView hidden="YES" wantsLayer="YES" useAuxiliaryDepthBufferStencil="NO" allowOffline="YES" wantsBestResolutionOpenGLSurface="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DEG-fq-cjd" customClass="CSOpenGLView">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="450"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="450"/>
|
||||||
</openGLView>
|
</openGLView>
|
||||||
|
<box hidden="YES" boxType="custom" cornerRadius="4" title="Box" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="4ap-Gi-2AO">
|
||||||
|
<rect key="frame" x="150" y="20" width="300" height="48"/>
|
||||||
|
<view key="contentView" id="gwO-Ty-LCX">
|
||||||
|
<rect key="frame" x="1" y="1" width="298" height="46"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Hzw-P6-1dH">
|
||||||
|
<rect key="frame" x="258" y="8" width="32" height="30"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="32" id="WrK-W9-mPP"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSTouchBarAudioOutputVolumeHighTemplate" id="5dB-4Y-iEl"/>
|
||||||
|
<color key="contentTintColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</imageView>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="p0w-Ti-Tu9">
|
||||||
|
<rect key="frame" x="8" y="8" width="32" height="30"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="32" id="dZn-Fc-fRU"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSTouchBarAudioOutputVolumeOffTemplate" id="mUH-aA-T9N"/>
|
||||||
|
<color key="contentTintColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</imageView>
|
||||||
|
<slider verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="zaz-lB-Iyt">
|
||||||
|
<rect key="frame" x="46" y="14" width="206" height="19"/>
|
||||||
|
<sliderCell key="cell" continuous="YES" state="on" alignment="left" maxValue="1" doubleValue="1" tickMarkPosition="above" sliderType="linear" id="Zvz-Og-dGA"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="setVolume:" target="-2" id="eEc-5v-I2c"/>
|
||||||
|
</connections>
|
||||||
|
</slider>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="p0w-Ti-Tu9" firstAttribute="leading" secondItem="gwO-Ty-LCX" secondAttribute="leading" constant="8" id="1t0-CW-zp9"/>
|
||||||
|
<constraint firstItem="Hzw-P6-1dH" firstAttribute="top" secondItem="gwO-Ty-LCX" secondAttribute="top" constant="8" id="OHP-c3-2Aa"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="Hzw-P6-1dH" secondAttribute="trailing" constant="8" id="W24-Wx-bdQ"/>
|
||||||
|
<constraint firstItem="zaz-lB-Iyt" firstAttribute="centerY" secondItem="p0w-Ti-Tu9" secondAttribute="centerY" id="f3C-My-T0S"/>
|
||||||
|
<constraint firstItem="zaz-lB-Iyt" firstAttribute="leading" secondItem="p0w-Ti-Tu9" secondAttribute="trailing" constant="8" id="n0g-y6-wHP"/>
|
||||||
|
<constraint firstItem="p0w-Ti-Tu9" firstAttribute="centerY" secondItem="gwO-Ty-LCX" secondAttribute="centerY" id="uTt-VY-o8c"/>
|
||||||
|
<constraint firstItem="Hzw-P6-1dH" firstAttribute="leading" secondItem="zaz-lB-Iyt" secondAttribute="trailing" constant="8" id="xCS-qF-Gz8"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="Hzw-P6-1dH" secondAttribute="bottom" constant="8" id="yaF-k8-W5B"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="300" id="HKL-vy-Mov"/>
|
||||||
|
</constraints>
|
||||||
|
<color key="fillColor" red="0.0" green="0.0" blue="0.0" alpha="0.5" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
</box>
|
||||||
</subviews>
|
</subviews>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="DEG-fq-cjd" firstAttribute="centerX" secondItem="gIp-Ho-8D9" secondAttribute="centerX" id="ES5-nL-N3h"/>
|
<constraint firstItem="DEG-fq-cjd" firstAttribute="centerX" secondItem="gIp-Ho-8D9" secondAttribute="centerX" id="ES5-nL-N3h"/>
|
||||||
|
<constraint firstItem="4ap-Gi-2AO" firstAttribute="centerX" secondItem="DEG-fq-cjd" secondAttribute="centerX" id="T41-z9-BsM"/>
|
||||||
|
<constraint firstItem="4ap-Gi-2AO" firstAttribute="bottom" secondItem="DEG-fq-cjd" secondAttribute="bottom" constant="-20" id="Tly-Uu-96H"/>
|
||||||
<constraint firstItem="DEG-fq-cjd" firstAttribute="height" secondItem="gIp-Ho-8D9" secondAttribute="height" id="YoB-qI-LFX"/>
|
<constraint firstItem="DEG-fq-cjd" firstAttribute="height" secondItem="gIp-Ho-8D9" secondAttribute="height" id="YoB-qI-LFX"/>
|
||||||
<constraint firstItem="DEG-fq-cjd" firstAttribute="centerY" secondItem="gIp-Ho-8D9" secondAttribute="centerY" id="d5Y-3a-CEI"/>
|
<constraint firstItem="DEG-fq-cjd" firstAttribute="centerY" secondItem="gIp-Ho-8D9" secondAttribute="centerY" id="d5Y-3a-CEI"/>
|
||||||
<constraint firstItem="DEG-fq-cjd" firstAttribute="width" secondItem="gIp-Ho-8D9" secondAttribute="width" id="mYS-bH-DST"/>
|
<constraint firstItem="DEG-fq-cjd" firstAttribute="width" secondItem="gIp-Ho-8D9" secondAttribute="width" id="mYS-bH-DST"/>
|
||||||
@ -45,6 +94,11 @@
|
|||||||
<outlet property="delegate" destination="-2" id="0bl-1N-x8E"/>
|
<outlet property="delegate" destination="-2" id="0bl-1N-x8E"/>
|
||||||
<outlet property="initialFirstResponder" destination="DEG-fq-cjd" id="9RI-Kx-QeN"/>
|
<outlet property="initialFirstResponder" destination="DEG-fq-cjd" id="9RI-Kx-QeN"/>
|
||||||
</connections>
|
</connections>
|
||||||
|
<point key="canvasLocation" x="141" y="147"/>
|
||||||
</window>
|
</window>
|
||||||
</objects>
|
</objects>
|
||||||
|
<resources>
|
||||||
|
<image name="NSTouchBarAudioOutputVolumeHighTemplate" width="23" height="30"/>
|
||||||
|
<image name="NSTouchBarAudioOutputVolumeOffTemplate" width="23" height="30"/>
|
||||||
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import AudioToolbox
|
import AudioToolbox
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
import QuartzCore
|
||||||
|
|
||||||
class MachineDocument:
|
class MachineDocument:
|
||||||
NSDocument,
|
NSDocument,
|
||||||
@ -62,6 +63,9 @@ class MachineDocument:
|
|||||||
activityPanel.setIsVisible(true)
|
activityPanel.setIsVisible(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The volume view.
|
||||||
|
@IBOutlet var volumeView: NSView!
|
||||||
|
|
||||||
// MARK: - NSDocument Overrides and NSWindowDelegate methods.
|
// MARK: - NSDocument Overrides and NSWindowDelegate methods.
|
||||||
|
|
||||||
/// Links this class to the MachineDocument NIB.
|
/// Links this class to the MachineDocument NIB.
|
||||||
@ -712,4 +716,55 @@ class MachineDocument:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Volume Control.
|
||||||
|
@IBAction func setVolume(_ sender: NSSlider!) {
|
||||||
|
if let machine = self.machine {
|
||||||
|
machine.setVolume(sender.floatValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This class is pure nonsense to work around Xcode's opaque behaviour.
|
||||||
|
// If I make the main class a sub of CAAnimationDelegate then the compiler
|
||||||
|
// generates a bridging header that doesn't include QuartzCore and therefore
|
||||||
|
// can't find a declaration of the CAAnimationDelegate protocol. Doesn't
|
||||||
|
// seem to matter what I add explicitly to the link stage, which version of
|
||||||
|
// macOS I set as the target, etc.
|
||||||
|
//
|
||||||
|
// So, the workaround: make my CAAnimationDelegate something that doesn't
|
||||||
|
// appear in the bridging header.
|
||||||
|
fileprivate class ViewFader: NSObject, CAAnimationDelegate {
|
||||||
|
var volumeView: NSView
|
||||||
|
|
||||||
|
init(view: NSView) {
|
||||||
|
volumeView = view
|
||||||
|
}
|
||||||
|
|
||||||
|
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
|
||||||
|
volumeView.isHidden = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fileprivate var animationFader: ViewFader? = nil
|
||||||
|
|
||||||
|
func openGLViewDidShowOSMouseCursor(_ view: CSOpenGLView) {
|
||||||
|
// The OS mouse cursor became visible, so show the volume controls.
|
||||||
|
animationFader = nil
|
||||||
|
volumeView.layer?.removeAllAnimations()
|
||||||
|
volumeView.isHidden = false
|
||||||
|
volumeView.layer?.opacity = 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
func openGLViewWillHideOSMouseCursor(_ view: CSOpenGLView) {
|
||||||
|
// The OS mouse cursor will be hidden, so hide the volume controls.
|
||||||
|
if !volumeView.isHidden && volumeView.layer?.animation(forKey: "opacity") == nil {
|
||||||
|
let fadeAnimation = CABasicAnimation(keyPath: "opacity")
|
||||||
|
fadeAnimation.fromValue = 1.0
|
||||||
|
fadeAnimation.toValue = 0.0
|
||||||
|
fadeAnimation.duration = 0.2
|
||||||
|
animationFader = ViewFader(view: volumeView)
|
||||||
|
fadeAnimation.delegate = animationFader
|
||||||
|
volumeView.layer?.add(fadeAnimation, forKey: "opacity")
|
||||||
|
volumeView.layer?.opacity = 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,11 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
|
|||||||
|
|
||||||
@property (nonatomic, readonly) BOOL canInsertMedia;
|
@property (nonatomic, readonly) BOOL canInsertMedia;
|
||||||
|
|
||||||
- (bool)supportsVideoSignal:(CSMachineVideoSignal)videoSignal;
|
- (BOOL)supportsVideoSignal:(CSMachineVideoSignal)videoSignal;
|
||||||
|
|
||||||
|
// Volume contorl.
|
||||||
|
- (void)setVolume:(float)volume;
|
||||||
|
@property (nonatomic, readonly) BOOL hasAudioOutput;
|
||||||
|
|
||||||
// Input control.
|
// Input control.
|
||||||
@property (nonatomic, readonly) BOOL hasExclusiveKeyboard;
|
@property (nonatomic, readonly) BOOL hasExclusiveKeyboard;
|
||||||
|
@ -613,7 +613,7 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (bool)supportsVideoSignal:(CSMachineVideoSignal)videoSignal {
|
- (BOOL)supportsVideoSignal:(CSMachineVideoSignal)videoSignal {
|
||||||
Configurable::Device *configurable_device = _machine->configurable_device();
|
Configurable::Device *configurable_device = _machine->configurable_device();
|
||||||
if(!configurable_device) return NO;
|
if(!configurable_device) return NO;
|
||||||
|
|
||||||
@ -708,6 +708,24 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
essential_modifiers.find(Inputs::Keyboard::Key::RightMeta) != essential_modifiers.end();
|
essential_modifiers.find(Inputs::Keyboard::Key::RightMeta) != essential_modifiers.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - Volume control
|
||||||
|
|
||||||
|
- (void)setVolume:(float)volume {
|
||||||
|
@synchronized(self) {
|
||||||
|
Outputs::Speaker::Speaker *speaker = _machine->crt_machine()->get_speaker();
|
||||||
|
if(speaker) {
|
||||||
|
return speaker->set_output_volume(volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)hasAudioOutput {
|
||||||
|
@synchronized(self) {
|
||||||
|
Outputs::Speaker::Speaker *speaker = _machine->crt_machine()->get_speaker();
|
||||||
|
return speaker ? YES : NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - Activity observation
|
#pragma mark - Activity observation
|
||||||
|
|
||||||
- (void)addLED:(NSString *)led {
|
- (void)addLED:(NSString *)led {
|
||||||
@ -761,9 +779,10 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
|
|
||||||
_timer = [[CSHighPrecisionTimer alloc] initWithTask:^{
|
_timer = [[CSHighPrecisionTimer alloc] initWithTask:^{
|
||||||
// Grab the time now and, therefore, the amount of time since the timer last fired
|
// Grab the time now and, therefore, the amount of time since the timer last fired
|
||||||
// (capped at half a second).
|
// (subject to a cap to avoid potential perpetual regression).
|
||||||
const auto timeNow = Time::nanos_now();
|
const auto timeNow = Time::nanos_now();
|
||||||
const auto duration = std::min(timeNow - lastTime, Time::Nanos(10'000'000'000 / TICKS));
|
lastTime = std::max(timeNow - Time::Nanos(10'000'000'000 / TICKS), lastTime);
|
||||||
|
const auto duration = timeNow - lastTime;
|
||||||
|
|
||||||
CGSize pixelSize;
|
CGSize pixelSize;
|
||||||
BOOL splitAndSync = NO;
|
BOOL splitAndSync = NO;
|
||||||
|
@ -50,6 +50,18 @@ typedef NS_ENUM(NSInteger, CSOpenGLViewRedrawEvent) {
|
|||||||
*/
|
*/
|
||||||
- (void)openGLViewDidReleaseMouse:(nonnull CSOpenGLView *)view;
|
- (void)openGLViewDidReleaseMouse:(nonnull CSOpenGLView *)view;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Announces that the OS mouse cursor is now being displayed again, after having been invisible.
|
||||||
|
@param view The view making the announcement.
|
||||||
|
*/
|
||||||
|
- (void)openGLViewDidShowOSMouseCursor:(nonnull CSOpenGLView *)view;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Announces that the OS mouse cursor will now be hidden.
|
||||||
|
@param view The view making the announcement.
|
||||||
|
*/
|
||||||
|
- (void)openGLViewWillHideOSMouseCursor:(nonnull CSOpenGLView *)view;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@protocol CSOpenGLViewResponderDelegate <NSObject>
|
@protocol CSOpenGLViewResponderDelegate <NSObject>
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
@import CoreVideo;
|
@import CoreVideo;
|
||||||
@import GLKit;
|
@import GLKit;
|
||||||
|
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
@interface CSOpenGLView () <NSDraggingDestination, CSApplicationEventDelegate>
|
@interface CSOpenGLView () <NSDraggingDestination, CSApplicationEventDelegate>
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@ -23,13 +25,16 @@
|
|||||||
NSTimer *_mouseHideTimer;
|
NSTimer *_mouseHideTimer;
|
||||||
BOOL _mouseIsCaptured;
|
BOOL _mouseIsCaptured;
|
||||||
|
|
||||||
volatile int32_t _isDrawingFlag;
|
atomic_int _isDrawingFlag;
|
||||||
BOOL _isInvalid;
|
BOOL _isInvalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)prepareOpenGL {
|
- (void)prepareOpenGL {
|
||||||
[super prepareOpenGL];
|
[super prepareOpenGL];
|
||||||
|
|
||||||
|
// Prepare the atomic int.
|
||||||
|
atomic_init(&_isDrawingFlag, 0);
|
||||||
|
|
||||||
// Set the clear colour.
|
// Set the clear colour.
|
||||||
[self.openGLContext makeCurrentContext];
|
[self.openGLContext makeCurrentContext];
|
||||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||||
@ -71,7 +76,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Ensure _isDrawingFlag has value 1 when drawing, 0 otherwise.
|
// Ensure _isDrawingFlag has value 1 when drawing, 0 otherwise.
|
||||||
OSAtomicIncrement32(&view->_isDrawingFlag);
|
atomic_store(&view->_isDrawingFlag, 1);
|
||||||
|
|
||||||
[view.displayLinkDelegate openGLViewDisplayLinkDidFire:view now:now outputTime:outputTime];
|
[view.displayLinkDelegate openGLViewDisplayLinkDidFire:view now:now outputTime:outputTime];
|
||||||
/*
|
/*
|
||||||
@ -84,7 +89,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
|||||||
access the display link itself as part of -drawAtTime:frequency:.
|
access the display link itself as part of -drawAtTime:frequency:.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
OSAtomicDecrement32(&view->_isDrawingFlag);
|
atomic_store(&view->_isDrawingFlag, 0);
|
||||||
|
|
||||||
return kCVReturnSuccess;
|
return kCVReturnSuccess;
|
||||||
}
|
}
|
||||||
@ -143,7 +148,10 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
|||||||
usleep((useconds_t)ceil(duration * 1000000.0));
|
usleep((useconds_t)ceil(duration * 1000000.0));
|
||||||
|
|
||||||
// Spin until _isDrawingFlag is 0 (and leave it as 0).
|
// Spin until _isDrawingFlag is 0 (and leave it as 0).
|
||||||
while(!OSAtomicCompareAndSwap32(0, 0, &_isDrawingFlag));
|
int expected_value = 0;
|
||||||
|
while(!atomic_compare_exchange_weak(&_isDrawingFlag, &expected_value, 0)) {
|
||||||
|
expected_value = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
@ -292,11 +300,13 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
|||||||
|
|
||||||
_mouseHideTimer = [NSTimer scheduledTimerWithTimeInterval:3.0 repeats:NO block:^(NSTimer * _Nonnull timer) {
|
_mouseHideTimer = [NSTimer scheduledTimerWithTimeInterval:3.0 repeats:NO block:^(NSTimer * _Nonnull timer) {
|
||||||
[NSCursor setHiddenUntilMouseMoves:YES];
|
[NSCursor setHiddenUntilMouseMoves:YES];
|
||||||
|
[self.delegate openGLViewWillHideOSMouseCursor:self];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseEntered:(NSEvent *)event {
|
- (void)mouseEntered:(NSEvent *)event {
|
||||||
|
[self.delegate openGLViewDidShowOSMouseCursor:self];
|
||||||
[super mouseEntered:event];
|
[super mouseEntered:event];
|
||||||
[self scheduleMouseHide];
|
[self scheduleMouseHide];
|
||||||
}
|
}
|
||||||
@ -305,6 +315,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
|||||||
[super mouseExited:event];
|
[super mouseExited:event];
|
||||||
[_mouseHideTimer invalidate];
|
[_mouseHideTimer invalidate];
|
||||||
_mouseHideTimer = nil;
|
_mouseHideTimer = nil;
|
||||||
|
[self.delegate openGLViewWillHideOSMouseCursor:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)releaseMouse {
|
- (void)releaseMouse {
|
||||||
@ -313,6 +324,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
|||||||
CGAssociateMouseAndMouseCursorPosition(true);
|
CGAssociateMouseAndMouseCursorPosition(true);
|
||||||
[NSCursor unhide];
|
[NSCursor unhide];
|
||||||
[self.delegate openGLViewDidReleaseMouse:self];
|
[self.delegate openGLViewDidReleaseMouse:self];
|
||||||
|
[self.delegate openGLViewDidShowOSMouseCursor:self];
|
||||||
((CSApplication *)[NSApplication sharedApplication]).eventDelegate = nil;
|
((CSApplication *)[NSApplication sharedApplication]).eventDelegate = nil;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -324,6 +336,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
|||||||
// Mouse capture is off, so don't play games with the cursor, just schedule it to
|
// Mouse capture is off, so don't play games with the cursor, just schedule it to
|
||||||
// hide in the near future.
|
// hide in the near future.
|
||||||
[self scheduleMouseHide];
|
[self scheduleMouseHide];
|
||||||
|
[self.delegate openGLViewDidShowOSMouseCursor:self];
|
||||||
} else {
|
} else {
|
||||||
if(_mouseIsCaptured) {
|
if(_mouseIsCaptured) {
|
||||||
// Mouse capture is on, so move the cursor back to the middle of the window, and
|
// Mouse capture is on, so move the cursor back to the middle of the window, and
|
||||||
@ -340,6 +353,8 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
|||||||
));
|
));
|
||||||
|
|
||||||
[self.responderDelegate mouseMoved:event];
|
[self.responderDelegate mouseMoved:event];
|
||||||
|
} else {
|
||||||
|
[self.delegate openGLViewDidShowOSMouseCursor:self];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -372,6 +387,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
|||||||
_mouseIsCaptured = YES;
|
_mouseIsCaptured = YES;
|
||||||
[NSCursor hide];
|
[NSCursor hide];
|
||||||
CGAssociateMouseAndMouseCursorPosition(false);
|
CGAssociateMouseAndMouseCursorPosition(false);
|
||||||
|
[self.delegate openGLViewWillHideOSMouseCursor:self];
|
||||||
[self.delegate openGLViewDidCaptureMouse:self];
|
[self.delegate openGLViewDidCaptureMouse:self];
|
||||||
if(self.shouldUsurpCommand) {
|
if(self.shouldUsurpCommand) {
|
||||||
((CSApplication *)[NSApplication sharedApplication]).eventDelegate = self;
|
((CSApplication *)[NSApplication sharedApplication]).eventDelegate = self;
|
||||||
|
@ -487,7 +487,7 @@ int main(int argc, char *argv[]) {
|
|||||||
const ParsedArguments arguments = parse_arguments(argc, argv);
|
const ParsedArguments arguments = parse_arguments(argc, argv);
|
||||||
|
|
||||||
// This may be printed either as
|
// This may be printed either as
|
||||||
const std::string usage_suffix = " [file or --new={machine}] [OPTIONS] [--rompath={path to ROMs}] [--speed={speed multiplier, e.g. 1.5}] [--logical-keyboard]";
|
const std::string usage_suffix = " [file or --new={machine}] [OPTIONS] [--rompath={path to ROMs}] [--speed={speed multiplier, e.g. 1.5}] [--logical-keyboard] [--volume={0.0 to 1.0}]";
|
||||||
|
|
||||||
// Print a help message if requested.
|
// Print a help message if requested.
|
||||||
if(arguments.selections.find("help") != arguments.selections.end() || arguments.selections.find("h") != arguments.selections.end()) {
|
if(arguments.selections.find("help") != arguments.selections.end() || arguments.selections.find("h") != arguments.selections.end()) {
|
||||||
@ -739,18 +739,39 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply the speed multiplier, if one was requested.
|
// Apply the speed multiplier, if one was requested.
|
||||||
const auto speed_argument = arguments.selections.find("speed");
|
{
|
||||||
if(speed_argument != arguments.selections.end()) {
|
const auto speed_argument = arguments.selections.find("speed");
|
||||||
const char *speed_string = speed_argument->second.c_str();
|
if(speed_argument != arguments.selections.end()) {
|
||||||
char *end;
|
const char *speed_string = speed_argument->second.c_str();
|
||||||
double speed = strtod(speed_string, &end);
|
char *end;
|
||||||
|
const double speed = strtod(speed_string, &end);
|
||||||
|
|
||||||
if(size_t(end - speed_string) != strlen(speed_string)) {
|
if(size_t(end - speed_string) != strlen(speed_string)) {
|
||||||
std::cerr << "Unable to parse speed: " << speed_string << std::endl;
|
std::cerr << "Unable to parse speed: " << speed_string << std::endl;
|
||||||
} else if(speed <= 0.0) {
|
} else if(speed <= 0.0) {
|
||||||
std::cerr << "Cannot run at speed " << speed_string << "; speeds must be positive." << std::endl;
|
std::cerr << "Cannot run at speed " << speed_string << "; speeds must be positive." << std::endl;
|
||||||
} else {
|
} else {
|
||||||
machine_runner.set_speed_multiplier(speed);
|
machine_runner.set_speed_multiplier(speed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the desired output volume, if requested.
|
||||||
|
{
|
||||||
|
const auto volume_argument = arguments.selections.find("volume");
|
||||||
|
if(volume_argument != arguments.selections.end()) {
|
||||||
|
const char *volume_string = volume_argument->second.c_str();
|
||||||
|
char *end;
|
||||||
|
const double volume = strtod(volume_string, &end);
|
||||||
|
|
||||||
|
if(size_t(end - volume_string) != strlen(volume_string)) {
|
||||||
|
std::cerr << "Unable to parse volume: " << volume_string << std::endl;
|
||||||
|
} else if(volume < 0.0 || volume > 1.0) {
|
||||||
|
std::cerr << "Cannot run with volume " << volume_string << "; volumes must be between 0.0 and 1.0." << std::endl;
|
||||||
|
} else {
|
||||||
|
const auto speaker = machine->crt_machine()->get_speaker();
|
||||||
|
if(speaker) speaker->set_output_volume(volume);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,9 +31,16 @@ namespace Speaker {
|
|||||||
template <typename SampleSource> class LowpassSpeaker: public Speaker {
|
template <typename SampleSource> class LowpassSpeaker: public Speaker {
|
||||||
public:
|
public:
|
||||||
LowpassSpeaker(SampleSource &sample_source) : sample_source_(sample_source) {
|
LowpassSpeaker(SampleSource &sample_source) : sample_source_(sample_source) {
|
||||||
|
// Propagate an initial volume level.
|
||||||
sample_source.set_sample_volume_range(32767);
|
sample_source.set_sample_volume_range(32767);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_output_volume(float volume) final {
|
||||||
|
// Clamp to the acceptable range, and set.
|
||||||
|
volume = std::min(std::max(0.0f, volume), 1.0f);
|
||||||
|
sample_source_.set_sample_volume_range(int16_t(32767.0f * volume));
|
||||||
|
}
|
||||||
|
|
||||||
// Implemented as per Speaker.
|
// Implemented as per Speaker.
|
||||||
float get_ideal_clock_rate_in_range(float minimum, float maximum) final {
|
float get_ideal_clock_rate_in_range(float minimum, float maximum) final {
|
||||||
std::lock_guard<std::mutex> lock_guard(filter_parameters_mutex_);
|
std::lock_guard<std::mutex> lock_guard(filter_parameters_mutex_);
|
||||||
|
@ -45,6 +45,9 @@ class Speaker {
|
|||||||
compute_output_rate();
|
compute_output_rate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the output volume, in the range [0, 1].
|
||||||
|
virtual void set_output_volume(float) = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Speeds a speed multiplier for this machine, e.g. that it is currently being run at 2.0x its normal rate.
|
Speeds a speed multiplier for this machine, e.g. that it is currently being run at 2.0x its normal rate.
|
||||||
This will affect the number of input samples that are combined to produce one output sample.
|
This will affect the number of input samples that are combined to produce one output sample.
|
||||||
@ -79,6 +82,8 @@ class Speaker {
|
|||||||
delegate_ = delegate;
|
delegate_ = delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This is primarily exposed for MultiSpeaker et al; it's not for general callers.
|
||||||
virtual void set_computed_output_rate(float cycles_per_second, int buffer_size, bool stereo) = 0;
|
virtual void set_computed_output_rate(float cycles_per_second, int buffer_size, bool stereo) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user