-
+
+
|
int |
@@ -73,13 +78,14 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
api.playbackRange = { startTick: 1000, endTick: 50000 };
```
@@ -98,5 +104,16 @@ var api = new AlphaTabApi(...);
api.PlaybackRange = new PlaybackRange { StartTick = 1000, EndTick = 50000 };
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.playbackRange = PlaybackRange.apply {
+ startTick = 1000
+ endTick = 50000
+}
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/playbackrangechanged.mdx b/docs/reference/api/playbackrangechanged.mdx
index 77eddef..3d76f9e 100644
--- a/docs/reference/api/playbackrangechanged.mdx
+++ b/docs/reference/api/playbackrangechanged.mdx
@@ -25,6 +25,7 @@ This event is fired when the playback range changed.
+
## Parameters
@@ -36,6 +37,9 @@ This event is fired when the playback range changed.
The information about the changed playback range.
+
+ The information about the changed playback range.
+
## PlaybackRangeChangedEventArgs Properties
@@ -47,6 +51,9 @@ This event is fired when the playback range changed.
The new playback range or null if no range is selected. (see [PlaybackRange](/docs/reference/api/playbackrange))
+
+ The new playback range or null if no range is selected. (see [PlaybackRange](/docs/reference/api/playbackrange))
+
## Examples
@@ -57,7 +64,8 @@ This event is fired when the playback range changed.
{ label: 'JavaScript', value: 'js', },
{ label: 'HTML', value: 'html', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
@@ -118,5 +126,20 @@ api.PlaybackRangeChanged.On(args =>
});
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.playbackRangeChanged.on { args ->
+ val playbackRange = args.playbackRange
+ if (playbackRange != null) {
+ highlightRangeInProgressBar(playbackRange.startTick, playbackRange.endTick)
+ } else {
+ clearHighlightInProgressBar()
+ }
+}
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/playbackspeed.mdx b/docs/reference/api/playbackspeed.mdx
index bdf71da..5948793 100644
--- a/docs/reference/api/playbackspeed.mdx
+++ b/docs/reference/api/playbackspeed.mdx
@@ -5,6 +5,8 @@ sidebar_custom_props:
category: Properties - Player
since: 0.9.4
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -36,13 +38,14 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
api.playbackSpeed = 0.5;
```
@@ -61,5 +64,13 @@ var api = new AlphaTabApi(...);
api.PlaybackSpeed = 0.5;
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.playbackSpeed = 0.5
+```
+
diff --git a/docs/reference/api/playbeat.mdx b/docs/reference/api/playbeat.mdx
index e4801db..289cdf5 100644
--- a/docs/reference/api/playbeat.mdx
+++ b/docs/reference/api/playbeat.mdx
@@ -5,7 +5,10 @@ sidebar_custom_props:
category: Methods - Player
since: 1.1.0
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
+import {ParameterTable, ParameterRow} from '@site/src/components/ParameterTable';
@@ -23,6 +26,7 @@ It is a playback of audio separate to the main song playback.
+
### Parameters
@@ -34,6 +38,9 @@ It is a playback of audio separate to the main song playback.
The single beat to play.
+
+ The single beat to play.
+
@@ -47,13 +54,14 @@ It is a playback of audio separate to the main song playback.
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
api.playBeat(api.score.tracks[0].staves[0].bars[0].voices[0].beats[0]);
```
@@ -61,7 +69,7 @@ api.playBeat(api.score.tracks[0].staves[0].bars[0].voices[0].beats[0]);
```js
-var beat = $('#alphaTab').alphaTab('score').tracks[0].staves[0].bars[0].voices[0].beats[0];
+const beat = $('#alphaTab').alphaTab('score').tracks[0].staves[0].bars[0].voices[0].beats[0];
$('#alphaTab').alphaTab('playBeat', beat);
```
@@ -73,5 +81,13 @@ var api = new AlphaTabApi(...);
api.PlayBeat(api.Score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0]);
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.playBeat(api.score.tracks[0].staves[0].bars[0].voices[0].beats[0])
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/playedbeatchanged.mdx b/docs/reference/api/playedbeatchanged.mdx
index 673c67b..646d70b 100644
--- a/docs/reference/api/playedbeatchanged.mdx
+++ b/docs/reference/api/playedbeatchanged.mdx
@@ -25,6 +25,7 @@ This event is fired when the played beat changed.
+
## Parameters
@@ -36,6 +37,9 @@ This event is fired when the played beat changed.
The new beat that is now being played..
+
+ The new beat that is now being played..
+
## Examples
@@ -46,7 +50,8 @@ This event is fired when the played beat changed.
{ label: 'JavaScript', value: 'js', },
{ label: 'HTML', value: 'html', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
@@ -88,5 +93,15 @@ api.PlayedBeatChanged.On(beat =>
});
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.playedBeatChanged.on { beat ->
+ updateFretboard(beat)
+}
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/player.mdx b/docs/reference/api/player.mdx
index f3c3d8c..329f0fa 100644
--- a/docs/reference/api/player.mdx
+++ b/docs/reference/api/player.mdx
@@ -5,6 +5,8 @@ sidebar_custom_props:
category: Properties - Player
since: 0.9.4
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -24,6 +26,7 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
+
## Examples
@@ -33,13 +36,14 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
setupPlayerEvents(api.settings);
```
@@ -58,5 +62,13 @@ var api = new AlphaTabApi(...);
SetupPlayerEvents(api.Player);
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+setupPlayerEvents(api.player)
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/playerfinished.mdx b/docs/reference/api/playerfinished.mdx
index 55dc0bc..29f48f1 100644
--- a/docs/reference/api/playerfinished.mdx
+++ b/docs/reference/api/playerfinished.mdx
@@ -25,6 +25,7 @@ This event is fired when the playback of the whole song finished. This event is
+
## Parameters
@@ -38,7 +39,8 @@ None
{ label: 'JavaScript', value: 'js', },
{ label: 'HTML', value: 'html', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
@@ -67,7 +69,6 @@ at.alphaTab('playbackSpeed', 0.5);
at.alphaTab('play');
```
-
@@ -90,7 +91,7 @@ api.play()
```csharp
var api = new AlphaTabApi(...);
-api.Finished.On(() =>
+api.PlayerFinished.On(() =>
{
// speed trainer
api.PlaybackSpeed = Math.Min(1.0, api.PlaybackSpeed + 0.1);
@@ -100,5 +101,19 @@ api.PlaybackSpeed = 0.5;
api.Play();
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.playerFinished.on {
+ // speed trainer
+ api.playbackSpeed = min(1.0, api.playbackSpeed + 0.1);
+}
+api.isLooping = true
+api.playbackSpeed = 0.5
+api.play()
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/playerpositionchanged.mdx b/docs/reference/api/playerpositionchanged.mdx
index 2d86bfe..b8d51cb 100644
--- a/docs/reference/api/playerpositionchanged.mdx
+++ b/docs/reference/api/playerpositionchanged.mdx
@@ -25,6 +25,7 @@ This event is fired when the current playback position of the song changed.
+
## Parameters
@@ -36,6 +37,9 @@ This event is fired when the current playback position of the song changed.
The information about the player position.
+
+ The information about the player position.
+
## PositionChangedEventArgs Properties
@@ -47,30 +51,45 @@ This event is fired when the current playback position of the song changed.
The current time position within the song in milliseconds.
+
+ The current time position within the song in milliseconds.
+
The total length of the song in milliseconds.
The current time position within the song in milliseconds.
+
+ The current time position within the song in milliseconds.
+
The current time position within the song in midi ticks.
The current time position within the song in midi ticks.
+
+ The current time position within the song in midi ticks.
+
The total length of the song in midi ticks.
The current time position within the song in midi ticks.
+
+ The current time position within the song in midi ticks.
+
Whether the position changed because of time seeking and not due to playback. since 1.2.0-alpha.97
Whether the position changed because of time seeking and not due to playback. since 1.2.0-alpha.97
+
+ Whether the position changed because of time seeking and not due to playback. since 1.2.0-alpha.97
+
## Examples
@@ -81,7 +100,8 @@ This event is fired when the current playback position of the song changed.
{ label: 'JavaScript', value: 'js', },
{ label: 'HTML', value: 'html', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
@@ -123,5 +143,15 @@ api.PlayerPositionChanged.On(args =>
});
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.playerPositionChanged.on { args ->
+ updatePlayerPosition(args)
+}
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/playerstate.mdx b/docs/reference/api/playerstate.mdx
index 4184bb0..05f5584 100644
--- a/docs/reference/api/playerstate.mdx
+++ b/docs/reference/api/playerstate.mdx
@@ -5,6 +5,8 @@ sidebar_custom_props:
category: Properties - Player
since: 0.9.4
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -27,6 +29,7 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
`1` for playing
+
## Examples
@@ -36,13 +39,14 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
if(api.playerState != alphaTab.synth.PlayerState.Playing) api.play();
```
@@ -50,7 +54,7 @@ if(api.playerState != alphaTab.synth.PlayerState.Playing) api.play();
```js
-var at = $('#alphaTab');
+const at = $('#alphaTab');
if(at.alphaTab('playerState') != alphaTab.synth.PlayerState.Playing) at.alphaTab('play');
```
@@ -62,5 +66,13 @@ var api = new AlphaTabApi(...);
if(api.PlayerState != PlayerState.Playing) api.Play();
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+if (api.playerState != PlayerState.Playing) api.play()
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/playerstatechanged.mdx b/docs/reference/api/playerstatechanged.mdx
index a17c080..17e5c52 100644
--- a/docs/reference/api/playerstatechanged.mdx
+++ b/docs/reference/api/playerstatechanged.mdx
@@ -25,6 +25,7 @@ This event is fired when the playback state changed.
+
## Parameters
@@ -36,6 +37,9 @@ This event is fired when the playback state changed.
The information about the player state change event.
+
+ The information about the player state change event.
+
## PlayerStateChangedEventArgs Properties
@@ -49,6 +53,9 @@ This event is fired when the playback state changed.
The new state of the player.
+
+ The new state of the player.
+
Whether the playback was fully stopped (including jump to start) or only paused.
@@ -62,7 +69,8 @@ This event is fired when the playback state changed.
{ label: 'JavaScript', value: 'js', },
{ label: 'HTML', value: 'html', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
@@ -104,5 +112,15 @@ api.PlayerStateChanged.On(args =>
});
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.playerStateChanged.on { args ->
+ updatePlayerControls(args)
+}
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/playnote.mdx b/docs/reference/api/playnote.mdx
index 4c6355d..7cecb47 100644
--- a/docs/reference/api/playnote.mdx
+++ b/docs/reference/api/playnote.mdx
@@ -5,6 +5,9 @@ sidebar_custom_props:
category: Methods - Player
since: 1.1.0
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+import {ParameterTable, ParameterRow} from '@site/src/components/ParameterTable';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -23,6 +26,7 @@ It is a playback of audio separate to the main song playback.
+
### Parameters
@@ -34,6 +38,9 @@ It is a playback of audio separate to the main song playback.
The single note to play.
+
+ The single note to play.
+
@@ -47,13 +54,14 @@ It is a playback of audio separate to the main song playback.
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
api.playNote(api.score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0]);
```
@@ -61,7 +69,7 @@ api.playNote(api.score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0]);
```js
-var note = $('#alphaTab').alphaTab('score').tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0];
+const note = $('#alphaTab').alphaTab('score').tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0];
$('#alphaTab').alphaTab('playNote', beat);
```
@@ -73,5 +81,13 @@ var api = new AlphaTabApi(...);
api.PlayNote(api.Score.Tracks[0].Staves[0].Bars[0].Voices[0].Beats[0].Notes[0]);
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.playNote(api.score.tracks[0].staves[0].bars[0].voices[0].beats[0].notes[0]);
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/playpause.mdx b/docs/reference/api/playpause.mdx
index e1e4bab..f4efa03 100644
--- a/docs/reference/api/playpause.mdx
+++ b/docs/reference/api/playpause.mdx
@@ -5,6 +5,8 @@ sidebar_custom_props:
category: Methods - Player
since: 0.9.4
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -20,13 +22,13 @@ Toggles between play/pause depending on the current player state. if the player
+
### Parameters
None
### Returns
-`true` if the playback was started, otherwise `false`. Reasons for not starting can be that the player is not ready or already playing.
## Examples
@@ -35,13 +37,14 @@ None
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
api.playPause();
```
@@ -60,5 +63,13 @@ var api = new AlphaTabApi(...);
api.PlayPause();
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.playPause()
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/postrenderfinished.mdx b/docs/reference/api/postrenderfinished.mdx
index ac0fc5d..5620a12 100644
--- a/docs/reference/api/postrenderfinished.mdx
+++ b/docs/reference/api/postrenderfinished.mdx
@@ -26,6 +26,7 @@ handlers are called, the whole rendering and display pipeline is completed.
+
## Parameters
@@ -39,7 +40,8 @@ None
{ label: 'JavaScript', value: 'js', },
{ label: 'HTML', value: 'html', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
@@ -80,5 +82,15 @@ api.PostRenderFinished.On(() =>
});
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.postRenderFinished.on {
+ hideLoadingIndicator();
+}
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/print.mdx b/docs/reference/api/print.mdx
index 629ab86..74fca70 100644
--- a/docs/reference/api/print.mdx
+++ b/docs/reference/api/print.mdx
@@ -6,6 +6,9 @@ sidebar_custom_props:
category: Methods - Core
since: 0.9.4
---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -60,7 +63,7 @@ Nothing
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
api.print();
api.print(undefined, { display: { barsPerRow: 5 } });
```
diff --git a/docs/reference/api/readyforplayback.mdx b/docs/reference/api/readyforplayback.mdx
index 797c3af..989aaf4 100644
--- a/docs/reference/api/readyforplayback.mdx
+++ b/docs/reference/api/readyforplayback.mdx
@@ -26,6 +26,7 @@ all background workers are started, the audio output is initialized, a soundfont
+
## Parameters
@@ -39,7 +40,8 @@ none
{ label: 'JavaScript', value: 'js', },
{ label: 'HTML', value: 'html', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
@@ -80,5 +82,15 @@ api.PlayerReady.On(() =>
});
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.playerReady.on {
+ enablePlayerControls()
+}
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/render.mdx b/docs/reference/api/render.mdx
index c76052f..e8b5f67 100644
--- a/docs/reference/api/render.mdx
+++ b/docs/reference/api/render.mdx
@@ -5,6 +5,9 @@ sidebar_custom_props:
category: Methods - Core
since: 0.9.4
---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -20,6 +23,7 @@ Initiates a re-rendering of the current setup. If rendering is not yet possible,
+
### Parameters
@@ -36,13 +40,14 @@ Nothing
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
api.render();
```
@@ -61,6 +66,13 @@ var api = new AlphaTabApi(...);
api.Render();
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.render()
+```
\ No newline at end of file
diff --git a/docs/reference/api/renderer.mdx b/docs/reference/api/renderer.mdx
index 620f5d9..bd83547 100644
--- a/docs/reference/api/renderer.mdx
+++ b/docs/reference/api/renderer.mdx
@@ -24,4 +24,5 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
+
\ No newline at end of file
diff --git a/docs/reference/api/renderfinished.mdx b/docs/reference/api/renderfinished.mdx
index 8b69759..7ddba69 100644
--- a/docs/reference/api/renderfinished.mdx
+++ b/docs/reference/api/renderfinished.mdx
@@ -26,6 +26,7 @@ the display component to visually display the rendered components when this even
+
## Parameters
@@ -39,7 +40,8 @@ None
{ label: 'JavaScript', value: 'js', },
{ label: 'HTML', value: 'html', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
@@ -80,5 +82,15 @@ api.RenderFinished.On(() =>
});
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.renderFinished.on {
+ updateProgressBar("Finishing")
+}
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/renderscore.mdx b/docs/reference/api/renderscore.mdx
index 4c75510..a8978c6 100644
--- a/docs/reference/api/renderscore.mdx
+++ b/docs/reference/api/renderscore.mdx
@@ -5,6 +5,8 @@ sidebar_custom_props:
category: Methods - Core
since: 0.9.4
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -19,28 +21,35 @@ Initiates a rendering of the score.
-
-
-
+
+
+
-
+
+
### Parameters
+
+ The score that contains the tracks to be rendered.
+
+
+ The indexes of the tracks that should be rendered. If not provided, the first track of the song will be rendered.
+
The score that contains the tracks to be rendered.
-
+
The indexes of the tracks that should be rendered. If not provided, the first track of the song will be rendered.
-
+
The score that contains the tracks to be rendered.
-
+
The indexes of the tracks that should be rendered. If not provided, the first track of the song will be rendered.
-
+
### Returns
@@ -53,13 +62,14 @@ Nothing
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
api.RenderScore(generateScore(),[ 2, 3 ]);
```
@@ -67,8 +77,8 @@ api.RenderScore(generateScore(),[ 2, 3 ]);
```js
-var at = $('#alphaTab');
-var score = at.alphaTab('score');
+const at = $('#alphaTab');
+const score = at.alphaTab('score');
at.alphaTab('renderScore', generateScore(), [ 2, 3 ]);
```
@@ -77,7 +87,15 @@ at.alphaTab('renderScore', generateScore(), [ 2, 3 ]);
```csharp
var api = new AlphaTabApi(...);
-api.RenderScore(GenerateScore(), new [] { 2, 3 });
+api.RenderScore(GenerateScore(), new double[] { 2, 3 });
+```
+
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.renderScore(generateScore(), alphaTab.collections.DoubleList(2, 3));
```
diff --git a/docs/reference/api/renderstarted.mdx b/docs/reference/api/renderstarted.mdx
index 7dfb299..4de339d 100644
--- a/docs/reference/api/renderstarted.mdx
+++ b/docs/reference/api/renderstarted.mdx
@@ -21,8 +21,9 @@ preparations are completed and the layout and render sequence is about to start.
## Types
-
-
+
+
+
## Parameters
@@ -41,7 +42,8 @@ preparations are completed and the layout and render sequence is about to start.
{ label: 'JavaScript', value: 'js', },
{ label: 'HTML', value: 'html', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
@@ -66,7 +68,7 @@ $('#alphaTab').on('alphaTab.renderStarted', () => {
```js
-document.querySelector('#alphaTab').addEventListener('alphaTab.renderStarted', (e) => {
+document.querySelector('#alphaTab').addEventListener('alphaTab.renderStarted', () => {
updateProgressBar("Rendering");
}, false);
```
@@ -76,11 +78,21 @@ document.querySelector('#alphaTab').addEventListener('alphaTab.renderStarted', (
```cs
var api = new AlphaTabApi(...);
-api.RenderStarted.On(() =>
+api.RenderStarted.On(resized =>
{
UpdateProgressBar("Rendering");
});
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.renderStarted.on { resized ->
+ updateProgressBar("Rendering");
+}
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/rendertracks.mdx b/docs/reference/api/rendertracks.mdx
index 2806bdb..7b5e667 100644
--- a/docs/reference/api/rendertracks.mdx
+++ b/docs/reference/api/rendertracks.mdx
@@ -5,6 +5,8 @@ sidebar_custom_props:
category: Methods - Core
since: 0.9.4
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -21,6 +23,7 @@ Initiates a rendering of the tracks. All tracks must be from the same score, oth
+
### Parameters
@@ -32,6 +35,9 @@ Initiates a rendering of the tracks. All tracks must be from the same score, oth
The tracks that should be rendered.
+
+ The tracks that should be rendered.
+
### Returns
@@ -44,13 +50,14 @@ Nothing
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
api.changeTrackMute([api.score.tracks[0], api.score.tracks[1]], true);
```
@@ -58,8 +65,8 @@ api.changeTrackMute([api.score.tracks[0], api.score.tracks[1]], true);
```js
-var at = $('#alphaTab');
-var score = at.alphaTab('score');
+const at = $('#alphaTab');
+const score = at.alphaTab('score');
at.alphaTab('renderTracks', [
api.score.tracks[2],
api.score.tracks[3]
@@ -75,6 +82,17 @@ api.RenderTracks(new []{
api.Score.Tracks[2],
api.Score.Tracks[3]
});
+```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.renderTracks(alphaTab.collections.List(
+ api.score.tracks[2],
+ api.score.tracks[3]
+}
+
```
\ No newline at end of file
diff --git a/docs/reference/api/resetsoundfonts.mdx b/docs/reference/api/resetsoundfonts.mdx
index b277eea..bae68f3 100644
--- a/docs/reference/api/resetsoundfonts.mdx
+++ b/docs/reference/api/resetsoundfonts.mdx
@@ -5,6 +5,8 @@ sidebar_custom_props:
category: Methods - Player
since: 0.9.4
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -26,6 +28,9 @@ instead of unloading the previous SoundFonts.
Unloads all presets from the previous loaded SoundFonts.
+
+ Unloads all presets from the previous loaded SoundFonts.
+
### Parameters
@@ -38,13 +43,14 @@ None
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
api.loadSoundFont('/assets/guitars.sf2', true);
api.loadSoundFont('/assets/pianos.sf2', true);
// ..
@@ -75,5 +81,17 @@ api.ResetSoundFonts();
api.LoadSoundFont(System.IO.File.OpenRead("synths.sf2"), true);
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.loadSoundFont(readResource("guitars.sf2"), true)
+api.loadSoundFont(readResource("pianos.sf2"), true)
+...
+api.ResetSoundFonts()
+api.LoadSoundFont(readResource("synths.sf2"), true)
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/resize.mdx b/docs/reference/api/resize.mdx
index efa6ffd..310128c 100644
--- a/docs/reference/api/resize.mdx
+++ b/docs/reference/api/resize.mdx
@@ -25,17 +25,21 @@ considered. This allows to implement scenarios where maybe the scale or the layo
## Types
-
+
+
## Parameters
+
+ The information about the resize event.
+
The information about the resize event.
-
+
The information about the resize event.
@@ -49,18 +53,27 @@ considered. This allows to implement scenarios where maybe the scale or the layo
The size before the resizing happened.
+
+ The size before the resizing happened.
+
The size after the resize was complete.
The size after the resize was complete.
+
+ The size after the resize was complete.
+
The settings currently used for rendering.
The settings currently used for rendering.
+
+ The settings currently used for rendering.
+
## Examples
@@ -71,7 +84,8 @@ considered. This allows to implement scenarios where maybe the scale or the layo
{ label: 'JavaScript', value: 'js', },
{ label: 'HTML', value: 'html', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
@@ -90,7 +104,7 @@ api.resize.on((args) => {
```js
$('#alphaTab').on('alphaTab.resize', (e, args) => {
- args.settings.scale = args.newWidth > 1300
+ args.settings.display.scale = args.newWidth > 1300
? 1.5
: (args.newWidth > 800) ? 1.3 : 1;
});
@@ -101,7 +115,7 @@ $('#alphaTab').on('alphaTab.resize', (e, args) => {
```js
document.querySelector('#alphaTab').addEventListener('alphaTab.resize', function(e) {
- e.detail.settings.scale = e.detail.newWidth > 1300
+ e.detail.settings.display.scale = e.detail.newWidth > 1300
? 1.5
: (args.newWidth > 800) ? 1.3 : 1;
}, false);
@@ -114,9 +128,21 @@ document.querySelector('#alphaTab').addEventListener('alphaTab.resize', function
var api = new AlphaTabApi(...);
api.Resize.On(args =>
{
- args.Settings.Scale = args.NewWidth > 1300
- ? 1.5f
- : (args.NewWidth > 800) ? 1.3f : 1;
+ args.Settings.Display.Scale = args.NewWidth > 1300
+ ? 1.5
+ : (args.NewWidth > 800) ? 1.3 : 1;
+});
+```
+
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.resize.on { args ->
+ args.settings.display.scale = args.newWidth > 1300
+ ? 1.5
+ : (args.newWidth > 800) ? 1.3 : 1;
});
```
diff --git a/docs/reference/api/score.mdx b/docs/reference/api/score.mdx
index bf00211..b6dbd2f 100644
--- a/docs/reference/api/score.mdx
+++ b/docs/reference/api/score.mdx
@@ -5,6 +5,8 @@ sidebar_custom_props:
category: Properties - Core
since: 0.9.4
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -22,8 +24,9 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
## Types
-
+
+
## Examples
@@ -33,13 +36,14 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
updateScoreInfo(api.score);
```
@@ -58,5 +62,13 @@ var api = new AlphaTabApi(...);
UpdateScoreInfo(api.Score);
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+updateScoreInfo(api.score)
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/scoreloaded.mdx b/docs/reference/api/scoreloaded.mdx
index 9806a0a..9db1322 100644
--- a/docs/reference/api/scoreloaded.mdx
+++ b/docs/reference/api/scoreloaded.mdx
@@ -27,6 +27,7 @@ This allows any modification of the score before further processing.
+
## Parameters
@@ -35,7 +36,10 @@ This allows any modification of the score before further processing.
The score that was loaded with applied transpositions.
-
+
+ The score that was loaded with applied transpositions.
+
+
The score that was loaded with applied transpositions.
@@ -48,7 +52,8 @@ This allows any modification of the score before further processing.
{ label: 'JavaScript', value: 'js', },
{ label: 'HTML', value: 'html', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
@@ -89,5 +94,15 @@ api.ScoreLoaded.On(score =>
});
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.scoreLoaded.on { score ->
+ updateSongInformationInUi(score)
+}
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/settings.mdx b/docs/reference/api/settings.mdx
index 2689acd..a68f4a1 100644
--- a/docs/reference/api/settings.mdx
+++ b/docs/reference/api/settings.mdx
@@ -5,6 +5,8 @@ sidebar_custom_props:
category: Properties - Core
since: 0.9.4
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -24,6 +26,7 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
+
## Examples
@@ -33,13 +36,14 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
showSettingsModal(api.settings);
```
@@ -58,5 +62,13 @@ var api = new AlphaTabApi(...);
ShowSettingsDialog(api.Settings);
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+showSettingsDialog(api.settings)
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/soundfontload.mdx b/docs/reference/api/soundfontload.mdx
index eb7af67..a7c55f6 100644
--- a/docs/reference/api/soundfontload.mdx
+++ b/docs/reference/api/soundfontload.mdx
@@ -41,7 +41,8 @@ This event is fired when the SoundFont is being loaded and reports the progress
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'HTML', value: 'html', },
- { label: 'jQuery', value: 'jq', }
+ { label: 'jQuery', value: 'jq', },
+ { label: 'Android', value: 'android', }
]
}>
diff --git a/docs/reference/api/soundfontloaded.mdx b/docs/reference/api/soundfontloaded.mdx
index 7566d9d..e1f4c6c 100644
--- a/docs/reference/api/soundfontloaded.mdx
+++ b/docs/reference/api/soundfontloaded.mdx
@@ -38,7 +38,8 @@ none
{ label: 'JavaScript', value: 'js', },
{ label: 'HTML', value: 'html', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
diff --git a/docs/reference/api/stop.mdx b/docs/reference/api/stop.mdx
index 0011d90..b7de23c 100644
--- a/docs/reference/api/stop.mdx
+++ b/docs/reference/api/stop.mdx
@@ -5,6 +5,8 @@ sidebar_custom_props:
category: Methods - Player
since: 0.9.4
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -21,6 +23,7 @@ it will move the playback position to the start of this range, not the whole son
+
### Parameters
@@ -36,13 +39,14 @@ Nothing
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
api.stop();
```
@@ -61,5 +65,13 @@ var api = new AlphaTabApi(...);
api.Stop();
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.stop()
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/tex.mdx b/docs/reference/api/tex.mdx
index 32c7b26..85b8890 100644
--- a/docs/reference/api/tex.mdx
+++ b/docs/reference/api/tex.mdx
@@ -5,6 +5,8 @@ sidebar_custom_props:
category: Methods - Core
since: 0.9.4
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -24,6 +26,7 @@ Tells alphaTab to render the given alphaTex.
+
### Parameters
@@ -50,13 +53,14 @@ Nothing
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
api.tex("\title 'Test' . 3.3.4");
```
@@ -65,7 +69,7 @@ api.tex("\title 'Test' . 3.3.4");
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
$('#alphaTab').alphaTab('tex', "\title 'Test' . 3.3.4");
```
@@ -77,5 +81,13 @@ var api = new AlphaTabApi(...);
api.Tex("\title 'Test' . 3.3.4");
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.tex("\title 'Test' . 3.3.4");
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/this.mdx b/docs/reference/api/this.mdx
index 145d463..7c1dd39 100644
--- a/docs/reference/api/this.mdx
+++ b/docs/reference/api/this.mdx
@@ -23,6 +23,6 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
## Example - jQuery
```js
-var api = $('#alphaTab').alphaTab('api');
+const api = $('#alphaTab').alphaTab('api');
api.render();
```
\ No newline at end of file
diff --git a/docs/reference/api/tickcache.mdx b/docs/reference/api/tickcache.mdx
index 51fd519..4c412de 100644
--- a/docs/reference/api/tickcache.mdx
+++ b/docs/reference/api/tickcache.mdx
@@ -5,6 +5,8 @@ sidebar_custom_props:
category: Properties - Player
since: 1.2.3
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -27,18 +29,22 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
## Types
-
+
+
## MidiTickLookup.findBeat Signatures
-
+
+ Find the beat played at a given midi tick.
+
+
Find the beat played at a given midi tick.
-
+
Find the beat played at a given midi tick.
@@ -46,13 +52,16 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
## MidiTickLookup.findBeat Parameters
-
- The tracks which should be looked at to find the beat. This allows a filtered search if needed.
+
+ The tracks indices in which to search the played beat for.
+
+
+ The tracks indices in which to search the played beat for.
-
- The tracks which should be looked at to find the beat. This allows a filtered search if needed.
+
+ The tracks indices in which to search the played beat for.
-
+
The midi tick for which the beat should be looked up.
@@ -64,6 +73,9 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
Used for optimized lookup during playback. By passing in a previous result lookup of the next one can be optimized using heuristics. (optional).
+
+ Used for optimized lookup during playback. By passing in a previous result lookup of the next one can be optimized using heuristics. (optional).
+
## Examples
@@ -73,15 +85,16 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
-var lookupResult = api.tickCache.findBeat(api.tracks, 100);
-var currentBeat = lookupResult?.currentBeat;
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const lookupResult = api.tickCache.findBeat(new Set([0, 1]), 100);
+const currentBeat = lookupResult?.currentBeat;
```
@@ -92,9 +105,18 @@ Not available
```csharp
var api = new AlphaTabApi(...);
-var lookupResult = api.TickCache.FindBeat(api.Tracks, 100);
+var lookupResult = api.TickCache.FindBeat(new AlphaTab.Core.EcmaScript.Set(0, 1), 100);
var currentBeat = lookupResult?.CurrentBeat;
```
+
+
+
+```csharp
+val api = AlphaTabApi(...)
+val lookupResult = api.tickCache.findBeat(alphaTab.core.ecmaScript.Set(0, 1), 100);
+val currentBeat = lookupResult?.CurrentBeat;
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/tickposition.mdx b/docs/reference/api/tickposition.mdx
index d47547c..c0dc206 100644
--- a/docs/reference/api/tickposition.mdx
+++ b/docs/reference/api/tickposition.mdx
@@ -5,6 +5,8 @@ sidebar_custom_props:
category: Properties - Player
since: 0.9.4
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -32,13 +34,14 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
api.tickPosition = 4000;
```
@@ -57,5 +60,13 @@ var api = new AlphaTabApi(...);
api.TickPosition = 4000;
```
+
+
+
+```csharp
+val api = AlphaTabApi(...)
+api.tickPosition = 4000
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/timeposition.mdx b/docs/reference/api/timeposition.mdx
index 8168269..a3a8d4b 100644
--- a/docs/reference/api/timeposition.mdx
+++ b/docs/reference/api/timeposition.mdx
@@ -5,6 +5,8 @@ sidebar_custom_props:
category: Properties - Player
since: 0.9.4
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -32,13 +34,14 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
api.timePosition = 4000;
```
@@ -57,5 +60,13 @@ var api = new AlphaTabApi(...);
api.TimePosition = 4000;
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+api.timePosition = 4000
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/tracks.mdx b/docs/reference/api/tracks.mdx
index d860588..b08d516 100644
--- a/docs/reference/api/tracks.mdx
+++ b/docs/reference/api/tracks.mdx
@@ -5,6 +5,8 @@ sidebar_custom_props:
category: Properties - Core
since: 0.9.4
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -24,6 +26,7 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
+
## Examples
@@ -33,13 +36,14 @@ import { PropertyDescription } from '@site/src/components/PropertyDescription';
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
fillTrackSelector(api.tracks);
```
@@ -58,5 +62,13 @@ var api = new AlphaTabApi(...);
FillTrackSelector(api.Tracks);
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+fillTrackSelector(api.tracks)
+```
+
\ No newline at end of file
diff --git a/docs/reference/api/updatesettings.mdx b/docs/reference/api/updatesettings.mdx
index 03e0b3e..06c79d6 100644
--- a/docs/reference/api/updatesettings.mdx
+++ b/docs/reference/api/updatesettings.mdx
@@ -5,6 +5,8 @@ sidebar_custom_props:
category: Methods - Core
since: 0.9.4
---
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
@@ -22,6 +24,7 @@ to trigger an update of the settings in all components. Then a re-rendering can
+
### Parameters
@@ -37,14 +40,15 @@ Nothing
values={[
{ label: 'JavaScript', value: 'js', },
{ label: 'jQuery', value: 'jq', },
- { label: 'C#', value: 'cs', }
+ { label: 'C#', value: 'cs', },
+ { label: 'Android', value: 'android', }
]
}>
```js
-var api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
-api.settings.scale = 2.0;
+const api = new alphaTab.AlphaTabApi(document.querySelector('#alphaTab'));
+api.settings.display.scale = 2.0;
api.updateSettings();
api.render();
```
@@ -53,8 +57,8 @@ api.render();
```js
-var at = $('#alphaTab');
-at.alphaTab('settings').scale = 2.0;
+const at = $('#alphaTab');
+at.alphaTab('settings').display.scale = 2.0;
at.alphaTab('updateSettings');
at.alphaTab('render');
```
@@ -65,10 +69,21 @@ at.alphaTab('render');
```csharp
var api = new AlphaTabApi(...);
-api.Settings.Scale = 2.0;
+api.Settings.Display.Scale = 2.0;
api.UpdateSettings();
api.Render()
```
+
+
+
+```kotlin
+val api = AlphaTabApi(...)
+
+api.settings.display.scale = 2.0
+api.updateSettings()
+api.render()
+```
+
\ No newline at end of file
diff --git a/docs/reference/scorerenderer/destroy.mdx b/docs/reference/scorerenderer/destroy.mdx
index 838b3e7..2c23483 100644
--- a/docs/reference/scorerenderer/destroy.mdx
+++ b/docs/reference/scorerenderer/destroy.mdx
@@ -25,6 +25,7 @@ to ensure all resources are leaked.
+
### Parameters
diff --git a/docs/reference/scorerenderer/error.mdx b/docs/reference/scorerenderer/error.mdx
index 1307c6a..d7a52ae 100644
--- a/docs/reference/scorerenderer/error.mdx
+++ b/docs/reference/scorerenderer/error.mdx
@@ -27,6 +27,7 @@ Instead a signal to this error handlers will be sent.
+
## Parameters
diff --git a/docs/reference/scorerenderer/partiallayoutfinished.mdx b/docs/reference/scorerenderer/partiallayoutfinished.mdx
index 6d101a0..7fb1256 100644
--- a/docs/reference/scorerenderer/partiallayoutfinished.mdx
+++ b/docs/reference/scorerenderer/partiallayoutfinished.mdx
@@ -30,6 +30,7 @@ This even is fired whenever one chunk of the music sheet was fully layed out.
+
## Parameters
diff --git a/docs/reference/scorerenderer/partialrenderfinished.mdx b/docs/reference/scorerenderer/partialrenderfinished.mdx
index 5d67c81..73f0827 100644
--- a/docs/reference/scorerenderer/partialrenderfinished.mdx
+++ b/docs/reference/scorerenderer/partialrenderfinished.mdx
@@ -31,10 +31,13 @@ a chunk was advertised through the [`partialLayoutFinished`](/docs/reference/sco
## Parameters
-
+
The object containing the information about the chunk.
-
+
+ The object containing the information about the chunk.
+
+
The object containing the information about the chunk.
diff --git a/docs/reference/scorerenderer/postrenderfinished.mdx b/docs/reference/scorerenderer/postrenderfinished.mdx
index 88a7eb1..47778e0 100644
--- a/docs/reference/scorerenderer/postrenderfinished.mdx
+++ b/docs/reference/scorerenderer/postrenderfinished.mdx
@@ -26,6 +26,7 @@ handlers are called, the whole rendering and display pipeline is completed.
+
## Parameters
diff --git a/docs/reference/scorerenderer/prerender.mdx b/docs/reference/scorerenderer/prerender.mdx
index 6885b1c..22d2328 100644
--- a/docs/reference/scorerenderer/prerender.mdx
+++ b/docs/reference/scorerenderer/prerender.mdx
@@ -26,6 +26,7 @@ preparations are completed and the layout and render sequence is about to start.
+
## Parameters
diff --git a/docs/reference/scorerenderer/render.mdx b/docs/reference/scorerenderer/render.mdx
index 87517a7..7a084de 100644
--- a/docs/reference/scorerenderer/render.mdx
+++ b/docs/reference/scorerenderer/render.mdx
@@ -21,6 +21,7 @@ Initiates a re-rendering of the current setup.
+
### Parameters
diff --git a/docs/reference/scorerenderer/renderfinished.mdx b/docs/reference/scorerenderer/renderfinished.mdx
index a915ee2..fdd3f49 100644
--- a/docs/reference/scorerenderer/renderfinished.mdx
+++ b/docs/reference/scorerenderer/renderfinished.mdx
@@ -27,6 +27,7 @@ the display component to visually display the rendered components when this even
+
## AlphaTab.Rendering.RenderFinishedEventArgs Properties
diff --git a/docs/reference/scorerenderer/renderresult.mdx b/docs/reference/scorerenderer/renderresult.mdx
index afb9a8b..97fcc76 100644
--- a/docs/reference/scorerenderer/renderresult.mdx
+++ b/docs/reference/scorerenderer/renderresult.mdx
@@ -12,6 +12,7 @@ import { SinceBadge } from '@site/src/components/SinceBadge';
import {TypeTable, TypeRow} from '@site/src/components/TypeTable';
+import {ParameterTable, ParameterRow} from '@site/src/components/ParameterTable';
## Description
This method initiates the rendering of a layed out chunk advertised through [`partialLayoutFinished`](/docs/reference/scorerenderer/partiallayoutfinished).
@@ -21,6 +22,7 @@ This method initiates the rendering of a layed out chunk advertised through [`pa
+
### Parameters
diff --git a/docs/reference/scorerenderer/renderscore.mdx b/docs/reference/scorerenderer/renderscore.mdx
index d988930..ca143bb 100644
--- a/docs/reference/scorerenderer/renderscore.mdx
+++ b/docs/reference/scorerenderer/renderscore.mdx
@@ -12,6 +12,7 @@ import { SinceBadge } from '@site/src/components/SinceBadge';
import {TypeTable, TypeRow} from '@site/src/components/TypeTable';
+import {ParameterTable, ParameterRow} from '@site/src/components/ParameterTable';
## Description
This method initiates the rendering of the specified tracks of the given score.
@@ -21,6 +22,7 @@ This method initiates the rendering of the specified tracks of the given score.
+
### Parameters
diff --git a/docs/reference/scorerenderer/resizerender.mdx b/docs/reference/scorerenderer/resizerender.mdx
index 63b9554..1fb3414 100644
--- a/docs/reference/scorerenderer/resizerender.mdx
+++ b/docs/reference/scorerenderer/resizerender.mdx
@@ -27,6 +27,7 @@ into the newly available space.
+
### Parameters
diff --git a/docs/reference/scorerenderer/updatesettings.mdx b/docs/reference/scorerenderer/updatesettings.mdx
index 700b99f..a1dfd51 100644
--- a/docs/reference/scorerenderer/updatesettings.mdx
+++ b/docs/reference/scorerenderer/updatesettings.mdx
@@ -12,6 +12,7 @@ import { SinceBadge } from '@site/src/components/SinceBadge';
import {TypeTable, TypeRow} from '@site/src/components/TypeTable';
+import {ParameterTable, ParameterRow} from '@site/src/components/ParameterTable';
## Description
This method updates the settings to the given object. On some platforms like JavaScript
@@ -26,12 +27,13 @@ This method will not trigger automatically any re-rendering.
+
### Parameters
-
+
The new settings object to use within alphaTab.
diff --git a/docs/reference/scorerenderer/width.mdx b/docs/reference/scorerenderer/width.mdx
index 76389e7..2d08ac4 100644
--- a/docs/reference/scorerenderer/width.mdx
+++ b/docs/reference/scorerenderer/width.mdx
@@ -24,14 +24,4 @@ this width must be specified manually.
For layouts that grow from left to right the width and height are usually calculated automatically based on
the contents.
-export function Dump() {
- return (
-
-{JSON.stringify(metadata, null, 4)}
-
- )
-}
-
-
-
diff --git a/docs/reference/settings.mdx b/docs/reference/settings.mdx
index 1cd3202..9199a22 100644
--- a/docs/reference/settings.mdx
+++ b/docs/reference/settings.mdx
@@ -6,26 +6,27 @@ import { ReferenceTable } from '@site/src/components/ReferenceTable';
This section contains a list of all properties that allow configuration of the alphaTab behavior.
-There are multiple ways how settings in alphaTab can be specified.For.net simply the normal classes are used and
-changes are signaled via `UpdateSettings` call.For JavaScript the interaction with the settings is a bit more sensitive
+There are multiple ways how settings in alphaTab can be specified. For .net, the normal classes are used and
+changes are signaled via an `UpdateSettings` call. For JavaScript, the interaction with the settings is a bit more sensitive
due to the lack of type safety and the support of JSON based settings.
Not all settings contain reasonable examples as they often just activate something within alphaTab
-or change the display of some notation.If you have questions on certain settings feel free to open a
-[Discussion on GitHub](https://github.com/CoderLine/alphaTab/discussions)
+or change the display of some notation. If you have questions on certain settings, feel free to open a
+[Discussion on GitHub](https://github.com/CoderLine/alphaTab/discussions).
The first and most important rule is: when interacting with the settings object directly, the correct structure and data types
-must be followed not to break alphaTab or make the changes useless. In order to simplify things when configuring alphaTab via JavaScript
+must be followed to not break alphaTab or make the changes useless. In order to simplify things when configuring alphaTab via JavaScript
there are 2 additional features:
1. AlphaTab can be configured via a simple plain JSON object.
This JSON format supports some aliases and also some value conversions like for enums, fonts and colors.
- This JSON schema can only be used at selected places.When attempting to set JSON values on the derives`alphaTab.Settings` object,
- this can lead to unexpected side effects.The JSON schema can be used when initializing alphaTab or by calling `settings.fillFromJson( ... )`.
+The JSON schema can only be used at selected places.
+Attempting to set JSON values on the derived `alphaTab.Settings` object can lead to unexpected side effects.
+The JSON schema can be used when initializing alphaTab or when calling `settings.fillFromJson( ... )`.
-2. AlphaTab can be configured via HTML data attributes
-All settings can also be added as data attributes on the element for which alphaTab is initialized.This is especially useful
-when multiple different instances of alphaTab are running on the same site but the main code to setup alphaTab should be shared.
+2. AlphaTab can be configured via HTML data attributes.
+All settings can also be added as data attributes on the element for which alphaTab is initialized. This is especially useful
+when multiple different instances of alphaTab are running on the same site but the main code to set up alphaTab is shared.
Individual settings can be specified on HTML elements.
The following table contains all the properties as they can be set on the general settings object.
diff --git a/docs/reference/settings/core/engine.mdx b/docs/reference/settings/core/engine.mdx
index c76fe0e..8d53e37 100644
--- a/docs/reference/settings/core/engine.mdx
+++ b/docs/reference/settings/core/engine.mdx
@@ -30,6 +30,11 @@ AlphaTab can use various render engines to draw the music notation. The availabl
`gdi` - Only available desktop .net. Uses [GDI+](https://docs.microsoft.com/en-us/dotnet/framework/winforms/advanced/graphics-and-drawing-in-windows-forms) for rendering
`svg` - Available on all .net platforms. Outputs SVG strings.
+
+ `skia` - Uses [Skia](https://skia.org/) for rendering
+ `android` - Uses [android.graphics.Canvas](https://developer.android.com/reference/android/graphics/Canvas) for rendering
+ `svg` - Outputs SVG strings.
+
## Default Value
@@ -39,4 +44,5 @@ AlphaTab can use various render engines to draw the music notation. The availabl
+
\ No newline at end of file
diff --git a/docs/reference/settings/core/fontdirectory.mdx b/docs/reference/settings/core/fontdirectory.mdx
index 5fa024a..1b25c12 100644
--- a/docs/reference/settings/core/fontdirectory.mdx
+++ b/docs/reference/settings/core/fontdirectory.mdx
@@ -31,4 +31,4 @@ Alternatively also a global variable `ALPHATAB_FONT` can be set on the page befo
## Default Value
-`${AlphaTabScriptFolder}/Font/`
\ No newline at end of file
+`${AlphaTabScriptFolder}/font/`
\ No newline at end of file
diff --git a/docs/reference/settings/core/loglevel.mdx b/docs/reference/settings/core/loglevel.mdx
index 277bbbf..6a46c81 100644
--- a/docs/reference/settings/core/loglevel.mdx
+++ b/docs/reference/settings/core/loglevel.mdx
@@ -49,6 +49,13 @@ AlphaTab internally does quite a bit of logging for debugging and informational
`Warning`
`Error`
+
+ `None`
+ `Debug`
+ `Info`
+ `Warning`
+ `Error`
+
## Default Value
diff --git a/docs/reference/settings/display/barsperrow.mdx b/docs/reference/settings/display/barsperrow.mdx
index 19bd803..0d72849 100644
--- a/docs/reference/settings/display/barsperrow.mdx
+++ b/docs/reference/settings/display/barsperrow.mdx
@@ -16,7 +16,7 @@ import {TypeTable, TypeRow} from '@site/src/components/TypeTable';
## Description
This setting sets the number of bars that should be put into one row during layouting. This setting is only respected
-when using the layoutMode `page` where bars are aligned in rows. [Demo](/docs/showcase/layouts#page-layout-5-bars)
+when using the layoutMode `page` where bars are aligned in rows. [Demo](/docs/showcase/layouts#page-layout-5-bars-per-row)
diff --git a/docs/reference/settings/display/justifylastsystem.mdx b/docs/reference/settings/display/justifylastsystem.mdx
new file mode 100644
index 0000000..105aa92
--- /dev/null
+++ b/docs/reference/settings/display/justifylastsystem.mdx
@@ -0,0 +1,36 @@
+---
+title: display.justifyLastSystem
+description: Whether to justify also the last system in page layouts.
+sidebar_custom_props:
+ jsOnParent: true
+ category: Display
+ since: 1.3.0-alpha.720
+---
+import { SinceBadge } from '@site/src/components/SinceBadge';
+
+
+
+import { PropertyDescription } from '@site/src/components/PropertyDescription';
+import {TypeTable, TypeRow} from '@site/src/components/TypeTable';
+
+## Description
+
+Setting this option to `true` tells alphaTab to also justify the last system (row) like it
+already does for the systems which are full.
+
+| Justification Disabled | Justification Enabled |
+|--------------------------------------------------------------|-------------------------------------------------------|
+| ![Disabled](/img/reference/property/justify-last-system-false.png) | ![Enabled](/img/reference/property/justify-last-system-true.png) |
+
+
+
+## Types
+
+
+
+
+
+
+## Default Value
+
+`false`
\ No newline at end of file
diff --git a/docs/reference/settings/display/layoutmode.mdx b/docs/reference/settings/display/layoutmode.mdx
index f896845..c39f72d 100644
--- a/docs/reference/settings/display/layoutmode.mdx
+++ b/docs/reference/settings/display/layoutmode.mdx
@@ -23,7 +23,7 @@ AlphaTab has various layout engines that arrange the rendered bars differently.
- `Page` - The bars are aligned in a [page-style fashion](/docs/showcase/layouts#page-layout")
+ `Page` - The bars are aligned in a [page-style fashion](/docs/showcase/layouts#page-layout)
`Horizontal` - The bars are aligned in a [left-to-right](/docs/showcase/layouts#horizontal-layout)
@@ -38,6 +38,10 @@ AlphaTab has various layout engines that arrange the rendered bars differently.
`Page`
`Horizontal`
+
+ `Page`
+ `Horizontal`
+
## Default Value
diff --git a/docs/reference/settings/display/resources.mdx b/docs/reference/settings/display/resources.mdx
index 9182b24..4592896 100644
--- a/docs/reference/settings/display/resources.mdx
+++ b/docs/reference/settings/display/resources.mdx
@@ -118,6 +118,7 @@ export function ResourceRow({resource, type}) {
+
|
@@ -167,6 +168,7 @@ Due to space reasons in the following table the common prefix of the settings ar
| HTML | `data-display-resources-` | `` |
| HTML | `data-resources-` | ` ` |
| .net | `Display.Resources.` | `settings.Display.Resources.WordsFonts = ...` |
+| Android | `display.resources.` | `settings.display.resources.wordsFonts = ...` |
## Resources
diff --git a/docs/reference/settings/display/startbar.mdx b/docs/reference/settings/display/startbar.mdx
index 7b1812f..0beae55 100644
--- a/docs/reference/settings/display/startbar.mdx
+++ b/docs/reference/settings/display/startbar.mdx
@@ -19,7 +19,7 @@ import {TypeTable, TypeRow} from '@site/src/components/TypeTable';
This setting sets the index of the first bar that should be rendered from the overall song. This setting can be used to
achieve a paging system or to only show partial bars of the same file. By this a tutorial alike display can be achieved
that explains various parts of the song. Please note that this is the bar number as shown in the music sheet (1-based) not the array index (0-based).
-[Demo](/docs/showcase/layouts#page-layout-range)
+[Demo](/docs/showcase/layouts#page-layout-bar-5-to-8)
diff --git a/docs/reference/settings/display/staveprofile.mdx b/docs/reference/settings/display/staveprofile.mdx
index e84e559..e3c9676 100644
--- a/docs/reference/settings/display/staveprofile.mdx
+++ b/docs/reference/settings/display/staveprofile.mdx
@@ -50,6 +50,13 @@ AlphaTab has various stave profiles that define which staves will be shown in fo
`Tab`
`TabMixed`
+
+ `Default`
+ `ScoreTab`
+ `Score`
+ `Tab`
+ `TabMixed`
+
## Default Value
diff --git a/docs/reference/settings/display/systemslayoutmode.mdx b/docs/reference/settings/display/systemslayoutmode.mdx
new file mode 100644
index 0000000..357391c
--- /dev/null
+++ b/docs/reference/settings/display/systemslayoutmode.mdx
@@ -0,0 +1,107 @@
+---
+title: display.systemsLayoutMode
+description: The mode used to arrange staves and systems.
+sidebar_custom_props:
+ jsOnParent: true
+ category: Display
+ since: 1.3.0-alpha.671
+---
+import { SinceBadge } from '@site/src/components/SinceBadge';
+
+
+
+import { PropertyDescription } from '@site/src/components/PropertyDescription';
+import {TypeTable, TypeRow} from '@site/src/components/TypeTable';
+
+## Description
+
+By default alphaTab uses an own (automatic) mode to arrange and scale the bars when
+putting them into staves. This property allows changing this mode to change the music sheet arrangement.
+
+
+
+## Types
+
+
+
+ `Automatic`
+ `UseModelLayout`
+
+
+ `automatic`
+ `usemodellayout`
+
+
+ `0` - Automatic
+ `1` - UseModelLayout
+
+
+ `Automatic`
+ `UseModelLayout`
+
+
+ `Automatic`
+ `UseModelLayout`
+
+
+
+## Default Value
+
+`automatic`
+
+## Supported File Formats:
+
+* Guitar Pro 6-8 1.3.0-alpha.671
+
+If you want/need support for more file formats to respect the sizing information feel free to [open a discussion](https://github.com/CoderLine/alphaTab/discussions/new?category=ideas) on GitHub.
+
+## Automatic Mode
+
+In the automatic mode alphaTab arranges the bars and staves using its internal mechanisms.
+
+For the `page` layout this means it will scale the bars according to the `stretchForce` and available width.
+Wrapping into new systems (rows) will happen when the row is considered "full".
+
+For the `horizontal` layout the `stretchForce` defines the sizing and no wrapping happens at all.
+
+## Model Layout mode
+
+File formats like Guitar Pro embed information about the layout in the file and alphaTab can read and use this information.
+When this mode is enabled, alphaTab will also actively use this information and try to respect it.
+
+alphaTab holds following information in the data model and developers can change those values (e.g. by tapping into the `scoreLoaded`) event.
+
+**Used when single tracks are rendered:**
+
+* `score.tracks[index].systemsLayout` - An array of numbers describing how many bars should be placed within each system (row).
+* `score.tracks[index].defaultSystemsLayout` - The number of bars to place in a system (row) when no value is defined in the `systemsLayout`.
+* `score.tracks[index].staves[index].bars[index].displayScale` - The relative size of this bar in the system it is placed. Note that this is not directly a percentage value. e.g. if there are 3 bars and all define scale 1, they are sized evenly.
+* `score.tracks[index].staves[index].bars[index].displayWidth` - The absolute size of this bar when displayed.
+
+**Used when multiple tracks are rendered:**
+
+* `score.systemsLayout` - Like the `systemsLayout` on track level.
+* `score.defaultSystemsLayout` - Like the `defaultSystemsLayout` on track level.
+* `score.masterBars[index].displayScale` - Like the `displayScale` on bar level.
+* `score.masterBars[index].displayWidth` - Like the `displayWidth` on bar level.
+
+### Page Layout
+
+The page layout uses the `systemsLayout` and `defaultSystemsLayout` to decide how many bars go into a single system (row).
+Additionally when sizing the bars within the system the `displayScale` is used. As indicated above, the scale is rather a ratio than a percentage value but percentages work also:
+
+
+
+The page layout does not use `displayWidth`. The use of absolute widths would break the proper alignments needed for this kind of display.
+
+Also note that the sizing is including any glyphs and notation elements within the bar. e.g. if there are clefs in the bar, they are still "squeezed" into the available size.
+It is not the case that the actual notes with their lengths are sized accordingly. This fits the sizing system of Guitar Pro and when files are customized there,
+alphaTab will match this layout quite close.
+
+### Horizontal Layout
+
+The horizontal layout uses the `displayWidth` to scale the bars to size the bars exactly as specified. This kind of sizing and layout can be useful for usecases like:
+
+* Comparing files against each other (top/bottom comparison)
+* Aligning the playback of multiple files on one screen assuming the same tempo (e.g. one file per track).
+
diff --git a/docs/reference/settings/notation/elements.mdx b/docs/reference/settings/notation/elements.mdx
index 3f1d9d9..adc2223 100644
--- a/docs/reference/settings/notation/elements.mdx
+++ b/docs/reference/settings/notation/elements.mdx
@@ -5,6 +5,9 @@ sidebar_custom_props:
category: Notation
since: 0.9.8
---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
import { SinceBadge } from '@site/src/components/SinceBadge';
diff --git a/docs/reference/settings/notation/fingeringmode.mdx b/docs/reference/settings/notation/fingeringmode.mdx
index 07f691f..7a6cb72 100644
--- a/docs/reference/settings/notation/fingeringmode.mdx
+++ b/docs/reference/settings/notation/fingeringmode.mdx
@@ -42,6 +42,10 @@ directly in the score along the notes. For some use cases of training courses an
`Score`
`SingleNoteEffectBand`
+
+ `Score`
+ `SingleNoteEffectBand`
+
## Default Value
diff --git a/docs/reference/settings/notation/notationmode.mdx b/docs/reference/settings/notation/notationmode.mdx
index db96b4e..404d110 100644
--- a/docs/reference/settings/notation/notationmode.mdx
+++ b/docs/reference/settings/notation/notationmode.mdx
@@ -70,6 +70,10 @@ Following default setting values are applied:
`SongBook`
`GuitarPro`
+
+ `SongBook`
+ `GuitarPro`
+
## Default Value
diff --git a/docs/reference/settings/notation/rhythmmode.mdx b/docs/reference/settings/notation/rhythmmode.mdx
index 76af877..476410f 100644
--- a/docs/reference/settings/notation/rhythmmode.mdx
+++ b/docs/reference/settings/notation/rhythmmode.mdx
@@ -41,6 +41,11 @@ This setting enables the display of rhythm notation on tab staffs. [Demo](/docs/
`ShowWithBeams`
`ShowWithBars`
+
+ `Hidden`
+ `ShowWithBeams`
+ `ShowWithBars`
+
## Default Value
diff --git a/docs/reference/settings/player/nativebrowsersmoothscroll.mdx b/docs/reference/settings/player/nativebrowsersmoothscroll.mdx
index 16964f6..da9273d 100644
--- a/docs/reference/settings/player/nativebrowsersmoothscroll.mdx
+++ b/docs/reference/settings/player/nativebrowsersmoothscroll.mdx
@@ -15,8 +15,9 @@ import {TypeTable, TypeRow} from '@site/src/components/TypeTable';
## Description
-This setting configures whether the [native browser feature](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions/behavior) for smooth scrolling should be used over
-a custom animation. If this setting is enabled, options like the `scrollSpeed` will not have any effect anymore.
+This setting configures whether the [native browser feature](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTo)
+for smooth scrolling should be used over a custom animation.
+If this setting is enabled, options like `player.scrollSpeed` will not have an effect anymore.
diff --git a/docs/reference/settings/player/outputmode.mdx b/docs/reference/settings/player/outputmode.mdx
new file mode 100644
index 0000000..1160b0c
--- /dev/null
+++ b/docs/reference/settings/player/outputmode.mdx
@@ -0,0 +1,41 @@
+---
+title: player.outputMode
+description: The mode used for playing audio samples
+sidebar_custom_props:
+ javaScriptOnly: true
+ category: Player - JavaScript Specific
+ since: 1.3.0
+---
+import { SinceBadge } from '@site/src/components/SinceBadge';
+
+
+
+import { PropertyDescription } from '@site/src/components/PropertyDescription';
+import {TypeTable, TypeRow} from '@site/src/components/TypeTable';
+
+## Description
+
+Controls how alphaTab will play the audio samples in the browser.
+
+
+
+## Types
+
+
+
+ `WebAudioAudioWorklets` - If [audio worklets](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Using_AudioWorklet) are available, they are used. If not fallback to the ScriptProcessorNode
+ `WebAudioScriptProcessor` - Using the [ScriptProcessorNode](https://developer.mozilla.org/en-US/docs/Web/API/ScriptProcessorNode) for playback
+
+
+ `webaudioaudioworklets`
+ `webaudioscriptprocessor`
+
+
+ `0` - WebAudioAudioWorklets
+ `1` - WebAudioScriptProcessor
+
+
+
+## Default Value
+
+`WebAudioAudioWorklets`
\ No newline at end of file
diff --git a/docs/reference/settings/player/scrollmode.mdx b/docs/reference/settings/player/scrollmode.mdx
index 84666e6..133d43b 100644
--- a/docs/reference/settings/player/scrollmode.mdx
+++ b/docs/reference/settings/player/scrollmode.mdx
@@ -41,6 +41,11 @@ This setting controls how alphaTab behaves for scrolling. It supports 3 modes:
`Continuous`
`OffScreen`
+
+ `Off`
+ `Continuous`
+ `OffScreen`
+
diff --git a/docs/reference/settings/player/scrollspeed.mdx b/docs/reference/settings/player/scrollspeed.mdx
index 348b6d3..d9f3b4e 100644
--- a/docs/reference/settings/player/scrollspeed.mdx
+++ b/docs/reference/settings/player/scrollspeed.mdx
@@ -14,7 +14,9 @@ import {TypeTable, TypeRow} from '@site/src/components/TypeTable';
## Description
-If possible from the platform, alphaTab will try to do a smooth scrolling to the played bar. This setting defines the speed of scrolling in milliseconds.
+If possible from the platform, alphaTab will try to do a smooth scrolling to the played bar.
+This setting defines the speed of scrolling in milliseconds.
+Note that `player.nativeBrowserSmoothScroll` must be set to `false` for this to have an effect.
diff --git a/docs/reference/settings/player/slide.mdx b/docs/reference/settings/player/slide.mdx
index 5dfa78f..508709f 100644
--- a/docs/reference/settings/player/slide.mdx
+++ b/docs/reference/settings/player/slide.mdx
@@ -52,6 +52,7 @@ export function SlideRow({setting}) {
+
|
diff --git a/docs/reference/settings/player/vibrato.mdx b/docs/reference/settings/player/vibrato.mdx
index 784f150..374527d 100644
--- a/docs/reference/settings/player/vibrato.mdx
+++ b/docs/reference/settings/player/vibrato.mdx
@@ -68,7 +68,8 @@ export function VibratoRow({setting}) {
-
+
+
|
diff --git a/docs/showcase/general.mdx b/docs/showcase/general.mdx
index 94b4eed..18de0d7 100644
--- a/docs/showcase/general.mdx
+++ b/docs/showcase/general.mdx
@@ -4,50 +4,10 @@ full_width: true
---
import { AlphaTab } from '@site/src/components/AlphaTab';
-import * as alphaTab from '@coderline/alphatab'
+import {ScoreDetails } from '@site/src/components/ShowCaseGeneralScoreDetails'
## Score Details
-export class ScoreDetails extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- score: null,
- error: null
- };
- }
- componentDidMount() {
- alphaTab.importer.ScoreLoader.loadScoreAsync(this.props.file, score =>
- {
- this.setState({score});
- },
- error => {
- this.setState({error});
- });
- }
- render() {
- if (this.state.error) {
- return this.state.error;
- } else if(this.state.score) {
- return (
- <>
- Title: {this.state.score.title}
- Subtitle: {this.state.score.subtitle}
- Album: {this.state.score.album}
- Tempo: {this.state.score.tempo}
- Bars: {this.state.score.masterBars.length}
- Tracks: {this.state.score.tracks.length}
-
- {this.state.score.tracks.map(t => - {t.name}
)}
-
- >);
- }
- else {
- return "Loading...";
- }
- }
-}
-
The full score details are available as object dom. This way you can read and process any like you need.
diff --git a/docs/tutorial-android/conclusion.mdx b/docs/tutorial-android/conclusion.mdx
new file mode 100644
index 0000000..9579288
--- /dev/null
+++ b/docs/tutorial-android/conclusion.mdx
@@ -0,0 +1,23 @@
+---
+title: Conclusion
+---
+
+This was it about the tutorial on how to learn using alphaTab for your projects.
+Next step for you would be to head over to the [reference docs](/docs/reference/settings)
+and learn about the full feature set of alphaTab and what additional features you might want to integrate into your app.
+
+
+
+
+It is up to your experience and creativity to develop more features around this base we build together.
+Maybe you want to build a speed-trainer or add a fretboard/piano display. Whatever you try to build
+usually narrows down to the same few steps:
+
+1. Check the reference docs on what events, settings and API calls exist to fulfill your task.
+2. Add some controls and style them
+3. Hook up the UI with alphaTab using the API object and the events/methods it provides.
+
+Feel free to give us feedback over at our GitHub repositories. If you need assistance, found a bug or need a new feature
+simply open a Issue with the corresponding template on our [main repository](https://github.com/CoderLine/alphaTab/issues).
+If you have feedback, need clarification or extension of the documentation pages, you might better open a new issue on the
+[repository for this documentation](https://github.com/CoderLine/alphaTabWebsite).
\ No newline at end of file
diff --git a/docs/tutorial-android/controls.mdx b/docs/tutorial-android/controls.mdx
new file mode 100644
index 0000000..b796e75
--- /dev/null
+++ b/docs/tutorial-android/controls.mdx
@@ -0,0 +1,1094 @@
+---
+title: More Controls
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+In the following steps of the tutorial we will add some basic controls for the end users which
+will allow them to:
+
+* Enable/Disable the count-in
+* Enable/Disable the metronome
+* Enable/Disable the looping
+* Change the zoom level
+* Change the layout of the music sheet
+
+The implementation will follow the same principle for all controls:
+
+1. We add the UI to the popup
+2. We add new properties to the viewmodel.
+3. We add event listeners to the UI and interact with the viewmodel.
+4. In the activity we listen to the viewmodel values and map them to the alphaTab API object
+
+## Toggle Control Style
+
+For the toggle controls we will add a small helper which will change the colors of the icon and text according to the
+check state. This code should not act as a reference how to properly style controls in android but is rather
+a simplistic approach for this tutorial.
+
+
+
+
+In this file we add some new colors for our buttons which will toggle some features in an on/off fashion.
+
+```xml
+#FF436d9d
+#7F436d9d
+#FF000000
+#7F000000
+```
+
+
+
+
+This helper takes care of handling the toggle button initializations and coloring.
+
+```kotlin
+private fun initToggle(
+ button: MaterialButton,
+ initialState: Boolean,
+ onChange: (newValue: Boolean) -> Unit
+) {
+ updateToggleColors(button, initialState)
+ button.addOnCheckedChangeListener { _, isChecked ->
+ updateToggleColors(button, isChecked)
+ onChange(isChecked)
+ }
+}
+
+private fun updateToggleColors(buttonView: MaterialButton, isChecked: Boolean) {
+ val textColor = if (isChecked) R.color.checkedTextColor else R.color.uncheckedTextColor
+ val iconColor = if (isChecked) R.color.checkedIconColor else R.color.uncheckedIconColor
+
+ val textColorList = ColorStateList.valueOf(ContextCompat.getColor(context, textColor))
+ val iconColorList = ColorStateList.valueOf(ContextCompat.getColor(context, iconColor))
+
+ buttonView.iconTint = iconColorList
+ buttonView.setTextColor(textColorList)
+}
+```
+
+
+
+## Count-In
+
+The count-in will become relevant when we activate the player in the next step, but we
+can already create the control which will set the count-in volume based on the toggle button state.
+
+
+
+
+```xml
+
+
+```
+
+
+
+```kotlin
+class MainViewModel : ViewModel() {
+ ...
+ // highlight-next-line
+ val countIn = MutableLiveData(false)
+}
+```
+
+
+
+```kotlin
+init {
+ ...
+
+ // highlight-start
+ initToggle(view.findViewById(R.id.countIn), mainViewModel.countIn.value ?: false) {
+ mainViewModel.countIn.value = it
+ }
+ // highlight-end
+
+ contentView = view
+}
+```
+
+
+
+
+```kotlin
+override fun onCreate(savedInstanceState: Bundle?) {
+ ...
+ // highlight-start
+ mViewModel.countIn.observe(this) {
+ mAlphaTabView.api.countInVolume = if (it) 1.0 else 0.0
+ }
+ // highlight-end
+}
+```
+
+
+
+
+## Metronome
+
+The metronome will become relevant when we activate the player in the next step, but we
+can already create the control which will set the metronome volume based on the toggle button state.
+
+
+
+
+```xml
+
+```
+
+
+
+```kotlin
+class MainViewModel : ViewModel() {
+ ...
+ // highlight-next-line
+ val metronome = MutableLiveData(false)
+}
+```
+
+
+
+```kotlin
+init {
+ ...
+
+ // highlight-start
+ initToggle(view.findViewById(R.id.metronome), mainViewModel.metronome.value ?: false) {
+ mainViewModel.metronome.value = it
+ }
+ // highlight-end
+
+ contentView = view
+}
+```
+
+
+
+
+```kotlin
+override fun onCreate(savedInstanceState: Bundle?) {
+ ...
+ // highlight-start
+ mViewModel.metronome.observe(this) {
+ mAlphaTabView.api.metronomeVolume = if (it) 1.0 else 0.0
+ }
+ // highlight-end
+}
+```
+
+
+
+
+## Looping
+
+Again nothing new here, rinse and repeat to extend the UI and map the UI state ultimately to the API object:
+
+
+
+
+```xml
+
+```
+
+
+
+```kotlin
+class MainViewModel : ViewModel() {
+ ...
+ // highlight-next-line
+ val looping = MutableLiveData(false)
+}
+```
+
+
+
+```kotlin
+init {
+ ...
+
+ // highlight-start
+ initToggle(view.findViewById(R.id.looping), mainViewModel.looping.value ?: false) {
+ mainViewModel.looping.value = it
+ }
+ // highlight-end
+
+ contentView = view
+}
+```
+
+
+
+
+```kotlin
+override fun onCreate(savedInstanceState: Bundle?) {
+ ...
+ // highlight-start
+ mViewModel.looping.observe(this) {
+ mAlphaTabView.api.isLooping = it
+ }
+ // highlight-end
+}
+```
+
+
+
+
+## Zoom
+
+As alphaTab can change the scale in which the music sheet is rendered, we offer the user
+a [popup menu](https://developer.android.com/develop/ui/views/components/menus#PopupMenu) to change this scale.
+
+
+
+
+
+```xml
+
+
+```
+
+
+
+
+```xml
+
+```
+
+
+
+```kotlin
+class MainViewModel : ViewModel() {
+ ...
+ // highlight-next-line
+ val zoomLevel = MutableLiveData(100)
+}
+```
+
+
+
+Here create a popup menu and inflate the pre-created menu items into it.
+As we know we labeled all menu items with `%` we can derive the zoom level from the label.
+
+```kotlin
+ init {
+ ...
+
+ // highlight-start
+ val zoom = view.findViewById(R.id.zoom)
+ @SuppressLint("SetTextI18n")
+ zoom.text = "${mainViewModel.zoomLevel.value}%"
+ zoom.setOnClickListener {
+ PopupMenu(context, zoom).apply {
+ setOnMenuItemClickListener {
+ val zoomLevel = it.title!!.trim('%').toString().toInt()
+ mainViewModel.zoomLevel.value = zoomLevel
+ this@ControlsPopupWindow.dismiss()
+ true
+ }
+ inflate(R.menu.zoom)
+ show()
+
+ }
+ }
+ // highlight-end
+
+ contentView = view
+ }
+```
+
+
+
+
+Updating the zoom level is a bit more effort than the previous tasks but still not very complicated:
+First we fill the settings with the new scale. Then we tell alphaTab to read this updated settings
+and re-render the music sheet.
+
+
+```kotlin
+ override fun onCreate(savedInstanceState: Bundle?) {
+ ...
+ // highlight-start
+ mViewModel.zoomLevel.observe(this) {
+ mAlphaTabView.settings.display.scale = it / 100.0
+ mAlphaTabView.api.updateSettings()
+ mAlphaTabView.renderTracks()
+ }
+ // highlight-end
+ }
+```
+
+
+
+
+## Layout
+
+alphaTab can either render the music sheet in a page-like fashion that grows from top to bottom
+along the available width. Or it can show the music sheet in a horizontal scrolling fashion.
+
+These options are also offered to the user via another popup menu. To actually apply the user selection
+we will again just fill the user input into the settings object and trigger an update just like for
+the zoom level:
+
+
+
+
+
+```xml
+
+
+```
+
+
+
+
+```xml
+
+```
+
+
+
+```kotlin
+// highlight-next-line
+import alphaTab.LayoutMode
+...
+class MainViewModel : ViewModel() {
+ ...
+ // highlight-next-line
+ val layout = MutableLiveData(LayoutMode.Page)
+}
+```
+
+
+
+Similar to the zoom level we use the PopupMenu and inflate the menu from the resource into it.
+To get the right alphaTab layout mode we match again against the labels.
+
+```kotlin
+ init {
+ ...
+
+ // highlight-start
+ val layout = view.findViewById(R.id.layout)
+ @SuppressLint("SetTextI18n")
+ layout.text = "${mainViewModel.layout.value!!.name}"
+ layout.setOnClickListener {
+ PopupMenu(context, layout).apply {
+ setOnMenuItemClickListener {
+ mainViewModel.layout.value = when(it.title) {
+ "Page" -> LayoutMode.Page
+ "Horizontal" -> LayoutMode.Horizontal
+ else -> throw IllegalStateException("Unknown Layout")
+ }
+ this@ControlsPopupWindow.dismiss()
+ true
+ }
+ inflate(R.menu.layout)
+ show()
+ }
+ }
+ // highlight-end
+
+ contentView = view
+ }
+```
+
+
+
+
+Changing the layout is again similar to the zoom level. Changing the setting, refreshing the settings state inside alphaTab and trigger a render.
+
+```kotlin
+ override fun onCreate(savedInstanceState: Bundle?) {
+ ...
+ // highlight-start
+ mViewModel.layout.observe(this) {
+ mAlphaTabView.settings.display.layoutMode = it
+ mAlphaTabView.api.updateSettings()
+ mAlphaTabView.renderTracks()
+ }
+ // highlight-end
+ }
+```
+
+
+
+
+## Result
+
+That was already it. As you can see now, it is typically the same task over and over to extend your app with new functionalities.
+The biggest complexity is to identify how you want to build your UI towards your user. Using your Android development expertise
+you give the user options to change the settings as desired. After that is "only" understanding how to tell alphaTab how to respect those settings.
+Often the idea for new features start in the [Settings](https://www.alphatab.net/docs/reference/settings) and [API](https://www.alphatab.net/docs/reference/api) reference to
+see what functionalities are offered.
+
+
+
+
+
+## Final Files
+
+
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+```xml
+
+
+```
+
+
+```xml
+
+
+```
+
+
+```xml
+
+
+ #FF000000
+ #FFFFFFFF
+ #FF436d9d
+ #7F436d9d
+ #FF000000
+ #7F000000
+
+```
+
+
+```xml
+
+
+
+
+
+
+
+```
+
+
+```kotlin
+package net.alphatab.tutorial.android
+
+import alphaTab.AlphaTabView
+import alphaTab.core.ecmaScript.Uint8Array
+import alphaTab.importer.ScoreLoader
+import alphaTab.model.Score
+import android.annotation.SuppressLint
+import android.net.Uri
+import android.os.Bundle
+import android.util.Log
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import android.widget.Toast
+import androidx.activity.enableEdgeToEdge
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.lifecycle.ViewModelProvider
+import java.io.ByteArrayOutputStream
+import kotlin.contracts.ExperimentalContracts
+
+@OptIn(ExperimentalContracts::class, ExperimentalUnsignedTypes::class)
+@SuppressLint("SetTextI18n")
+class MainActivity : AppCompatActivity() {
+
+ private lateinit var mAlphaTabView: AlphaTabView
+ private lateinit var mTrackName: TextView
+ private lateinit var mSongName: TextView
+
+ private lateinit var mViewModel: MainViewModel
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enableEdgeToEdge()
+ setContentView(R.layout.activity_main)
+ ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
+ val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
+ v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
+ insets
+ }
+
+ mAlphaTabView = findViewById(R.id.alphatab)
+ mTrackName = findViewById(R.id.trackName)
+ mSongName = findViewById(R.id.songName)
+ findViewById(R.id.info).setOnClickListener {
+ val popup = ControlsPopupWindow(
+ this, mViewModel,
+ ) {
+ mOpenFile.launch(arrayOf("*/*"))
+ }
+ popup.width = ViewGroup.LayoutParams.MATCH_PARENT
+ popup.height = ViewGroup.LayoutParams.MATCH_PARENT
+ popup.showAtLocation(mAlphaTabView, Gravity.CENTER, 0, 0)
+ }
+
+ mViewModel = ViewModelProvider(this)[MainViewModel::class.java]
+ mViewModel.settings.observe(this) {
+ mAlphaTabView.settings = it
+ }
+ mViewModel.tracks.observe(this) {
+ mAlphaTabView.tracks = it
+ val first = it?.firstOrNull()
+ if (first != null) {
+ mTrackName.text = first.name
+ mSongName.text = "${first.score.title} - ${first.score.artist}"
+ }
+ }
+ mViewModel.countIn.observe(this) {
+ mAlphaTabView.api.countInVolume = if (it) 1.0 else 0.0
+ }
+ mViewModel.metronome.observe(this) {
+ mAlphaTabView.api.metronomeVolume = if (it) 1.0 else 0.0
+ }
+ mViewModel.looping.observe(this) {
+ mAlphaTabView.api.isLooping = it
+ }
+ mViewModel.zoomLevel.observe(this) {
+ mAlphaTabView.settings.display.scale = it / 100.0
+ mAlphaTabView.api.updateSettings()
+ mAlphaTabView.renderTracks()
+ }
+ mViewModel.layout.observe(this) {
+ mAlphaTabView.settings.display.layoutMode = it
+ mAlphaTabView.api.updateSettings()
+ mAlphaTabView.renderTracks()
+ }
+ }
+
+ private val mOpenFile = registerForActivityResult(ActivityResultContracts.OpenDocument()) {
+ val uri = it ?: return@registerForActivityResult
+ val score: Score
+ try {
+ val fileData = readFileData(uri)
+ score = ScoreLoader.loadScoreFromBytes(fileData, mAlphaTabView.settings)
+ Log.i("AlphaTab", "File loaded: ${score.title}")
+ } catch (e: Exception) {
+ Log.e("AlphaTab", "Failed to load file: $e, ${e.stackTraceToString()}")
+ Toast.makeText(this, "Failed to load file: ${e.message}", Toast.LENGTH_LONG).show()
+ return@registerForActivityResult
+ }
+
+ try {
+ mViewModel.score.value = score
+ mViewModel.tracks.value = arrayListOf(score.tracks[0])
+ } catch (e: Exception) {
+ Log.e("AlphaTab", "Failed to render file: $e, ${e.stackTraceToString()}")
+ Toast.makeText(this, "Failed to render file: ${e.message}", Toast.LENGTH_LONG).show()
+ }
+ }
+
+ private fun readFileData(uri: Uri): Uint8Array {
+ val inputStream = contentResolver.openInputStream(uri)
+ inputStream.use {
+ ByteArrayOutputStream().use {
+ inputStream!!.copyTo(it)
+ return Uint8Array(it.toByteArray().asUByteArray())
+ }
+ }
+ }
+}
+```
+
+
+```kotlin
+package net.alphatab.tutorial.android
+
+import alphaTab.LayoutMode
+import alphaTab.Settings
+import alphaTab.model.Score
+import alphaTab.model.Track
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import kotlin.contracts.ExperimentalContracts
+
+@ExperimentalUnsignedTypes
+@ExperimentalContracts
+class MainViewModel : ViewModel() {
+ val score = MutableLiveData()
+ val tracks = MutableLiveData?>()
+ val countIn = MutableLiveData(false)
+ val metronome = MutableLiveData(false)
+ val looping = MutableLiveData(false)
+ val zoomLevel = MutableLiveData(100)
+ val layout = MutableLiveData(LayoutMode.Page)
+ val settings = MutableLiveData().apply {
+ value = Settings().apply {
+ this.player.enableCursor = true
+ this.player.enablePlayer = true
+ this.player.enableUserInteraction = true
+ this.display.barCountPerPartial = 4.0
+ this.display.resources.barNumberFont
+ }
+ }
+}
+
+```
+
+
+```kotlin
+package net.alphatab.tutorial.android
+
+import alphaTab.LayoutMode
+import alphaTab.model.Track
+import android.annotation.SuppressLint
+import android.content.Context
+import android.content.res.ColorStateList
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ArrayAdapter
+import android.widget.ListView
+import android.widget.PopupMenu
+import android.widget.PopupWindow
+import android.widget.TextView
+import androidx.core.content.ContextCompat
+import com.google.android.material.button.MaterialButton
+import kotlin.contracts.ExperimentalContracts
+
+@OptIn(ExperimentalContracts::class, ExperimentalUnsignedTypes::class)
+@SuppressLint("InflateParams")
+class ControlsPopupWindow(
+ private val context: Context,
+ private val mainViewModel: MainViewModel,
+ onOpenFile: () -> Unit
+) : PopupWindow(context) {
+ private val mOpenButton: MaterialButton
+ private val mTrackList: ListView
+
+ init {
+ val view = LayoutInflater.from(context).inflate(R.layout.popup_controls, null)
+
+ mOpenButton = view.findViewById(R.id.openFile)
+ mOpenButton.setOnClickListener {
+ onOpenFile()
+ dismiss()
+ }
+
+ mTrackList = view.findViewById(R.id.trackList)
+ mTrackList.adapter =
+ TrackListAdapter(context, mainViewModel.score.value?.tracks?.toList() ?: emptyList())
+ mTrackList.setOnItemClickListener { _, _, position, _ ->
+ mainViewModel.tracks.value =
+ mutableListOf((mTrackList.adapter as TrackListAdapter).getItem(position)!!)
+ dismiss()
+ }
+
+ view.findViewById(R.id.back).setOnClickListener {
+ dismiss()
+ }
+
+ initToggle(view.findViewById(R.id.countIn), mainViewModel.countIn.value ?: false) {
+ mainViewModel.countIn.value = it
+ }
+
+ initToggle(view.findViewById(R.id.metronome), mainViewModel.metronome.value ?: false) {
+ mainViewModel.metronome.value = it
+ }
+
+ initToggle(view.findViewById(R.id.looping), mainViewModel.looping.value ?: false) {
+ mainViewModel.looping.value = it
+ }
+
+ val zoom = view.findViewById(R.id.zoom)
+ @SuppressLint("SetTextI18n")
+ zoom.text = "${mainViewModel.zoomLevel.value}%"
+ zoom.setOnClickListener {
+ PopupMenu(context, zoom).apply {
+ setOnMenuItemClickListener {
+ val zoomLevel = it.title!!.trim('%').toString().toInt()
+ mainViewModel.zoomLevel.value = zoomLevel
+ this@ControlsPopupWindow.dismiss()
+ true
+ }
+ inflate(R.menu.zoom)
+ show()
+
+ }
+ }
+
+ val layout = view.findViewById(R.id.layout)
+ @SuppressLint("SetTextI18n")
+ layout.text = "${mainViewModel.layout.value!!.name}"
+ layout.setOnClickListener {
+ PopupMenu(context, layout).apply {
+ setOnMenuItemClickListener {
+ mainViewModel.layout.value = when(it.title) {
+ "Page" -> LayoutMode.Page
+ "Horizontal" -> LayoutMode.Horizontal
+ else -> throw IllegalStateException("Unknown Layout")
+ }
+ this@ControlsPopupWindow.dismiss()
+ true
+ }
+ inflate(R.menu.layout)
+ show()
+ }
+ }
+
+ contentView = view
+ }
+
+ private fun initToggle(
+ button: MaterialButton,
+ initialState: Boolean,
+ onChange: (newValue: Boolean) -> Unit
+ ) {
+ updateToggleColors(button, initialState)
+ button.addOnCheckedChangeListener { _, isChecked ->
+ updateToggleColors(button, isChecked)
+ onChange(isChecked)
+ }
+ }
+
+ private fun updateToggleColors(buttonView: MaterialButton, isChecked: Boolean) {
+ val textColor = if (isChecked) R.color.checkedTextColor else R.color.uncheckedTextColor
+ val iconColor = if (isChecked) R.color.checkedIconColor else R.color.uncheckedIconColor
+
+ val textColorList = ColorStateList.valueOf(ContextCompat.getColor(context, textColor))
+ val iconColorList = ColorStateList.valueOf(ContextCompat.getColor(context, iconColor))
+
+ buttonView.iconTint = iconColorList
+ buttonView.setTextColor(textColorList)
+ }
+
+
+ class TrackListAdapter(context: Context, tracks: List
+
\ No newline at end of file
diff --git a/docs/tutorial-android/introduction.mdx b/docs/tutorial-android/introduction.mdx
new file mode 100644
index 0000000..5faa1f5
--- /dev/null
+++ b/docs/tutorial-android/introduction.mdx
@@ -0,0 +1,36 @@
+---
+title: Introduction
+---
+
+# What are we building?
+
+We will build a component which connects all main aspects of alphaTab together.
+The result will look similar to what you might have already seen on the homepage but in a
+mobile app variant:
+
+
+
+
+The overall outline of the tutorial can be seen in the menu and we will step by step
+add each feature until we have a full player component which displays music sheets.
+After this tutorial you should have all knowledge needed to use alphaTab in your own
+project.
+
+This tutorial will leave out some features which are provided on the control of the homepage
+but you will learn all required steps to build the remaining parts.
+
+# Prerequisites
+
+As a pre-requisite you should be already familiar at least with the basics of Android development.
+As alphaTab is a **library** (or **SDK** if you like) for building apps/websites that
+want to show music sheets basic knowledge about software development is needed.
+
+For simplicity we will rather leave out aspects like following special patterns and rather put the functional code
+close to the UI. In a real app you will likely follow stricter separation of components and use a reactive and layered architecture.
+
+Goal of this tutorial is to show you the usage and interaction with alphaTab and not teach proper Android app development.
+
+To follow this tutorial you will need to have a Android IDE installed. We will use Android Studio
+but beside the project setup anyhow most of things will happen on code level so feel free to use any IDE you are familiar with.
+
+So, let's get started! :wink:
\ No newline at end of file
diff --git a/docs/tutorial-android/player.mdx b/docs/tutorial-android/player.mdx
new file mode 100644
index 0000000..a9020c8
--- /dev/null
+++ b/docs/tutorial-android/player.mdx
@@ -0,0 +1,785 @@
+---
+title: Player
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+
+## Basic setup
+
+Now as we have the basic component ready, it is time to add some sound.
+AlphaTab comes with an audio synthesis engine named AlphaSynth to play the songs
+via Midi and a SoundFont2 file.
+
+To get the player configured is also quite easy as it is enabled by default in the main control.
+alphaTab ships a free SoundFont2 file for usage on your website and it is already loaded by default.
+You can choose to load a different one using the [`loadSoundFont`](/docs/reference/api/loadsoundfont) method.
+
+We just need to add some UI elements for the user to start/pause the playback and show the current position.
+
+
+ ## Main player controls
+
+Now we need to add the required controls for the users to play and pause the audio.
+We will add one button for play/pause which will change the icon depending on the current playback state. The placeholder we already prepared earlier.
+
+We will use the [`playPause()`](/docs/reference/api/playpause) and [`stop()`](/docs/reference/api/stop) to send the right signals to alphaTab and we use the
+[`playerStateChanged`](/docs/reference/api/playerstatechanged) event to update the button icon. To ensure that the user does not interact with the player
+before it is ready, we will keep the play button hidden until the player is ready.
+
+
+
+
+We adjust the button slightly to be invisible on start.
+
+```xml
+...
+
+```
+
+
+
+
+In the main activity we set up the click to start/pause playback and listen to the state change to change the icon and show the button.
+
+```kotlin
+ override fun onCreate(savedInstanceState: Bundle?) {
+ ...
+ val playPause = findViewById(R.id.playPause)
+ playPause.setOnClickListener {
+ mAlphaTabView.api.playPause()
+ }
+ mAlphaTabView.api.playerStateChanged.on {
+ val image = if(it.state == PlayerState.Playing) R.drawable.baseline_pause_24 else R.drawable.baseline_play_arrow_24
+ playPause.setImageResource(image)
+ }
+ mAlphaTabView.api.playerReady.on {
+ playPause.visibility = View.VISIBLE
+ }
+ }
+```
+
+
+
+
+The result looks like this:
+
+
+
+
+## Showing the current time
+
+In this step we will add a textual indicator the current position of the playback.
+You might use the provided information to also build a progress bar indicator for the users
+which could be even interactive with seeking. This tutorial will skip this part.
+
+As in most other steps we will add some new UI elements and hook it up with information from alphaTab.
+In this case the alphaTab event `playerPositionChanged` provides the necessary data.
+
+
+
+
+We adjust the button slightly to be invisible on start.
+
+```xml
+
+ // highlight-start
+
+ // highlight-end
+```
+
+
+
+
+In the main activity we set up the click to start/pause playback and listen to the state change to change the icon and show the button.
+
+```kotlin
+override fun onCreate(savedInstanceState: Bundle?) {
+ ...
+ val timePosition = findViewById(R.id.timePosition)
+ var previousTime = -1
+ mAlphaTabView.api.playerPositionChanged.on {
+ // prevent too many UI updates
+ val currentSeconds = (it.currentTime / 1000).toInt()
+ if (currentSeconds == previousTime) {
+ return@on
+ }
+
+ previousTime = currentSeconds;
+
+ val currentTimePosition = it.currentTime.toDuration(DurationUnit.MILLISECONDS)
+ val totalTimePosition = it.endTime.toDuration(DurationUnit.MILLISECONDS)
+
+ val currentTimePositionText = currentTimePosition.toComponents { hours, minutes, seconds, _ -> "%02d:%02d".format(hours * 60 + minutes, seconds) }
+ val totalTimePositionText = totalTimePosition.toComponents { hours, minutes, seconds, _ -> "%02d:%02d".format(hours * 60 + minutes, seconds) }
+
+ timePosition.text = "$currentTimePositionText / $totalTimePositionText"
+ }
+}
+```
+
+
+
+
+The result looks like this:
+
+
+
+## Final Files
+
+
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+```xml
+
+
+```
+
+
+```xml
+
+
+```
+
+
+```xml
+
+
+ #FF000000
+ #FFFFFFFF
+ #FF436d9d
+ #7F436d9d
+ #FF000000
+ #7F000000
+
+```
+
+
+```xml
+
+
+
+
+
+
+
+```
+
+
+```kotlin
+package net.alphatab.tutorial.android
+
+import alphaTab.AlphaTabView
+import alphaTab.core.ecmaScript.Uint8Array
+import alphaTab.importer.ScoreLoader
+import alphaTab.model.Score
+import alphaTab.synth.PlayerState
+import android.annotation.SuppressLint
+import android.net.Uri
+import android.os.Bundle
+import android.util.Log
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageButton
+import android.widget.TextView
+import android.widget.Toast
+import androidx.activity.enableEdgeToEdge
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.lifecycle.ViewModelProvider
+import java.io.ByteArrayOutputStream
+import kotlin.contracts.ExperimentalContracts
+import kotlin.time.Duration
+import kotlin.time.DurationUnit
+import kotlin.time.toDuration
+
+@OptIn(ExperimentalContracts::class, ExperimentalUnsignedTypes::class)
+@SuppressLint("SetTextI18n")
+class MainActivity : AppCompatActivity() {
+
+ private lateinit var mAlphaTabView: AlphaTabView
+ private lateinit var mTrackName: TextView
+ private lateinit var mSongName: TextView
+
+ private lateinit var mViewModel: MainViewModel
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enableEdgeToEdge()
+ setContentView(R.layout.activity_main)
+ ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
+ val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
+ v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
+ insets
+ }
+
+ mAlphaTabView = findViewById(R.id.alphatab)
+ mTrackName = findViewById(R.id.trackName)
+ mSongName = findViewById(R.id.songName)
+ findViewById(R.id.info).setOnClickListener {
+ val popup = ControlsPopupWindow(
+ this, mViewModel,
+ ) {
+ mOpenFile.launch(arrayOf("*/*"))
+ }
+ popup.width = ViewGroup.LayoutParams.MATCH_PARENT
+ popup.height = ViewGroup.LayoutParams.MATCH_PARENT
+ popup.showAtLocation(mAlphaTabView, Gravity.CENTER, 0, 0)
+ }
+
+ mViewModel = ViewModelProvider(this)[MainViewModel::class.java]
+ mViewModel.settings.observe(this) {
+ mAlphaTabView.settings = it
+ }
+ mViewModel.tracks.observe(this) {
+ mAlphaTabView.tracks = it
+ val first = it?.firstOrNull()
+ if (first != null) {
+ mTrackName.text = first.name
+ mSongName.text = "${first.score.title} - ${first.score.artist}"
+ }
+ }
+ mViewModel.countIn.observe(this) {
+ mAlphaTabView.api.countInVolume = if (it) 1.0 else 0.0
+ }
+ mViewModel.metronome.observe(this) {
+ mAlphaTabView.api.metronomeVolume = if (it) 1.0 else 0.0
+ }
+ mViewModel.looping.observe(this) {
+ mAlphaTabView.api.isLooping = it
+ }
+ mViewModel.zoomLevel.observe(this) {
+ mAlphaTabView.settings.display.scale = it / 100.0
+ mAlphaTabView.api.updateSettings()
+ mAlphaTabView.renderTracks()
+ }
+ mViewModel.layout.observe(this) {
+ mAlphaTabView.settings.display.layoutMode = it
+ mAlphaTabView.api.updateSettings()
+ mAlphaTabView.renderTracks()
+ }
+
+ val playPause = findViewById(R.id.playPause)
+ playPause.setOnClickListener {
+ mAlphaTabView.api.playPause()
+ }
+
+ mAlphaTabView.api.playerStateChanged.on {
+ val image =
+ if (it.state == PlayerState.Playing) R.drawable.baseline_pause_24 else R.drawable.baseline_play_arrow_24
+ playPause.setImageResource(image)
+ }
+ mAlphaTabView.api.playerReady.on {
+ playPause.visibility = View.VISIBLE
+ }
+
+ val timePosition = findViewById(R.id.timePosition)
+ var previousTime = -1
+ mAlphaTabView.api.playerPositionChanged.on {
+ // prevent too many UI updates
+ val currentSeconds = (it.currentTime / 1000).toInt()
+ if (currentSeconds == previousTime) {
+ return@on
+ }
+
+ previousTime = currentSeconds;
+
+ val currentTimePosition = it.currentTime.toDuration(DurationUnit.MILLISECONDS)
+ val totalTimePosition = it.endTime.toDuration(DurationUnit.MILLISECONDS)
+
+ val currentTimePositionText = currentTimePosition.toComponents { hours, minutes, seconds, _ -> "%02d:%02d".format(hours * 60 + minutes, seconds) }
+ val totalTimePositionText = totalTimePosition.toComponents { hours, minutes, seconds, _ -> "%02d:%02d".format(hours * 60 + minutes, seconds) }
+
+ timePosition.text = "$currentTimePositionText / $totalTimePositionText"
+ }
+ }
+
+ private val mOpenFile = registerForActivityResult(ActivityResultContracts.OpenDocument()) {
+ val uri = it ?: return@registerForActivityResult
+ val score: Score
+ try {
+ val fileData = readFileData(uri)
+ score = ScoreLoader.loadScoreFromBytes(fileData, mAlphaTabView.settings)
+ Log.i("AlphaTab", "File loaded: ${score.title}")
+ } catch (e: Exception) {
+ Log.e("AlphaTab", "Failed to load file: $e, ${e.stackTraceToString()}")
+ Toast.makeText(this, "Failed to load file: ${e.message}", Toast.LENGTH_LONG).show()
+ return@registerForActivityResult
+ }
+
+ try {
+ mViewModel.score.value = score
+ mViewModel.tracks.value = arrayListOf(score.tracks[0])
+ } catch (e: Exception) {
+ Log.e("AlphaTab", "Failed to render file: $e, ${e.stackTraceToString()}")
+ Toast.makeText(this, "Failed to render file: ${e.message}", Toast.LENGTH_LONG).show()
+ }
+ }
+
+ private fun readFileData(uri: Uri): Uint8Array {
+ val inputStream = contentResolver.openInputStream(uri)
+ inputStream.use {
+ ByteArrayOutputStream().use {
+ inputStream!!.copyTo(it)
+ return Uint8Array(it.toByteArray().asUByteArray())
+ }
+ }
+ }
+}
+```
+
+
+```kotlin
+package net.alphatab.tutorial.android
+
+import alphaTab.LayoutMode
+import alphaTab.Settings
+import alphaTab.model.Score
+import alphaTab.model.Track
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import kotlin.contracts.ExperimentalContracts
+
+@ExperimentalUnsignedTypes
+@ExperimentalContracts
+class MainViewModel : ViewModel() {
+ val score = MutableLiveData()
+ val tracks = MutableLiveData?>()
+ val countIn = MutableLiveData(false)
+ val metronome = MutableLiveData(false)
+ val looping = MutableLiveData(false)
+ val zoomLevel = MutableLiveData(100)
+ val layout = MutableLiveData(LayoutMode.Page)
+ val settings = MutableLiveData().apply {
+ value = Settings().apply {
+ this.player.enableCursor = true
+ this.player.enablePlayer = true
+ this.player.enableUserInteraction = true
+ this.display.barCountPerPartial = 4.0
+ this.display.resources.barNumberFont
+ }
+ }
+}
+
+```
+
+
+```kotlin
+package net.alphatab.tutorial.android
+
+import alphaTab.LayoutMode
+import alphaTab.model.Track
+import android.annotation.SuppressLint
+import android.content.Context
+import android.content.res.ColorStateList
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ArrayAdapter
+import android.widget.ListView
+import android.widget.PopupMenu
+import android.widget.PopupWindow
+import android.widget.TextView
+import androidx.core.content.ContextCompat
+import com.google.android.material.button.MaterialButton
+import kotlin.contracts.ExperimentalContracts
+
+@OptIn(ExperimentalContracts::class, ExperimentalUnsignedTypes::class)
+@SuppressLint("InflateParams")
+class ControlsPopupWindow(
+ private val context: Context,
+ private val mainViewModel: MainViewModel,
+ onOpenFile: () -> Unit
+) : PopupWindow(context) {
+ private val mOpenButton: MaterialButton
+ private val mTrackList: ListView
+
+ init {
+ val view = LayoutInflater.from(context).inflate(R.layout.popup_controls, null)
+
+ mOpenButton = view.findViewById(R.id.openFile)
+ mOpenButton.setOnClickListener {
+ onOpenFile()
+ dismiss()
+ }
+
+ mTrackList = view.findViewById(R.id.trackList)
+ mTrackList.adapter =
+ TrackListAdapter(context, mainViewModel.score.value?.tracks?.toList() ?: emptyList())
+ mTrackList.setOnItemClickListener { _, _, position, _ ->
+ mainViewModel.tracks.value =
+ mutableListOf((mTrackList.adapter as TrackListAdapter).getItem(position)!!)
+ dismiss()
+ }
+
+ view.findViewById(R.id.back).setOnClickListener {
+ dismiss()
+ }
+
+ initToggle(view.findViewById(R.id.countIn), mainViewModel.countIn.value ?: false) {
+ mainViewModel.countIn.value = it
+ }
+
+ initToggle(view.findViewById(R.id.metronome), mainViewModel.metronome.value ?: false) {
+ mainViewModel.metronome.value = it
+ }
+
+ initToggle(view.findViewById(R.id.looping), mainViewModel.looping.value ?: false) {
+ mainViewModel.looping.value = it
+ }
+
+ val zoom = view.findViewById(R.id.zoom)
+ @SuppressLint("SetTextI18n")
+ zoom.text = "${mainViewModel.zoomLevel.value}%"
+ zoom.setOnClickListener {
+ PopupMenu(context, zoom).apply {
+ setOnMenuItemClickListener {
+ val zoomLevel = it.title!!.trim('%').toString().toInt()
+ mainViewModel.zoomLevel.value = zoomLevel
+ this@ControlsPopupWindow.dismiss()
+ true
+ }
+ inflate(R.menu.zoom)
+ show()
+
+ }
+ }
+
+ val layout = view.findViewById(R.id.layout)
+ @SuppressLint("SetTextI18n")
+ layout.text = "${mainViewModel.layout.value!!.name}"
+ layout.setOnClickListener {
+ PopupMenu(context, layout).apply {
+ setOnMenuItemClickListener {
+ mainViewModel.layout.value = when(it.title) {
+ "Page" -> LayoutMode.Page
+ "Horizontal" -> LayoutMode.Horizontal
+ else -> throw IllegalStateException("Unknown Layout")
+ }
+ this@ControlsPopupWindow.dismiss()
+ true
+ }
+ inflate(R.menu.layout)
+ show()
+ }
+ }
+
+ contentView = view
+ }
+
+ private fun initToggle(
+ button: MaterialButton,
+ initialState: Boolean,
+ onChange: (newValue: Boolean) -> Unit
+ ) {
+ updateToggleColors(button, initialState)
+ button.addOnCheckedChangeListener { _, isChecked ->
+ updateToggleColors(button, isChecked)
+ onChange(isChecked)
+ }
+ }
+
+ private fun updateToggleColors(buttonView: MaterialButton, isChecked: Boolean) {
+ val textColor = if (isChecked) R.color.checkedTextColor else R.color.uncheckedTextColor
+ val iconColor = if (isChecked) R.color.checkedIconColor else R.color.uncheckedIconColor
+
+ val textColorList = ColorStateList.valueOf(ContextCompat.getColor(context, textColor))
+ val iconColorList = ColorStateList.valueOf(ContextCompat.getColor(context, iconColor))
+
+ buttonView.iconTint = iconColorList
+ buttonView.setTextColor(textColorList)
+ }
+
+
+ class TrackListAdapter(context: Context, tracks: List) :
+ ArrayAdapter(context, android.R.layout.simple_list_item_1, tracks) {
+
+ private val mInflater: LayoutInflater = LayoutInflater.from(context)
+
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
+ val view =
+ convertView ?: mInflater.inflate(android.R.layout.simple_list_item_1, parent, false)
+
+ if (view !is TextView) {
+ throw IllegalStateException("Expected simple_list_item_1 to be a TextView")
+ }
+
+ val item = getItem(position)
+ view.text = item!!.name
+ return view
+ }
+ }
+}
+```
+
+
\ No newline at end of file
diff --git a/docs/tutorial-android/setup.mdx b/docs/tutorial-android/setup.mdx
new file mode 100644
index 0000000..f6ebe31
--- /dev/null
+++ b/docs/tutorial-android/setup.mdx
@@ -0,0 +1,26 @@
+---
+title: Setup Project
+---
+
+First we will set the stage for our new application.
+We launch Android Studio and create a new project for Phone And Tablet with an Empty Views Activity.
+
+:::info
+
+Important: Be sure to use a project with the term "Views" in it so that the classical XML Views are used as UI framework.
+We have it [on the roadmap](https://github.com/CoderLine/alphaTab/issues/1475) to also support Jetpack Compose as newer modern alternative but it requires some further development.
+Vote for the issue if you need this. If you are experienced in Android development you an try [wrapping the standard view based control](https://developer.android.com/develop/ui/compose/libraries).
+
+:::
+
+
+
+Then we enter some project name and choose a location.
+
+
+
+After the project is created and loaded, we add alphaTab as dependency to the project and let Android Studio synchronize the Gradle configuration.
+
+
+
+With this is everything we need to actually get started on developing.
\ No newline at end of file
diff --git a/docs/tutorial-android/track-selector.mdx b/docs/tutorial-android/track-selector.mdx
new file mode 100644
index 0000000..ba7ecb1
--- /dev/null
+++ b/docs/tutorial-android/track-selector.mdx
@@ -0,0 +1,716 @@
+---
+title: Track Selector
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+## Adding a ViewModel with custom bindings
+
+To ensure our application persists the state properly we will use a ViewModel to hold our data and synchronize it with the UI.
+In case the activity is re-created (e.g. on rotation) we can re-initialize the UI with the correct state.
+
+Read more about viewmodels here: https://developer.android.com/topic/libraries/architecture/viewmodel
+
+We add a new class to the project which holds our settings and the track list to be displayed.
+
+```kotlin
+package net.alphatab.tutorial.android
+
+import alphaTab.Settings
+import alphaTab.model.Score
+import alphaTab.model.Track
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import kotlin.contracts.ExperimentalContracts
+
+@ExperimentalUnsignedTypes
+@ExperimentalContracts
+class MainViewModel : ViewModel() {
+ val score = MutableLiveData()
+ val tracks = MutableLiveData?>()
+ val settings = MutableLiveData().apply {
+ value = Settings().apply {
+ this.player.enableCursor = true
+ this.player.enablePlayer = true
+ this.player.enableUserInteraction = true
+ this.display.barCountPerPartial = 4.0
+ this.display.resources.barNumberFont
+ }
+ }
+}
+```
+
+And in the UI we hook up the correct viewmodel bindings:
+
+```kotlin
+class MainActivity : AppCompatActivity() {
+ ....
+ // highlight-next-line
+ private lateinit var mViewModel: MainViewModel
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ ...
+ // highlight-start
+ mViewModel = ViewModelProvider(this)[MainViewModel::class.java]
+ mViewModel.settings.observe(this) {
+ mAlphaTabView.settings = it
+ }
+ mViewModel.tracks.observe(this) {
+ mAlphaTabView.tracks = it
+ val first = it?.firstOrNull()
+ if (first != null) {
+ mTrackName.text = first.name
+ mSongName.text = "${first.score.title} - ${first.score.artist}"
+ }
+ }
+ // highlight-end
+ }
+
+ private val mOpenFile = registerForActivityResult(ActivityResultContracts.OpenDocument()) {
+ ...
+ try {
+ // highlight-start
+ mViewModel.score.value = score
+ mViewModel.tracks.value = arrayListOf(score.tracks[0])
+ // highlight-end
+ } catch (e: Exception) {
+ Log.e("AlphaTab", "Failed to render file: $e, ${e.stackTraceToString()}")
+ Toast.makeText(this, "Failed to render file: ${e.message}", Toast.LENGTH_LONG).show()
+ }
+ }
+
+```
+
+With this change you will notice that you can rotate the phone and still see the right information displayed.
+
+
+
+## Preparing some styles
+
+For the sake of styling we add some styles to our code. This code should not act as a reference
+how to properly style controls in android but is rather a simplistic approach for this tutorial.
+
+
+
+
+In this file we add a custom style for our popup control button setting various alignment and coloring bits.
+
+```xml
+
+```
+
+
+
+
+## Preparing the Control Popup
+
+As written in the previous chapter we will put all controls (beside the play button) inside a popup window.
+Therefore we have to create first a new layout which we can show as popup. It will feature some buttons on the top and
+a track list on the remaining space.
+
+The code for the popup we will put into an own class.
+
+
+
+
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+
+There is quite a bit of code here but nothing specific or special to alphaTab:
+
+* We initialize the UI of the popup.
+* We add a listener to the open file button which calls over a handler to trigger the file open.
+* We add a listener to the list view and fill the selected track into the viewmodel.
+
+To keep it simple we keep the file open logic in the MainActivity and just trigger it from the popup. This way we can keep using `registerForActivityResult` but in a
+proper app you would use better ways of decoupling the logic.
+
+```kotlin
+package net.alphatab.tutorial.android
+
+import alphaTab.model.Track
+import android.annotation.SuppressLint
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ArrayAdapter
+import android.widget.ListView
+import android.widget.PopupWindow
+import android.widget.TextView
+import com.google.android.material.button.MaterialButton
+import kotlin.contracts.ExperimentalContracts
+
+@OptIn(ExperimentalContracts::class, ExperimentalUnsignedTypes::class)
+@SuppressLint("InflateParams")
+class ControlsPopupWindow(
+ context: Context,
+ private val mainViewModel: MainViewModel,
+ onOpenFile: () -> Unit
+) : PopupWindow(context) {
+ private val mOpenButton: MaterialButton
+ private val mTrackList: ListView
+
+ init {
+ val view = LayoutInflater.from(context).inflate(R.layout.popup_controls, null)
+
+ mOpenButton = view.findViewById(R.id.openFile)
+ mOpenButton.setOnClickListener {
+ onOpenFile()
+ dismiss()
+ }
+
+ mTrackList = view.findViewById(R.id.trackList)
+ mTrackList.adapter = TrackListAdapter(context, mainViewModel.score.value?.tracks?.toList() ?: emptyList())
+ mTrackList.setOnItemClickListener { _, _, position, _ ->
+ mainViewModel.tracks.value =
+ mutableListOf((mTrackList.adapter as TrackListAdapter).getItem(position)!!)
+ dismiss()
+ }
+
+ view.findViewById(R.id.back).setOnClickListener {
+ dismiss()
+ }
+
+ contentView = view
+ }
+
+ class TrackListAdapter(context: Context, tracks: List) :
+ ArrayAdapter(context, android.R.layout.simple_list_item_1, tracks) {
+
+ private val mInflater: LayoutInflater = LayoutInflater.from(context)
+
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
+ val view =
+ convertView ?: mInflater.inflate(android.R.layout.simple_list_item_1, parent, false)
+
+ if (view !is TextView) {
+ throw IllegalStateException("Expected simple_list_item_1 to be a TextView")
+ }
+
+ val item = getItem(position)
+ view.text = item!!.name
+ return view
+ }
+ }
+}
+```
+
+
+
+
+## Connecting the popup
+
+Now we need to change the MainActivity to show the popup window and react on the listeners to update the UI.
+We modify the MainActivity accordingly:
+
+```kotlin
+findViewById(R.id.info).setOnClickListener {
+ val popup = ControlsPopupWindow(
+ this, mViewModel,
+ ) {
+ mOpenFile.launch(arrayOf("*/*"))
+ }
+ popup.width = ViewGroup.LayoutParams.MATCH_PARENT
+ popup.height = ViewGroup.LayoutParams.MATCH_PARENT
+ popup.showAtLocation(mAlphaTabView, Gravity.CENTER, 0, 0)
+}
+
+```
+
+## Result
+
+
+
+
+
+## Final Files
+
+
+
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+
+```kotlin
+package net.alphatab.tutorial.android
+
+import alphaTab.AlphaTabView
+import alphaTab.core.ecmaScript.Uint8Array
+import alphaTab.importer.ScoreLoader
+import alphaTab.model.Score
+import android.annotation.SuppressLint
+import android.net.Uri
+import android.os.Bundle
+import android.util.Log
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import android.widget.Toast
+import androidx.activity.enableEdgeToEdge
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.lifecycle.ViewModelProvider
+import java.io.ByteArrayOutputStream
+import kotlin.contracts.ExperimentalContracts
+
+@OptIn(ExperimentalContracts::class, ExperimentalUnsignedTypes::class)
+@SuppressLint("SetTextI18n")
+class MainActivity : AppCompatActivity() {
+
+ private lateinit var mAlphaTabView: AlphaTabView
+ private lateinit var mTrackName: TextView
+ private lateinit var mSongName: TextView
+
+ private lateinit var mViewModel: MainViewModel
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enableEdgeToEdge()
+ setContentView(R.layout.activity_main)
+ ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
+ val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
+ v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
+ insets
+ }
+
+ mAlphaTabView = findViewById(R.id.alphatab)
+ mTrackName = findViewById(R.id.trackName)
+ mSongName = findViewById(R.id.songName)
+ findViewById(R.id.info).setOnClickListener {
+ val popup = ControlsPopupWindow(
+ this, mViewModel,
+ ) {
+ mOpenFile.launch(arrayOf("*/*"))
+ }
+ popup.width = ViewGroup.LayoutParams.MATCH_PARENT
+ popup.height = ViewGroup.LayoutParams.MATCH_PARENT
+ popup.showAtLocation(mAlphaTabView, Gravity.CENTER, 0, 0)
+ }
+
+ mViewModel = ViewModelProvider(this)[MainViewModel::class.java]
+ mViewModel.settings.observe(this) {
+ mAlphaTabView.settings = it
+ }
+ mViewModel.tracks.observe(this) {
+ mAlphaTabView.tracks = it
+ val first = it?.firstOrNull()
+ if (first != null) {
+ mTrackName.text = first.name
+ mSongName.text = "${first.score.title} - ${first.score.artist}"
+ }
+ }
+ }
+
+ private val mOpenFile = registerForActivityResult(ActivityResultContracts.OpenDocument()) {
+ val uri = it ?: return@registerForActivityResult
+ val score: Score
+ try {
+ val fileData = readFileData(uri)
+ score = ScoreLoader.loadScoreFromBytes(fileData, mAlphaTabView.settings)
+ Log.i("AlphaTab", "File loaded: ${score.title}")
+ } catch (e: Exception) {
+ Log.e("AlphaTab", "Failed to load file: $e, ${e.stackTraceToString()}")
+ Toast.makeText(this, "Failed to load file: ${e.message}", Toast.LENGTH_LONG).show()
+ return@registerForActivityResult
+ }
+
+ try {
+ mViewModel.score.value = score
+ mViewModel.tracks.value = arrayListOf(score.tracks[0])
+ } catch (e: Exception) {
+ Log.e("AlphaTab", "Failed to render file: $e, ${e.stackTraceToString()}")
+ Toast.makeText(this, "Failed to render file: ${e.message}", Toast.LENGTH_LONG).show()
+ }
+ }
+
+ private fun readFileData(uri: Uri): Uint8Array {
+ val inputStream = contentResolver.openInputStream(uri)
+ inputStream.use {
+ ByteArrayOutputStream().use {
+ inputStream!!.copyTo(it)
+ return Uint8Array(it.toByteArray().asUByteArray())
+ }
+ }
+ }
+}
+```
+
+
+
+
+```kotlin
+package net.alphatab.tutorial.android
+
+import alphaTab.Settings
+import alphaTab.model.Score
+import alphaTab.model.Track
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import kotlin.contracts.ExperimentalContracts
+
+@ExperimentalUnsignedTypes
+@ExperimentalContracts
+class MainViewModel : ViewModel() {
+ val score = MutableLiveData()
+ val tracks = MutableLiveData?>()
+ val settings = MutableLiveData().apply {
+ value = Settings().apply {
+ this.player.enableCursor = true
+ this.player.enablePlayer = true
+ this.player.enableUserInteraction = true
+ this.display.barCountPerPartial = 4.0
+ this.display.resources.barNumberFont
+ }
+ }
+}
+```
+
+
+
+
+```kotlin
+package net.alphatab.tutorial.android
+
+import alphaTab.model.Track
+import android.annotation.SuppressLint
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ArrayAdapter
+import android.widget.ListView
+import android.widget.PopupWindow
+import android.widget.TextView
+import com.google.android.material.button.MaterialButton
+import kotlin.contracts.ExperimentalContracts
+
+@OptIn(ExperimentalContracts::class, ExperimentalUnsignedTypes::class)
+@SuppressLint("InflateParams")
+class ControlsPopupWindow(
+ context: Context,
+ private val mainViewModel: MainViewModel,
+ onOpenFile: () -> Unit
+) : PopupWindow(context) {
+ private val mOpenButton: MaterialButton
+ private val mTrackList: ListView
+
+ init {
+ val view = LayoutInflater.from(context).inflate(R.layout.popup_controls, null)
+
+ mOpenButton = view.findViewById(R.id.openFile)
+ mOpenButton.setOnClickListener {
+ onOpenFile()
+ dismiss()
+ }
+
+ mTrackList = view.findViewById(R.id.trackList)
+ mTrackList.adapter = TrackListAdapter(context, mainViewModel.score.value?.tracks?.toList() ?: emptyList())
+ mTrackList.setOnItemClickListener { _, _, position, _ ->
+ mainViewModel.tracks.value =
+ mutableListOf((mTrackList.adapter as TrackListAdapter).getItem(position)!!)
+ dismiss()
+ }
+
+ view.findViewById(R.id.back).setOnClickListener {
+ dismiss()
+ }
+
+ contentView = view
+ }
+
+ class TrackListAdapter(context: Context, tracks: List) :
+ ArrayAdapter(context, android.R.layout.simple_list_item_1, tracks) {
+
+ private val mInflater: LayoutInflater = LayoutInflater.from(context)
+
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
+ val view =
+ convertView ?: mInflater.inflate(android.R.layout.simple_list_item_1, parent, false)
+
+ if (view !is TextView) {
+ throw IllegalStateException("Expected simple_list_item_1 to be a TextView")
+ }
+
+ val item = getItem(position)
+ view.text = item!!.name
+ return view
+ }
+ }
+}
+```
+
+
+
+
+```xml
+
+
+
+
+
+
+
+```
+
+
+
\ No newline at end of file
diff --git a/docs/tutorial-android/viewport.mdx b/docs/tutorial-android/viewport.mdx
new file mode 100644
index 0000000..8d30288
--- /dev/null
+++ b/docs/tutorial-android/viewport.mdx
@@ -0,0 +1,428 @@
+---
+title: Main Viewport
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+## Basic setup
+
+As our application will grow in this tutorial with new UI elements, we already prepare
+the rough structure according to the final solution.
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+You can already see a bit the structure of the control.
+This step is not really specific to alphaTab but important to understand from a structural perspective.
+We are building a layout with main viewport and a bottom toolbar. The bottom bar will hold information and main media controls.
+
+Further controls and the track selection we will open in an overlay/popup when a user taps on the song information.
+
+For your project your development and design skills will be be needed to structure the elements right.
+
+:::tip
+
+Icons can be added to your project via right-click on the `res` folder > New > Vector Asset
+
+:::
+
+The result will looks like this:
+
+
+
+
+:::note
+
+From now on the tutorial might only list small code snippets depending on the changes added to the component.
+The final file with all changes applied, will be added at the bottom of each tutorial section.
+
+:::
+
+
+## Initialize alphaTab
+
+Now let's finally bring alphaTab into the game. We add an alphaTab control to the
+viewport. For now we will open a file picker when the user taps the song info.
+
+
+
+
+We replace the `TextView` with a `alphaTab.AlphaTabView`.
+
+```xml
+
+```
+
+
+
+
+* First we load references to our views with `findViewById`.
+* Then we hook up a click listener to the info control to spawn a file selector via `ActivityResultContracts.OpenDocument()`.
+* When a file is opened we first read it into memory.
+* With the raw file data available we use the [`ScoreLoader`](/docs/guides/lowlevel-apis#loading-files-via-scoreloader) to load a score from the file.
+* With the `Score` loaded we pass it into the UI to be displayed.
+
+```kotlin
+package net.alphatab.tutorial.android
+
+import alphaTab.AlphaTabView
+import alphaTab.core.ecmaScript.Uint8Array
+import alphaTab.importer.ScoreLoader
+import alphaTab.model.Score
+import android.annotation.SuppressLint
+import android.net.Uri
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.widget.TextView
+import android.widget.Toast
+import androidx.activity.enableEdgeToEdge
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import java.io.ByteArrayOutputStream
+import kotlin.contracts.ExperimentalContracts
+
+@OptIn(ExperimentalContracts::class, ExperimentalUnsignedTypes::class)
+@SuppressLint("SetTextI18n")
+class MainActivity : AppCompatActivity() {
+
+ private lateinit var mAlphaTabView: AlphaTabView
+ private lateinit var mTrackName: TextView
+ private lateinit var mSongName: TextView
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enableEdgeToEdge()
+ setContentView(R.layout.activity_main)
+ ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
+ val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
+ v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
+ insets
+ }
+
+ mAlphaTabView = findViewById(R.id.alphatab)
+ mTrackName = findViewById(R.id.trackName)
+ mSongName = findViewById(R.id.songName)
+ findViewById(R.id.info).setOnClickListener {
+ mOpenFile.launch(arrayOf("*/*"))
+ }
+ }
+
+ private val mOpenFile = registerForActivityResult(ActivityResultContracts.OpenDocument()) {
+ val uri = it ?: return@registerForActivityResult
+ val score: Score
+ try {
+ val fileData = readFileData(uri)
+ score = ScoreLoader.loadScoreFromBytes(fileData, mAlphaTabView.settings)
+ Log.i("AlphaTab", "File loaded: ${score.title}")
+ } catch (e: Exception) {
+ Log.e("AlphaTab", "Failed to load file: $e, ${e.stackTraceToString()}")
+ Toast.makeText(this, "Failed to load file: ${e.message}", Toast.LENGTH_LONG).show()
+ return@registerForActivityResult
+ }
+
+ try {
+ mTrackName.text = score.tracks[0].name
+ mSongName.text = "${score.title} - ${score.artist}"
+ mAlphaTabView.tracks = arrayListOf(score.tracks[0])
+ } catch (e: Exception) {
+ Log.e("AlphaTab", "Failed to render file: $e, ${e.stackTraceToString()}")
+ Toast.makeText(this, "Failed to render file: ${e.message}", Toast.LENGTH_LONG).show()
+ }
+ }
+
+ private fun readFileData(uri: Uri): Uint8Array {
+ val inputStream = contentResolver.openInputStream(uri)
+ inputStream.use {
+ ByteArrayOutputStream().use {
+ inputStream!!.copyTo(it)
+ return Uint8Array(it.toByteArray().asUByteArray())
+ }
+ }
+ }
+}
+```
+
+
+
+
+## Result
+
+With this code we can launch the app and open a test file. Test files can be added to the simulator via drag&drop onto the simulator screen.
+When running debug versions of your app the opening can be quite slow, to experience the real performace deploy a release version and test it.
+
+
+
+
+
+
+## Final Files
+
+
+
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+
+```kotlin
+package net.alphatab.tutorial.android
+
+import alphaTab.AlphaTabView
+import alphaTab.core.ecmaScript.Uint8Array
+import alphaTab.importer.ScoreLoader
+import alphaTab.model.Score
+import android.annotation.SuppressLint
+import android.net.Uri
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.widget.TextView
+import android.widget.Toast
+import androidx.activity.enableEdgeToEdge
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import java.io.ByteArrayOutputStream
+import kotlin.contracts.ExperimentalContracts
+
+@OptIn(ExperimentalContracts::class, ExperimentalUnsignedTypes::class)
+@SuppressLint("SetTextI18n")
+class MainActivity : AppCompatActivity() {
+
+ private lateinit var mAlphaTabView: AlphaTabView
+ private lateinit var mTrackName: TextView
+ private lateinit var mSongName: TextView
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enableEdgeToEdge()
+ setContentView(R.layout.activity_main)
+ ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
+ val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
+ v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
+ insets
+ }
+
+ mAlphaTabView = findViewById(R.id.alphatab)
+ mTrackName = findViewById(R.id.trackName)
+ mSongName = findViewById(R.id.songName)
+ findViewById(R.id.info).setOnClickListener {
+ mOpenFile.launch(arrayOf("*/*"))
+ }
+ }
+
+ private val mOpenFile = registerForActivityResult(ActivityResultContracts.OpenDocument()) {
+ val uri = it ?: return@registerForActivityResult
+ val score: Score
+ try {
+ val fileData = readFileData(uri)
+ score = ScoreLoader.loadScoreFromBytes(fileData, mAlphaTabView.settings)
+ Log.i("AlphaTab", "File loaded: ${score.title}")
+ } catch (e: Exception) {
+ Log.e("AlphaTab", "Failed to load file: $e, ${e.stackTraceToString()}")
+ Toast.makeText(this, "Failed to load file: ${e.message}", Toast.LENGTH_LONG).show()
+ return@registerForActivityResult
+ }
+
+ try {
+ mTrackName.text = score.tracks[0].name
+ mSongName.text = "${score.title} - ${score.artist}"
+ mAlphaTabView.tracks = arrayListOf(score.tracks[0])
+ } catch (e: Exception) {
+ Log.e("AlphaTab", "Failed to render file: $e, ${e.stackTraceToString()}")
+ Toast.makeText(this, "Failed to render file: ${e.message}", Toast.LENGTH_LONG).show()
+ }
+ }
+
+ private fun readFileData(uri: Uri): Uint8Array {
+ val inputStream = contentResolver.openInputStream(uri)
+ inputStream.use {
+ ByteArrayOutputStream().use {
+ inputStream!!.copyTo(it)
+ return Uint8Array(it.toByteArray().asUByteArray())
+ }
+ }
+ }
+}
+```
+
+
+
\ No newline at end of file
diff --git a/docs/tutorial-net/controls.mdx b/docs/tutorial-net/controls.mdx
index b6eded8..c64197a 100644
--- a/docs/tutorial-net/controls.mdx
+++ b/docs/tutorial-net/controls.mdx
@@ -9,9 +9,9 @@ In the following steps of the tutorial we will add some basic controls for the e
will allow them to:
* See the song title and artist
+* Enable/Disable the count-in
* Enable/Disable the metronome
* Enable/Disable the looping
-* Print the music sheet
* Change the zoom level
* Change the layout of the music sheet
@@ -306,7 +306,7 @@ and re-render the music sheet.
## Layout
alphaTab can either render the music sheet in a page-like fashion that grows from top to bottom
-along the available with. Or it can show the music sheet in a horizontal scrolling fashion.
+along the available width. Or it can show the music sheet in a horizontal scrolling fashion.
These options are also offered to the user via another ComboBox. To actually apply the user selection
we will again just fill the user input into the settings object and trigger an update just like for
diff --git a/docs/tutorial-net/introduction.mdx b/docs/tutorial-net/introduction.mdx
index 2807741..0a143e4 100644
--- a/docs/tutorial-net/introduction.mdx
+++ b/docs/tutorial-net/introduction.mdx
@@ -31,8 +31,7 @@ Most aspects of the tutorial can be directly mapped to the usage in WinForms as
The WinForms version suffers of some restrictions due to it's nature of mapping native OS controls to .net. WPF provides a lot of
nice features like hardware accelleration on the rendering and very flexible layouting mechanisms.
-We will target the .net core 3.1 runtime in this tutorial as we decided not to publish a .net 4.8 package. If you have an urgent
-need of supporting the classical .net framework feel free to reach out to us.
+We will target the .net 8.0 runtime in this tutorial.
To follow this tutorial you will need to have a .net IDE installed. We will use Visual Studio 2019 Community edition
but beside the project setup anyhow most of things will happen on code level so feel free to use any IDE you are familiar with.
diff --git a/docs/tutorial-net/setup.mdx b/docs/tutorial-net/setup.mdx
index 9286fd7..278ffd0 100644
--- a/docs/tutorial-net/setup.mdx
+++ b/docs/tutorial-net/setup.mdx
@@ -22,4 +22,4 @@ Then search for the package `AlphaTab.Windows` and install it.
Additionally install the `FontAwesome.Sharp` package which we will use for some icons
-in the UI. Wioth this is everything we need to actually get started on developing.
\ No newline at end of file
+in the UI. With this is everything we need to actually get started on developing.
\ No newline at end of file
diff --git a/docs/tutorial-net/viewport.mdx b/docs/tutorial-net/viewport.mdx
index 9574cf0..4450f3e 100644
--- a/docs/tutorial-net/viewport.mdx
+++ b/docs/tutorial-net/viewport.mdx
@@ -81,7 +81,7 @@ The final file with all changes applied, will be added at the bottom of each tut
:::
-The result will look look like this:
+The result looks like this:
diff --git a/docs/tutorial-web/track-selector.mdx b/docs/tutorial-web/track-selector.mdx
index 36bf93b..f1f1786 100644
--- a/docs/tutorial-web/track-selector.mdx
+++ b/docs/tutorial-web/track-selector.mdx
@@ -40,7 +40,6 @@ that we will use for all items in the track selector and some place in the sideb
@@ -429,4 +428,4 @@ tracks as active in the sidebar with a CSS class.
|