mirror of https://gitlab.com/openlp/web-remote.git
Add transposing and use a pipe to format the chords
This commit is contained in:
parent
a083bea652
commit
f04941bf74
|
@ -28,6 +28,8 @@ import { FormsModule } from '@angular/forms';
|
|||
import { ChordViewComponent } from './components/chord-view/chord-view.component';
|
||||
import { StageViewComponent } from './components/stage-view/stage-view.component';
|
||||
import { MainViewComponent } from './components/main-view/main-view.component';
|
||||
import { ChordProPipe } from './components/chord-view/chordpro.pipe';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -38,7 +40,8 @@ import { MainViewComponent } from './components/main-view/main-view.component';
|
|||
OpenLPSlidesComponent,
|
||||
ChordViewComponent,
|
||||
StageViewComponent,
|
||||
MainViewComponent
|
||||
MainViewComponent,
|
||||
ChordProPipe
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
|
|
@ -4,11 +4,9 @@
|
|||
<span *ngFor="let tag of tags" [class.active]="tag.active">{{ tag.text }}</span>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="slide currentSlide" [innerHTML]="currentSlides[activeSlide]?.chords_text">
|
||||
</div>
|
||||
<div class="slide currentSlide song" [innerHTML]="chordproFormatted(currentSlides[0])|chordpro:transpose"></div>
|
||||
<div class="nextSlides">
|
||||
<div class="slide" [class.first]="slide.first_slide_of_tag" *ngFor="let slide of nextSlides" [innerHTML]="slide.chords_text">
|
||||
</div>
|
||||
<div class="slide song" [class.first]="slide.first_slide_of_tag" *ngFor="let slide of nextSlides" [innerHTML]="chordproFormatted(slide)|chordpro:transpose"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { OpenLPService } from '../../openlp.service';
|
||||
import { Slide } from '../../responses';
|
||||
import { Observable } from 'rxjs';
|
||||
|
@ -7,7 +7,9 @@ import { StageViewComponent } from '../stage-view/stage-view.component';
|
|||
@Component({
|
||||
selector: 'app-chord-view',
|
||||
templateUrl: './chord-view.component.html',
|
||||
styleUrls: ['./chord-view.component.scss', '../overlay.scss']
|
||||
styleUrls: ['./chord-view.component.scss', '../overlay.scss', './chordpro.scss'],
|
||||
encapsulation: ViewEncapsulation.None // needed for the chords to be displayed
|
||||
|
||||
})
|
||||
export class ChordViewComponent extends StageViewComponent {
|
||||
transpose: number = 0;
|
||||
|
@ -19,4 +21,18 @@ export class ChordViewComponent extends StageViewComponent {
|
|||
transposeDown(): void {
|
||||
this.transpose--;
|
||||
}
|
||||
|
||||
chordproFormatted(slide: Slide): string {
|
||||
if (!slide) {
|
||||
return '';
|
||||
}
|
||||
let chordpro: string = slide.chords_text;
|
||||
chordpro = chordpro.replace(/<span class="\w*\s*\w*">/g, '');
|
||||
chordpro = chordpro.replace(/<span>/g, '',);
|
||||
chordpro = chordpro.replace(/<\/span>/g, '');
|
||||
chordpro = chordpro.replace(/<strong>/g, '[');
|
||||
chordpro = chordpro.replace(/<\/strong>/g, ']');
|
||||
|
||||
return chordpro;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
/**
|
||||
* ChordProPipe
|
||||
*
|
||||
* A pipe for angular 2/4 that translate ChordPro-formatted text into an HTML representation, to be used in conjunction with a set of styles
|
||||
* for proper display.
|
||||
*
|
||||
* If you make improvements, please send them to me for incorporation.
|
||||
*
|
||||
* @author David Quinn-Jacobs (dqj@authentrics.com)
|
||||
* @licence Use this in any way you like, with no constraints.
|
||||
*/
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { MAT_AUTOCOMPLETE_SCROLL_STRATEGY_FACTORY } from '@angular/material';
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
|
||||
@Pipe({ name: 'chordpro' })
|
||||
|
||||
export class ChordProPipe implements PipeTransform {
|
||||
private readonly MAX_HALF_STEPS = 11;
|
||||
|
||||
constructor(private sanitizer: DomSanitizer) {
|
||||
this.notesSharpNotation['german'] = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','H'];
|
||||
this.notesFlatNotation['german'] = ['C','Db','D','Eb','Fb','F','Gb','G','Ab','A','B','H'];
|
||||
this.notesSharpNotation['english'] = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'];
|
||||
this.notesFlatNotation['english'] = ['C','Db','D','Eb','Fb','F','Gb','G','Ab','A','Bb','B'];
|
||||
this.notesSharpNotation['neo-latin'] = ['Do','Do#','Re','Re#','Mi','Fa','Fa#','Sol','Sol#','La','La#','Si'];
|
||||
this.notesFlatNotation['neo-latin'] = ['Do','Reb','Re','Mib','Fab','Fa','Solb','Sol','Lab','La','Sib','Si'];
|
||||
}
|
||||
|
||||
private keys = [
|
||||
{ name: 'Ab', value: 0 },
|
||||
{ name: 'A', value: 1 },
|
||||
{ name: 'A#', value: 2 },
|
||||
{ name: 'Bb', value: 2 },
|
||||
{ name: 'B', value: 3 },
|
||||
{ name: 'C', value: 4 },
|
||||
{ name: 'C#', value: 5 },
|
||||
{ name: 'Db', value: 5 },
|
||||
{ name: 'D', value: 6 },
|
||||
{ name: 'D#', value: 7 },
|
||||
{ name: 'Eb', value: 7 },
|
||||
{ name: 'E', value: 8 },
|
||||
{ name: 'F', value: 9 },
|
||||
{ name: 'F#', value: 10 },
|
||||
{ name: 'Gb', value: 10 },
|
||||
{ name: 'G', value: 11 },
|
||||
{ name: 'G#', value: 0 }
|
||||
];
|
||||
notesSharpNotation = {};
|
||||
notesFlatNotation = {};
|
||||
|
||||
|
||||
/**
|
||||
* @var chordRegex Expression used to determine if given line contains a chord.
|
||||
* @type {RegExp}
|
||||
*/
|
||||
private chordRegex = /\[([^\]]*)\]/;
|
||||
|
||||
/**
|
||||
* Pipe transformation for ChordPro-formatted song texts.
|
||||
* @param {string} song
|
||||
* @param {number} nHalfSteps
|
||||
* @returns {string}
|
||||
*/
|
||||
transform(song: string, nHalfSteps: number): string|SafeHtml {
|
||||
let transformed = song;
|
||||
try {
|
||||
if (song !== undefined && song) {
|
||||
return this.sanitizer.bypassSecurityTrustHtml(this.parseToHTML(song, nHalfSteps));
|
||||
}
|
||||
else {
|
||||
return transformed;
|
||||
}
|
||||
}
|
||||
catch (exception) {
|
||||
console.warn('chordpro translation error', exception);
|
||||
}
|
||||
}
|
||||
|
||||
chordRoot(chord) {
|
||||
let root = '';
|
||||
let ch2 = '';
|
||||
if (chord && chord.length > 0) {
|
||||
root = chord.substr(0, 1);
|
||||
if (chord.length > 1) {
|
||||
ch2 = chord.substr(1, 1);
|
||||
if (ch2 === 'b' || ch2 === '#') {
|
||||
root += ch2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
restOfChord(chord) {
|
||||
let rest = '';
|
||||
let root = this.chordRoot(chord);
|
||||
if (chord.length > root.length) {
|
||||
rest = chord.substr(root.length);
|
||||
}
|
||||
return rest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transpose the given chord the given (positive or negative) number of half steps.
|
||||
* @param {string} chordRoot
|
||||
* @param {number} nHalfSteps
|
||||
* @returns {string}
|
||||
*/
|
||||
transposeChord(chordRoot, nHalfSteps) {
|
||||
let pos = -1;
|
||||
for (let i = 0; i < this.keys.length; i++) {
|
||||
if (this.keys[i].name === chordRoot) {
|
||||
pos = this.keys[i].value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pos >= 0) {
|
||||
pos += nHalfSteps;
|
||||
if (pos < 0) {
|
||||
pos += this.MAX_HALF_STEPS;
|
||||
}
|
||||
else if (pos > this.MAX_HALF_STEPS) {
|
||||
pos -= this.MAX_HALF_STEPS + 1;
|
||||
}
|
||||
for (let i = 0; i < this.keys.length; i++) {
|
||||
if (this.keys[i].value === pos) {
|
||||
return this.keys[i].name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return chordRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string containing a ChordPro-formatted song, building an array of output HTML lines.
|
||||
*
|
||||
* @param {number} nHalfSteps
|
||||
* @param {string} song
|
||||
*/
|
||||
private parseToHTML(song: string, nHalfSteps = 0): string {
|
||||
// we are currently receiving html, we need to replace that stuff,
|
||||
// becuase it gets messed up when a chord is placed on it..
|
||||
// shouldn't be relevant if we actually get chordpro format
|
||||
song = song.replace(/–/g, '-');
|
||||
console.log('parsing: ', song);
|
||||
let comp = this;
|
||||
if (!song) {
|
||||
return '';
|
||||
}
|
||||
let chordText: string = '';
|
||||
let lastChord: string = '';
|
||||
song.split(comp.chordRegex).forEach((part, index) => {
|
||||
if (index % 2 == 0) {
|
||||
// text
|
||||
if (lastChord) {
|
||||
chordText += `<span data-chord="${lastChord}">${part.substring(0, 1)}</span>${part.substring(1)}`
|
||||
lastChord = '';
|
||||
} else {
|
||||
chordText += part;
|
||||
}
|
||||
} else {
|
||||
// chord
|
||||
lastChord = part.replace(/[[]]/, '');
|
||||
if (nHalfSteps !== 0) {
|
||||
let chordRoot = comp.chordRoot(lastChord);
|
||||
let newRoot = comp.transposeChord(chordRoot, nHalfSteps);
|
||||
lastChord = newRoot + comp.restOfChord(lastChord);
|
||||
}
|
||||
}
|
||||
});
|
||||
return chordText;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
.song {
|
||||
white-space: pre-wrap;
|
||||
line-height: 2;
|
||||
|
||||
span[data-chord]:before {
|
||||
position: relative;
|
||||
top: -1em;
|
||||
display: inline-block;
|
||||
content: attr(data-chord);
|
||||
width: 0;
|
||||
color: yellow;
|
||||
}
|
||||
}
|
||||
|
||||
.nextSlides {
|
||||
.song {
|
||||
span[data-chord]:before {
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue