Incremental work
This commit is contained in:
parent
9d2bb9fc65
commit
803415c988
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
|||||||
.vs/
|
.vs/
|
||||||
packages/
|
packages/
|
||||||
/TODO
|
/TODO
|
||||||
|
mybuild.bat
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="$(ObjDir)Android.dll" />
|
<Reference Include="$(ObjDir)Android.dll" />
|
||||||
<Reference Include="$(FNA_DLL)" />
|
<Reference Include="$(FNA_DLL)" />
|
||||||
|
<Reference Include="System.dll" />
|
||||||
<Compile Include="src\**\*.cs" />
|
<Compile Include="src\**\*.cs" />
|
||||||
<CustomAdditionalCompileInputs Include="FNA.filter" />
|
<CustomAdditionalCompileInputs Include="FNA.filter" />
|
||||||
<Filter Include="FNA.filter" />
|
<Filter Include="FNA.filter" />
|
||||||
|
@ -102,3 +102,11 @@
|
|||||||
|
|
||||||
*.Content.*
|
*.Content.*
|
||||||
*.Storage.*
|
*.Storage.*
|
||||||
|
|
||||||
|
*.Audio.AudioChannels
|
||||||
|
*.Audio.SoundState
|
||||||
|
*.Audio.*Exception
|
||||||
|
|
||||||
|
*.Media.MediaQueue
|
||||||
|
*.Media.MediaState
|
||||||
|
*.Media.SongCollection
|
||||||
|
29
BNA/lzxdecoder.license
Normal file
29
BNA/lzxdecoder.license
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
LzxDecoder.cs was derived from libmspack
|
||||||
|
Copyright 2003-2004 Stuart Caie
|
||||||
|
Copyright 2011 Ali Scissons
|
||||||
|
|
||||||
|
The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted
|
||||||
|
by Microsoft Corporation.
|
||||||
|
|
||||||
|
This source file is Dual licensed; meaning the end-user of this source file
|
||||||
|
may redistribute/modify it under the LGPL 2.1 or MS-PL licenses.
|
||||||
|
|
||||||
|
About
|
||||||
|
-----
|
||||||
|
This derived work is recognized by Stuart Caie and is authorized to adapt
|
||||||
|
any changes made to lzxd.c in his libmspack library and will still retain
|
||||||
|
this dual licensing scheme. Big thanks to Stuart Caie!
|
||||||
|
|
||||||
|
This file is a pure C# port of the lzxd.c file from libmspack, with minor
|
||||||
|
changes towards the decompression of XNB files. The original decompression
|
||||||
|
software of LZX encoded data was written by Suart Caie in his
|
||||||
|
libmspack/cabextract projects, which can be located at
|
||||||
|
http://http://www.cabextract.org.uk/
|
||||||
|
|
||||||
|
GNU Lesser General Public License, Version 2.1
|
||||||
|
----------------------------------------------
|
||||||
|
https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
|
||||||
|
|
||||||
|
Microsoft Public License
|
||||||
|
------------------------
|
||||||
|
http://www.opensource.org/licenses/ms-pl.html
|
22
BNA/monoxna.license
Normal file
22
BNA/monoxna.license
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
MIT License
|
||||||
|
Copyright 2006 The Mono.Xna Team
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
@ -22,7 +22,6 @@ namespace Microsoft.Xna.Framework.Graphics
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// CreateProgram
|
// CreateProgram
|
||||||
//
|
//
|
||||||
|
@ -37,7 +37,8 @@ namespace Microsoft.Xna.Framework.Graphics
|
|||||||
if ((state.TargetFramebuffer = id[0]) == 0)
|
if ((state.TargetFramebuffer = id[0]) == 0)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, state.TargetFramebuffer);
|
GLES20.glBindFramebuffer(GLES30.GL_DRAW_FRAMEBUFFER,
|
||||||
|
state.TargetFramebuffer);
|
||||||
|
|
||||||
int attachmentIndex = GLES20.GL_COLOR_ATTACHMENT0;
|
int attachmentIndex = GLES20.GL_COLOR_ATTACHMENT0;
|
||||||
foreach (var renderTarget in renderTargetsCopy)
|
foreach (var renderTarget in renderTargetsCopy)
|
||||||
@ -48,7 +49,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
|||||||
// from FNA3D_GetMaxMultiSampleCount, which we never do
|
// from FNA3D_GetMaxMultiSampleCount, which we never do
|
||||||
throw new PlatformNotSupportedException();
|
throw new PlatformNotSupportedException();
|
||||||
/*GLES20.glFramebufferRenderbuffer(
|
/*GLES20.glFramebufferRenderbuffer(
|
||||||
GLES20.GL_FRAMEBUFFER, attachmentIndex,
|
GLES20.GL_DRAW_FRAMEBUFFER, attachmentIndex,
|
||||||
GLES20.GL_RENDERBUFFER, (int) renderTarget.colorBuffer);*/
|
GLES20.GL_RENDERBUFFER, (int) renderTarget.colorBuffer);*/
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -60,7 +61,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
|||||||
+ renderTarget.data2;
|
+ renderTarget.data2;
|
||||||
}
|
}
|
||||||
GLES20.glFramebufferTexture2D(
|
GLES20.glFramebufferTexture2D(
|
||||||
GLES20.GL_FRAMEBUFFER, attachmentIndex,
|
GLES30.GL_DRAW_FRAMEBUFFER, attachmentIndex,
|
||||||
attachmentType, (int) renderTarget.texture, 0);
|
attachmentType, (int) renderTarget.texture, 0);
|
||||||
}
|
}
|
||||||
attachmentIndex++;
|
attachmentIndex++;
|
||||||
@ -71,12 +72,12 @@ namespace Microsoft.Xna.Framework.Graphics
|
|||||||
while (attachmentIndex < lastAttachmentPlusOne)
|
while (attachmentIndex < lastAttachmentPlusOne)
|
||||||
{
|
{
|
||||||
GLES20.glFramebufferRenderbuffer(
|
GLES20.glFramebufferRenderbuffer(
|
||||||
GLES20.GL_FRAMEBUFFER, attachmentIndex++,
|
GLES30.GL_DRAW_FRAMEBUFFER, attachmentIndex++,
|
||||||
GLES20.GL_RENDERBUFFER, 0);
|
GLES20.GL_RENDERBUFFER, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
GLES20.glFramebufferRenderbuffer(
|
GLES20.glFramebufferRenderbuffer(
|
||||||
GLES20.GL_FRAMEBUFFER, GLES30.GL_DEPTH_STENCIL_ATTACHMENT,
|
GLES30.GL_DRAW_FRAMEBUFFER, GLES30.GL_DEPTH_STENCIL_ATTACHMENT,
|
||||||
GLES20.GL_RENDERBUFFER, (int) depthStencilBuffer);
|
GLES20.GL_RENDERBUFFER, (int) depthStencilBuffer);
|
||||||
|
|
||||||
state.RenderToTexture = true;
|
state.RenderToTexture = true;
|
||||||
@ -100,7 +101,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
|||||||
var renderer = Renderer.Get(device);
|
var renderer = Renderer.Get(device);
|
||||||
renderer.Send( () =>
|
renderer.Send( () =>
|
||||||
{
|
{
|
||||||
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
|
GLES20.glBindFramebuffer(GLES30.GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
var state = (State) renderer.UserData;
|
var state = (State) renderer.UserData;
|
||||||
state.RenderToTexture = false;
|
state.RenderToTexture = false;
|
||||||
@ -138,7 +139,10 @@ namespace Microsoft.Xna.Framework.Graphics
|
|||||||
|
|
||||||
GLES20.glBindTexture(attachmentType, texture);
|
GLES20.glBindTexture(attachmentType, texture);
|
||||||
GLES20.glGenerateMipmap(attachmentType);
|
GLES20.glGenerateMipmap(attachmentType);
|
||||||
GLES20.glBindTexture(attachmentType, 0);
|
|
||||||
|
var state = (State) renderer.UserData;
|
||||||
|
if (state.TextureOnLastUnit != 0)
|
||||||
|
GLES20.glBindTexture(attachmentType, state.TextureOnLastUnit);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,6 +208,75 @@ namespace Microsoft.Xna.Framework.Graphics
|
|||||||
return ((State) Renderer.Get(graphicsDevice.GLDevice).UserData).RenderToTexture;
|
return ((State) Renderer.Get(graphicsDevice.GLDevice).UserData).RenderToTexture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get Texture Data
|
||||||
|
//
|
||||||
|
|
||||||
|
private static void GetTextureData(Renderer renderer, int textureId,
|
||||||
|
int x, int y, int w, int h, int level,
|
||||||
|
object dataObject, int dataOffset, int dataLength)
|
||||||
|
{
|
||||||
|
java.nio.Buffer buffer = dataObject switch
|
||||||
|
{
|
||||||
|
sbyte[] byteArray =>
|
||||||
|
java.nio.ByteBuffer.wrap(byteArray, dataOffset, dataLength),
|
||||||
|
|
||||||
|
int[] intArray =>
|
||||||
|
java.nio.IntBuffer.wrap(intArray, dataOffset / 4, dataLength / 4),
|
||||||
|
|
||||||
|
_ => throw new ArgumentException(dataObject?.GetType().ToString()),
|
||||||
|
};
|
||||||
|
|
||||||
|
renderer.Send( () =>
|
||||||
|
{
|
||||||
|
var state = (State) renderer.UserData;
|
||||||
|
if (state.SourceFramebuffer == 0)
|
||||||
|
{
|
||||||
|
var id = new int[1];
|
||||||
|
GLES20.glGenFramebuffers(1, id, 0);
|
||||||
|
if ((state.SourceFramebuffer = id[0]) == 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = state.TextureConfigs[textureId];
|
||||||
|
if (config[1] != (int) SurfaceFormat.Color)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException(
|
||||||
|
((SurfaceFormat) config[1]).ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
GLES20.glBindFramebuffer(GLES30.GL_READ_FRAMEBUFFER,
|
||||||
|
state.SourceFramebuffer);
|
||||||
|
|
||||||
|
GLES20.glFramebufferTexture2D(
|
||||||
|
GLES30.GL_READ_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
|
||||||
|
GLES20.GL_TEXTURE_2D, textureId, level);
|
||||||
|
|
||||||
|
GLES20.glReadPixels(x, y, w, h,
|
||||||
|
GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buffer);
|
||||||
|
|
||||||
|
GLES20.glBindFramebuffer(GLES30.GL_READ_FRAMEBUFFER, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// FNA3D_GetTextureData2D
|
||||||
|
//
|
||||||
|
|
||||||
|
public static void FNA3D_GetTextureData2D(IntPtr device, IntPtr texture,
|
||||||
|
int x, int y, int w, int h, int level,
|
||||||
|
IntPtr data, int dataLength)
|
||||||
|
{
|
||||||
|
// FNA Texture2D uses GCHandle::Alloc and GCHandle::AddrOfPinnedObject.
|
||||||
|
// we use GCHandle::FromIntPtr to convert that address to an object reference.
|
||||||
|
// see also: system.runtime.interopservices.GCHandle struct in baselib.
|
||||||
|
int dataOffset = (int) data;
|
||||||
|
var dataObject = System.Runtime.InteropServices.GCHandle.FromIntPtr(data).Target;
|
||||||
|
|
||||||
|
GetTextureData(Renderer.Get(device), (int) texture,
|
||||||
|
x, y, w, h, level, dataObject, dataOffset, dataLength);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// DepthFormatToDepthStorage
|
// DepthFormatToDepthStorage
|
||||||
//
|
//
|
||||||
@ -238,6 +311,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
|||||||
private partial class State
|
private partial class State
|
||||||
{
|
{
|
||||||
public bool RenderToTexture;
|
public bool RenderToTexture;
|
||||||
|
public int SourceFramebuffer;
|
||||||
public int TargetFramebuffer;
|
public int TargetFramebuffer;
|
||||||
public int ActiveAttachments;
|
public int ActiveAttachments;
|
||||||
}
|
}
|
||||||
|
@ -257,7 +257,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
|||||||
{
|
{
|
||||||
string reason = (bitmap == null) ? "unspecified error"
|
string reason = (bitmap == null) ? "unspecified error"
|
||||||
: $"unsupported config '{bitmap.getConfig()}'";
|
: $"unsupported config '{bitmap.getConfig()}'";
|
||||||
throw new System.BadImageFormatException(
|
throw new BadImageFormatException(
|
||||||
$"Load failed for bitmap image '{titleStream.Name}': {reason}");
|
$"Load failed for bitmap image '{titleStream.Name}': {reason}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,15 +341,22 @@ namespace Microsoft.Xna.Framework.Graphics
|
|||||||
ref FNA3D_SamplerState sampler)
|
ref FNA3D_SamplerState sampler)
|
||||||
{
|
{
|
||||||
var samplerCopy = sampler;
|
var samplerCopy = sampler;
|
||||||
|
int textureId = (int) texture;
|
||||||
var renderer = Renderer.Get(device);
|
var renderer = Renderer.Get(device);
|
||||||
|
|
||||||
renderer.Send( () =>
|
renderer.Send( () =>
|
||||||
{
|
{
|
||||||
var state = (State) renderer.UserData;
|
var state = (State) renderer.UserData;
|
||||||
var config = state.TextureConfigs[(int) texture];
|
var config = state.TextureConfigs[textureId];
|
||||||
|
|
||||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + index);
|
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + index);
|
||||||
GLES20.glBindTexture(config[0], (int) texture);
|
GLES20.glBindTexture(config[0], textureId);
|
||||||
|
|
||||||
|
if (index == renderer.TextureUnits - 1)
|
||||||
|
state.TextureOnLastUnit = textureId;
|
||||||
|
|
||||||
|
if (textureId == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
GLES20.glTexParameteri(config[0], GLES30.GL_TEXTURE_MAX_LEVEL,
|
GLES20.glTexParameteri(config[0], GLES30.GL_TEXTURE_MAX_LEVEL,
|
||||||
config[2] - 1);
|
config[2] - 1);
|
||||||
@ -571,6 +578,8 @@ namespace Microsoft.Xna.Framework.Graphics
|
|||||||
// #1 - SurfaceFormat
|
// #1 - SurfaceFormat
|
||||||
// #2 - levels count
|
// #2 - levels count
|
||||||
public Dictionary<int, int[]> TextureConfigs = new Dictionary<int, int[]>();
|
public Dictionary<int, int[]> TextureConfigs = new Dictionary<int, int[]>();
|
||||||
|
|
||||||
|
public int TextureOnLastUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -226,3 +226,24 @@ namespace Microsoft.Xna.Framework.Input.Touch
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace Microsoft.Xna.Framework.Media
|
||||||
|
{
|
||||||
|
|
||||||
|
//
|
||||||
|
// MediaQueue
|
||||||
|
//
|
||||||
|
|
||||||
|
[java.attr.Discard] // discard in output
|
||||||
|
public class MediaQueue
|
||||||
|
{
|
||||||
|
public MediaQueue() { }
|
||||||
|
public Song ActiveSong { get; }
|
||||||
|
public int ActiveSongIndex { get; set; }
|
||||||
|
public void Add(Song song) { }
|
||||||
|
public void Clear() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
331
BNA/src/MediaPlayer.cs
Normal file
331
BNA/src/MediaPlayer.cs
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
#pragma warning disable 0436
|
||||||
|
|
||||||
|
namespace Microsoft.Xna.Framework.Media
|
||||||
|
{
|
||||||
|
|
||||||
|
public static class MediaPlayer
|
||||||
|
{
|
||||||
|
|
||||||
|
[java.attr.RetainType] private static android.media.MediaPlayer player;
|
||||||
|
[java.attr.RetainType] private static MediaQueue queue;
|
||||||
|
[java.attr.RetainType] private static java.util.concurrent.atomic.AtomicInteger state;
|
||||||
|
[java.attr.RetainType] private static float volume;
|
||||||
|
[java.attr.RetainType] private static bool muted;
|
||||||
|
[java.attr.RetainType] private static bool looping;
|
||||||
|
|
||||||
|
//
|
||||||
|
// static constructor
|
||||||
|
//
|
||||||
|
|
||||||
|
static MediaPlayer()
|
||||||
|
{
|
||||||
|
volume = 1f;
|
||||||
|
state = new java.util.concurrent.atomic.AtomicInteger(0);
|
||||||
|
queue = new MediaQueue();
|
||||||
|
var watcher = new Watcher();
|
||||||
|
player = new android.media.MediaPlayer();
|
||||||
|
player.setOnPreparedListener(watcher);
|
||||||
|
player.setOnCompletionListener(watcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// PrepareAndStart
|
||||||
|
//
|
||||||
|
|
||||||
|
private static void PrepareAndStart()
|
||||||
|
{
|
||||||
|
int oldState = state.getAndSet((int) MediaState.Playing);
|
||||||
|
player.prepareAsync();
|
||||||
|
if (oldState != (int) MediaState.Playing && MediaStateChanged != null)
|
||||||
|
{
|
||||||
|
MediaStateChanged(null, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Play
|
||||||
|
//
|
||||||
|
|
||||||
|
public static void Play(Song song)
|
||||||
|
{
|
||||||
|
if (ActiveSongChanged != null)
|
||||||
|
throw new PlatformNotSupportedException("ActiveSongChanged");
|
||||||
|
|
||||||
|
if (song.isAsset)
|
||||||
|
{
|
||||||
|
var asset = GameRunner.Singleton.Activity.getAssets().openFd(song.path);
|
||||||
|
player.setDataSource(asset.getFileDescriptor(),
|
||||||
|
asset.getStartOffset(), asset.getLength());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
player.setDataSource(song.path);
|
||||||
|
|
||||||
|
Queue.Clear();
|
||||||
|
Queue.Add(song);
|
||||||
|
Queue.ActiveSongIndex = 0;
|
||||||
|
|
||||||
|
PrepareAndStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Play (SongCollection)
|
||||||
|
//
|
||||||
|
|
||||||
|
public static void Play(SongCollection songs, int index)
|
||||||
|
{
|
||||||
|
if (songs.Count == 0)
|
||||||
|
{
|
||||||
|
Queue.Clear();
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
else if (songs.Count == 1)
|
||||||
|
{
|
||||||
|
Play((Song) (object) songs[0]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new PlatformNotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Play(SongCollection songs) => Play(songs, 0);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Pause
|
||||||
|
//
|
||||||
|
|
||||||
|
public static void Pause()
|
||||||
|
{
|
||||||
|
if (State == MediaState.Playing)
|
||||||
|
{
|
||||||
|
if (player.isPlaying())
|
||||||
|
player.pause();
|
||||||
|
State = MediaState.Paused;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Resume
|
||||||
|
//
|
||||||
|
|
||||||
|
public static void Resume()
|
||||||
|
{
|
||||||
|
if (State == MediaState.Paused)
|
||||||
|
{
|
||||||
|
player.start();
|
||||||
|
State = MediaState.Playing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Stop
|
||||||
|
//
|
||||||
|
|
||||||
|
public static void Stop()
|
||||||
|
{
|
||||||
|
if (State != MediaState.Stopped)
|
||||||
|
{
|
||||||
|
player.stop();
|
||||||
|
State = MediaState.Stopped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MoveNext, MovePrevious
|
||||||
|
//
|
||||||
|
|
||||||
|
public static void MoveNext()
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
if (looping)
|
||||||
|
PrepareAndStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void MovePrevious() => MoveNext();
|
||||||
|
|
||||||
|
//
|
||||||
|
// IsMuted, Volume
|
||||||
|
//
|
||||||
|
|
||||||
|
public static bool IsMuted
|
||||||
|
{
|
||||||
|
get => muted;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
muted = value;
|
||||||
|
Volume = volume;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float Volume
|
||||||
|
{
|
||||||
|
get => volume;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value < 0f)
|
||||||
|
value = 0f;
|
||||||
|
else if (value > 1f)
|
||||||
|
value = 1f;
|
||||||
|
volume = value;
|
||||||
|
|
||||||
|
if (muted)
|
||||||
|
value = 0f;
|
||||||
|
player.setVolume(value, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool GameHasControl => true;
|
||||||
|
public static bool IsShuffled { get; set; }
|
||||||
|
|
||||||
|
//
|
||||||
|
// IsRepeating
|
||||||
|
//
|
||||||
|
|
||||||
|
public static bool IsRepeating
|
||||||
|
{
|
||||||
|
get => looping;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value != looping)
|
||||||
|
{
|
||||||
|
looping = value;
|
||||||
|
player.setLooping(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// State
|
||||||
|
//
|
||||||
|
|
||||||
|
public static MediaState State
|
||||||
|
{
|
||||||
|
get => (MediaState) state.get();
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if ( state.getAndSet((int) value) != (int) value
|
||||||
|
&& MediaStateChanged != null)
|
||||||
|
{
|
||||||
|
MediaStateChanged(null, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// PlayPosition
|
||||||
|
//
|
||||||
|
|
||||||
|
public static TimeSpan PlayPosition
|
||||||
|
=> TimeSpan.FromMilliseconds(player.getCurrentPosition());
|
||||||
|
|
||||||
|
//
|
||||||
|
// Properties
|
||||||
|
//
|
||||||
|
|
||||||
|
public static event EventHandler<EventArgs> ActiveSongChanged;
|
||||||
|
public static event EventHandler<EventArgs> MediaStateChanged;
|
||||||
|
|
||||||
|
public static MediaQueue Queue => queue;
|
||||||
|
|
||||||
|
//
|
||||||
|
// IsVisualizationEnabled
|
||||||
|
//
|
||||||
|
|
||||||
|
public static bool IsVisualizationEnabled
|
||||||
|
{
|
||||||
|
get => false;
|
||||||
|
set => throw new PlatformNotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Watcher
|
||||||
|
//
|
||||||
|
|
||||||
|
private class Watcher : android.media.MediaPlayer.OnPreparedListener,
|
||||||
|
android.media.MediaPlayer.OnCompletionListener
|
||||||
|
{
|
||||||
|
|
||||||
|
//
|
||||||
|
// onPrepared
|
||||||
|
//
|
||||||
|
|
||||||
|
[java.attr.RetainName]
|
||||||
|
public void onPrepared(android.media.MediaPlayer player)
|
||||||
|
{
|
||||||
|
if (MediaPlayer.State == MediaState.Playing)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var duration = player.getDuration();
|
||||||
|
if (duration != -1)
|
||||||
|
{
|
||||||
|
MediaPlayer.Queue.ActiveSong.Duration =
|
||||||
|
TimeSpan.FromMilliseconds(duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
player.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// onCompletion
|
||||||
|
//
|
||||||
|
|
||||||
|
[java.attr.RetainName]
|
||||||
|
public void onCompletion(android.media.MediaPlayer player)
|
||||||
|
{
|
||||||
|
MediaPlayer.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Song
|
||||||
|
//
|
||||||
|
|
||||||
|
public sealed class Song : IEquatable<Song>, IDisposable
|
||||||
|
{
|
||||||
|
[java.attr.RetainType] public string path;
|
||||||
|
[java.attr.RetainType] public bool isAsset;
|
||||||
|
|
||||||
|
public bool IsDisposed { get; private set; }
|
||||||
|
public string Name { get; private set; }
|
||||||
|
public TimeSpan Duration { get; set; }
|
||||||
|
public bool IsProtected => false;
|
||||||
|
public bool IsRated => false;
|
||||||
|
public int PlayCount => 0;
|
||||||
|
public int Rating => 0;
|
||||||
|
public int TrackNumber => 0;
|
||||||
|
|
||||||
|
public static Song FromUri(string name, Uri uri)
|
||||||
|
{
|
||||||
|
var song = new Song() { Name = name };
|
||||||
|
if (uri.IsAbsoluteUri && uri.IsFile)
|
||||||
|
song.path = uri.LocalPath;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
song.path = uri.ToString();
|
||||||
|
song.isAsset = true;
|
||||||
|
}
|
||||||
|
return song;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Song() => Dispose();
|
||||||
|
public void Dispose() => IsDisposed = true;
|
||||||
|
|
||||||
|
public override int GetHashCode() => base.GetHashCode();
|
||||||
|
public bool Equals(Song other) => (((object) other) != null) && (path == other.path);
|
||||||
|
public override bool Equals(object other) => Equals(other as Song);
|
||||||
|
public static bool operator ==(Song song1, Song song2)
|
||||||
|
=> (song1 == null) ? (song2 == null) : song1.Equals(song2);
|
||||||
|
public static bool operator !=(Song song1, Song song2) => ! (song1 == song2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
631
BNA/src/SoundEffect.cs
Normal file
631
BNA/src/SoundEffect.cs
Normal file
@ -0,0 +1,631 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
#pragma warning disable 0436
|
||||||
|
|
||||||
|
namespace Microsoft.Xna.Framework.Audio
|
||||||
|
{
|
||||||
|
|
||||||
|
public sealed class SoundEffect : IDisposable
|
||||||
|
{
|
||||||
|
|
||||||
|
[java.attr.RetainType] public object dataArray;
|
||||||
|
[java.attr.RetainType] public int dataCount;
|
||||||
|
[java.attr.RetainType] public int sampleRate;
|
||||||
|
[java.attr.RetainType] public int channelConfig;
|
||||||
|
[java.attr.RetainType] public int markerFrame;
|
||||||
|
|
||||||
|
[java.attr.RetainType] public static java.util.ArrayList instancesList = new java.util.ArrayList();
|
||||||
|
[java.attr.RetainType] public static java.util.concurrent.locks.ReentrantLock instancesLock = new java.util.concurrent.locks.ReentrantLock();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Constructor (for ContentReader)
|
||||||
|
//
|
||||||
|
|
||||||
|
public SoundEffect(string name, byte[] buffer, int offset, int count,
|
||||||
|
ushort wFormatTag, ushort nChannels,
|
||||||
|
uint nSamplesPerSec, uint nAvgBytesPerSec,
|
||||||
|
ushort nBlockAlign, ushort wBitsPerSample,
|
||||||
|
int loopStart, int loopLength)
|
||||||
|
{
|
||||||
|
if (wFormatTag != 1 /* WAVE_FORMAT_PCM */)
|
||||||
|
throw new ArgumentException("bad wFormatTag");
|
||||||
|
if (offset != 0)
|
||||||
|
throw new ArgumentException("bad offset");
|
||||||
|
if (nBlockAlign != nChannels * wBitsPerSample / 8)
|
||||||
|
throw new ArgumentException("bad nBlockAlign");
|
||||||
|
if (nAvgBytesPerSec != nSamplesPerSec * nBlockAlign)
|
||||||
|
throw new ArgumentException("bad nAvgBytesPerSec");
|
||||||
|
|
||||||
|
sampleRate = (int) nSamplesPerSec;
|
||||||
|
channelConfig = (nChannels == 1) ? android.media.AudioFormat.CHANNEL_OUT_MONO
|
||||||
|
: (nChannels == 2) ? android.media.AudioFormat.CHANNEL_OUT_STEREO
|
||||||
|
: throw new ArgumentException("bad nChannels");
|
||||||
|
|
||||||
|
if (wBitsPerSample == 8)
|
||||||
|
{
|
||||||
|
dataArray = buffer;
|
||||||
|
dataCount = count;
|
||||||
|
}
|
||||||
|
else if (wBitsPerSample == 16)
|
||||||
|
{
|
||||||
|
int shortCount = count / 2;
|
||||||
|
var shortBuffer = new short[shortCount];
|
||||||
|
java.nio.ByteBuffer.wrap((sbyte[]) (object) buffer)
|
||||||
|
.order(java.nio.ByteOrder.LITTLE_ENDIAN).asShortBuffer()
|
||||||
|
.get(shortBuffer);
|
||||||
|
dataArray = shortBuffer;
|
||||||
|
dataCount = shortCount;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new ArgumentException("bad wBitsPerSample");
|
||||||
|
|
||||||
|
markerFrame = dataCount / nChannels;
|
||||||
|
|
||||||
|
Name = name;
|
||||||
|
Duration = TimeSpan.FromSeconds(count / (double) nAvgBytesPerSec);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Constructor
|
||||||
|
//
|
||||||
|
|
||||||
|
public SoundEffect(byte[] buffer, int sampleRate, AudioChannels channels)
|
||||||
|
: this(null, buffer, 0, buffer.Length,
|
||||||
|
1 /* WAVE_FORMAT_PCM */, (ushort) channels, (uint) sampleRate,
|
||||||
|
(uint) (sampleRate * ((ushort) channels * 2)),
|
||||||
|
(ushort) ((ushort) channels * 2), 16, 0, 0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Constructor
|
||||||
|
//
|
||||||
|
|
||||||
|
public SoundEffect(byte[] buffer, int offset, int count, int sampleRate,
|
||||||
|
AudioChannels channels, int loopStart, int loopLength)
|
||||||
|
: this(null, buffer, offset, count,
|
||||||
|
1 /* WAVE_FORMAT_PCM */, (ushort) channels, (uint) sampleRate,
|
||||||
|
(uint) (sampleRate * ((ushort) channels * 2)),
|
||||||
|
(ushort) ((ushort) channels * 2), 16, loopStart, loopLength)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// FromStream
|
||||||
|
//
|
||||||
|
|
||||||
|
public static SoundEffect FromStream(Stream stream)
|
||||||
|
{
|
||||||
|
using (BinaryReader reader = new BinaryReader(stream))
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (new string(reader.ReadChars(4)) != "RIFF")
|
||||||
|
break;
|
||||||
|
reader.ReadUInt32(); // skip chunk size
|
||||||
|
if (new string(reader.ReadChars(4)) != "WAVE")
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (new string(reader.ReadChars(4)) != "fmt ")
|
||||||
|
break;
|
||||||
|
if (reader.ReadInt32() != 16) // fmt chunk size always 16
|
||||||
|
break;
|
||||||
|
|
||||||
|
var wFormatTag = reader.ReadUInt16();
|
||||||
|
var nChannels = reader.ReadUInt16();
|
||||||
|
var nSamplesPerSec = reader.ReadUInt32();
|
||||||
|
var nAvgBytesPerSec = reader.ReadUInt32();
|
||||||
|
var nBlockAlign = reader.ReadUInt16();
|
||||||
|
var wBitsPerSample = reader.ReadUInt16();
|
||||||
|
|
||||||
|
if (new string(reader.ReadChars(4)) != "data")
|
||||||
|
break;
|
||||||
|
|
||||||
|
var count = reader.ReadInt32();
|
||||||
|
var buffer = reader.ReadBytes(count);
|
||||||
|
|
||||||
|
return new SoundEffect(null, buffer, 0, count, wFormatTag, nChannels,
|
||||||
|
nSamplesPerSec, nAvgBytesPerSec,
|
||||||
|
nBlockAlign, wBitsPerSample, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new BadImageFormatException("invalid wave data for sound effect");
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Destructor
|
||||||
|
//
|
||||||
|
|
||||||
|
~SoundEffect() => Dispose();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Dispose
|
||||||
|
//
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (! IsDisposed)
|
||||||
|
{
|
||||||
|
IsDisposed = true;
|
||||||
|
DiscardInstance(null, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Properties
|
||||||
|
//
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
public TimeSpan Duration { get; private set; }
|
||||||
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
|
//
|
||||||
|
// Play
|
||||||
|
//
|
||||||
|
|
||||||
|
public bool Play() => Play(1f, 0f, 0f);
|
||||||
|
|
||||||
|
public bool Play(float volume, float pitch, float pan)
|
||||||
|
=> CreateInstance().Play(volume, pitch, pan);
|
||||||
|
|
||||||
|
//
|
||||||
|
// CreateInstance
|
||||||
|
//
|
||||||
|
|
||||||
|
public SoundEffectInstance CreateInstance()
|
||||||
|
{
|
||||||
|
var inst = new SoundEffectInstance(this);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
instancesLock.@lock();
|
||||||
|
instancesList.add(new java.lang.@ref.WeakReference(inst));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
instancesLock.unlock();
|
||||||
|
}
|
||||||
|
return inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// DiscardInstance
|
||||||
|
//
|
||||||
|
|
||||||
|
public static void DiscardInstance(SoundEffectInstance discardInstance, SoundEffect discardEffect)
|
||||||
|
{
|
||||||
|
if (instancesLock.isHeldByCurrentThread())
|
||||||
|
return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
instancesLock.@lock();
|
||||||
|
for (int idx = instancesList.size(); idx-- > 0;)
|
||||||
|
{
|
||||||
|
var instRef = (java.lang.@ref.WeakReference) instancesList.get(idx);
|
||||||
|
var inst = (SoundEffectInstance) instRef.get();
|
||||||
|
if (inst == discardInstance || inst == null || inst.ShouldDiscard(discardEffect))
|
||||||
|
instancesList.remove(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
instancesLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ReleaseInstance
|
||||||
|
//
|
||||||
|
|
||||||
|
public static bool ReleaseInstance()
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
instancesLock.@lock();
|
||||||
|
int num = instancesList.size();
|
||||||
|
for (int idx = 0; idx < num; idx++)
|
||||||
|
{
|
||||||
|
var instRef = (java.lang.@ref.WeakReference) instancesList.get(idx);
|
||||||
|
var inst = (SoundEffectInstance) instRef.get();
|
||||||
|
if (inst != null && inst.ReleaseTrack(false))
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
instancesLock.unlock();
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GetSampleDuration, GetSampleSizeInBytes
|
||||||
|
//
|
||||||
|
|
||||||
|
public static TimeSpan GetSampleDuration(int sizeInBytes, int sampleRate,
|
||||||
|
AudioChannels channels)
|
||||||
|
=> TimeSpan.FromSeconds(
|
||||||
|
((sizeInBytes / (2 * (int) channels)) / (float) sampleRate));
|
||||||
|
|
||||||
|
public static int GetSampleSizeInBytes(TimeSpan duration, int sampleRate,
|
||||||
|
AudioChannels channels)
|
||||||
|
=> (int) (duration.TotalSeconds * sampleRate * 2 * (int) channels);
|
||||||
|
|
||||||
|
//
|
||||||
|
// MasterVolume, DistanceScale, DopplerScale, SpeedOfSound (no-op)
|
||||||
|
//
|
||||||
|
|
||||||
|
public static float MasterVolume { get; set; }
|
||||||
|
public static float DistanceScale { get; set; }
|
||||||
|
public static float DopplerScale { get; set; }
|
||||||
|
public static float SpeedOfSound { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// SoundEffectInstance
|
||||||
|
//
|
||||||
|
|
||||||
|
public class SoundEffectInstance : IDisposable
|
||||||
|
{
|
||||||
|
[java.attr.RetainType] private SoundEffect effect;
|
||||||
|
[java.attr.RetainType] private SoundEffectInstanceWatcher watcher;
|
||||||
|
[java.attr.RetainType] private android.media.AudioTrack track;
|
||||||
|
[java.attr.RetainType] private float pitch, pan, volume;
|
||||||
|
[java.attr.RetainType] private bool isLooped;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Constructor (for SoundEffect.CreateInstance)
|
||||||
|
//
|
||||||
|
|
||||||
|
public SoundEffectInstance(SoundEffect fromEffect)
|
||||||
|
{
|
||||||
|
effect = fromEffect;
|
||||||
|
volume = 1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// CreateTrack
|
||||||
|
//
|
||||||
|
|
||||||
|
private void CreateTrack(bool tryRelease)
|
||||||
|
{
|
||||||
|
int numToWrite, numWritten;
|
||||||
|
|
||||||
|
if (effect.dataArray is sbyte[] byteData)
|
||||||
|
{
|
||||||
|
numToWrite = byteData.Length;
|
||||||
|
track = new android.media.AudioTrack(
|
||||||
|
android.media.AudioManager.STREAM_MUSIC,
|
||||||
|
effect.sampleRate, effect.channelConfig,
|
||||||
|
android.media.AudioFormat.ENCODING_PCM_8BIT,
|
||||||
|
numToWrite, android.media.AudioTrack.MODE_STATIC);
|
||||||
|
numWritten = track.write(byteData, 0, numToWrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (effect.dataArray is short[] shortData)
|
||||||
|
{
|
||||||
|
numToWrite = shortData.Length;
|
||||||
|
track = new android.media.AudioTrack(
|
||||||
|
android.media.AudioManager.STREAM_MUSIC,
|
||||||
|
effect.sampleRate, effect.channelConfig,
|
||||||
|
android.media.AudioFormat.ENCODING_PCM_16BIT,
|
||||||
|
numToWrite * 2, android.media.AudioTrack.MODE_STATIC);
|
||||||
|
numWritten = track.write(shortData, 0, numToWrite);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
numToWrite = 0;
|
||||||
|
numWritten = android.media.AudioTrack.ERROR_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numWritten != numToWrite)
|
||||||
|
{
|
||||||
|
track = null;
|
||||||
|
if (numWritten < 0)
|
||||||
|
{
|
||||||
|
if (SoundEffect.ReleaseInstance())
|
||||||
|
CreateTrack(false);
|
||||||
|
}
|
||||||
|
if (track == null)
|
||||||
|
{
|
||||||
|
GameRunner.Log($"SoundEffectInstance '{effect.Name}' error {numWritten}/{numToWrite}");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
track.setNotificationMarkerPosition(effect.markerFrame);
|
||||||
|
track.setPlaybackPositionUpdateListener(watcher = new SoundEffectInstanceWatcher());
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ReleaseTrack
|
||||||
|
//
|
||||||
|
|
||||||
|
public bool ReleaseTrack(bool disposing)
|
||||||
|
{
|
||||||
|
var track = this.track;
|
||||||
|
if (track != null)
|
||||||
|
{
|
||||||
|
if (disposing || track.getPlayState() == 1 /* android.media.AudioTrack.PLAYSTATE_STOPPED */)
|
||||||
|
{
|
||||||
|
this.track = null;
|
||||||
|
track.setPlaybackPositionUpdateListener(null);
|
||||||
|
track.stop();
|
||||||
|
track.release();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Destructor
|
||||||
|
//
|
||||||
|
|
||||||
|
~SoundEffectInstance() => Dispose(true);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Dispose
|
||||||
|
//
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (! IsDisposed)
|
||||||
|
{
|
||||||
|
ReleaseTrack(true);
|
||||||
|
SoundEffect.DiscardInstance(this, null);
|
||||||
|
effect = null;
|
||||||
|
IsDisposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => Dispose(true);
|
||||||
|
|
||||||
|
//
|
||||||
|
// ShouldDiscard
|
||||||
|
//
|
||||||
|
|
||||||
|
public bool ShouldDiscard(SoundEffect fromEffect)
|
||||||
|
{
|
||||||
|
if (effect == fromEffect)
|
||||||
|
Dispose(true);
|
||||||
|
return IsDisposed;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Properties
|
||||||
|
//
|
||||||
|
|
||||||
|
public SoundState State
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var track = this.track;
|
||||||
|
if (track == null)
|
||||||
|
return SoundState.Stopped;
|
||||||
|
|
||||||
|
return track.getPlayState() switch
|
||||||
|
{
|
||||||
|
1 /* android.media.AudioTrack.PLAYSTATE_STOPPED */ => SoundState.Stopped,
|
||||||
|
2 /* android.media.AudioTrack.PLAYSTATE_PAUSED */ => SoundState.Paused,
|
||||||
|
3 /* android.media.AudioTrack.PLAYSTATE_PLAYING */ => SoundState.Playing,
|
||||||
|
_ => throw new InvalidOperationException()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsDisposed { get; protected set; }
|
||||||
|
public float Pitch
|
||||||
|
{
|
||||||
|
get => pitch;
|
||||||
|
set => SetPlaybackRate(value, State == SoundState.Playing);
|
||||||
|
}
|
||||||
|
public float Pan
|
||||||
|
{
|
||||||
|
get => pan;
|
||||||
|
set => SetStereoVolume(volume, value, State == SoundState.Playing);
|
||||||
|
}
|
||||||
|
public float Volume
|
||||||
|
{
|
||||||
|
get => volume;
|
||||||
|
set => SetStereoVolume(value, pan, State == SoundState.Playing);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// IsLooped
|
||||||
|
//
|
||||||
|
// note that looping does not respect any custom loop points,
|
||||||
|
// and always occurs on the entire effect
|
||||||
|
//
|
||||||
|
|
||||||
|
public virtual bool IsLooped
|
||||||
|
{
|
||||||
|
get => isLooped;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (State == SoundState.Playing)
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
isLooped = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// SetPlaybackRate
|
||||||
|
//
|
||||||
|
|
||||||
|
private void SetPlaybackRate(float pitch, bool playing)
|
||||||
|
{
|
||||||
|
if (pitch < -1f)
|
||||||
|
pitch = -1f;
|
||||||
|
else if (pitch > 1f)
|
||||||
|
pitch = 1f;
|
||||||
|
this.pitch = pitch;
|
||||||
|
|
||||||
|
if (playing)
|
||||||
|
{
|
||||||
|
// convert pitch from range 0 .. 1 to range 0.5 .. 2
|
||||||
|
// (which represents half to twice the sample rate)
|
||||||
|
if (pitch < 0f)
|
||||||
|
pitch = 1f + pitch * 0.5f;
|
||||||
|
else if (pitch > 0f)
|
||||||
|
pitch = 1f + pitch;
|
||||||
|
int r = (int) (effect.sampleRate * pitch);
|
||||||
|
var track = this.track;
|
||||||
|
if (track != null)
|
||||||
|
track.setPlaybackRate(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// SetStereoVolume
|
||||||
|
//
|
||||||
|
|
||||||
|
private void SetStereoVolume(float volume, float pan, bool playing)
|
||||||
|
{
|
||||||
|
if (volume < 0f)
|
||||||
|
volume = 0f;
|
||||||
|
else if (volume > 1f)
|
||||||
|
volume = 1f;
|
||||||
|
this.volume = volume;
|
||||||
|
|
||||||
|
if (pan < -1f)
|
||||||
|
pan = -1f;
|
||||||
|
else if (pan > 1f)
|
||||||
|
pan = 1f;
|
||||||
|
this.pan = pan;
|
||||||
|
|
||||||
|
if (playing)
|
||||||
|
{
|
||||||
|
float leftGain = 1f;
|
||||||
|
float rightGain = 1f;
|
||||||
|
if (pan < 0f)
|
||||||
|
{
|
||||||
|
leftGain *= 0f - pan;
|
||||||
|
rightGain *= pan + 1f;
|
||||||
|
}
|
||||||
|
else if (pan > 0f)
|
||||||
|
{
|
||||||
|
rightGain *= pan;
|
||||||
|
leftGain *= 1f - pan;
|
||||||
|
}
|
||||||
|
var track = this.track;
|
||||||
|
if (track != null)
|
||||||
|
track.setStereoVolume(leftGain * volume, rightGain * volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Play, Pause, Resume, Stop
|
||||||
|
//
|
||||||
|
|
||||||
|
public virtual void Play()
|
||||||
|
{
|
||||||
|
if (State != SoundState.Playing)
|
||||||
|
{
|
||||||
|
var track = this.track;
|
||||||
|
if (track == null)
|
||||||
|
{
|
||||||
|
CreateTrack(true);
|
||||||
|
track = this.track;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (track != null)
|
||||||
|
{
|
||||||
|
SetPlaybackRate(pitch, true);
|
||||||
|
SetStereoVolume(volume, pan, true);
|
||||||
|
|
||||||
|
watcher.instance = this;
|
||||||
|
track.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Pause()
|
||||||
|
{
|
||||||
|
var track = this.track;
|
||||||
|
if (track != null && State == SoundState.Playing)
|
||||||
|
track.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Resume() => Play();
|
||||||
|
|
||||||
|
public void Stop() => Stop(true);
|
||||||
|
|
||||||
|
public void Stop(bool immediate)
|
||||||
|
{
|
||||||
|
if (immediate)
|
||||||
|
{
|
||||||
|
var track = this.track;
|
||||||
|
if (track != null && State != SoundState.Stopped)
|
||||||
|
track.stop();
|
||||||
|
}
|
||||||
|
if (watcher != null)
|
||||||
|
watcher.instance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Play (for SoundEffect.Play)
|
||||||
|
//
|
||||||
|
|
||||||
|
public bool Play(float volume, float pitch, float pan)
|
||||||
|
{
|
||||||
|
this.volume = volume;
|
||||||
|
this.pitch = pitch;
|
||||||
|
this.pan = pan;
|
||||||
|
Play();
|
||||||
|
if (track == null)
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Apply3D (no-op)
|
||||||
|
//
|
||||||
|
|
||||||
|
public void Apply3D(AudioListener listener, AudioEmitter emitter) { }
|
||||||
|
public void Apply3D(AudioListener[] listeners, AudioEmitter emitter) { }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// SoundEffectInstanceWatcher
|
||||||
|
//
|
||||||
|
|
||||||
|
public class SoundEffectInstanceWatcher :
|
||||||
|
android.media.AudioTrack.OnPlaybackPositionUpdateListener
|
||||||
|
{
|
||||||
|
[java.attr.RetainType] public SoundEffectInstance instance;
|
||||||
|
|
||||||
|
[java.attr.RetainName]
|
||||||
|
public void onMarkerReached(android.media.AudioTrack track)
|
||||||
|
{
|
||||||
|
// release the strong reference to the SoundEffectInstance,
|
||||||
|
// so it can be garbage collected if not otherwise referenced
|
||||||
|
track.stop();
|
||||||
|
var instance = this.instance;
|
||||||
|
if (instance != null)
|
||||||
|
{
|
||||||
|
if (instance.IsLooped && (! instance.IsDisposed))
|
||||||
|
track.play();
|
||||||
|
else
|
||||||
|
this.instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[java.attr.RetainName]
|
||||||
|
public void onPeriodicNotification(android.media.AudioTrack track) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -59,4 +59,10 @@ namespace Microsoft.Xna.Framework
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*internal static class TitleLocation
|
||||||
|
{
|
||||||
|
// there is no file path for asset files which are part of the APK
|
||||||
|
public static string Path => throw new System.PlatformNotSupportedException();
|
||||||
|
}*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@
|
|||||||
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
|
<Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86" />
|
||||||
<Reference Include="Microsoft.Xna.Framework.Input.Touch, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL" />
|
<Reference Include="Microsoft.Xna.Framework.Input.Touch, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL" />
|
||||||
<Reference Include="Microsoft.Xna.Framework.Storage, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL" />
|
<Reference Include="Microsoft.Xna.Framework.Storage, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=MSIL" />
|
||||||
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Windows.Forms" />
|
<Reference Include="System.Windows.Forms" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -3,6 +3,7 @@ using System;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
using Microsoft.Xna.Framework.Graphics;
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
|
using Microsoft.Xna.Framework.Audio;
|
||||||
|
|
||||||
namespace Demo1
|
namespace Demo1
|
||||||
{
|
{
|
||||||
@ -14,6 +15,8 @@ namespace Demo1
|
|||||||
public Texture2D white;
|
public Texture2D white;
|
||||||
private SpriteBatch spriteBatch;
|
private SpriteBatch spriteBatch;
|
||||||
private Font myFont;
|
private Font myFont;
|
||||||
|
private SoundEffectInstance effect;
|
||||||
|
private bool playEffect;
|
||||||
private int pageNumber, pageNumberOld;
|
private int pageNumber, pageNumberOld;
|
||||||
private bool paused;
|
private bool paused;
|
||||||
private bool anyDrawText;
|
private bool anyDrawText;
|
||||||
@ -58,8 +61,33 @@ namespace Demo1
|
|||||||
// Texture2D.FromStream(GraphicsDevice, stream);
|
// Texture2D.FromStream(GraphicsDevice, stream);
|
||||||
//
|
//
|
||||||
// to disable processing, open Properties on the image in the
|
// to disable processing, open Properties on the image in the
|
||||||
// Content project, set "Build Action: None", and
|
// Content project, and change the following in the Advanced tab:
|
||||||
// "Copy to Output Directory: Copy if newer".
|
// "Build Action: None" and "Copy to Output Directory: Copy if newer".
|
||||||
|
|
||||||
|
effect = Content.Load<SoundEffect>("effect").CreateInstance();
|
||||||
|
effect.Pitch = 0.2f;
|
||||||
|
|
||||||
|
// the XNA content processor for Song converts music files to WMA
|
||||||
|
// format, which Android does not support playing. use MP3 instead.
|
||||||
|
//
|
||||||
|
// to disable processing, open Properties on the image in the
|
||||||
|
// Content project, and change the following in the Advanced tab:
|
||||||
|
// "Build Action: None" and "Copy to Output Directory: Copy if newer".
|
||||||
|
//
|
||||||
|
// note that XNA on Windows may not play MP3 files which contain ID3
|
||||||
|
// tags. use one of the many free utilities to remove such tags.
|
||||||
|
//
|
||||||
|
// unsupported BNA MediaPlayer: queueing more than one song at a time;
|
||||||
|
// the ActiveSongChanged event; visualization data.
|
||||||
|
|
||||||
|
Microsoft.Xna.Framework.Media.MediaPlayer.Volume = 0.05f;
|
||||||
|
Microsoft.Xna.Framework.Media.MediaPlayer.Play(
|
||||||
|
Microsoft.Xna.Framework.Media.Song.FromUri("Song1",
|
||||||
|
new System.Uri(Content.RootDirectory + "/music.mp3", UriKind.Relative)));
|
||||||
|
Microsoft.Xna.Framework.Media.MediaPlayer.IsRepeating = true;
|
||||||
|
// song duration is populated during the call to Play()
|
||||||
|
// Console.WriteLine(Microsoft.Xna.Framework.Media.MediaPlayer.Queue.ActiveSong.Duration);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -80,12 +108,26 @@ namespace Demo1
|
|||||||
if (paused)
|
if (paused)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (playEffect)
|
||||||
|
{
|
||||||
|
effect.Play();
|
||||||
|
playEffect = false;
|
||||||
|
}
|
||||||
|
|
||||||
// basic scene management for the purpose of this demo:
|
// basic scene management for the purpose of this demo:
|
||||||
// after either arrow at the top of the screen is clicked, and
|
// after either arrow at the top of the screen is clicked, and
|
||||||
// the current page number has changed, create the new 'page'.
|
// the current page number has changed, create the new 'page'.
|
||||||
|
|
||||||
if (pageNumber != pageNumberOld)
|
if (pageNumber != pageNumberOld)
|
||||||
{
|
{
|
||||||
|
if (pageComponent != null)
|
||||||
|
{
|
||||||
|
if (effect.State == SoundState.Playing)
|
||||||
|
effect.Stop();
|
||||||
|
effect.Pan = (pageNumber > pageNumberOld) ? 1f : -1f;
|
||||||
|
playEffect = true;
|
||||||
|
}
|
||||||
|
|
||||||
const int LAST_PAGE = 4;
|
const int LAST_PAGE = 4;
|
||||||
if (pageNumber <= 0)
|
if (pageNumber <= 0)
|
||||||
pageNumber = LAST_PAGE;
|
pageNumber = LAST_PAGE;
|
||||||
|
@ -42,7 +42,7 @@ namespace Demo1
|
|||||||
{
|
{
|
||||||
if (renderToTexture)
|
if (renderToTexture)
|
||||||
{
|
{
|
||||||
((Game1)Game).DrawFlushBatch();
|
((Game1) Game).DrawFlushBatch();
|
||||||
renderTargetWidth = Config.ClientWidth;
|
renderTargetWidth = Config.ClientWidth;
|
||||||
renderTargetHeight = Config.ClientHeight;
|
renderTargetHeight = Config.ClientHeight;
|
||||||
if (renderTarget != null)
|
if (renderTarget != null)
|
||||||
|
@ -48,6 +48,10 @@ namespace Demo1
|
|||||||
file = container.OpenFile("state-v1.bin", FileMode.OpenOrCreate);
|
file = container.OpenFile("state-v1.bin", FileMode.OpenOrCreate);
|
||||||
if (file.Length > 4)
|
if (file.Length > 4)
|
||||||
Read(file);
|
Read(file);
|
||||||
|
|
||||||
|
// reset file in case we crash
|
||||||
|
file.SetLength(0);
|
||||||
|
file.Flush();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<!--
|
||||||
|
to resolve error in Microsoft.Xna.GameStudio.ContentPipeline.targets line 78,
|
||||||
|
error loading pipeline assembly Microsoft.Build.Framework.dll:
|
||||||
|
cd C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin
|
||||||
|
gacutil /i Microsoft.Build.Framework.dll
|
||||||
|
see also https://github.com/dotnet/msbuild/issues/1831
|
||||||
|
-->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ProjectGuid>{E50B6FE1-C91C-4226-BA4B-75DEFDEF4CA3}</ProjectGuid>
|
<ProjectGuid>{E50B6FE1-C91C-4226-BA4B-75DEFDEF4CA3}</ProjectGuid>
|
||||||
<ProjectTypeGuids>{96E2B04D-8817-42c6-938A-82C39BA4D311};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
<ProjectTypeGuids>{96E2B04D-8817-42c6-938A-82C39BA4D311};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
@ -64,6 +71,21 @@
|
|||||||
<Processor>TextureProcessor</Processor>
|
<Processor>TextureProcessor</Processor>
|
||||||
</Compile>
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="effect.mp3">
|
||||||
|
<Name>effect</Name>
|
||||||
|
<Importer>Mp3Importer</Importer>
|
||||||
|
<Processor>SoundEffectProcessor</Processor>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="music.mp3">
|
||||||
|
<Name>music</Name>
|
||||||
|
<Importer>Mp3Importer</Importer>
|
||||||
|
<Processor>SongProcessor</Processor>
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\$(XnaFrameworkVersion)\Microsoft.Xna.GameStudio.ContentPipeline.targets" />
|
<Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\$(XnaFrameworkVersion)\Microsoft.Xna.GameStudio.ContentPipeline.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
BIN
Demo1/Demo1Content/effect.mp3
Normal file
BIN
Demo1/Demo1Content/effect.mp3
Normal file
Binary file not shown.
BIN
Demo1/Demo1Content/music.mp3
Normal file
BIN
Demo1/Demo1Content/music.mp3
Normal file
Binary file not shown.
@ -1,5 +1,11 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
to resolve error in Microsoft.PackageDependencyResolution.targets about
|
||||||
|
Assets file 'project.assets.json' doesn't have a target for 'net461'
|
||||||
|
make sure you use the latest nuget.exe, or at least version 5.8.0
|
||||||
|
-->
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<TargetFramework>net461</TargetFramework>
|
<TargetFramework>net461</TargetFramework>
|
||||||
@ -14,6 +20,9 @@
|
|||||||
|
|
||||||
<Compile Include="Library1.fs" />
|
<Compile Include="Library1.fs" />
|
||||||
|
|
||||||
|
<PackageReference Include="FSharp.Core" Version="4.7.0" />
|
||||||
|
<PackageReference Update="FSharp.Core" Version="4.7.0" />
|
||||||
|
|
||||||
<Reference Include="Microsoft.Xna.Framework">
|
<Reference Include="Microsoft.Xna.Framework">
|
||||||
<HintPath>C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.dll</HintPath>
|
<HintPath>C:\Program Files (x86)\Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86\Microsoft.Xna.Framework.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
@ -7,8 +7,10 @@ open Microsoft.Xna.Framework.Graphics
|
|||||||
type StencilDemo (game : Game) =
|
type StencilDemo (game : Game) =
|
||||||
inherit DrawableGameComponent(game)
|
inherit DrawableGameComponent(game)
|
||||||
|
|
||||||
let mutable spriteBatch = null
|
let mutable spriteBatch = new SpriteBatch (game.GraphicsDevice)
|
||||||
let mutable texture = null
|
let mutable logoTexture = game.Content.Load("fsharp256")
|
||||||
|
let mutable backTexture = new Texture2D(game.GraphicsDevice, 16, 16)
|
||||||
|
let mutable counter = 0
|
||||||
|
|
||||||
// using option here solely to force a dependency on FSharp.Core.dll
|
// using option here solely to force a dependency on FSharp.Core.dll
|
||||||
let mutable rectFunc : (Game -> Rectangle) option = None
|
let mutable rectFunc : (Game -> Rectangle) option = None
|
||||||
@ -26,12 +28,36 @@ type StencilDemo (game : Game) =
|
|||||||
(float32) (Math.Cos(gameTime.TotalGameTime.TotalMilliseconds * 0.001)))
|
(float32) (Math.Cos(gameTime.TotalGameTime.TotalMilliseconds * 0.001)))
|
||||||
|
|
||||||
override Game.Initialize() =
|
override Game.Initialize() =
|
||||||
spriteBatch <- new SpriteBatch (game.GraphicsDevice)
|
|
||||||
texture <- game.Content.Load("fsharp256")
|
|
||||||
rectFunc <- Some logoRect
|
rectFunc <- Some logoRect
|
||||||
colorFunc <- Some logoColor
|
colorFunc <- Some logoColor
|
||||||
|
|
||||||
override Game.Draw gameTime =
|
override Game.Draw gameTime =
|
||||||
|
|
||||||
|
let backArray = Array.zeroCreate (16 * 16)
|
||||||
|
backTexture.GetData backArray
|
||||||
|
|
||||||
|
if counter = 0
|
||||||
|
then
|
||||||
|
backArray.[12 * 16] <- 0xFF0000FF
|
||||||
|
counter <- 1
|
||||||
|
else
|
||||||
|
for y = 4 to 11 do
|
||||||
|
for x = 0 to 15 do
|
||||||
|
let idx = y * 16 + x
|
||||||
|
backArray.[idx] <- backArray.[idx + 1]
|
||||||
|
backArray.[idx + 1] <- 0
|
||||||
|
counter <- match counter with
|
||||||
|
| 60 -> 0
|
||||||
|
| n -> n + 1
|
||||||
|
backTexture.SetData backArray
|
||||||
|
|
||||||
|
spriteBatch.Begin (SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, DepthStencilState.None, new RasterizerState())
|
||||||
|
spriteBatch.Draw (backTexture,
|
||||||
|
Rectangle(0, 0, game.Window.ClientBounds.Width, game.Window.ClientBounds.Height),
|
||||||
|
Nullable (Rectangle(0, 0, 16, 16)),
|
||||||
|
Color.White)
|
||||||
|
spriteBatch.End ()
|
||||||
|
|
||||||
spriteBatch.Begin ()
|
spriteBatch.Begin ()
|
||||||
spriteBatch.Draw (texture, rectFunc.Value game, colorFunc.Value gameTime)
|
spriteBatch.Draw (logoTexture, rectFunc.Value game, colorFunc.Value gameTime)
|
||||||
spriteBatch.End ()
|
spriteBatch.End ()
|
@ -17,6 +17,8 @@
|
|||||||
CONTENT_DIR = path to Content directory
|
CONTENT_DIR = path to Content directory
|
||||||
e.g. $(OutputDir)/MyGame/Debug/Content
|
e.g. $(OutputDir)/MyGame/Debug/Content
|
||||||
|
|
||||||
|
ICON_PNG = path to a PNG file to use as the app icon
|
||||||
|
|
||||||
APK_OUTPUT = path to copy the final APK
|
APK_OUTPUT = path to copy the final APK
|
||||||
e.g. $(OutputDir)/MyGame.apk
|
e.g. $(OutputDir)/MyGame.apk
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ echo Building BNA. Command:
|
|||||||
echo MSBuild BNA -p:Configuration=Release
|
echo MSBuild BNA -p:Configuration=Release
|
||||||
echo ========================================
|
echo ========================================
|
||||||
MSBuild BNA -p:Configuration=Release
|
MSBuild BNA -p:Configuration=Release
|
||||||
|
if errorlevel 1 goto :EOF
|
||||||
pause
|
pause
|
||||||
|
|
||||||
echo ========================================
|
echo ========================================
|
||||||
@ -38,14 +39,17 @@ echo nuget restore Demo1
|
|||||||
echo msbuild Demo1 -p:Configuration=Release -p:Platform="x86"
|
echo msbuild Demo1 -p:Configuration=Release -p:Platform="x86"
|
||||||
echo ========================================
|
echo ========================================
|
||||||
nuget restore Demo1
|
nuget restore Demo1
|
||||||
|
if errorlevel 1 goto :EOF
|
||||||
msbuild Demo1 -p:Configuration=Release -p:Platform="x86"
|
msbuild Demo1 -p:Configuration=Release -p:Platform="x86"
|
||||||
|
if errorlevel 1 goto :EOF
|
||||||
pause
|
pause
|
||||||
|
|
||||||
echo ========================================
|
echo ========================================
|
||||||
echo Converting Demo1 to APK. Command:
|
echo Converting Demo1 to APK. Command:
|
||||||
echo MSBuild MakeAPK.project -p:INPUT_DLL=.obj\Demo1\Release\Demo1.exe -p:INPUT_DLL_2=.obj\Demo1\Release\Demo1FSharp.dll -p:INPUT_DLL_3=.obj\Demo1\Release\FSharp.Core.dll -p:CONTENT_DIR=.obj\Demo1\Release\Content -p:ICON_PNG=Demo1\Demo1\GameThumbnail.png -p:ANDROID_MANIFEST=Demo1\AndroidManifest.xml -p:KEYSTORE_FILE=.\my.keystore -p:KEYSTORE_PWD=123456 -p:APK_OUTPUT=.obj\Demo1.apk -p:APK_TEMP_DIR=.obj\Demo1\Release\TempApk -p:EXTRA_JAR_1=.obj\BNA.jar -p:EXTRA_JAR_2=%BLUEBONNET_LIB%
|
echo MSBuild MakeAPK.project -p:INPUT_DLL=.obj\Demo1\Release\Demo1.exe -p:INPUT_DLL_2=.obj\Demo1\Release\Demo1FSharp.dll -p:INPUT_DLL_3=.obj\Demo1\Release\FSharp.Core.dll -p:CONTENT_DIR=.obj\Demo1\Release\Content -p:ICON_PNG=Demo1\Demo1\GameThumbnail.png -p:ANDROID_MANIFEST=Demo1\AndroidManifest.xml -p:KEYSTORE_FILE=.\my.keystore -p:KEYSTORE_PWD=123456 -p:APK_OUTPUT=.obj\Demo1.apk -p:APK_TEMP_DIR=.obj\Demo1\Release\TempApk -p:EXTRA_JAR_1=.obj\BNA.jar -p:EXTRA_JAR_2=%BLUEBONNET_LIB%
|
||||||
echo ========================================
|
echo ========================================
|
||||||
MSBuild MakeAPK.project -p:INPUT_DLL=.obj\Demo1\Release\Demo1.exe -p:INPUT_DLL_2=.obj\Demo1\Release\Demo1FSharp.dll -p:INPUT_DLL_3=.obj\Demo1\Release\FSharp.Core.dll -p:CONTENT_DIR=.obj\Demo1\Release\Content -p:ICON_PNG=Demo1\Demo1\GameThumbnail.png -p:ANDROID_MANIFEST=Demo1\AndroidManifest.xml -p:KEYSTORE_FILE=.\my.keystore -p:KEYSTORE_PWD=123456 -p:APK_OUTPUT=.obj\Demo1.apk -p:APK_TEMP_DIR=.obj\Demo1\Release\TempApk -p:EXTRA_JAR_1=.obj\BNA.jar -p:EXTRA_JAR_2=%BLUEBONNET_LIB%
|
MSBuild MakeAPK.project -p:INPUT_DLL=.obj\Demo1\Release\Demo1.exe -p:INPUT_DLL_2=.obj\Demo1\Release\Demo1FSharp.dll -p:INPUT_DLL_3=.obj\Demo1\Release\FSharp.Core.dll -p:CONTENT_DIR=.obj\Demo1\Release\Content -p:ICON_PNG=Demo1\Demo1\GameThumbnail.png -p:ANDROID_MANIFEST=Demo1\AndroidManifest.xml -p:KEYSTORE_FILE=.\my.keystore -p:KEYSTORE_PWD=123456 -p:APK_OUTPUT=.obj\Demo1.apk -p:APK_TEMP_DIR=.obj\Demo1\Release\TempApk -p:EXTRA_JAR_1=.obj\BNA.jar -p:EXTRA_JAR_2=%BLUEBONNET_LIB%
|
||||||
|
if errorlevel 1 goto :EOF
|
||||||
|
|
||||||
echo ========================================
|
echo ========================================
|
||||||
echo All done
|
echo All done
|
||||||
|
Loading…
x
Reference in New Issue
Block a user