Code Monkey home page Code Monkey logo

muse2pokecrystal's People

Contributors

chillenb avatar hyperdriveguy avatar nephitejnf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

muse2pokecrystal's Issues

Optimize ASM Output

The with the output of even some of the most simple and repetitive songs being so long, it would be really nice if we implemented some kind of algorithm to reduce bloat.

  • Rest optimization (Done in #14)
  • Branched channels/callchannel optimization
  • Loopchannel optimization

First of all, output of sequential rests are 1:1 with input. Musescore does not automatically compress rests when they are deleted. This means that we get a lot of output like this for example:

note __, 2
note __, 2
note __, 2
note __, 2
note __, 1
note __, 8

This can be optimized to:

note __, 16
note __, 1

This optimization alone would save several bytes. The length just can't exceed 16.

There is also a much more daunting optimization using callchannel that could potentially save even more space. My proposed algorithm would happen post-conversion, be toggled by a flag, and likely be in a separate file:

  1. The raw asm output is read into an array.
  2. The algorithm selects at least 4 or 5 notes/octaves/commands and stores them in a new two dimensional array: the search array.
  3. The file array is copied to a backup array.
  4. It iterates through the file array comparing it to the active array within the search array. If a match is found, the algorithm replaces the matching lines with a line storing the word "break" followed by the index of the search array.
  5. If there is more than one match for the searched block the algorithm overwrites the current index of the search array, increasing it by one line.
  6. If the first or second search yields less matches or only one match, the search block is shifted either back to its previous position or iterates forward. The backup array is restored if it reverts to the previous position.
  7. After the entire file is iterated through, each "break" in the file array is given a matching label, in the format of callchannel Music_<SongName>_Branch<index>. The search array is formatted and appended to the array as well.
  8. The file array is written to the output.

This one is gonna be while before its properly implemented.

Parse non-note commands

Parsing non-note commands by outputting the text on a specific note right before it would greatly reduce the amount of work required to find notes that need command changes (ie duty cycles and stereopanning). This also has potential to enable converting xml at pokecrystal build time for even faster deployment.

Proper handling of chords

Currently, Muse2pokecrystal will simply throw an error if any chords exist in the same channel.
There are a couple alternative ways to handle this:

Algorithm 1: Ignore all notes that have the <chord/> element

Pros:

  • Easy to implement

Cons:

  • Only the bottom notes of a chord lack the <chord/> element
    • This may not sound pleasing
    • May result in redundant notes depending upon input
    • Probably will delete the melody in most songs
  • Just not as cool

Algorithm 2: Ignore all notes that aren't the melody

Pros:

  • Sounds exactly as intended in many cases
  • Not too complex an algorithm
  • Ideal for channel 2

Cons:

  • Doesn't work as well for channels 1, 3, and 4
  • Poor option for chords that are purely meant to harmonize

Algorithm 3: Randomly choose a note from the chord

Two different modes:

  • One that excludes the top notes (user says it's the melody)
  • One that includes the entire chord (all harmony)

Pros:

  • More variety in songs
  • May work well for channels 1 and 3 (maybe 4?)
  • Generally cool concept

Cons:

  • Doesn't always produce desirable result due to being, well, random
  • Seeds will need to be saved if the output is to be reproduced (probably to a temporary file by default)
  • Relatively complex

Algorithm 4: Intelligent selection (Spanning across channels)

Explanation:

Note: I really dislike the idea of having to parse something spoopy like this. What will likely happen instead is there will still be a placeholder command and a separate chord list (at this point, likely a three dimensional one which is...not ideal) and an index that points to the placeholder.
Pre-process the notes and commands adding placeholders for notes formatted like so:
[[[list_index], [note, octave, length, tie_start_or_end], [note, octave, length, tie_start_or_end], ...], ...]
For example:
[[[4], ['A_', 4, 4, None], ['F#', 5, 8, 'start'], ...], ...]
Apply algorithm 2 for channel 2 only.
Apply algorithm 3 with weights based on what other notes exist in other channels. Prioritize unique notes.
Apply algorithm 1 to channel 4. Don't use too many different seeds.

Pros:

  • It can take a lot of junk and output something decent
  • Balanced and ideal mix of algorithms
  • Handles every channel

Cons:

  • This thing is likely a hefty amount of complex code.
  • Difficult to understand and implement correctly
  • Three dimensional lists are realllly gross if supported at all (possible to work around)
  • Tied notes are gonna be hard
  • Might as well make a neural network

Algorithm 5: Intelligent selection (single channel)

Simple version of algorithm 4 without the wacky list.

Pros:

  • Fairly resilient
  • Still pretty balanced
  • Per channel handling

Cons:

  • Still a fairly complex algorithm
  • Harder to weight values

I will try and see if I can implement an algorithm similar to algorithm 5. Ideally, the functionality of 4 should still be the target but it's a lot of work for very little gain.

Refactor and Cleanup

Before I get around to documenting my new changes, it's worth mentioning that he code is currently pretty messy and a little redundant in some places. Hopefully this will make the code smaller, more readable, and less prone to errors.

Tasks

  • Split up code into more digestible pieces
    • Split out most of the text
    • Extract reusable data
    • Refactor all of the channel parsing into a single class
    • Refactor process_score into a class
    • Related functions in their own files
  • More breakpoints and smart rerunning (affects user defined loops)
  • Get rid of unnecessary parameters in functions
  • Conform to most of the PEP standards (note_print says hi)

Custom loops are parsed incorrectly with optimizations

This sometimes can be worked around with the -x parameter.
However, I have made an edge case. Lucky me.
Flags:

python muse2pokecrystal.py /tmp/He_Approaches.musicxml ../polishedcrystal/audio/music/lookyoungster.asm -c defaultsong.ini -n "Look Youngster" -flCx --noiseless

Configuration (close to default):

[Channel 1]
volume = $77
notetype = $c
intensity = $97
dutycycle = $1
tone =
stereopanning = $0f
vibrato = false
vibrato_delay = $00
vibrato_extent = $00
;custom_commands =


[Channel 2]
notetype = $c
intensity = $a7
dutycycle = $2
tone =
stereopanning = $f0
vibrato = false
vibrato_delay = $00
vibrato_extent = $00
;custom_commands =


[Channel 3]
notetype = $c
intensity = $19
tone =
stereopanning = $ff
vibrato = false
vibrato_delay = $00
vibrato_extent = $00
;custom_commands =


[Channel 4]
notetype = $c
togglenoise = 1
stereopanning = $ff
;custom_commands =

The script output here exhibits issues #22 and #23 as well.

Music_LookYoungster:
	musicheader 3, 1, Music_LookYoungster_Ch1
	musicheader 1, 2, Music_LookYoungster_Ch2
	musicheader 1, 3, Music_LookYoungster_Ch3


Music_LookYoungster_Ch1:
	tempo 320
	volume $77
	notetype $c, $97
	dutycycle $1
	stereopanning $0f
	note __, 8
	note __, 8
	note __, 8
	note __, 8

Music_LookYoungster_Ch1_Loop:
	octave -1
	note __, 8
	note __, 8
	note __, 8
	note __, 8
	octave 3
	note E_, 1
	note E_, 1
	note G_, 1
	note E_, 1
	note A#, 2
	note B_, 2
	note E_, 1
	note E_, 1
	note G_, 1
	note E_, 1
	note A#, 2
	note B_, 2
	note E_, 1
	note E_, 1
	note G_, 1
	note E_, 1
	note A#, 2
	note B_, 2
	octave 4
	note C_, 2
	octave 3
	note B_, 2
	note A#, 2
	note B_, 2
	jumpchannel Music_LookYoungster_Ch1_Loop


Music_LookYoungster_Ch2:
	notetype $c, $a7
	dutycycle $2
	stereopanning $f0
	note __, 8
	note __, 8
	note __, 8
	note __, 8

Music_LookYoungster_Ch2_Loop:
	octave -1
	octave 3
	note B_, 1
	octave 3
	note A#, 1
	octave 3
	note B_, 1
	octave 3
	note A#, 1
	octave 4
	note D_, 1
	note E_, 1
	note D_, 1
	note E_, 1
	octave 3
	note B_, 1
	octave 3
	note A#, 1
	octave 3
	note B_, 1
	octave 3
	note A#, 1
	octave 4
	note D_, 1
	note E_, 1
	note D_, 1
	note E_, 1
	octave 3
	note B_, 1
	octave 3
	note A#, 1
	octave 3
	note B_, 1
	octave 3
	note A#, 1
	octave 4
	note D_, 1
	note E_, 1
	note D_, 1
	note E_, 1
	octave 3
	note G_, 2
	note E_, 2
	note F#, 2
	note E_, 2
	octave 4
	note B_, 1
	octave 4
	note A#, 1
	octave 4
	note B_, 1
	octave 4
	note A#, 1
	octave 5
	note D_, 1
	note E_, 1
	note D_, 1
	note E_, 1
	octave 4
	note B_, 1
	octave 4
	note A#, 1
	octave 4
	note B_, 1
	octave 4
	note A#, 1
	octave 5
	note D_, 1
	note E_, 1
	note D_, 1
	note E_, 1
	octave 4
	note B_, 1
	octave 4
	note A#, 1
	octave 4
	note B_, 1
	octave 4
	note A#, 1
	octave 5
	note D_, 1
	note E_, 1
	note D_, 1
	note E_, 1
	octave 4
	note G_, 2
	note E_, 2
	note F#, 2
	note E_, 2
	jumpchannel Music_LookYoungster_Ch2_Loop


Music_LookYoungster_Ch3:
	notetype $c, $19
	octave 2
	note E_, 1
	note E_, 1
	note G_, 1
	note E_, 1
	note A#, 2
	note B_, 2
	note E_, 1
	note E_, 1
	note G_, 1
	note E_, 1
	note A#, 2
	note B_, 2
	note E_, 1
	note E_, 1
	note G_, 1
	note E_, 1
	note A#, 2
	note B_, 2
	octave 3
	note C_, 2
	octave 2
	note B_, 2
	note A#, 2
	note B_, 2

Music_LookYoungster_Ch3_Loop:
	octave 2
	note E_, 1
	note E_, 1
	note G_, 1
	note E_, 1
	note A#, 2
	note B_, 2
	note E_, 1
	note E_, 1
	note G_, 1
	note E_, 1
	note A#, 2
	note B_, 2
	note E_, 1
	note E_, 1
	note G_, 1
	note E_, 1
	note A#, 2
	note B_, 2
	octave 3
	note C_, 2
	octave 2
	note B_, 2
	note A#, 2
	note B_, 2
	note E_, 1
	note E_, 1
	note G_, 1
	note E_, 1
	note A#, 2
	note B_, 2
	note E_, 1
	note E_, 1
	note G_, 1
	note E_, 1
	note A#, 2
	note B_, 2
	note E_, 1
	note E_, 1
	note G_, 1
	note E_, 1
	note A#, 2
	note B_, 2
	octave 3
	note C_, 2
	octave 2
	note B_, 2
	note A#, 2
	note B_, 2
	jumpchannel Music_LookYoungster_Ch3_Loop


Music_LookYoungster_Ch4:
	notetype $c
	togglenoise 1
	note __, 8
	note __, 8
	note __, 8
	note __, 8

Music_LookYoungster_Ch4_Loop:
	octave -1
	note __, 8
	note __, 8
	note __, 8
	note __, 8
	note __, 8
	note __, 8
	note __, 8
	note __, 8
	jumpchannel Music_LookYoungster_Ch4_Loop

MusicXML: https://cdn.discordapp.com/attachments/706331639650844672/714368456442511410/He_Approaches.musicxml

Account for musicxml note length behavior better

The Issue

The tempo formula (19200 / bpm) assumes that a note size of 1 in the note macro is equivalent to a 1/16 note. However, this is note always the case. Let's take a look at a part of the Template.musicxml in the repo:

    <measure number="7" width="57.54">
      <note>
        <rest/>
        <duration>4</duration>
        <voice>1</voice>
        </note>
      </measure>

The most important thing to notice here is the <duration> tag. We can see that the rest spans the entire measure; this is proven both by the fact of it being the only child <note> and when opening the file in Musescore.

Currently, Muse2pokecrystal just takes the listed bpm, applies it to the set tempo formula and assumes that <duration>4</duration> is not a whole note.

The Fix

There is an xml tag called <divisions>, a child of the <attributes> tag. By default, this tag holds a value of 1. As I understand it, we can find the smallest note in a score by doing (1/4) / divisions. A value of 1 indicates that a quarter note is the smallest note in the entire score. A value of 4 means the smallest note is a sixteenth note.
A modified formula that takes this into account though you may want to check my algebra:

(19200 / bpm) * (4 / divisions)

Other Implications

While this slightly increases complexity of the tempo formula (it's getting it's own function in the refactor anyway), properly implementing this and pairing it with #12 will allow more complex songs with a much wider note length range.

More accurate drums

Input should at least better approximate to the output. I honestly know very little about the drumkits so this is gonna be a difficult thing to do. Main thing I've noticed is that having a bass drum + snare doesn't sound anything like it should.

Tutorial/Documentation

After #14, a lot of the docs have become obsolete, using old syntax and suggesting old workarounds. This needs to be updated at some point (probably once development switches from core Muse2pokecrystal to the proposed Musescore plugin).

Also, it's becoming resilient enough to where a tutorial would be beneficial for a lot of people.

License?

Lower priority, but we probably want to throw a license on this bad boy at some point (because we can).

My recommendation would be GPLv3 for code (maybe even AGPL...even though muse2pokecrystal isn't designed to run on a server at this point) and CC0 for the MusicXML template.

Support different music macros

There's a couple different macros formats that support could be added for.

New Pokecrystal

New Pokecrystal changes a lot of macros. While some improve readability significantly, others greatly depart from the methods currently used to parse (looking at you drum_note). This will help a lot with future proofing songs.

Pokered

Most of the macros are identical to new Pokecrystal at first glance. Honestly support for Pokered will likely happen by freak accident.

Prism

Prism just changes the music headers:

Music_Song:
	channelcount 3
	channel 1, Music_Song_Ch1
	channel 2, Music_Song_Ch2
	channel 3, Music_Song_Ch3

Easy to implement, the only difference between Prism headers and new Pokecrystal is channelcount macro is referred to as channel_count in new Pokecrystal.

Fake longer notes

By automatically setting the low intensity nibble to f muse2pokecrystal may be able to create an effect where when a note with a length longer than 16 will be split into multiple notes but still hold it's effect of sounding like a single note.

I have not extensively tested this behavior, but I may work it into the refactor.

Musescore Plugin

Musescore allows for plugins that are written in QML. Utilizing the plugin functionality, we could make exports more user friendly and have more precise error checking (especially errors due to chords or tuplets). Additionally (if I am not mistaken this could be impossible or out of scope), custom elements could be added in Musescore for sound engine commands via the plugin interface as well.

Tuplet handling

Other tools (TriteHexagon's Midi2ASM) now do triplet handling.
This handling is done using the notetype macro. I was previously under the impression that all channels have to have the same notetype length when being played, but I'm starting to realize that may not necessarily be the case if the lengths are divisible. This needs a bit more digging, I haven't tested a whole lot yet.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.