Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Guitar Pro 7 Exporter #447

Merged
merged 31 commits into from
Dec 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6eb71f1
Add XML writer
Danielku15 Nov 29, 2020
baca787
Setup base structure for GP7 export
Danielku15 Nov 29, 2020
289b627
Temporary uncompressed zippping
Danielku15 Nov 29, 2020
7650c61
Finished uncompressed zip writing
Danielku15 Nov 30, 2020
e7024f6
Fix test
Danielku15 Dec 1, 2020
ac8991a
Further WIP
Danielku15 Dec 3, 2020
df41504
WIP
Danielku15 Dec 9, 2020
5cd2dc0
Working with missing features
Danielku15 Dec 13, 2020
67d438c
Fixed all tests
Danielku15 Dec 13, 2020
91b18fa
Better test infrastructure for Gp7 exporter
Danielku15 Dec 13, 2020
f5ef0b1
WIP
Danielku15 Dec 13, 2020
a034f33
Update test suite to GP7
Danielku15 Dec 13, 2020
440a34c
Extensions on articulations for better exports
Danielku15 Dec 13, 2020
5b803a2
Added part configuration writing
Danielku15 Dec 13, 2020
3310dc3
Add support for sound changes in GP7
Danielku15 Dec 13, 2020
9b1f156
Proper handling of instrument changes
Danielku15 Dec 14, 2020
d6e780b
Fixed tests
Danielku15 Dec 14, 2020
32bae45
WIP
Danielku15 Dec 16, 2020
fc226f3
Fixed tests
Danielku15 Dec 27, 2020
0cddcc8
Fixed C# version
Danielku15 Dec 27, 2020
60405d4
Fix detected GP issues
Danielku15 Dec 27, 2020
40e559a
Restore changes after rebase
Danielku15 Dec 27, 2020
c7f5efe
Fixed C# version
Danielku15 Dec 28, 2020
0c6a74a
Restored old dynamics on grace test file
Danielku15 Dec 30, 2020
0ecb4a5
Slight performance improvements
Danielku15 Dec 30, 2020
75f2b42
Added deflate algorithm
Danielku15 Dec 30, 2020
630ee9a
Bit codestyle cleanup
Danielku15 Dec 30, 2020
1a8e318
Added some zip tests
Danielku15 Dec 30, 2020
25aa3ea
Fixed C# version
Danielku15 Dec 30, 2020
5eacb90
Better unsupport format handling for alphaTex
Danielku15 Dec 30, 2020
dac4645
Add download button to playground
Danielku15 Dec 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ bin/
.vs/
*.user
*.tgz
test-results/
test-results/
debug.log
34 changes: 34 additions & 0 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,19 @@ module.exports = function (config) {
});
}
);
app.post(
'/save-file',
upload.fields([
{
name: 'file',
maxCount: 1
}
]),
function (req, res) {
log.info(`save file ${req.file}`);
res.send(JSON.stringify('OK'));
}
);
app.post(
'/save-visual-error',
upload.fields([
Expand All @@ -95,6 +108,27 @@ module.exports = function (config) {
res.send(JSON.stringify('OK'));
}
);
app.post(
'/list-files',
upload.fields([
{
name: 'expected',
maxCount: 1
},
{
name: 'actual',
maxCount: 1
},
{
name: 'diff',
maxCount: 1
}
]),
function (req, res) {
log.info(`save visual error ${req.file}`);
res.send(JSON.stringify('OK'));
}
);
}
},

Expand Down
3 changes: 3 additions & 0 deletions playground-template/control-template.html
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@
<a href="#" class="at-print" data-toggle="tooltip" data-placement="top" title="Print">
<i class="fas fa-print"></i>
</a>
<a href="#" class="at-download" data-toggle="tooltip" data-placement="top" title="Download">
<i class="fas fa-download"></i>
</a>

<div class="btn-group dropup">
<button
Expand Down
11 changes: 11 additions & 0 deletions playground-template/control.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,17 @@ function setupControl(selector) {
at.print();
};

control.querySelector('.at-download').onclick = function (e) {
const exporter = new alphaTab.exporter.Gp7Exporter();
const data = exporter.export(at.score, at.settings);
const a = document.createElement('a');
a.download = at.score.title.length > 0 ? at.score.title + '.gp' : 'song.gp';
a.href = URL.createObjectURL(new Blob([data]));
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};

control.querySelectorAll('.at-zoom-options a').forEach(function (a) {
a.onclick = function (e) {
e.preventDefault();
Expand Down
21 changes: 13 additions & 8 deletions src.compiler/csharp/CSharpAstPrinter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -857,24 +857,29 @@ export default class CSharpAstPrinter {
}

private writeStringTemplateExpression(expr: cs.StringTemplateExpression) {
this.write('$@"');
this.write('string.Format(System.Globalization.CultureInfo.InvariantCulture, @"');
let exprs: cs.Expression[] = [];
expr.chunks.forEach(c => {
if (c.nodeType === cs.SyntaxKind.StringLiteral) {
const escapedText = (c as cs.StringLiteral).text
.split('"')
.join('""')
.split('{')
.join('{{')
.split('}')
.join('}}');
.split('\n')
.join('\\n')
.split('\r')
.join('\\r');
this.write(escapedText);
} else {
this.write('{');
this.writeExpression(c as cs.Expression);
this.write('}');
this.write(`{${exprs.length}}`);
exprs.push(c as cs.Expression);
}
});
this.write('"');
exprs.forEach(expr => {
this.write(', ');
this.writeExpression(expr);
})
this.write(')');
}

private writeIsExpression(expr: cs.IsExpression) {
Expand Down
91 changes: 86 additions & 5 deletions src.compiler/csharp/CSharpAstTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ export default class CSharpAstTransformer {
const text = node.getSourceFile().text;
// check for /*@target web*/ marker
const commentText = text.substr(node.getStart() - node.getLeadingTriviaWidth(), node.getLeadingTriviaWidth());
if(commentText.indexOf('/*@target web*/') >= 0) {
if (commentText.indexOf('/*@target web*/') >= 0) {
return true;
}
}
Expand Down Expand Up @@ -1228,6 +1228,15 @@ export default class CSharpAstTransformer {
csMethod.body = this.visitBlock(csMethod, classElement.body);
}

switch (csMethod.name) {
case 'ToString':
if (csMethod.parameters.length === 0) {
csMethod.isVirtual = false;
csMethod.isOverride = true;
}
break;
}

parent.members.push(csMethod);

this._context.registerSymbol(csMethod);
Expand Down Expand Up @@ -1982,6 +1991,10 @@ export default class CSharpAstTransformer {
return null;
}

if (csExpr.operator === "~") {
csExpr.operand = this.makeInt(csExpr.operand);
}

return csExpr;
}

Expand Down Expand Up @@ -2285,8 +2298,10 @@ export default class CSharpAstTransformer {
switch (expression.operatorToken.kind) {
case ts.SyntaxKind.AmpersandToken:
case ts.SyntaxKind.GreaterThanGreaterThanToken:
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
case ts.SyntaxKind.LessThanLessThanToken:
case ts.SyntaxKind.BarToken:
case ts.SyntaxKind.CaretToken:
binaryExpression.left = this.makeInt(binaryExpression.left);
binaryExpression.right = this.makeInt(binaryExpression.right);
break;
Expand Down Expand Up @@ -2631,6 +2646,25 @@ export default class CSharpAstTransformer {
}
});

return csExpr;
} else if (this.isSetInitializer(expression)) {
const csExpr = {
parent: parent,
tsNode: expression,
nodeType: cs.SyntaxKind.InvocationExpression,
arguments: [],
expression: {} as cs.Expression
} as cs.InvocationExpression;

csExpr.expression = this.makeMemberAccess(csExpr, 'AlphaTab.Core.TypeHelper', 'SetInitializer');

expression.elements.forEach(e => {
const ex = this.visitExpression(csExpr, e);
if (ex) {
csExpr.arguments.push(ex);
}
});

return csExpr;
} else {
const csExpr = {
Expand Down Expand Up @@ -2672,6 +2706,15 @@ export default class CSharpAstTransformer {
return false;
}

private isSetInitializer(expression: ts.ArrayLiteralExpression) {
const isCandidate = expression.parent.kind === ts.SyntaxKind.NewExpression;
if (!isCandidate) {
return false;
}

return this._context.typeChecker.getTypeAtLocation(expression.parent).symbol.name === 'Set';
}

private visitPropertyAccessExpression(parent: cs.Node, expression: ts.PropertyAccessExpression) {
const memberAccess = {
expression: {} as cs.Expression,
Expand Down Expand Up @@ -2699,11 +2742,24 @@ export default class CSharpAstTransformer {
case 'length':
memberAccess.member = 'Count';
break;
case 'reverse':
memberAccess.member = 'Reversed';
break;
case 'push':
memberAccess.member = 'Add';
break;
}
break;
case 'String':
switch (memberAccess.tsSymbol!.name) {
case 'trimRight':
memberAccess.member = 'TrimEnd';
break;
case 'trimLeft':
memberAccess.member = 'TrimStart';
break;
}
break;
}
}
}
Expand Down Expand Up @@ -2799,8 +2855,8 @@ export default class CSharpAstTransformer {

private visitElementAccessExpression(parent: cs.Node, expression: ts.ElementAccessExpression) {
// Enum[value] => value.ToString()
const symbol = this._context.typeChecker.getSymbolAtLocation(expression.expression);
if (symbol && symbol.flags & ts.SymbolFlags.Enum) {
const enumType = this._context.typeChecker.getTypeAtLocation(expression.expression);
if (enumType?.symbol && enumType.symbol.flags & ts.SymbolFlags.RegularEnum) {
const callExpr = {
parent: parent,
arguments: [],
Expand Down Expand Up @@ -2843,6 +2899,7 @@ export default class CSharpAstTransformer {
return null;
}

const symbol = this._context.typeChecker.getSymbolAtLocation(expression.expression);
let type = symbol ? this._context.typeChecker.getTypeOfSymbolAtLocation(symbol!, expression.expression) : null;
if (type) {
type = this._context.typeChecker.getNonNullableType(type);
Expand Down Expand Up @@ -2905,6 +2962,31 @@ export default class CSharpAstTransformer {
}
});

// number.ToString
const isNumberToString = ts.isPropertyAccessExpression(expression.expression)
&& this._context.typeChecker.getTypeAtLocation(expression.expression.expression).flags & ts.TypeFlags.Number
&& (expression.expression.name as ts.Identifier).text === 'toString'
&& expression.arguments.length === 0;

if (isNumberToString) {
const invariantCultureInfo = {
parent: parent,
nodeType: cs.SyntaxKind.MemberAccessExpression,
tsNode: expression,
expression: null!,
member: 'InvariantCulture'
} as cs.MemberAccessExpression;

invariantCultureInfo.expression = {
parent: invariantCultureInfo,
tsNode: expression.expression,
nodeType: cs.SyntaxKind.Identifier,
text: 'System.Globalization.CultureInfo'
} as cs.Identifier;

callExpression.arguments.push(invariantCultureInfo);
}

if (expression.typeArguments) {
callExpression.typeArguments = [];
expression.typeArguments.forEach(a =>
Expand Down Expand Up @@ -3216,9 +3298,8 @@ export default class CSharpAstTransformer {
case ts.SyntaxKind.LessThanLessThanToken:
return '<<';
case ts.SyntaxKind.GreaterThanGreaterThanToken:
return '>>';
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
return '>>>';
return '>>';

case ts.SyntaxKind.LessThanToken:
return '<';
Expand Down
8 changes: 7 additions & 1 deletion src.compiler/csharp/CSharpEmitterContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ export default class CSharpEmitterContext {
}

// object -> object
if(tsType.flags === ts.TypeFlags.NonPrimitive && 'objectFlags' in tsType && 'intrinsicName' in tsType) {
if (tsType.flags === ts.TypeFlags.NonPrimitive && 'objectFlags' in tsType && 'intrinsicName' in tsType) {
const unknown = handleNullablePrimitive(cs.PrimitiveType.Object);
unknown.isNullable = true;
return unknown;
Expand Down Expand Up @@ -933,6 +933,12 @@ export default class CSharpEmitterContext {
return null;
}

// For Enum[value] we do not smart cast value to a number
if (ts.isElementAccessExpression(expression.parent) &&
expression.parent.argumentExpression === expression) {
return null;
}

// we consider the expression as smart casted if the declared symbol has a different
// contextual type than the declared type.
let symbol = this.typeChecker.getSymbolAtLocation(expression);
Expand Down
12 changes: 12 additions & 0 deletions src.csharp/AlphaTab.Test/Test/Globals.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ public void ToEqual(object expected, string message = null)
Assert.AreEqual(expected, _actual, _message + message);
}

public void ToBeCloseTo(double expected, string message = null)
{
if (_actual is IConvertible c)
{
Assert.AreEqual(expected, c.ToDouble(System.Globalization.CultureInfo.InvariantCulture), 0.001, _message + message);
}
else
{
Assert.Fail("ToBeCloseTo can only be used with numeric operands");
}
}

public void ToBe(object expected)
{
if (expected is int i && _actual is double)
Expand Down
10 changes: 9 additions & 1 deletion src.csharp/AlphaTab.Test/TestPlatform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,17 @@ public static async Task<Uint8Array> LoadFile(string path)
return new Uint8Array(ms.ToArray());
}

public static async Task SaveFile(string name, Uint8Array data)
{
var path = Path.Combine("test-results", name);
Directory.CreateDirectory(Path.GetDirectoryName(path));
await using var fs = new FileStream(Path.Combine("test-results", name), FileMode.Create);
await fs.WriteAsync(data.Data.Array!, data.Data.Offset, data.Data.Count);
}

public static Task<IList<string>> ListDirectory(string path)
{
return Task.FromResult((IList<string>)Directory.EnumerateFiles(path)
return Task.FromResult((IList<string>) Directory.EnumerateFiles(path)
.Select(Path.GetFileName)
.ToList());
}
Expand Down
20 changes: 20 additions & 0 deletions src.csharp/AlphaTab/Core/Dom/TextEncoder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Text;
using AlphaTab.Core.EcmaScript;

namespace AlphaTab.Core.Dom
{
internal class TextEncoder
{
private readonly Encoding _encoding;

public TextEncoder()
{
_encoding = Encoding.UTF8;
}

public Uint8Array Encode(string value)
{
return _encoding.GetBytes(value);
}
}
}
Loading