Skip to content

Commit

Permalink
Merge pull request #1 from intelektron/develop
Browse files Browse the repository at this point in the history
Version 1.1
  • Loading branch information
intelektron authored Jan 17, 2024
2 parents 5c77eca + 3b1f904 commit a92b0cc
Show file tree
Hide file tree
Showing 29 changed files with 1,307 additions and 187 deletions.
81 changes: 56 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,32 +38,45 @@ See [web/example.php](web/example.php) for demo with CSS styling.

require __DIR__ . '/vendor/autoload.php';

$txt = "{t:Chordpro-PHP Song}
{st:Nicolas Wurtz}
{c:GPL3 2019 Nicolas Wurtz}
$txt = "{t:A Nice Sample Song}
{st:Grzegorz Pietrzak}
{key:C}

# Let's start it!
[C]This is the [Dm]beautiful [Em]song
I [Dm]wrote in [F/G]Chordpro for[C]mat [Dm/F]
Let's sing it a[C/E]long
[Bb] It's ea[Dm]sy to do [F]that [C]

{soc}
[F] [G] [C]This is the refrain
[F] [G] [C]We could sing it twice
[C]Let's sing this [G]song [Am]together [Em]aloud
[F]It's an [C]example [Dm]with some nice [G]sound

{soc: Chorus}
[Bb]Whenever you [Am7]need to [Bb]format your [Am7]chords
[Dm]The solution to your [F]problems [G]is very close
{eoc}

{c:Final}
[Em/D]This is the [Bb]end.
";
{comment: Now we recite some text}
Sometimes you write text
And there's no more room for chords

{comment: Sometimes you play music without any words}
[C] [G] [Am] [Em]

You don't know where the chords are? ~ [F] [C]
You don't have to know ~ [G] [G/F#]

{sot: Outro}
E-12---------------------|
B----11-12---------------|
G----------11s13-14------|
D-------------------10-12|
A------------------------|
E------------------------|
{eot}

{comment: The end}
Let's finish this song. [G] It's the end of the show.";

$parser = new ChordPro\Parser();

// Choose one of these formatters according to your needs.
$htmlFormatter = new ChordPro\Formatter\HtmlFormatter();
$monospaceFormatter = new ChordPro\Formatter\MonospaceFormatter();
$jsonFormatter = new ChordPro\Formatter\JSONFormatter();

// Create song object after parsing $txt.
$song = $parser->parse($txt);
Expand All @@ -72,20 +85,18 @@ $song = $parser->parse($txt);
$transposer = new ChordPro\Transposer();

// Define how many semitones you want to transpose by.
$transposer->transpose($song, -5);
// $transposer->transpose($song, -5);

// If the song key is known, you can also transpose from key to key.
// $transposer->transpose($song,'Abm');

// The formatter has some options
$options = [
'ignore_metadata' => 'title',
'ignore_metadata' => 'copyright',
];

// Render !
$html = $htmlFormatter->format($song, $options);
$monospaced = $monospaceFormatter->format($song, $options);
$json = $jsonFormatter->format($song, $options);
```

![Screenshot](web/example.png)
Expand Down Expand Up @@ -189,12 +200,12 @@ The ChordPro format allows to organize your songs into sections. The following s
Will be converted to:

```html
<div class="chordpro-verse-comment">Verse 1</div>
<div class="chordpro-verse">
<div class="chordpro-section-label chordpro-verse-label">Verse 1</div>
<div class="chordpro-section chordpro-verse">
...
</div>

<div class="chordpro-foobar">
<div class="chordpro-section chordpro-foobar">
...
</div>
```
Expand All @@ -219,13 +230,18 @@ The library reads ChordPro metadata and renders it as HTML in the following way:
```chordpro
{title: Let's Sing!}
{c: Very loud}
{key: C}
```

Becomes:

``` html
<div class="chordpro-title">Let's Sing!</div>
<div class="chordpro-comment">Very loud</div>
<div class="chordpro-metadata chordpro-title">Let's Sing!</div>
<div class="chordpro-metadata chordpro-comment">Very loud</div>
<div class="chordpro-metadata chordpro-key">
<span class="chordpro-metadata-name">Key: </span>
<span class="chordpro-metadata-value">C</span>
</div>
```

The names of metadata are not restricted in any way, however, there are some standard ones described by ChordPro format. The following shortcuts are supported:
Expand All @@ -235,3 +251,18 @@ The names of metadata are not restricted in any way, however, there are some sta
- `{c}``{comment}`
- `{ci}``{comment_italic}`
- `{cb}``{comment_box}`

## Extensions to ChordPro 6.x

The library provides some non-standard features that can be useful for songbook creators.

### Inline chords

If you don't know how to assign the chord to syllables, or it's not important to you, you can use the inline chords assigned to the lyric line:

```chordpro
You don't know where the chords are? ~ [F] [C]
You don't have to know ~ [G] [G/F#]
```

The chords appear above the line (in the HTML formatter) or to the right (in the monospace formatter).
7 changes: 6 additions & 1 deletion src/Block.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class Block
/**
* @param Chord[] $chords The chords.
*/
public function __construct(private array $chords, private string $text)
public function __construct(private array $chords, private string $text, private bool $lineEnd = false)
{
}

Expand All @@ -29,4 +29,9 @@ public function getText(): string
{
return $this->text;
}

public function isLineEnd(): bool
{
return $this->lineEnd;
}
}
89 changes: 71 additions & 18 deletions src/Formatter/HtmlFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,26 +52,50 @@ private function getMetadataHtml(Metadata $metadata): string
return '';
}

$match = [];
if (preg_match('/^start_of_(.*)/', $metadata->getName(), $match) === 1) {
$type = preg_replace('/[\W_\-]/', '', $match[1]);
if ($metadata->isSectionStart()) {
$type = $metadata->getSectionType();
$content = '';
if (null !== $metadata->getValue()) {
$content = '<div class="chordpro-'.$type.'-comment">'.$metadata->getValue()."</div>\n";
$content = '<div class="chordpro-section-label chordpro-'.$type.'-label">'.$metadata->getValue()."</div>\n";
}
return $content.'<div class="chordpro-'.$type.'">'."\n";
} elseif (preg_match('/^end_of_(.*)/', $metadata->getName()) === 1) {
return $content.'<div class="chordpro-section chordpro-'.$type.'">'."\n";
} elseif ($metadata->isSectionEnd()) {
return "</div>\n";
} else {
$name = preg_replace('/[\W_\-]/', '', mb_strtolower($metadata->getName()));
return '<div class="chordpro-'.$name.'">'.$metadata->getValue()."</div>\n";
if ($metadata->getName() === 'key' && null != $this->notation) {
$value = $this->notation->convertChordRootToNotation($metadata->getValue() ?? '');
} else {
$value = $metadata->getValue();
}
$type = $metadata->getNameSlug();
$output = '<div class="chordpro-metadata chordpro-'.$type.'">';
if ($metadata->isNameNecessary()) {
$output .= '<span class="chordpro-metadata-name">'.$metadata->getHumanName().': </span>';
$output .= '<span class="chordpro-metadata-value">'.$value.'</span>';
} else {
$output .= $value;
}
$output .= "</div>\n";
return $output;
}
}

private function getLyricsHtml(Lyrics $lyrics): string
{
$line = '<div class="chordpro-line">'."\n";
foreach ($lyrics->getBlocks() as $block) {
$classes = ['chordpro-line'];
if ($lyrics->hasInlineChords()) {
$classes[] = 'chordpro-line-inline-chords';
}
if (!$lyrics->hasChords()) {
$classes[] = 'chordpro-line-text-only';
}
if (!$lyrics->hasText()) {
$classes[] = 'chordpro-line-chords-only';
}
$line = '<div class="'.implode(' ', $classes).'">'."\n";
$lineChords = '';
$lineText = '';
foreach ($lyrics->getBlocks() as $num => $block) {

$originalChords = [];
$chords = [];
Expand All @@ -96,22 +120,51 @@ private function getLyricsHtml(Lyrics $lyrics): string
$originalChord = implode('/', $originalChords);
$text = $this->blankChars($block->getText());

$line .= '<span class="chordpro-block">' .
'<span class="chordpro-chord" data-chord="'.$originalChord.'">'.$chord.'</span>' .
'<span class="chordpro-text">'.$text.'</span>' .
'</span>';
if ($lyrics->hasInlineChords()) {
if ($num === 0) {
$lineText = '<div class="chordpro-inline-text">'.$text.'</div>';
} else {
$lineChords .= '<span class="chordpro-chord" data-chord="'.$originalChord.'">'.$chord.'</span>';
}
} elseif ($lyrics->hasChords() && $lyrics->hasText()) {
$line .= '<span class="chordpro-block">' .
'<span class="chordpro-chord" data-chord="'.$originalChord.'">'.$chord.'</span>' .
'<span class="chordpro-text">'.$text.'</span>' .
'</span>';
} elseif ($lyrics->hasChords()) {
$line .= '<span class="chordpro-block">' .
'<span class="chordpro-chord" data-chord="'.$originalChord.'">'.$chord.'</span>' .
'</span>';

} elseif ($lyrics->hasText()) {
$line .= '<span class="chordpro-block">' .
'<span class="chordpro-text">'.$text.'</span>' .
'</span>';
}
}

if ($lyrics->hasInlineChords()) {
$line .= '<div class="chordpro-inline-block">';
$line .= '<div class="chordpro-inline-chords">' . $lineChords . '</div>';
$line .= $lineText;
$line .= '</div>';
}

$line .= "\n</div>\n";
return $line;
}

private function getLyricsOnlyHtml(Lyrics $lyrics): string
{
$line = '<div class="chordpro-line">'."\n";
$text = '';
foreach ($lyrics->getBlocks() as $block) {
$line .= ltrim($block->getText());
$text .= ltrim($block->getText());
}
$text = rtrim($text);
if ($text === '') {
return '';
} else {
return '<div class="chordpro-line">'."\n".rtrim($text)."\n</div>\n";
}
$line .= "\n</div>\n";
return $line;
}
}
62 changes: 50 additions & 12 deletions src/Formatter/JSONFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,32 @@ private function getMetadataJSON(Metadata $metadata): array
if (in_array($metadata->getName(), $this->ignoreMetadata, true)) {
return [];
}
return [

if ($metadata->isSectionStart()) {
$metadataItem = [
'type' => 'section_start',
'sectionType' => $metadata->getSectionType(),
];
if (null !== $metadata->getValue()) {
$metadataItem['label'] = $metadata->getValue();
}
} elseif ($metadata->isSectionEnd()) {
$metadataItem = [
'type' => 'section_end',
'sectionType' => $metadata->getSectionType(),
];
} else {
$metadataItem = [
'type' => 'metadata',
'name' => $metadata->getName(),
'value' => $metadata->getValue(),
];
];
if ($metadata->getHumanName() != $metadata->getName()) {
$metadataItem['humanName'] = $metadata->getHumanName();
}
}

return $metadataItem;
}

/**
Expand All @@ -80,14 +101,26 @@ private function getLyricsJSON(Lyrics $lyrics): array
$originalChords[] = $slicedChord->getOriginalName();
}
}
$chord = implode('/', $chords).' ';
$originalChord = implode('/', $originalChords).' ';
$chord = implode('/', $chords);
$originalChord = implode('/', $originalChords);

$text = $block->getText();
$return[] = ['chord' => trim($chord), 'text' => $text, 'originalChord' => trim($originalChord)];
$blockArray = [];
if ($text !== '') {
$blockArray['text'] = rtrim($text);
}
$chord = trim($chord);
if ($chord !== '') {
$blockArray['chord'] = $chord;
$blockArray['originalChord'] = $originalChord;
}
if ($block->isLineEnd()) {
$blockArray['lineEnd'] = true;
}
$return[] = $blockArray;
}
return [
'type' => 'line',
'type' => $lyrics->hasInlineChords() ? 'line_inline' : 'line',
'blocks' => $return,
];
}
Expand All @@ -97,13 +130,18 @@ private function getLyricsJSON(Lyrics $lyrics): array
*/
private function getLyricsOnlyJSON(Lyrics $lyrics): array
{
$return = '';
$text = '';
foreach ($lyrics->getBlocks() as $block) {
$return .= ltrim($block->getText());
$text .= ltrim($block->getText());
}
$text = rtrim($text);
if ($text === '') {
return [];
} else {
return [
'type' => 'line',
'text' => $text,
];
}
return [
'type' => 'line',
'text' => $return,
];
}
}
Loading

0 comments on commit a92b0cc

Please sign in to comment.