Task 087: .CHO File Format

Task 087: .CHO File Format

File Format Specifications for the .CHO File Format

The .CHO file format is associated with the ChordPro format, a text-based file format used for representing song lyrics with embedded chords and metadata. It is commonly used for lead sheets in music applications. The format consists of plain text with directives enclosed in curly braces {} for metadata and formatting, chords in square brackets [], and lyrics as plain text. The specification is defined by the ChordPro standard (version 6 as of the current date), which allows for easy creation and editing with any text editor.

1. List of All Properties of This File Format Intrinsic to Its File System

The properties of the ChordPro file format are the directives that control metadata, formatting, structure, and content. Below is a comprehensive list extracted from reliable sources, including names, descriptions, and examples where available:

title: title string (or t:string)

  • Description: Specifies the title of the song, appearing at the top. Should be the first directive.
  • Example: {title: Song Title} or {t: Song Title}

artist: artist name

  • Description: Specifies the name of the artist or any string, acting like a subtitle.
  • Example: {artist: Artist Name}

subtitle: subtitle string (or st:string)

  • Description: Specifies a subtitle for the song.
  • Example: {subtitle: Song Subtitle} or {st: Song Subtitle}

start_of_chorus (or soc)

  • Description: Indicates the start of a chorus, indenting the text and adding a highlight line to the left.
  • Example: {start_of_chorus} or {soc}

end_of_chorus (or eoc)

  • Description: Marks the end of the chorus, ending the indent and highlight.
  • Example: {end_of_chorus} or {eoc}

comment: string (or c:string)

  • Description: Prints the string as a comment.
  • Example: {comment: This is a comment} or {c: This is a comment}

start_of_tab (or sot)

  • Description: Indicates the start of a tab section, using fixed-width font for ASCII tabs. Guitar tab sections are not printed on lyrics sheets.
  • Example: {start_of_tab} or {sot}

end_of_tab (or eot)

  • Description: Marks the end of the tab section.
  • Example: {end_of_tab} or {eot}

column_break (or colb)

  • Description: Forces a column break, splitting the song into multiple columns.
  • Example: {column_break} or {colb}

key: xyz (or k:xyz)

  • Description: Specifies the key the chart is written in (xyz is a valid key); supports transposition.
  • Example: {key: C} or {k: C}

define

  • Description: Defines chord positions and optionally fingering for chords.
  • Example: {define: Am base-fret 1 frets 0 0 0 2 2 1}

new_page (or np)

  • Description: Causes a page break in PDFs; no effect in web displays.
  • Example: {new_page} or {np}

Extensions (Additional Properties)

soh ... eoh

  • Description: Highlights text in yellow.
  • Example: {soh}Highlighted text{eoh}

sohr ... eoh

  • Description: Highlights text in red.
  • Example: {sohr}Highlighted text{eoh}

sohb ... eoh

  • Description: Highlights text in blue.
  • Example: {sohb}Highlighted text{eoh}

sohg ... eoh

  • Description: Highlights text in green.
  • Example: {sohg}Highlighted text{eoh}

sohp ... eoh

  • Description: Highlights text in pink.
  • Example: {sohp}Highlighted text{eoh}

sohy ... eoh

  • Description: Highlights text in grey.
  • Example: {sohy}Highlighted text{eoh}

soho ... eoh

  • Description: Highlights text in orange.
  • Example: {soho}Highlighted text{eoh}

These properties are intrinsic to the format as they define the structure, metadata, and presentation of the content within the file system.

Upon extensive searching, direct download links for .CHO files were not readily available in public sources. However, examples of ChordPro content (which uses .CHO extension) can be found in documentation. For illustration, here are two sources where .CHO-compatible content can be downloaded or copied:

3. Ghost Blog Embedded HTML JavaScript for Drag and Drop .CHO File Dump

The following is an embeddable HTML/JavaScript code for a Ghost blog. It allows users to drag and drop a .CHO file, parses the directives (properties), and dumps them to the screen.

Drag and drop a .CHO file here

This code uses a simple regex to extract directives and displays them as a JSON array.

4. Python Class for .CHO File

The following Python class can open, decode (parse), read, write, and print all properties from a .CHO file.

import re

class ChoFile:
    def __init__(self, filename=None):
        self.filename = filename
        self.content = ''
        self.properties = []
        if filename:
            self.read(filename)

    def read(self, filename):
        with open(filename, 'r') as f:
            self.content = f.read()
        self.properties = re.findall(r'\{(.*?)\}', self.content)
        return self.properties

    def print_properties(self):
        for prop in self.properties:
            print(prop)

    def write(self, filename=None):
        if not filename:
            filename = self.filename
        with open(filename, 'w') as f:
            f.write(self.content)

    def add_property(self, directive):
        self.content += f'{{{directive}}}\n'
        self.properties.append(directive)

# Example usage:
# cho = ChoFile('example.cho')
# cho.print_properties()
# cho.add_property('new_directive: value')
# cho.write()

This class parses directives using regex, prints them to console, and allows writing modifications.

5. Java Class for .CHO File

The following Java class can open, decode, read, write, and print all properties from a .CHO file.

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ChoFile {
    private String filename;
    private String content;
    private List<String> properties;

    public ChoFile(String filename) {
        this.filename = filename;
        this.properties = new ArrayList<>();
        read();
    }

    public void read() {
        try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
            StringBuilder sb = new StringBuilder();
            String line;
            while (line = br.readLine() != null) {
                sb.append(line).append("\n");
            }
            content = sb.toString();
            Pattern pattern = Pattern.compile("\\{(.*?)\\}");
            Matcher matcher = pattern.matcher(content);
            while (matcher.find()) {
                properties.add(matcher.group(1));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void printProperties() {
        for (String prop : properties) {
            System.out.println(prop);
        }
    }

    public void write() {
        try (BufferedWriter bw = new BufferedWriter(new FileWriter(filename))) {
            bw.write(content);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void addProperty(String directive) {
        content += "{" + directive + "}\n";
        properties.add(directive);
    }

    // Example usage:
    // public static void main(String[] args) {
    //     ChoFile cho = new ChoFile("example.cho");
    //     cho.printProperties();
    //     cho.addProperty("new_directive: value");
    //     cho.write();
    // }
}

This class uses regex to parse directives, prints them to console, and supports writing.

6. JavaScript Class for .CHO File

The following JavaScript class can open (using FileReader), decode, read, write (using Blob for download), and print all properties from a .CHO file to console.

class ChoFile {
    constructor() {
        this.content = '';
        this.properties = [];
    }

    read(file) {
        return new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.onload = (e) => {
                this.content = e.target.result;
                this.properties = this.content.match(/\{(.*?)\}/g) || [];
                this.properties = this.properties.map(p => p.replace(/[\{\}]/g, '').trim());
                resolve(this.properties);
            };
            reader.onerror = reject;
            reader.readAsText(file);
        });
    }

    printProperties() {
        this.properties.forEach(prop => console.log(prop));
    }

    write(filename = 'output.cho') {
        const blob = new Blob([this.content], { type: 'text/plain' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = filename;
        a.click();
        URL.revokeObjectURL(url);
    }

    addProperty(directive) {
        this.content += `{${directive}}\n`;
        this.properties.push(directive);
    }
}

// Example usage:
// const cho = new ChoFile();
// const input = document.getElementById('fileInput'); // Assume <input type="file" id="fileInput">
// input.addEventListener('change', async (e) => {
//     await cho.read(e.target.files[0]);
//     cho.printProperties();
//     cho.addProperty('new_directive: value');
//     cho.write();
// });

This class parses directives, prints to console, and triggers a file download for writing.

7. C "Class" for .CHO File

Since C does not support classes natively, the following uses a struct with functions to open, decode, read, write, and print all properties from a .CHO file to console.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_PROPERTIES 100
#define MAX_PROP_LENGTH 256
#define MAX_CONTENT 10000

typedef struct {
    char *filename;
    char content[MAX_CONTENT];
    char properties[MAX_PROPERTIES][MAX_PROP_LENGTH];
    int prop_count;
} ChoFile;

void cho_init(ChoFile *cho, const char *filename) {
    cho->filename = strdup(filename);
    cho->prop_count = 0;
    memset(cho->content, 0, MAX_CONTENT);
    memset(cho->properties, 0, sizeof(cho->properties));
}

void cho_read(ChoFile *cho) {
    FILE *fp = fopen(cho->filename, "r");
    if (fp) {
        fread(cho->content, 1, MAX_CONTENT - 1, fp);
        fclose(fp);
    }

    // Simple parsing for directives (basic, not full regex)
    char *ptr = cho->content;
    while ((ptr = strstr(ptr, "{")) != NULL) {
        char *end = strstr(ptr, "}");
        if (end) {
            int len = end - ptr - 1;
            if (len > 0 && cho->prop_count < MAX_PROPERTIES) {
                strncpy(cho->properties[cho->prop_count], ptr + 1, len);
                cho->properties[cho->prop_count][len] = '\0';
                cho->prop_count++;
            }
            ptr = end + 1;
        } else {
            break;
        }
    }
}

void cho_print_properties(ChoFile *cho) {
    for (int i = 0; i < cho->prop_count; i++) {
        printf("%s\n", cho->properties[i]);
    }
}

void cho_write(ChoFile *cho) {
    FILE *fp = fopen(cho->filename, "w");
    if (fp) {
        fwrite(cho->content, 1, strlen(cho->content), fp);
        fclose(fp);
    }
}

void cho_add_property(ChoFile *cho, const char *directive) {
    strcat(cho->content, "{");
    strcat(cho->content, directive);
    strcat(cho->content, "}\n");
    if (cho->prop_count < MAX_PROPERTIES) {
        strcpy(cho->properties[cho->prop_count++], directive);
    }
}

void cho_free(ChoFile *cho) {
    free(cho->filename);
}

// Example usage:
// int main() {
//     ChoFile cho;
//     cho_init(&cho, "example.cho");
//     cho_read(&cho);
//     cho_print_properties(&cho);
//     cho_add_property(&cho, "new_directive: value");
//     cho_write(&cho);
//     cho_free(&cho);
//     return 0;
// }

This implementation uses basic string searching for parsing, prints to console, and supports writing.