Merge branch 'master' of https://github.com/tildearrow/furnace into k053260

This commit is contained in:
cam900 2023-06-21 20:06:09 +09:00
commit a413868fcb
53 changed files with 1248 additions and 834 deletions

1
.gitmodules vendored
View File

@ -1,6 +1,7 @@
[submodule "extern/SDL"]
path = extern/SDL
url = https://github.com/libsdl-org/SDL.git
branch = release-2.28.x
[submodule "extern/libsndfile"]
path = extern/libsndfile
url = https://github.com/libsndfile/libsndfile.git

View File

@ -119,7 +119,7 @@ check out the [Releases](https://github.com/tildearrow/furnace/releases) page. a
---
# quick references
- **discussion**: see the [Discussions](https://github.com/tildearrow/furnace/discussions) section, or the [official Discord server](https://discord.gg/EfrwT2wq7z).
- **discussion**: see the [Discussions](https://github.com/tildearrow/furnace/discussions) section, the [official Revolt](https://rvlt.gg/GRPS6tmc) or the [official Discord server](https://discord.gg/EfrwT2wq7z).
- **help**: check out the [documentation](doc/README.md). it's incomplete though.
## packages

View File

@ -186,7 +186,7 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
// Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead
// of TRANSPORT_LE. Let's force ourselves to connect low energy.
private BluetoothGatt connectGatt(boolean managed) {
if (Build.VERSION.SDK_INT >= 23) {
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
try {
return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE);
} catch (Exception e) {
@ -429,7 +429,7 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
}
});
}
}
}
else if (newState == 0) {
mIsConnected = false;
}

View File

@ -170,7 +170,7 @@ public class HIDDeviceManager {
Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol());
Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount());
// Get endpoint details
// Get endpoint details
for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
{
UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
@ -251,6 +251,8 @@ public class HIDDeviceManager {
0x20d6, // PowerA
0x24c6, // PowerA
0x2c22, // Qanba
0x2dc8, // 8BitDo
0x9886, // ASTRO Gaming
};
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
@ -271,14 +273,16 @@ public class HIDDeviceManager {
final int XB1_IFACE_SUBCLASS = 71;
final int XB1_IFACE_PROTOCOL = 208;
final int[] SUPPORTED_VENDORS = {
0x044f, // Thrustmaster
0x045e, // Microsoft
0x0738, // Mad Catz
0x0e6f, // PDP
0x0f0d, // Hori
0x10f5, // Turtle Beach
0x1532, // Razer Wildcat
0x20d6, // PowerA
0x24c6, // PowerA
0x2dc8, /* 8BitDo */
0x2dc8, // 8BitDo
0x2e24, // Hyperkin
};
@ -353,13 +357,13 @@ public class HIDDeviceManager {
private void initializeBluetooth() {
Log.d(TAG, "Initializing Bluetooth");
if (Build.VERSION.SDK_INT <= 30 &&
if (Build.VERSION.SDK_INT <= 30 /* Android 11.0 (R) */ &&
mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH");
return;
}
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18)) {
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18 /* Android 4.3 (JELLY_BEAN_MR2) */)) {
Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE");
return;
}
@ -524,7 +528,7 @@ public class HIDDeviceManager {
for (HIDDevice device : mDevicesById.values()) {
device.setFrozen(frozen);
}
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
@ -573,7 +577,7 @@ public class HIDDeviceManager {
try {
final int FLAG_MUTABLE = 0x02000000; // PendingIntent.FLAG_MUTABLE, but don't require SDK 31
int flags;
if (Build.VERSION.SDK_INT >= 31) {
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
flags = FLAG_MUTABLE;
} else {
flags = 0;

View File

@ -52,7 +52,7 @@ class HIDDeviceUSB implements HIDDevice {
@Override
public String getSerialNumber() {
String result = null;
if (Build.VERSION.SDK_INT >= 21) {
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
try {
result = mDevice.getSerialNumber();
}
@ -74,7 +74,7 @@ class HIDDeviceUSB implements HIDDevice {
@Override
public String getManufacturerName() {
String result = null;
if (Build.VERSION.SDK_INT >= 21) {
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
result = mDevice.getManufacturerName();
}
if (result == null) {
@ -86,7 +86,7 @@ class HIDDeviceUSB implements HIDDevice {
@Override
public String getProductName() {
String result = null;
if (Build.VERSION.SDK_INT >= 21) {
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
result = mDevice.getProductName();
}
if (result == null) {

View File

@ -29,6 +29,7 @@ public class SDL {
// This function stores the current activity (SDL or not)
public static void setContext(Context context) {
SDLAudioManager.setContext(context);
mContext = context;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,8 @@
package org.libsdl.app;
import android.content.Context;
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
@ -8,34 +11,67 @@ import android.media.MediaRecorder;
import android.os.Build;
import android.util.Log;
public class SDLAudioManager
{
import java.util.Arrays;
public class SDLAudioManager {
protected static final String TAG = "SDLAudio";
protected static AudioTrack mAudioTrack;
protected static AudioRecord mAudioRecord;
protected static Context mContext;
private static final int[] NO_DEVICES = {};
private static AudioDeviceCallback mAudioDeviceCallback;
public static void initialize() {
mAudioTrack = null;
mAudioRecord = null;
mAudioDeviceCallback = null;
if(Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */)
{
mAudioDeviceCallback = new AudioDeviceCallback() {
@Override
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
Arrays.stream(addedDevices).forEach(deviceInfo -> addAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
}
@Override
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
Arrays.stream(removedDevices).forEach(deviceInfo -> removeAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
}
};
}
}
public static void setContext(Context context) {
mContext = context;
if (context != null) {
registerAudioDeviceCallback();
}
}
public static void release(Context context) {
unregisterAudioDeviceCallback(context);
}
// Audio
protected static String getAudioFormatString(int audioFormat) {
switch (audioFormat) {
case AudioFormat.ENCODING_PCM_8BIT:
return "8-bit";
case AudioFormat.ENCODING_PCM_16BIT:
return "16-bit";
case AudioFormat.ENCODING_PCM_FLOAT:
return "float";
default:
return Integer.toString(audioFormat);
case AudioFormat.ENCODING_PCM_8BIT:
return "8-bit";
case AudioFormat.ENCODING_PCM_16BIT:
return "16-bit";
case AudioFormat.ENCODING_PCM_FLOAT:
return "float";
default:
return Integer.toString(audioFormat);
}
}
protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
int channelConfig;
int sampleSize;
int frameSize;
@ -43,14 +79,14 @@ public class SDLAudioManager
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", requested " + desiredFrames + " frames of " + desiredChannels + " channel " + getAudioFormatString(audioFormat) + " audio at " + sampleRate + " Hz");
/* On older devices let's use known good settings */
if (Build.VERSION.SDK_INT < 21) {
if (Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) {
if (desiredChannels > 2) {
desiredChannels = 2;
}
}
/* AudioTrack has sample rate limitation of 48000 (fixed in 5.0.2) */
if (Build.VERSION.SDK_INT < 22) {
if (Build.VERSION.SDK_INT < 22 /* Android 5.1 (LOLLIPOP_MR1) */) {
if (sampleRate < 8000) {
sampleRate = 8000;
} else if (sampleRate > 48000) {
@ -59,7 +95,7 @@ public class SDLAudioManager
}
if (audioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
int minSDKVersion = (isCapture ? 23 : 21);
int minSDKVersion = (isCapture ? 23 /* Android 6.0 (M) */ : 21 /* Android 5.0 (LOLLIPOP) */);
if (Build.VERSION.SDK_INT < minSDKVersion) {
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
}
@ -120,7 +156,7 @@ public class SDLAudioManager
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
break;
case 8:
if (Build.VERSION.SDK_INT >= 23) {
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
channelConfig = AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
} else {
Log.v(TAG, "Requested " + desiredChannels + " channels, getting 5.1 surround");
@ -201,6 +237,10 @@ public class SDLAudioManager
return null;
}
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) {
mAudioRecord.setPreferredDevice(getOutputAudioDeviceInfo(deviceId));
}
mAudioRecord.startRecording();
}
@ -224,6 +264,10 @@ public class SDLAudioManager
return null;
}
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) {
mAudioTrack.setPreferredDevice(getInputAudioDeviceInfo(deviceId));
}
mAudioTrack.play();
}
@ -238,11 +282,73 @@ public class SDLAudioManager
return results;
}
private static AudioDeviceInfo getInputAudioDeviceInfo(int deviceId) {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS))
.filter(deviceInfo -> deviceInfo.getId() == deviceId)
.findFirst()
.orElse(null);
} else {
return null;
}
}
private static AudioDeviceInfo getOutputAudioDeviceInfo(int deviceId) {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS))
.filter(deviceInfo -> deviceInfo.getId() == deviceId)
.findFirst()
.orElse(null);
} else {
return null;
}
}
private static void registerAudioDeviceCallback() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null);
}
}
private static void unregisterAudioDeviceCallback(Context context) {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
audioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames);
public static int[] getAudioOutputDevices() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
} else {
return NO_DEVICES;
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] getAudioInputDevices() {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
} else {
return NO_DEVICES;
}
}
/**
* This method is called by SDL using JNI.
*/
public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
}
/**
@ -254,6 +360,11 @@ public class SDLAudioManager
return;
}
if (android.os.Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) {
Log.e(TAG, "Attempted to make an incompatible audio call with uninitialized audio! (floating-point output is supported since Android 5.0 Lollipop)");
return;
}
for (int i = 0; i < buffer.length;) {
int result = mAudioTrack.write(buffer, i, buffer.length - i, AudioTrack.WRITE_BLOCKING);
if (result > 0) {
@ -326,18 +437,22 @@ public class SDLAudioManager
/**
* This method is called by SDL using JNI.
*/
public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames);
public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
}
/** This method is called by SDL using JNI. */
public static int captureReadFloatBuffer(float[] buffer, boolean blocking) {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
return 0;
} else {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
}
}
/** This method is called by SDL using JNI. */
public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
if (Build.VERSION.SDK_INT < 23) {
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
return mAudioRecord.read(buffer, 0, buffer.length);
} else {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
@ -346,7 +461,7 @@ public class SDLAudioManager
/** This method is called by SDL using JNI. */
public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
if (Build.VERSION.SDK_INT < 23) {
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
return mAudioRecord.read(buffer, 0, buffer.length);
} else {
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
@ -391,4 +506,9 @@ public class SDLAudioManager
}
public static native int nativeSetupJNI();
public static native void removeAudioDevice(boolean isCapture, int deviceId);
public static native void addAudioDevice(boolean isCapture, int deviceId);
}

View File

@ -24,7 +24,7 @@ public class SDLControllerManager
public static native int nativeAddJoystick(int device_id, String name, String desc,
int vendor_id, int product_id,
boolean is_accelerometer, int button_mask,
int naxes, int nhats, int nballs);
int naxes, int axis_mask, int nhats, int nballs);
public static native int nativeRemoveJoystick(int device_id);
public static native int nativeAddHaptic(int device_id, String name);
public static native int nativeRemoveHaptic(int device_id);
@ -42,7 +42,7 @@ public class SDLControllerManager
public static void initialize() {
if (mJoystickHandler == null) {
if (Build.VERSION.SDK_INT >= 19) {
if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) {
mJoystickHandler = new SDLJoystickHandler_API19();
} else {
mJoystickHandler = new SDLJoystickHandler_API16();
@ -50,7 +50,7 @@ public class SDLControllerManager
}
if (mHapticHandler == null) {
if (Build.VERSION.SDK_INT >= 26) {
if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) {
mHapticHandler = new SDLHapticHandler_API26();
} else {
mHapticHandler = new SDLHapticHandler();
@ -168,6 +168,32 @@ class SDLJoystickHandler_API16 extends SDLJoystickHandler {
arg1Axis = MotionEvent.AXIS_GAS;
}
// Make sure the AXIS_Z is sorted between AXIS_RY and AXIS_RZ.
// This is because the usual pairing are:
// - AXIS_X + AXIS_Y (left stick).
// - AXIS_RX, AXIS_RY (sometimes the right stick, sometimes triggers).
// - AXIS_Z, AXIS_RZ (sometimes the right stick, sometimes triggers).
// This sorts the axes in the above order, which tends to be correct
// for Xbox-ish game pads that have the right stick on RX/RY and the
// triggers on Z/RZ.
//
// Gamepads that don't have AXIS_Z/AXIS_RZ but use
// AXIS_LTRIGGER/AXIS_RTRIGGER are unaffected by this.
//
// References:
// - https://developer.android.com/develop/ui/views/touch-and-input/game-controllers/controller-input
// - https://www.kernel.org/doc/html/latest/input/gamepad.html
if (arg0Axis == MotionEvent.AXIS_Z) {
arg0Axis = MotionEvent.AXIS_RZ - 1;
} else if (arg0Axis > MotionEvent.AXIS_Z && arg0Axis < MotionEvent.AXIS_RZ) {
--arg0Axis;
}
if (arg1Axis == MotionEvent.AXIS_Z) {
arg1Axis = MotionEvent.AXIS_RZ - 1;
} else if (arg1Axis > MotionEvent.AXIS_Z && arg1Axis < MotionEvent.AXIS_RZ) {
--arg1Axis;
}
return arg0Axis - arg1Axis;
}
}
@ -210,7 +236,7 @@ class SDLJoystickHandler_API16 extends SDLJoystickHandler {
mJoysticks.add(joystick);
SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc,
getVendorId(joystickDevice), getProductId(joystickDevice), false,
getButtonMask(joystickDevice), joystick.axes.size(), joystick.hats.size()/2, 0);
getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, 0);
}
}
}
@ -291,6 +317,9 @@ class SDLJoystickHandler_API16 extends SDLJoystickHandler {
public int getVendorId(InputDevice joystickDevice) {
return 0;
}
public int getAxisMask(List<InputDevice.MotionRange> ranges) {
return -1;
}
public int getButtonMask(InputDevice joystickDevice) {
return -1;
}
@ -308,6 +337,43 @@ class SDLJoystickHandler_API19 extends SDLJoystickHandler_API16 {
return joystickDevice.getVendorId();
}
@Override
public int getAxisMask(List<InputDevice.MotionRange> ranges) {
// For compatibility, keep computing the axis mask like before,
// only really distinguishing 2, 4 and 6 axes.
int axis_mask = 0;
if (ranges.size() >= 2) {
// ((1 << SDL_GAMEPAD_AXIS_LEFTX) | (1 << SDL_GAMEPAD_AXIS_LEFTY))
axis_mask |= 0x0003;
}
if (ranges.size() >= 4) {
// ((1 << SDL_GAMEPAD_AXIS_RIGHTX) | (1 << SDL_GAMEPAD_AXIS_RIGHTY))
axis_mask |= 0x000c;
}
if (ranges.size() >= 6) {
// ((1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER) | (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER))
axis_mask |= 0x0030;
}
// Also add an indicator bit for whether the sorting order has changed.
// This serves to disable outdated gamecontrollerdb.txt mappings.
boolean have_z = false;
boolean have_past_z_before_rz = false;
for (InputDevice.MotionRange range : ranges) {
int axis = range.getAxis();
if (axis == MotionEvent.AXIS_Z) {
have_z = true;
} else if (axis > MotionEvent.AXIS_Z && axis < MotionEvent.AXIS_RZ) {
have_past_z_before_rz = true;
}
}
if (have_z && have_past_z_before_rz) {
// If both these exist, the compare() function changed sorting order.
// Set a bit to indicate this fact.
axis_mask |= 0x8000;
}
return axis_mask;
}
@Override
public int getButtonMask(InputDevice joystickDevice) {
int button_mask = 0;
@ -743,7 +809,7 @@ class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
@Override
public boolean supportsRelativeMouse() {
return (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27));
return (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */);
}
@Override
@ -753,7 +819,7 @@ class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
@Override
public boolean setRelativeMouseEnabled(boolean enabled) {
if (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27)) {
if (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */) {
if (enabled) {
SDLActivity.getContentView().requestPointerCapture();
} else {

View File

@ -0,0 +1,405 @@
package org.libsdl.app;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Build;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
/**
SDLSurface. This is what we draw on, so we need to know when it's created
in order to do anything useful.
Because of this, that's where we set up the SDL thread
*/
public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
View.OnKeyListener, View.OnTouchListener, SensorEventListener {
// Sensors
protected SensorManager mSensorManager;
protected Display mDisplay;
// Keep track of the surface size to normalize touch events
protected float mWidth, mHeight;
// Is SurfaceView ready for rendering
public boolean mIsSurfaceReady;
// Startup
public SDLSurface(Context context) {
super(context);
getHolder().addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnKeyListener(this);
setOnTouchListener(this);
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
setOnGenericMotionListener(SDLActivity.getMotionListener());
// Some arbitrary defaults to avoid a potential division by zero
mWidth = 1.0f;
mHeight = 1.0f;
mIsSurfaceReady = false;
}
public void handlePause() {
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
}
public void handleResume() {
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnKeyListener(this);
setOnTouchListener(this);
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
}
public Surface getNativeSurface() {
return getHolder().getSurface();
}
// Called when we have a valid drawing surface
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.v("SDL", "surfaceCreated()");
SDLActivity.onNativeSurfaceCreated();
}
// Called when we lose the surface
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.v("SDL", "surfaceDestroyed()");
// Transition to pause, if needed
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
SDLActivity.handleNativeState();
mIsSurfaceReady = false;
SDLActivity.onNativeSurfaceDestroyed();
}
// Called when the surface is resized
@Override
public void surfaceChanged(SurfaceHolder holder,
int format, int width, int height) {
Log.v("SDL", "surfaceChanged()");
if (SDLActivity.mSingleton == null) {
return;
}
mWidth = width;
mHeight = height;
int nDeviceWidth = width;
int nDeviceHeight = height;
try
{
if (Build.VERSION.SDK_INT >= 17 /* Android 4.2 (JELLY_BEAN_MR1) */) {
DisplayMetrics realMetrics = new DisplayMetrics();
mDisplay.getRealMetrics( realMetrics );
nDeviceWidth = realMetrics.widthPixels;
nDeviceHeight = realMetrics.heightPixels;
}
} catch(Exception ignored) {
}
synchronized(SDLActivity.getContext()) {
// In case we're waiting on a size change after going fullscreen, send a notification.
SDLActivity.getContext().notifyAll();
}
Log.v("SDL", "Window size: " + width + "x" + height);
Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate());
SDLActivity.onNativeResize();
// Prevent a screen distortion glitch,
// for instance when the device is in Landscape and a Portrait App is resumed.
boolean skip = false;
int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
if (mWidth > mHeight) {
skip = true;
}
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
if (mWidth < mHeight) {
skip = true;
}
}
// Special Patch for Square Resolution: Black Berry Passport
if (skip) {
double min = Math.min(mWidth, mHeight);
double max = Math.max(mWidth, mHeight);
if (max / min < 1.20) {
Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
skip = false;
}
}
// Don't skip in MultiWindow.
if (skip) {
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
if (SDLActivity.mSingleton.isInMultiWindowMode()) {
Log.v("SDL", "Don't skip in Multi-Window");
skip = false;
}
}
}
if (skip) {
Log.v("SDL", "Skip .. Surface is not ready.");
mIsSurfaceReady = false;
return;
}
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
SDLActivity.onNativeSurfaceChanged();
/* Surface is ready */
mIsSurfaceReady = true;
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
SDLActivity.handleNativeState();
}
// Key events
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
return SDLActivity.handleKeyEvent(v, keyCode, event, null);
}
// Touch events
@Override
public boolean onTouch(View v, MotionEvent event) {
/* Ref: http://developer.android.com/training/gestures/multi.html */
int touchDevId = event.getDeviceId();
final int pointerCount = event.getPointerCount();
int action = event.getActionMasked();
int pointerFingerId;
int i = -1;
float x,y,p;
/*
* Prevent id to be -1, since it's used in SDL internal for synthetic events
* Appears when using Android emulator, eg:
* adb shell input mouse tap 100 100
* adb shell input touchscreen tap 100 100
*/
if (touchDevId < 0) {
touchDevId -= 1;
}
// 12290 = Samsung DeX mode desktop mouse
// 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN
// 0x2 = SOURCE_CLASS_POINTER
if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) {
int mouseButton = 1;
try {
Object object = event.getClass().getMethod("getButtonState").invoke(event);
if (object != null) {
mouseButton = (Integer) object;
}
} catch(Exception ignored) {
}
// We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
// if we are. We'll leverage our existing mouse motion listener
SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
x = motionListener.getEventX(event);
y = motionListener.getEventY(event);
SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
} else {
switch(action) {
case MotionEvent.ACTION_MOVE:
for (i = 0; i < pointerCount; i++) {
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_DOWN:
// Primary pointer up/down, the index is always zero
i = 0;
/* fallthrough */
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN:
// Non primary pointer up/down
if (i == -1) {
i = event.getActionIndex();
}
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
break;
case MotionEvent.ACTION_CANCEL:
for (i = 0; i < pointerCount; i++) {
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
}
break;
default:
break;
}
}
return true;
}
// Sensor events
public void enableSensor(int sensortype, boolean enabled) {
// TODO: This uses getDefaultSensor - what if we have >1 accels?
if (enabled) {
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(sensortype),
SensorManager.SENSOR_DELAY_GAME, null);
} else {
mSensorManager.unregisterListener(this,
mSensorManager.getDefaultSensor(sensortype));
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
// Since we may have an orientation set, we won't receive onConfigurationChanged events.
// We thus should check here.
int newOrientation;
float x, y;
switch (mDisplay.getRotation()) {
case Surface.ROTATION_90:
x = -event.values[1];
y = event.values[0];
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
break;
case Surface.ROTATION_270:
x = event.values[1];
y = -event.values[0];
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
break;
case Surface.ROTATION_180:
x = -event.values[0];
y = -event.values[1];
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
break;
case Surface.ROTATION_0:
default:
x = event.values[0];
y = event.values[1];
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
break;
}
if (newOrientation != SDLActivity.mCurrentOrientation) {
SDLActivity.mCurrentOrientation = newOrientation;
SDLActivity.onNativeOrientationChanged(newOrientation);
}
SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
y / SensorManager.GRAVITY_EARTH,
event.values[2] / SensorManager.GRAVITY_EARTH);
}
}
// Captured pointer events for API 26.
public boolean onCapturedPointerEvent(MotionEvent event)
{
int action = event.getActionMasked();
float x, y;
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
case MotionEvent.ACTION_BUTTON_PRESS:
case MotionEvent.ACTION_BUTTON_RELEASE:
// Change our action value to what SDL's code expects.
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
action = MotionEvent.ACTION_DOWN;
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
action = MotionEvent.ACTION_UP;
}
x = event.getX(0);
y = event.getY(0);
int button = event.getButtonState();
SDLActivity.onNativeMouse(button, action, x, y, true);
return true;
}
return false;
}
}

View File

@ -32,3 +32,6 @@ a sound and input chip developed by Atari for their 8-bit computers (Atari 400,
- filtered output on channel 2 (I suggest you to set channel 4 volume to 0).
- use for PWM effects (not automatic!).
- bit 0: 15KHz mode.
- 12xx: toggle two-tone mode.
- when enabled, channel 2 modulates channel 1. I don't know how, but it does.
- only on ASAP core.

View File

@ -316,27 +316,27 @@ void CSAADevice::_TickAndOutputStereo(unsigned int& left_mixed, unsigned int& ri
m_Noise0.Tick();
m_Noise1.Tick();
m_Amp0.TickAndOutputStereo(temp_left, temp_right);
oscBuf[0]->data[oscBuf[0]->needle++]=(temp_left+temp_right)<<3;
oscBuf[0]->data[oscBuf[0]->needle++]=(temp_left+temp_right)<<5;
accum_left += temp_left;
accum_right += temp_right;
m_Amp1.TickAndOutputStereo(temp_left, temp_right);
oscBuf[1]->data[oscBuf[1]->needle++]=(temp_left+temp_right)<<3;
oscBuf[1]->data[oscBuf[1]->needle++]=(temp_left+temp_right)<<5;
accum_left += temp_left;
accum_right += temp_right;
m_Amp2.TickAndOutputStereo(temp_left, temp_right);
oscBuf[2]->data[oscBuf[2]->needle++]=(temp_left+temp_right)<<3;
oscBuf[2]->data[oscBuf[2]->needle++]=(temp_left+temp_right)<<5;
accum_left += temp_left;
accum_right += temp_right;
m_Amp3.TickAndOutputStereo(temp_left, temp_right);
oscBuf[3]->data[oscBuf[3]->needle++]=(temp_left+temp_right)<<3;
oscBuf[3]->data[oscBuf[3]->needle++]=(temp_left+temp_right)<<5;
accum_left += temp_left;
accum_right += temp_right;
m_Amp4.TickAndOutputStereo(temp_left, temp_right);
oscBuf[4]->data[oscBuf[4]->needle++]=(temp_left+temp_right)<<3;
oscBuf[4]->data[oscBuf[4]->needle++]=(temp_left+temp_right)<<5;
accum_left += temp_left;
accum_right += temp_right;
m_Amp5.TickAndOutputStereo(temp_left, temp_right);
oscBuf[5]->data[oscBuf[5]->needle++]=(temp_left+temp_right)<<3;
oscBuf[5]->data[oscBuf[5]->needle++]=(temp_left+temp_right)<<5;
accum_left += temp_left;
accum_right += temp_right;
}

2
extern/SDL vendored

@ -1 +1 @@
Subproject commit 55b03c7493a7abed33cf803d1380a40fa8af903f
Subproject commit ffa78e6bead23e2ba3adf8ec2367ff2218d4343c

View File

@ -1137,8 +1137,9 @@ namespace IGFD
IGFD::FileManager::FileManager()
{
puFsRoot = std::string(1u, PATH_SEP);
for (int i=0; i<4; i++) {
puSortingDirection[i]=true;
puSortingDirection[0]=true;
for (int i=1; i<4; i++) {
puSortingDirection[i]=false;
}
}

View File

@ -5683,7 +5683,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) {
SafeWriter* DivEngine::saveDMF(unsigned char version) {
// fail if version is not supported
if (version<24 || version>26) {
if (version>26) version=26;
if (version<24) {
logE("cannot save in this version!");
lastError="invalid version to save in! this is a bug!";
return NULL;
@ -5817,7 +5818,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
w->writeC(curSubSong->timeBase);
w->writeC(curSubSong->speeds.val[0]);
w->writeC((curSubSong->speeds.len>=2)?curSubSong->speeds.val[1]:curSubSong->speeds.val[0]);
w->writeC((intHz<=53)?1:0);
w->writeC((intHz<=53)?0:1);
w->writeC((intHz!=60 && intHz!=50));
char customHz[4];
memset(customHz,0,4);

View File

@ -76,7 +76,8 @@ void DivPlatformArcade::acquire_nuked(short** buf, size_t len) {
}
for (int i=0; i<8; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]>>1;
int chOut=(int16_t)fm.ch_out[i];
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut<<1,-32768,32767);
}
if (o[0]<-32768) o[0]=-32768;
@ -111,7 +112,8 @@ void DivPlatformArcade::acquire_ymfm(short** buf, size_t len) {
fm_ymfm->generate(&out_ymfm);
for (int i=0; i<8; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))>>1;
int chOut=fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1);
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut,-32768,32767);
}
os[0]=out_ymfm.data[0];

View File

@ -187,9 +187,9 @@ void DivPlatformAY8910::acquire(short** buf, size_t len) {
buf[0][i]=ayBuf[0][0];
buf[1][i]=buf[0][i];
oscBuf[0]->data[oscBuf[0]->needle++]=sunsoftVolTable[31-(ay->lastIndx&31)]<<2;
oscBuf[1]->data[oscBuf[1]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>5)&31)]<<2;
oscBuf[2]->data[oscBuf[2]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>10)&31)]<<2;
oscBuf[0]->data[oscBuf[0]->needle++]=CLAMP(sunsoftVolTable[31-(ay->lastIndx&31)]<<3,-32768,32767);
oscBuf[1]->data[oscBuf[1]->needle++]=CLAMP(sunsoftVolTable[31-((ay->lastIndx>>5)&31)]<<3,-32768,32767);
oscBuf[2]->data[oscBuf[2]->needle++]=CLAMP(sunsoftVolTable[31-((ay->lastIndx>>10)&31)]<<3,-32768,32767);
}
} else {
for (size_t i=0; i<len; i++) {
@ -205,9 +205,9 @@ void DivPlatformAY8910::acquire(short** buf, size_t len) {
buf[1][i]=buf[0][i];
}
oscBuf[0]->data[oscBuf[0]->needle++]=ayBuf[0][0]<<1;
oscBuf[1]->data[oscBuf[1]->needle++]=ayBuf[1][0]<<1;
oscBuf[2]->data[oscBuf[2]->needle++]=ayBuf[2][0]<<1;
oscBuf[0]->data[oscBuf[0]->needle++]=ayBuf[0][0]<<2;
oscBuf[1]->data[oscBuf[1]->needle++]=ayBuf[1][0]<<2;
oscBuf[2]->data[oscBuf[2]->needle++]=ayBuf[2][0]<<2;
}
}
}

View File

@ -186,9 +186,9 @@ void DivPlatformAY8930::acquire(short** buf, size_t len) {
buf[1][i]=buf[0][i];
}
oscBuf[0]->data[oscBuf[0]->needle++]=ayBuf[0][0]<<1;
oscBuf[1]->data[oscBuf[1]->needle++]=ayBuf[1][0]<<1;
oscBuf[2]->data[oscBuf[2]->needle++]=ayBuf[2][0]<<1;
oscBuf[0]->data[oscBuf[0]->needle++]=ayBuf[0][0]<<2;
oscBuf[1]->data[oscBuf[1]->needle++]=ayBuf[1][0]<<2;
oscBuf[2]->data[oscBuf[2]->needle++]=ayBuf[2][0]<<2;
}
}

View File

@ -55,7 +55,7 @@ void DivPlatformBubSysWSG::acquire(short** buf, size_t len) {
chanOut=chan[i].waveROM[k005289.addr(i)]*(regPool[2+i]&0xf);
out+=chanOut;
if (writeOscBuf==0) {
oscBuf[i]->data[oscBuf[i]->needle++]=chanOut<<6;
oscBuf[i]->data[oscBuf[i]->needle++]=chanOut<<7;
}
}
}

View File

@ -80,18 +80,18 @@ void DivPlatformC64::acquire(short** buf, size_t len) {
sid_fp.clock(4,&buf[0][i]);
if (++writeOscBuf>=4) {
writeOscBuf=0;
oscBuf[0]->data[oscBuf[0]->needle++]=(sid_fp.lastChanOut[0]-dcOff)>>6;
oscBuf[1]->data[oscBuf[1]->needle++]=(sid_fp.lastChanOut[1]-dcOff)>>6;
oscBuf[2]->data[oscBuf[2]->needle++]=(sid_fp.lastChanOut[2]-dcOff)>>6;
oscBuf[0]->data[oscBuf[0]->needle++]=(sid_fp.lastChanOut[0]-dcOff)>>5;
oscBuf[1]->data[oscBuf[1]->needle++]=(sid_fp.lastChanOut[1]-dcOff)>>5;
oscBuf[2]->data[oscBuf[2]->needle++]=(sid_fp.lastChanOut[2]-dcOff)>>5;
}
} else {
sid.clock();
buf[0][i]=sid.output();
if (++writeOscBuf>=16) {
writeOscBuf=0;
oscBuf[0]->data[oscBuf[0]->needle++]=(sid.last_chan_out[0]-dcOff)>>6;
oscBuf[1]->data[oscBuf[1]->needle++]=(sid.last_chan_out[1]-dcOff)>>6;
oscBuf[2]->data[oscBuf[2]->needle++]=(sid.last_chan_out[2]-dcOff)>>6;
oscBuf[0]->data[oscBuf[0]->needle++]=(sid.last_chan_out[0]-dcOff)>>5;
oscBuf[1]->data[oscBuf[1]->needle++]=(sid.last_chan_out[1]-dcOff)>>5;
oscBuf[2]->data[oscBuf[2]->needle++]=(sid.last_chan_out[2]-dcOff)>>5;
}
}
}

View File

@ -32,7 +32,7 @@ void DivPlatformDummy::acquire(short** buf, size_t len) {
if (chan[j].active) {
if (!isMuted[j]) {
chanOut=(((signed short)chan[j].pos)*chan[j].amp*chan[j].vol)>>12;
oscBuf[j]->data[oscBuf[j]->needle++]=chanOut>>1;
oscBuf[j]->data[oscBuf[j]->needle++]=chanOut<<1;
out+=chanOut;
} else {
oscBuf[j]->data[oscBuf[j]->needle++]=0;

View File

@ -64,7 +64,7 @@ void DivPlatformFDS::acquire_puNES(short* buf, size_t len) {
buf[i]=sample;
if (++writeOscBuf>=32) {
writeOscBuf=0;
oscBuf->data[oscBuf->needle++]=sample;
oscBuf->data[oscBuf->needle++]=sample*3;
}
}
}
@ -80,7 +80,7 @@ void DivPlatformFDS::acquire_NSFPlay(short* buf, size_t len) {
buf[i]=sample;
if (++writeOscBuf>=32) {
writeOscBuf=0;
oscBuf->data[oscBuf->needle++]=sample;
oscBuf->data[oscBuf->needle++]=sample*3;
}
}
}

View File

@ -71,8 +71,13 @@ void DivPlatformGA20::acquire(short** buf, size_t len) {
delay=w.delay;
}
}
short *buffer[4] = {&ga20Buf[0][h],&ga20Buf[1][h],&ga20Buf[2][h],&ga20Buf[3][h]};
ga20.sound_stream_update(buffer, 1);
short *buffer[4]={
&ga20Buf[0][h],
&ga20Buf[1][h],
&ga20Buf[2][h],
&ga20Buf[3][h]
};
ga20.sound_stream_update(buffer,1);
buf[0][h]=(signed int)(ga20Buf[0][h]+ga20Buf[1][h]+ga20Buf[2][h]+ga20Buf[3][h])>>2;
for (int i=0; i<4; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=ga20Buf[i][h]>>1;

View File

@ -74,7 +74,7 @@ void DivPlatformGB::acquire(short** buf, size_t len) {
buf[1][i]=gb->apu_output.final_sample.right;
for (int i=0; i<4; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(gb->apu_output.current_sample[i].left+gb->apu_output.current_sample[i].right)<<5;
oscBuf[i]->data[oscBuf[i]->needle++]=(gb->apu_output.current_sample[i].left+gb->apu_output.current_sample[i].right)<<6;
}
}
}

View File

@ -122,6 +122,8 @@ void DivPlatformGenesis::processDAC(int iRate) {
urgentWrite(0x2a,(unsigned char)sample+0x80);
chan[5].dacReady=false;
}
} else {
urgentWrite(0x2a,0x80);
}
chan[5].dacPos++;
if (!chan[5].dacDirection && (s->isLoopable() && chan[5].dacPos>=(unsigned int)s->loopEnd)) {
@ -187,7 +189,7 @@ void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) {
oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<6;
oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<6;
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=fm.dacdata<<6;
oscBuf[i]->data[oscBuf[i]->needle++]=((fm.dacdata^0x100)-0x100)<<6;
oscBuf[6]->data[oscBuf[6]->needle++]=0;
}
} else {
@ -252,7 +254,7 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) {
oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<6;
oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<6;
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=fm_ymfm->debug_dac_data()<<6;
oscBuf[i]->data[oscBuf[i]->needle++]=((fm_ymfm->debug_dac_data()^0x100)-0x100)<<6;
oscBuf[6]->data[oscBuf[6]->needle++]=0;
}
} else {

View File

@ -85,9 +85,9 @@ void DivPlatformMMC5::acquire(short** buf, size_t len) {
if (++writeOscBuf>=32) {
writeOscBuf=0;
oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:((mmc5->S3.output*10)<<6);
oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:((mmc5->S4.output*10)<<6);
oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:((mmc5->pcm.output*2)<<5);
oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:((mmc5->S3.output)<<11);
oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:((mmc5->S4.output)<<11);
oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:((mmc5->pcm.output)<<7);
}
}
}

View File

@ -118,7 +118,7 @@ void DivPlatformN163::acquire(short** buf, size_t len) {
buf[0][i]=out;
if (n163.voice_cycle()==0x78) for (int i=0; i<8; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=n163.voice_out(i)<<6;
oscBuf[i]->data[oscBuf[i]->needle++]=n163.voice_out(i)<<7;
}
// command queue

View File

@ -115,11 +115,11 @@ void DivPlatformNES::acquire_puNES(short** buf, size_t len) {
buf[0][i]=sample;
if (++writeOscBuf>=32) {
writeOscBuf=0;
oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:(nes->S1.output<<10);
oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:(nes->S2.output<<10);
oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:(nes->TR.output<<10);
oscBuf[3]->data[oscBuf[3]->needle++]=isMuted[3]?0:(nes->NS.output<<10);
oscBuf[4]->data[oscBuf[4]->needle++]=isMuted[4]?0:(nes->DMC.output<<7);
oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:(nes->S1.output<<11);
oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:(nes->S2.output<<11);
oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:(nes->TR.output<<11);
oscBuf[3]->data[oscBuf[3]->needle++]=isMuted[3]?0:(nes->NS.output<<11);
oscBuf[4]->data[oscBuf[4]->needle++]=isMuted[4]?0:(nes->DMC.output<<8);
}
}
}
@ -142,11 +142,11 @@ void DivPlatformNES::acquire_NSFPlay(short** buf, size_t len) {
buf[0][i]=sample;
if (++writeOscBuf>=32) {
writeOscBuf=0;
oscBuf[0]->data[oscBuf[0]->needle++]=nes1_NP->out[0]<<10;
oscBuf[1]->data[oscBuf[1]->needle++]=nes1_NP->out[1]<<10;
oscBuf[2]->data[oscBuf[2]->needle++]=nes2_NP->out[0]<<10;
oscBuf[3]->data[oscBuf[3]->needle++]=nes2_NP->out[1]<<10;
oscBuf[4]->data[oscBuf[4]->needle++]=nes2_NP->out[2]<<7;
oscBuf[0]->data[oscBuf[0]->needle++]=nes1_NP->out[0]<<11;
oscBuf[1]->data[oscBuf[1]->needle++]=nes1_NP->out[1]<<11;
oscBuf[2]->data[oscBuf[2]->needle++]=nes2_NP->out[0]<<11;
oscBuf[3]->data[oscBuf[3]->needle++]=nes2_NP->out[1]<<12;
oscBuf[4]->data[oscBuf[4]->needle++]=nes2_NP->out[2]<<8;
}
}
}

View File

@ -220,45 +220,45 @@ void DivPlatformOPL::acquire_nuked(short** buf, size_t len) {
if (fm.rhy&0x20) {
for (int i=0; i<melodicChans+1; i++) {
unsigned char ch=outChanMap[i];
int chOut=0;
if (ch==255) continue;
oscBuf[i]->data[oscBuf[i]->needle]=0;
if (fm.channel[i].out[0]!=NULL) {
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[0];
chOut+=*fm.channel[ch].out[0];
}
if (fm.channel[i].out[1]!=NULL) {
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[1];
chOut+=*fm.channel[ch].out[1];
}
if (fm.channel[i].out[2]!=NULL) {
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[2];
chOut+=*fm.channel[ch].out[2];
}
if (fm.channel[i].out[3]!=NULL) {
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[3];
chOut+=*fm.channel[ch].out[3];
}
oscBuf[i]->needle++;
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut<<(i==melodicChans?1:2),-32768,32767);
}
// special
oscBuf[melodicChans+1]->data[oscBuf[melodicChans+1]->needle++]=fm.slot[16].out*3;
oscBuf[melodicChans+2]->data[oscBuf[melodicChans+2]->needle++]=fm.slot[14].out*3;
oscBuf[melodicChans+3]->data[oscBuf[melodicChans+3]->needle++]=fm.slot[17].out*3;
oscBuf[melodicChans+4]->data[oscBuf[melodicChans+4]->needle++]=fm.slot[13].out*3;
oscBuf[melodicChans+1]->data[oscBuf[melodicChans+1]->needle++]=fm.slot[16].out*4;
oscBuf[melodicChans+2]->data[oscBuf[melodicChans+2]->needle++]=fm.slot[14].out*4;
oscBuf[melodicChans+3]->data[oscBuf[melodicChans+3]->needle++]=fm.slot[17].out*4;
oscBuf[melodicChans+4]->data[oscBuf[melodicChans+4]->needle++]=fm.slot[13].out*4;
} else {
for (int i=0; i<chans; i++) {
unsigned char ch=outChanMap[i];
int chOut=0;
if (ch==255) continue;
oscBuf[i]->data[oscBuf[i]->needle]=0;
if (fm.channel[i].out[0]!=NULL) {
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[0];
chOut+=*fm.channel[ch].out[0];
}
if (fm.channel[i].out[1]!=NULL) {
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[1];
chOut+=*fm.channel[ch].out[1];
}
if (fm.channel[i].out[2]!=NULL) {
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[2];
chOut+=*fm.channel[ch].out[2];
}
if (fm.channel[i].out[3]!=NULL) {
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[3];
chOut+=*fm.channel[ch].out[3];
}
oscBuf[i]->needle++;
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut<<2,-32768,32767);
}
}

View File

@ -68,7 +68,7 @@ void DivPlatformOPLL::acquire_nuked(short** buf, size_t len) {
unsigned char nextOut=cycleMapOPLL[fm.cycles];
if ((nextOut>=6 && properDrums) || !isMuted[nextOut]) {
os+=(o[0]+o[1]);
if (vrc7 || (fm.rm_enable&0x20)) oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=(o[0]+o[1])<<5;
if (vrc7 || (fm.rm_enable&0x20)) oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=(o[0]+o[1])<<6;
} else {
if (vrc7 || (fm.rm_enable&0x20)) oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=0;
}
@ -76,7 +76,7 @@ void DivPlatformOPLL::acquire_nuked(short** buf, size_t len) {
if (!(vrc7 || (fm.rm_enable&0x20))) for (int i=0; i<9; i++) {
unsigned char ch=visMapOPLL[i];
if ((i>=6 && properDrums) || !isMuted[ch]) {
oscBuf[ch]->data[oscBuf[ch]->needle++]=(fm.output_ch[i])<<5;
oscBuf[ch]->data[oscBuf[ch]->needle++]=(fm.output_ch[i])<<6;
} else {
oscBuf[ch]->data[oscBuf[ch]->needle++]=0;
}
@ -107,7 +107,6 @@ void DivPlatformOPLL::tick(bool sysTick) {
rWrite(0x36,drumVol[0]);
rWrite(0x37,drumVol[1]|(drumVol[4]<<4));
rWrite(0x38,drumVol[3]|(drumVol[2]<<4));
break;
} else if (i<6 || !drums) {
if (i<9) {
rWrite(0x30+i,((15-VOL_SCALE_LOG_BROKEN(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4));

View File

@ -35,6 +35,7 @@ const char* regCheatSheetPOKEY[]={
"AUDF4", "6",
"AUDC4", "7",
"AUDCTL", "8",
"SKCTL", "F",
NULL
};
@ -154,6 +155,11 @@ void DivPlatformPOKEY::tick(bool sysTick) {
}
}
if (skctlChanged) {
skctlChanged=false;
rWrite(15,skctl);
}
for (int i=0; i<4; i++) {
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,parent->song.linearPitch?chan[i].pitch:0,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,parent->song.linearPitch?chan[i].pitch2:0,chipClock,CHIP_DIVIDER);
@ -320,6 +326,10 @@ int DivPlatformPOKEY::dispatch(DivCommand c) {
audctl=c.value&0xff;
audctlChanged=true;
break;
case DIV_CMD_STD_NOISE_FREQ:
skctl=c.value?0x8b:0x03;
skctlChanged=true;
break;
case DIV_CMD_NOTE_PORTA: {
int destFreq=NOTE_PERIODIC(c.value2);
bool return2=false;
@ -385,6 +395,7 @@ void DivPlatformPOKEY::forceIns() {
chan[i].freqChanged=true;
}
audctlChanged=true;
skctlChanged=true;
}
void* DivPlatformPOKEY::getChanState(int ch) {
@ -408,7 +419,7 @@ unsigned char* DivPlatformPOKEY::getRegisterPool() {
}
int DivPlatformPOKEY::getRegisterPoolSize() {
return 9;
return 16;
}
void DivPlatformPOKEY::reset() {
@ -430,6 +441,8 @@ void DivPlatformPOKEY::reset() {
audctl=0;
audctlChanged=true;
skctl=3;
skctlChanged=true;
}
bool DivPlatformPOKEY::keyOffAffectsArp(int ch) {

View File

@ -48,8 +48,8 @@ class DivPlatformPOKEY: public DivDispatch {
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
};
std::queue<QueuedWrite> writes;
unsigned char audctl;
bool audctlChanged;
unsigned char audctl, skctl;
bool audctlChanged, skctlChanged;
unsigned char oscBufDelay;
PokeyState pokey;
AltASAP::Pokey altASAP;

View File

@ -42,7 +42,7 @@ void DivPlatformPV1000::acquire(short** buf, size_t len) {
short samp=d65010g031_sound_tick(&d65010g031,1);
buf[0][h]=samp;
for (int i=0; i<3; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(d65010g031.out[i]);
oscBuf[i]->data[oscBuf[i]->needle++]=d65010g031.out[i]<<1;
}
}
}

View File

@ -87,7 +87,7 @@ void DivPlatformSCC::acquire(short** buf, size_t len) {
buf[0][h]=out;
for (int i=0; i<5; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=scc->voice_out(i)<<6;
oscBuf[i]->data[oscBuf[i]->needle++]=scc->voice_out(i)<<7;
}
}
}

View File

@ -58,9 +58,9 @@ void DivPlatformSM8521::acquire(short** buf, size_t len) {
sm8521_sound_tick(&sm8521,8);
buf[0][h]=sm8521.out<<6;
for (int i=0; i<2; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=sm8521.sg[i].base.out<<5;
oscBuf[i]->data[oscBuf[i]->needle++]=sm8521.sg[i].base.out<<7;
}
oscBuf[2]->data[oscBuf[2]->needle++]=sm8521.noise.base.out<<5;
oscBuf[2]->data[oscBuf[2]->needle++]=sm8521.noise.base.out<<7;
}
}

View File

@ -509,7 +509,7 @@ public:
}
if (oscb!=NULL) {
oscb[i]->data[oscb[i]->needle++]=oscbWrite>>1;
oscb[i]->data[oscb[i]->needle++]=oscbWrite;
}
}

View File

@ -39,7 +39,7 @@ static constexpr int MuteInit = 2;
static constexpr int MuteSerialInput = 8;
//just some magick value to match the audio level of mzpokeysnd
static constexpr int16_t MAGICK_VOLUME_BOOSTER = 160;
static constexpr int16_t MAGICK_OSC_VOLUME_BOOSTER = 2;
static constexpr int16_t MAGICK_OSC_VOLUME_BOOSTER = 6;
struct PokeyBase
{

View File

@ -87,7 +87,7 @@ void DivPlatformSwan::acquire(short** buf, size_t len) {
buf[0][h]=samp[0];
buf[1][h]=samp[1];
for (int i=0; i<4; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(ws->sample_cache[i][0]+ws->sample_cache[i][1])<<5;
oscBuf[i]->data[oscBuf[i]->needle++]=(ws->sample_cache[i][0]+ws->sample_cache[i][1])<<6;
}
}
}

View File

@ -54,7 +54,7 @@ void DivPlatformT6W28::acquire(short** buf, size_t len) {
tempL=0;
tempR=0;
for (int i=0; i<4; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(out[i][1].curValue+out[i][2].curValue)<<6;
oscBuf[i]->data[oscBuf[i]->needle++]=(out[i][1].curValue+out[i][2].curValue)<<7;
tempL+=out[i][1].curValue<<7;
tempR+=out[i][2].curValue<<7;
}

View File

@ -51,8 +51,8 @@ void DivPlatformTIA::acquire(short** buf, size_t len) {
}
if (++chanOscCounter>=114) {
chanOscCounter=0;
oscBuf[0]->data[oscBuf[0]->needle++]=tia.myChannelOut[0]>>1;
oscBuf[1]->data[oscBuf[1]->needle++]=tia.myChannelOut[1]>>1;
oscBuf[0]->data[oscBuf[0]->needle++]=tia.myChannelOut[0];
oscBuf[1]->data[oscBuf[1]->needle++]=tia.myChannelOut[1];
}
}
}

View File

@ -78,7 +78,7 @@ void DivPlatformTX81Z::acquire(short** buf, size_t len) {
fm_ymfm->generate(&out_ymfm);
for (int i=0; i<8; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))>>1;
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1),-32768,32767);
}
os[0]=out_ymfm.data[0];

View File

@ -107,7 +107,7 @@ void DivPlatformVB::acquire(short** buf, size_t len) {
tempL=0;
tempR=0;
for (int i=0; i<6; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(vb->last_output[i][0]+vb->last_output[i][1])*4;
oscBuf[i]->data[oscBuf[i]->needle++]=(vb->last_output[i][0]+vb->last_output[i][1])*8;
tempL+=vb->last_output[i][0];
tempR+=vb->last_output[i][1];
}

View File

@ -69,7 +69,7 @@ void DivPlatformVIC20::acquire(short** buf, size_t len) {
vic_sound_machine_calculate_samples(vic,&samp,1,1,0,SAMP_DIVIDER);
buf[0][h]=samp;
for (int i=0; i<4; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=vic->ch[i].out?(vic->volume<<10):0;
oscBuf[i]->data[oscBuf[i]->needle++]=vic->ch[i].out?(vic->volume<<11):0;
}
}
}

View File

@ -87,9 +87,9 @@ void DivPlatformVRC6::acquire(short** buf, size_t len) {
if (++writeOscBuf>=32) {
writeOscBuf=0;
for (int i=0; i<2; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=vrc6.pulse_out(i)<<9;
oscBuf[i]->data[oscBuf[i]->needle++]=vrc6.pulse_out(i)<<11;
}
oscBuf[2]->data[oscBuf[2]->needle++]=vrc6.sawtooth_out()<<9;
oscBuf[2]->data[oscBuf[2]->needle++]=vrc6.sawtooth_out()<<10;
}
// Command part

View File

@ -231,11 +231,11 @@ void DivPlatformYM2203::acquire_combo(short** buf, size_t len) {
buf[0][h]=os;
for (int i=0; i<3; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]>>1;
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]<<1;
}
for (int i=3; i<6; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2]>>1;
oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2]<<1;
}
}
}
@ -282,11 +282,11 @@ void DivPlatformYM2203::acquire_ymfm(short** buf, size_t len) {
for (int i=0; i<3; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))>>1;
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))<<1;
}
for (int i=3; i<6; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2]>>1;
oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2]<<1;
}
}
}

View File

@ -402,12 +402,12 @@ void DivPlatformYM2608::acquire_combo(short** buf, size_t len) {
for (int i=0; i<psgChanOffs; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]>>1;
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]<<1;
}
ssge->get_last_out(ssgOut);
for (int i=psgChanOffs; i<adpcmAChanOffs; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1;
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]<<1;
}
for (int i=adpcmAChanOffs; i<adpcmBChanOffs; i++) {
@ -471,12 +471,12 @@ void DivPlatformYM2608::acquire_ymfm(short** buf, size_t len) {
buf[1][h]=os[1];
for (int i=0; i<6; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))>>1;
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))<<1;
}
ssge->get_last_out(ssgOut);
for (int i=6; i<9; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-6]>>1;
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-6]<<1;
}
for (int i=9; i<15; i++) {

View File

@ -333,12 +333,12 @@ void DivPlatformYM2610::acquire_combo(short** buf, size_t len) {
for (int i=0; i<psgChanOffs; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[bchOffs[i]]>>1;
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[bchOffs[i]]<<1;
}
ssge->get_last_out(ssgOut);
for (int i=psgChanOffs; i<adpcmAChanOffs; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1;
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]<<1;
}
for (int i=adpcmAChanOffs; i<adpcmBChanOffs; i++) {
@ -404,12 +404,12 @@ void DivPlatformYM2610::acquire_ymfm(short** buf, size_t len) {
buf[1][h]=os[1];
for (int i=0; i<psgChanOffs; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))>>1;
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))<<1;
}
ssge->get_last_out(ssgOut);
for (int i=psgChanOffs; i<adpcmAChanOffs; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1;
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]<<1;
}
for (int i=adpcmAChanOffs; i<adpcmBChanOffs; i++) {

View File

@ -401,12 +401,12 @@ void DivPlatformYM2610B::acquire_combo(short** buf, size_t len) {
for (int i=0; i<psgChanOffs; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]>>1;
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]<<1;
}
ssge->get_last_out(ssgOut);
for (int i=psgChanOffs; i<adpcmAChanOffs; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1;
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]<<1;
}
for (int i=adpcmAChanOffs; i<adpcmBChanOffs; i++) {
@ -471,12 +471,12 @@ void DivPlatformYM2610B::acquire_ymfm(short** buf, size_t len) {
for (int i=0; i<psgChanOffs; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))>>1;
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))<<1;
}
ssge->get_last_out(ssgOut);
for (int i=psgChanOffs; i<adpcmAChanOffs; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1;
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]<<1;
}
for (int i=adpcmAChanOffs; i<adpcmBChanOffs; i++) {

View File

@ -1188,6 +1188,11 @@ void DivEngine::nextRow() {
nextSpeed=speeds.val[curSpeed];
}
/*
if (skipping) {
ticks=1;
}*/
// post row details
for (int i=0; i<chans; i++) {
DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][curOrder],false);
@ -1314,7 +1319,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
subticks=tickMult;
if (stepPlay!=1) {
tempoAccum+=curSubSong->virtualTempoN;
tempoAccum+=(skipping && curSubSong->virtualTempoN<curSubSong->virtualTempoD)?curSubSong->virtualTempoD:curSubSong->virtualTempoN;
while (tempoAccum>=curSubSong->virtualTempoD) {
tempoAccum-=curSubSong->virtualTempoD;
if (--ticks<=0) {
@ -1564,8 +1569,10 @@ void DivEngine::runMidiClock(int totalCycles) {
if (speedSum<1.0) speedSum=1.0;
if (vD<1) vD=1;
double bpm=((24.0*divider)/(timeBase*hl*speedSum))*(double)curSubSong->virtualTempoN/vD;
if (bpm<1.0) bpm=1.0;
int increment=got.rate*pow(2,MASTER_CLOCK_PREC)/(bpm);
midiClockCycles+=got.rate*pow(2,MASTER_CLOCK_PREC)/(bpm);
midiClockCycles+=increment;
midiClockDrift+=fmod(got.rate*pow(2,MASTER_CLOCK_PREC),(double)(bpm));
if (midiClockDrift>=(bpm)) {
midiClockDrift-=(bpm);

View File

@ -1169,6 +1169,7 @@ void DivEngine::registerSystems() {
{
{0x10, {DIV_CMD_WAVE, "10xx: Set waveform (0 to 7)"}},
{0x11, {DIV_CMD_STD_NOISE_MODE, "11xx: Set AUDCTL"}},
{0x12, {DIV_CMD_STD_NOISE_FREQ, "12xx: Toggle two-tone mode"}},
}
);

View File

@ -22,6 +22,7 @@
#include "../ta-log.h"
#include "imgui.h"
#include "imgui_internal.h"
#include "misc/cpp/imgui_stdlib.h"
#define FURNACE_FFT_SIZE 4096
#define FURNACE_FFT_RATE 80.0
@ -284,159 +285,266 @@ void FurnaceGUI::drawChanOsc() {
ImGui::ColorPicker4("Color",(float*)&chanOscColor);
}
ImGui::Text("Text format:");
ImGui::SameLine();
ImGui::InputText("##TextFormat",&chanOscTextFormat);
if (ImGui::IsItemHovered()) {
if (ImGui::BeginTooltip()) {
ImGui::TextUnformatted(
"format guide:\n"
"- %c: channel name\n"
"- %C: channel short name\n"
"- %d: channel number (starting from 0)\n"
"- %D: channel number (starting from 1)\n"
"- %i: instrument name\n"
"- %I: instrument number (decimal)\n"
"- %x: instrument number (hex)\n"
"- %s: chip name\n"
"- %S: chip ID\n"
"- %v: volume (decimal)\n"
"- %V: volume (percentage)\n"
"- %b: volume (hex)\n"
"- %%: percent sign"
);
ImGui::EndTooltip();
}
}
ImGui::ColorEdit4("Text color",(float*)&chanOscTextColor);
if (ImGui::Button("OK")) {
chanOscOptions=false;
}
}
} else {
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0.0f,0.0f));
float availY=ImGui::GetContentRegionAvail().y;
if (ImGui::BeginTable("ChanOsc",chanOscCols,ImGuiTableFlags_Borders)) {
std::vector<DivDispatchOscBuffer*> oscBufs;
std::vector<ChanOscStatus*> oscFFTs;
std::vector<int> oscChans;
int chans=e->getTotalChannelCount();
ImGuiWindow* window=ImGui::GetCurrentWindow();
ImVec2 waveform[512];
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0.0f,0.0f));
float availY=ImGui::GetContentRegionAvail().y;
if (ImGui::BeginTable("ChanOsc",chanOscCols,ImGuiTableFlags_Borders)) {
std::vector<DivDispatchOscBuffer*> oscBufs;
std::vector<ChanOscStatus*> oscFFTs;
std::vector<int> oscChans;
int chans=e->getTotalChannelCount();
ImGuiWindow* window=ImGui::GetCurrentWindow();
ImVec2 waveform[512];
ImGuiStyle& style=ImGui::GetStyle();
ImGuiStyle& style=ImGui::GetStyle();
for (int i=0; i<chans; i++) {
DivDispatchOscBuffer* buf=e->getOscBuffer(i);
if (buf!=NULL && e->curSubSong->chanShow[i]) {
oscBufs.push_back(buf);
oscFFTs.push_back(&chanOscChan[i]);
oscChans.push_back(i);
for (int i=0; i<chans; i++) {
DivDispatchOscBuffer* buf=e->getOscBuffer(i);
if (buf!=NULL && e->curSubSong->chanShow[i]) {
oscBufs.push_back(buf);
oscFFTs.push_back(&chanOscChan[i]);
oscChans.push_back(i);
}
}
}
int rows=(oscBufs.size()+(chanOscCols-1))/chanOscCols;
int rows=(oscBufs.size()+(chanOscCols-1))/chanOscCols;
for (size_t i=0; i<oscBufs.size(); i++) {
if (i%chanOscCols==0) ImGui::TableNextRow();
ImGui::TableNextColumn();
for (size_t i=0; i<oscBufs.size(); i++) {
if (i%chanOscCols==0) ImGui::TableNextRow();
ImGui::TableNextColumn();
DivDispatchOscBuffer* buf=oscBufs[i];
ChanOscStatus* fft=oscFFTs[i];
int ch=oscChans[i];
if (buf==NULL) {
ImGui::Text("Error!");
} else {
ImVec2 size=ImGui::GetContentRegionAvail();
size.y=availY/rows;
DivDispatchOscBuffer* buf=oscBufs[i];
ChanOscStatus* fft=oscFFTs[i];
int ch=oscChans[i];
if (buf==NULL) {
ImGui::Text("Error!");
} else {
ImVec2 size=ImGui::GetContentRegionAvail();
size.y=availY/rows;
if (centerSettingReset) {
buf->readNeedle=buf->needle;
}
if (centerSettingReset) {
buf->readNeedle=buf->needle;
}
// check FFT status existence
if (fft->plan==NULL) {
logD("creating FFT plan for channel %d",ch);
fft->inBuf=(double*)fftw_malloc(FURNACE_FFT_SIZE*sizeof(double));
fft->outBuf=(fftw_complex*)fftw_malloc(FURNACE_FFT_SIZE*sizeof(fftw_complex));
fft->plan=fftw_plan_dft_r2c_1d(FURNACE_FFT_SIZE,fft->inBuf,fft->outBuf,FFTW_ESTIMATE);
}
// check FFT status existence
if (fft->plan==NULL) {
logD("creating FFT plan for channel %d",ch);
fft->inBuf=(double*)fftw_malloc(FURNACE_FFT_SIZE*sizeof(double));
fft->outBuf=(fftw_complex*)fftw_malloc(FURNACE_FFT_SIZE*sizeof(fftw_complex));
fft->plan=fftw_plan_dft_r2c_1d(FURNACE_FFT_SIZE,fft->inBuf,fft->outBuf,FFTW_ESTIMATE);
}
int displaySize=(float)(buf->rate)*(chanOscWindowSize/1000.0f);
int displaySize=(float)(buf->rate)*(chanOscWindowSize/1000.0f);
ImVec2 minArea=window->DC.CursorPos;
ImVec2 maxArea=ImVec2(
minArea.x+size.x,
minArea.y+size.y
);
ImRect rect=ImRect(minArea,maxArea);
ImRect inRect=rect;
inRect.Min.x+=dpiScale;
inRect.Min.y+=2.0*dpiScale;
inRect.Max.x-=dpiScale;
inRect.Max.y-=2.0*dpiScale;
ImVec2 minArea=window->DC.CursorPos;
ImVec2 maxArea=ImVec2(
minArea.x+size.x,
minArea.y+size.y
);
ImRect rect=ImRect(minArea,maxArea);
ImRect inRect=rect;
inRect.Min.x+=dpiScale;
inRect.Min.y+=2.0*dpiScale;
inRect.Max.x-=dpiScale;
inRect.Max.y-=2.0*dpiScale;
int precision=inRect.Max.x-inRect.Min.x;
if (precision<1) precision=1;
if (precision>512) precision=512;
int precision=inRect.Max.x-inRect.Min.x;
if (precision<1) precision=1;
if (precision>512) precision=512;
ImGui::ItemSize(size,style.FramePadding.y);
if (ImGui::ItemAdd(rect,ImGui::GetID("chOscDisplay"))) {
if (!e->isRunning()) {
for (unsigned short i=0; i<precision; i++) {
float x=(float)i/(float)precision;
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f));
}
} else {
float minLevel=1.0f;
float maxLevel=-1.0f;
float dcOff=0.0f;
unsigned short needlePos=buf->needle;
for (int i=0; i<FURNACE_FFT_SIZE; i++) {
fft->inBuf[i]=(double)buf->data[(unsigned short)(needlePos-displaySize*2+((i*displaySize*2)/FURNACE_FFT_SIZE))]/32768.0;
}
fftw_execute(fft->plan);
// find origin frequency
int point=1;
double candAmp=0.0;
for (unsigned short i=1; i<512; i++) {
fftw_complex& f=fft->outBuf[i];
// AMPLITUDE
double amp=sqrt(pow(f[0],2.0)+pow(f[1],2.0))/pow((double)i,0.8);
if (amp>candAmp) {
point=i;
candAmp=amp;
ImGui::ItemSize(size,style.FramePadding.y);
if (ImGui::ItemAdd(rect,ImGui::GetID("chOscDisplay"))) {
if (!e->isRunning()) {
for (unsigned short i=0; i<precision; i++) {
float x=(float)i/(float)precision;
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f));
}
} else {
float minLevel=1.0f;
float maxLevel=-1.0f;
float dcOff=0.0f;
unsigned short needlePos=buf->needle;
for (int i=0; i<FURNACE_FFT_SIZE; i++) {
fft->inBuf[i]=(double)buf->data[(unsigned short)(needlePos-displaySize*2+((i*displaySize*2)/FURNACE_FFT_SIZE))]/32768.0;
}
fftw_execute(fft->plan);
// find origin frequency
int point=1;
double candAmp=0.0;
for (unsigned short i=1; i<512; i++) {
fftw_complex& f=fft->outBuf[i];
// AMPLITUDE
double amp=sqrt(pow(f[0],2.0)+pow(f[1],2.0))/pow((double)i,0.8);
if (amp>candAmp) {
point=i;
candAmp=amp;
}
}
// PHASE
fftw_complex& candPoint=fft->outBuf[point];
double phase=((double)(displaySize*2)/(double)point)*(0.5+(atan2(candPoint[1],candPoint[0])/(M_PI*2)));
if (chanOscWaveCorr) {
needlePos-=phase;
}
chanOscPitch[ch]=(float)point/32.0f;
/*
String cPhase=fmt::sprintf("%d cphase: %f vol: %f",point,phase,chanOscVol[ch]);
dl->AddText(inRect.Min,0xffffffff,cPhase.c_str());
*/
needlePos-=displaySize;
for (unsigned short i=0; i<precision; i++) {
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/precision))]/32768.0f;
if (minLevel>y) minLevel=y;
if (maxLevel<y) maxLevel=y;
}
dcOff=(minLevel+maxLevel)*0.5f;
for (unsigned short i=0; i<precision; i++) {
float x=(float)i/(float)precision;
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/precision))]/32768.0f;
y-=dcOff;
if (y<-0.5f) y=-0.5f;
if (y>0.5f) y=0.5f;
y*=chanOscAmplify;
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y));
}
}
ImU32 color=ImGui::GetColorU32(chanOscColor);
if (chanOscUseGrad) {
float xVal=computeGradPos(chanOscColorX,ch);
float yVal=computeGradPos(chanOscColorY,ch);
// PHASE
fftw_complex& candPoint=fft->outBuf[point];
double phase=((double)(displaySize*2)/(double)point)*(0.5+(atan2(candPoint[1],candPoint[0])/(M_PI*2)));
xVal=CLAMP(xVal,0.0f,1.0f);
yVal=CLAMP(yVal,0.0f,1.0f);
if (chanOscWaveCorr) {
needlePos-=phase;
color=chanOscGrad.get(xVal,1.0f-yVal);
}
chanOscPitch[ch]=(float)point/32.0f;
/*
String cPhase=fmt::sprintf("%d cphase: %f vol: %f",point,phase,chanOscVol[ch]);
dl->AddText(inRect.Min,0xffffffff,cPhase.c_str());
*/
ImGui::PushClipRect(inRect.Min,inRect.Max,false);
needlePos-=displaySize;
for (unsigned short i=0; i<precision; i++) {
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/precision))]/32768.0f;
if (minLevel>y) minLevel=y;
if (maxLevel<y) maxLevel=y;
}
dcOff=(minLevel+maxLevel)*0.5f;
for (unsigned short i=0; i<precision; i++) {
float x=(float)i/(float)precision;
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/precision))]/32768.0f;
y-=dcOff;
if (y<-0.5f) y=-0.5f;
if (y>0.5f) y=0.5f;
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y));
dl->AddPolyline(waveform,precision,color,ImDrawFlags_None,dpiScale);
if (!chanOscTextFormat.empty()) {
String text;
bool inFormat=false;
for (char i: chanOscTextFormat) {
if (inFormat) {
switch (i) {
case 'c':
text+=e->getChannelName(ch);
break;
case 'C':
text+=e->getChannelShortName(ch);
break;
case 'd':
text+=fmt::sprintf("%d",ch);
break;
case 'D':
text+=fmt::sprintf("%d",ch+1);
break;
case 'i': {
DivChannelState* chanState=e->getChanState(ch);
if (chanState==NULL) break;
DivInstrument* ins=e->getIns(chanState->lastIns);
text+=ins->name;
break;
}
case 'I': {
DivChannelState* chanState=e->getChanState(ch);
if (chanState==NULL) break;
text+=fmt::sprintf("%d",chanState->lastIns);
break;
}
case 'x': {
DivChannelState* chanState=e->getChanState(ch);
if (chanState==NULL) break;
if (chanState->lastIns<0) {
text+="??";
} else {
text+=fmt::sprintf("%.2X",chanState->lastIns);
}
break;
}
case 's': {
text+=e->getSystemName(e->sysOfChan[ch]);
break;
}
case 'S': {
text+=fmt::sprintf("%d",e->dispatchOfChan[ch]);
break;
}
case 'v':
break;
case 'V':
break;
case 'b':
break;
case '%':
text+='%';
break;
default:
text+='%';
text+=i;
break;
}
inFormat=false;
} else {
if (i=='%') {
inFormat=true;
} else {
text+=i;
}
}
}
dl->AddText(ImLerp(inRect.Min,inRect.Max,ImVec2(0.0f,0.0f)),ImGui::GetColorU32(chanOscTextColor),text.c_str());
}
ImGui::PopClipRect();
}
ImU32 color=ImGui::GetColorU32(chanOscColor);
if (chanOscUseGrad) {
float xVal=computeGradPos(chanOscColorX,ch);
float yVal=computeGradPos(chanOscColorY,ch);
xVal=CLAMP(xVal,0.0f,1.0f);
yVal=CLAMP(yVal,0.0f,1.0f);
color=chanOscGrad.get(xVal,1.0f-yVal);
}
ImGui::PushClipRect(inRect.Min,inRect.Max,false);
dl->AddPolyline(waveform,precision,color,ImDrawFlags_None,dpiScale);
ImGui::PopClipRect();
}
}
}
ImGui::EndTable();
ImGui::EndTable();
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
chanOscOptions=!chanOscOptions;
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
chanOscOptions=!chanOscOptions;
}
}
ImGui::PopStyleVar();
}
ImGui::PopStyleVar();
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_CHAN_OSC;
ImGui::End();

View File

@ -5994,13 +5994,22 @@ bool FurnaceGUI::init() {
chanOscCols=e->getConfInt("chanOscCols",3);
chanOscColorX=e->getConfInt("chanOscColorX",GUI_OSCREF_CENTER);
chanOscColorY=e->getConfInt("chanOscColorY",GUI_OSCREF_CENTER);
chanOscTextX=e->getConfFloat("chanOscTextX",0.0f);
chanOscTextY=e->getConfFloat("chanOscTextY",0.0f);
chanOscAmplify=e->getConfFloat("chanOscAmplify",0.95f);
chanOscWindowSize=e->getConfFloat("chanOscWindowSize",20.0f);
chanOscWaveCorr=e->getConfBool("chanOscWaveCorr",true);
chanOscOptions=e->getConfBool("chanOscOptions",false);
chanOscNormalize=e->getConfBool("chanOscNormalize",false);
chanOscTextFormat=e->getConfString("chanOscTextFormat","%c");
chanOscColor.x=e->getConfFloat("chanOscColorR",1.0f);
chanOscColor.y=e->getConfFloat("chanOscColorG",1.0f);
chanOscColor.z=e->getConfFloat("chanOscColorB",1.0f);
chanOscColor.w=e->getConfFloat("chanOscColorA",1.0f);
chanOscTextColor.x=e->getConfFloat("chanOscTextColorR",1.0f);
chanOscTextColor.y=e->getConfFloat("chanOscTextColorG",1.0f);
chanOscTextColor.z=e->getConfFloat("chanOscTextColorB",1.0f);
chanOscTextColor.w=e->getConfFloat("chanOscTextColorA",0.75f);
chanOscUseGrad=e->getConfBool("chanOscUseGrad",false);
chanOscGrad.fromString(e->getConfString("chanOscGrad",""));
chanOscGrad.render();
@ -6487,13 +6496,22 @@ void FurnaceGUI::commitState() {
e->setConf("chanOscCols",chanOscCols);
e->setConf("chanOscColorX",chanOscColorX);
e->setConf("chanOscColorY",chanOscColorY);
e->setConf("chanOscTextX",chanOscTextX);
e->setConf("chanOscTextY",chanOscTextY);
e->setConf("chanOscAmplify",chanOscAmplify);
e->setConf("chanOscWindowSize",chanOscWindowSize);
e->setConf("chanOscWaveCorr",chanOscWaveCorr);
e->setConf("chanOscOptions",chanOscOptions);
e->setConf("chanOscNormalize",chanOscNormalize);
e->setConf("chanOscTextFormat",chanOscTextFormat);
e->setConf("chanOscColorR",chanOscColor.x);
e->setConf("chanOscColorG",chanOscColor.y);
e->setConf("chanOscColorB",chanOscColor.z);
e->setConf("chanOscColorA",chanOscColor.w);
e->setConf("chanOscTextColorR",chanOscTextColor.x);
e->setConf("chanOscTextColorG",chanOscTextColor.y);
e->setConf("chanOscTextColorB",chanOscTextColor.z);
e->setConf("chanOscTextColorA",chanOscTextColor.w);
e->setConf("chanOscUseGrad",chanOscUseGrad);
e->setConf("chanOscGrad",chanOscGrad.toString());
@ -6915,11 +6933,17 @@ FurnaceGUI::FurnaceGUI():
chanOscColorX(GUI_OSCREF_CENTER),
chanOscColorY(GUI_OSCREF_CENTER),
chanOscWindowSize(20.0f),
chanOscTextX(0.0f),
chanOscTextY(0.0f),
chanOscAmplify(0.95f),
chanOscWaveCorr(true),
chanOscOptions(false),
updateChanOscGradTex(true),
chanOscUseGrad(false),
chanOscNormalize(false),
chanOscTextFormat("%c"),
chanOscColor(1.0f,1.0f,1.0f,1.0f),
chanOscTextColor(1.0f,1.0f,1.0f,0.75f),
chanOscGrad(64,64),
chanOscGradTex(NULL),
followLog(true),

View File

@ -1948,9 +1948,10 @@ class FurnaceGUI {
// per-channel oscilloscope
int chanOscCols, chanOscColorX, chanOscColorY;
float chanOscWindowSize;
bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex, chanOscUseGrad;
ImVec4 chanOscColor;
float chanOscWindowSize, chanOscTextX, chanOscTextY, chanOscAmplify;
bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex, chanOscUseGrad, chanOscNormalize;
String chanOscTextFormat;
ImVec4 chanOscColor, chanOscTextColor;
Gradient2D chanOscGrad;
void* chanOscGradTex;
float chanOscLP0[DIV_MAX_CHANS];