Orgs in Java

Discussion in 'Music' started by Wedge of Cheese, Oct 13, 2010.

Old topic!
The last post in this thread is over 60 days old. Posting in this thread will be considered a bump, so please make an attempt to be courteous if you go ahead with it.

If the last post is over 6 months old, it may instead be a better idea to start a new topic. If you aren't sure about what to do, feel free to ask a staff member for help, or try to locate a 'general questions'-type thread if it exists in this (sub-)forum.
  1. Oct 13, 2010
    Wedge of Cheese
    If you snoose, you loose
    "Heavy swords for sale. Suitable for most RPG Protagonists. Apply now!"
    Join Date: Jul 2, 2008
    Location: trapped in mspaint
    Posts: 1885
    Age: 22
    So I made a thingy in case there are any Java programmers out there interested in giving their Java programs the ability to play *.org files.

    Java organya player
    Source code

    This version is hopefully final.

    Also, I figure I might as well post this here for convenience:
    Notes on Organya playback details by Bavi_H



    The format of the "orgsamp.dat" file:

    Code:
    All integer values are unsigned big-endian,
    except sample frames, which are signed (2s complement).
    
    1 byte: number of melody samples (100)
    3 bytes: number of sample frames per melody sample (256)
    for each melody sample:
    for each sample frame:
    1 byte: sample frame
    1 byte: number of drum samples (28)
    2 bytes: sampling rate of drum samples in Hertz at lowest non-zero frequency (2205)
    for each drum sample:
    3 bytes: number of sample frames
    for each sample frame:
    1 byte: sample frame


    The *.org format specs:

    Code:
    All integer values are unsigned little-endian.
    A "click" is the smallest unit of time in an org file.
    
    6 bytes: ascii string "Org-02" (or "Org-03" if the file uses percussion instruments only available in orgmaker 2.05)
    2 bytes: "wait" value (the length of a click in milliseconds)
    1 byte: beats per measure
    1 byte: clicks per beat
    4 bytes: position of the loop start, in clicks (the first click being position 0)
    4 bytes: position of the loop end, in clicks
    for each track:
    2 bytes: "freq" value*
    1 byte: instrument
    1 byte: 1 if "pi" checkbox is checked, 0 otherwise*
    2 bytes: number of resources
    for each track:
    for each resource:
    4 bytes: position of the resource, in clicks
    for each resource:
    1 byte: note (0=lowest note, 45=A440, 95=highest note, 255=no change)
    for each resource:
    1 byte: duration (in clicks, I believe this is ignored if note value is "no change")
    for each resource:
    1 byte: volume (0=silent, 200=default, 254=max, 255=no change)
    for each resource:
    1 byte: pan (0=full left, 6=center, 12=full right, 255=no change)
    
    *Even though orgmaker only allows you to edit these for melody tracks, percussion tracks also have this data, 
    with the default values of freq=1000, pi=0.
    I haven't tested to see if modifying these has any effect on playback.
     
  2. Oct 14, 2010
    Carrotlord
    Not anymore
    "Run, rabbit run. Dig that hole, forget the sun."
    Join Date: Jan 28, 2010
    Location: Internet
    Posts: 1368
    Age: 25
    Some cool stuff here.

    It's nice that Pixel's music formats are more available in other mediums.
     
  3. Nov 13, 2010
    CapFuture
    Neophyte Member
    "Fresh from the Bakery"
    Join Date: Nov 13, 2010
    Location:
    Posts: 4
    First of all thanks for the Java version :D

    Unfortunately there is some kind of bug when loading Wanpak2.org aka Scorching Back: An ArrayIndexOutOfBoundsException happens in the constructor of Organya...

    Code:
    this.data = new int[14][this.songLen];
    this.retrig = new boolean[14][this.songLen];
    
    for (int i = 0; i < 14; i++) {
    int volume = 0, hold = 0, pan = 0;
    
    for (int j = 0; j < this.tracksizes[i]; j++) {
    orgStream.read(stuff, 0, 4);
    
    final int time = unsign(stuff[0]) + 256 * stuff[1];
    try {
    this.data[i][time] = 1;
    } catch (ArrayIndexOutOfBoundsException e) {
    e.printStackTrace();
    }
    }
    
    ...
    }
    

    time can be >= than this.data.length, why so ever...

    Unfortunately I don't know a lot about the org format and neither hacking the org file to set the "correct" song length nor only allowing times which are smaller than this.data.length solved the problem...

    Any hint where the problem lies?
     
  4. Nov 13, 2010
    Wedge of Cheese
    If you snoose, you loose
    "Heavy swords for sale. Suitable for most RPG Protagonists. Apply now!"
    Join Date: Jul 2, 2008
    Location: trapped in mspaint
    Posts: 1885
    Age: 22
    Ah, good catch there.

    replace this:
    Code:
    data[i][unsign(stuff[0])+256*stuff[1]]=1;
    with this:
    Code:
    int time=unsign(stuff[0])+256*stuff[1];
    if(time
     
  5. Nov 14, 2010
    CapFuture
    Neophyte Member
    "Fresh from the Bakery"
    Join Date: Nov 13, 2010
    Location:
    Posts: 4
    Ok, that's the same fix that I've applied ^^

    But it causes another bug in getSample():
    Just as the bug before the samp2 might try to access the melody array where it's not defined... I fixed it with another check which solved the problem.

    Here's the code:
    Code:
    double samp2 = 0.0;
    
    if (j < 8) {
    final int pos = 256 * this.instruments[j] + (int) (256 * ((this.tpos[j] + 1) % 256));
    
    if (pos < this.melody.length) {
    samp2 = this.melody[pos];
    }
    } else {
    samp2 = (this.tpos[j] + 1 < this.drums[j - 8].length) ? unsign(this.drums[j - 8][(int) this.tpos[j] + 1]) - 128 : 0.0;
    }
    

    Finally the Scorching Back playback works like a charm :)
     
  6. Nov 14, 2010
    Wedge of Cheese
    If you snoose, you loose
    "Heavy swords for sale. Suitable for most RPG Protagonists. Apply now!"
    Join Date: Jul 2, 2008
    Location: trapped in mspaint
    Posts: 1885
    Age: 22
    Actually, looking back at that code, it looks like I messed up the parenthesis in that line, and the line after that (which does interpolation) was wrong too. I fail at life apparently...

    Code:
    double samp2=j
     
  7. Nov 14, 2010
    CapFuture
    Neophyte Member
    "Fresh from the Bakery"
    Join Date: Nov 13, 2010
    Location:
    Posts: 4
    Ahh... ok

    But I also think that it sounds better without the interpolation than with it :p

    When I find some time I'll programm a little gui for a more convenient player ^^

    Thanks alot for your quick fixes. :)
    After the java xm player went offline I had no org player which runs natively under OSX. But this is even better *likes source code*
     
  8. Nov 18, 2010
    CapFuture
    Neophyte Member
    "Fresh from the Bakery"
    Join Date: Nov 13, 2010
    Location:
    Posts: 4
    Hi again

    I created a little gui using Wedge's playback-engine.
    If the tune list is empty just press play and you can select any number of tracks from a single directory to the list. To empty the list later on press eject. Everything else is self-explanatory.

    Currently you cannot remove single tunes or mix them from different directories or save playlists, etc...

    http://www.mediafire.com/?779x6o6wx2x7l1x

    The zip file contains a runnable jar as well as the "wave100" file which contains the samples for the melody and drum instruments (see first post). Both have to be in the same directory or otherwise you won't here anything.

    The app ist Java 1.5 complient and don't expect miracles from the app ^^
     
  9. Nov 18, 2010
    Wedge of Cheese
    If you snoose, you loose
    "Heavy swords for sale. Suitable for most RPG Protagonists. Apply now!"
    Join Date: Jul 2, 2008
    Location: trapped in mspaint
    Posts: 1885
    Age: 22
    Ah I think I figured out the best way to handle the interpolation. Use it for drum tracks, but not for melody tracks. So say like double samp=samp1; if(j>=8) samp+=all that other interpolation stuff; If you play a song that has low percussion, it sounds really bad without interpolation.
     
  10. Nov 18, 2010
    Lace
    Lesbian Seagull
    "Life begins and ends with Nu."
    Join Date: Jan 4, 2008
    Location: Hunky Dory
    Posts: 3050
    This is sorta random, but could you port this to the far lesser language of C?
    Or make a dll?


    Haven't looked at the code yet, but I'm sure it'll be interesting.
    Good work bud.
     
  11. Nov 18, 2010
    Wedge of Cheese
    If you snoose, you loose
    "Heavy swords for sale. Suitable for most RPG Protagonists. Apply now!"
    Join Date: Jul 2, 2008
    Location: trapped in mspaint
    Posts: 1885
    Age: 22
    Eh, syntactically C is very similar to Java, so you could probably just do it yourself. Or you could ask Noxid, because he's trying to edit his C++ pxtone player to play orgs as well based on the code I sent him (with comments). Or I could forward you said commented code, in fact I'll go do that now.
     
  12. Nov 18, 2010
    Lace
    Lesbian Seagull
    "Life begins and ends with Nu."
    Join Date: Jan 4, 2008
    Location: Hunky Dory
    Posts: 3050
    ja I know they're close. (Laze triumphing over Lace atm)
    And tharnks.
     
  13. Dec 17, 2011
    Wedge of Cheese
    If you snoose, you loose
    "Heavy swords for sale. Suitable for most RPG Protagonists. Apply now!"
    Join Date: Jul 2, 2008
    Location: trapped in mspaint
    Posts: 1885
    Age: 22
    Alright, I'm going to occasionally update the first post of this thread with stuff over the course of the next week or so. Starting with the .org format specs.
     
  14. Dec 18, 2011
    Bavi_H
    Senior Member
    "Master using it, and you can have this!"
    Join Date: Jul 11, 2009
    Location: Texas, USA
    Posts: 78
    When you compose in OrgMaker version 2:
    * If you only use drums available in OrgMaker version 1 (Bass01 to Tom02), the file will begin with "Org-02".
    * If you use any drums that are new in OrgMaker version 2 (Bass04 to Cat), the file will begin with "Org-03".
     
  15. Dec 18, 2011
    Wedge of Cheese
    If you snoose, you loose
    "Heavy swords for sale. Suitable for most RPG Protagonists. Apply now!"
    Join Date: Jul 2, 2008
    Location: trapped in mspaint
    Posts: 1885
    Age: 22
    Ah, thanks. Updated first post.
     
  16. Dec 19, 2011
    Noxid
    Pretty Skater in a Sailor Suit
    "Life begins and ends with Nu."
    Join Date: Aug 28, 2009
    Location: space or some shit
    Posts: 5188
    Are you sure this isn't 4 bytes?
    I'm pretty sure this is 4 bytes.

    also
    I think the duration is used in "No Change" notes for continuing a note if its length extends beyond 0xFF ticks.
     
  17. Dec 19, 2011
    Wedge of Cheese
    If you snoose, you loose
    "Heavy swords for sale. Suitable for most RPG Protagonists. Apply now!"
    Join Date: Jul 2, 2008
    Location: trapped in mspaint
    Posts: 1885
    Age: 22
    Ah yes, you're right. First post fixed.
     
  18. Dec 19, 2011
    Noxid
    Pretty Skater in a Sailor Suit
    "Life begins and ends with Nu."
    Join Date: Aug 28, 2009
    Location: space or some shit
    Posts: 5188
    So I'm a bit confused; at first I thought this meant
    Code:
    [track1]
    [track 1 event 1]
    [t1 e1 note]
    [t1 e1 duration]
    [t1 e1 volume]
    [t1 e1 pan]
    [track 1 event 2]
    [t1 e2 note]
    [t1 e2 duration]
    [t1 e2 volume]
    [t1 e2 pan]
    [track 1 event 3]
    [t1 e3 note]
    [t1 e3 duration]
    [t1 e3 volume]
    [t1 e3 pan]
    ...
    [track 1 event n]
    [t1 en note]
    [t1 en duration]
    [t1 en volume]
    [t1 en pan]
    [track 2]
    [track 2 event 1]
    ...
    but looking at some of the data it seems that
    Code:
    [track 1]
    [t1 e1 note]
    [t1 e2 note]
    [t1 e3 note] 
    ...
    [t1 en note]
    [t1 e1 duration]
    [t1 e2 duration]
    [t1 e3 duration]
    ...
    [track 2]
    [t2 e1 note]
    [t2 e2 note]
    ...
    might be more likely. Clarification would be appreciated.
     
  19. Dec 19, 2011
    Wedge of Cheese
    If you snoose, you loose
    "Heavy swords for sale. Suitable for most RPG Protagonists. Apply now!"
    Join Date: Jul 2, 2008
    Location: trapped in mspaint
    Posts: 1885
    Age: 22
    Fixed.

    Also, so people who weren't on IRC don't get confused, I've experimentally verified that the org format does not allow notes longer than 255 clicks.
     
  20. Dec 21, 2011
    Bavi_H
    Senior Member
    "Master using it, and you can have this!"
    Join Date: Jul 11, 2009
    Location: Texas, USA
    Posts: 78
    Pi and Freq

    When I examined what recordings of melodic OrgMaker notes looked like in Audacity, I found that when the Pi (short for pizzicato?) checkbox is enabled, OrgMaker outputs a specific number of wave periods in each OrgMaker octave.

    Code:
      OrgMaker
    octave  periods
    =======  =======
    0      4
    1      8
    2     12
    3     16
    4     20
    5     24
    6     28
    7     32
    In other words, octave 0 starts with 4 periods, each additional octave adds 4 periods.

    Terminology reminder: In a periodic wave, the period is the length before the wave repeats. For example, if you use a sine wave, one period is one cycle of the sine wave.

    I mainly tested with the English version of OrgMaker v2, I think I tested with other versions, but can't remember. I think I remember noticing that clicking on the piano keys and the track buttons also used the same number of periods, so OrgMaker might use the same method for emiting those notes as it does for Pi notes. (The track buttons use OrgMaker note C3 = middle C.) Edit: Tested again now, they're not.

    Freq is more complicated to explain my testing methods that prove how it works. (Update: Posted OrgMaker Notes - Pitch.) Here's what I've found: In OrgMaker octave 3 (the octave with middle C and A440) the pitch frequency (periods per second) of the OrgMaker notes are:

    Code:
    C    ( 33408 + (f-1000) ) / 128
    C#   ( 35584 + (f-1000) ) / 128
    D    ( 37632 + (f-1000) ) / 128
    D#   ( 39808 + (f-1000) ) / 128
    E    ( 42112 + (f-1000) ) / 128
    F    ( 44672 + (f-1000) ) / 128
    F#   ( 47488 + (f-1000) ) / 128
    G    ( 50048 + (f-1000) ) / 128
    G#   ( 52992 + (f-1000) ) / 128
    A    ( 56320 + (f-1000) ) / 128
    A#   ( 59648 + (f-1000) ) / 128
    B    ( 63232 + (f-1000) ) / 128
    f is the Freq number for the track (range 100 to 1900, default 1000).
    Each time you go up an octave, the denominator halves (the resulting pitch frequencies double).
    Each time you go down an octave, the denominator doubles (the resulting pitch frequencies half).
    In all octaves, the numerators stay the same.

    __________

    When I found cavestory.org, I was immediately fascinated by OrgMaker and the ORG file formats. In my first post here, I chipped in about how MIDI files store tempo and mentioned I was making a ORG to MIDI converter of my own.

    I did made decent start on a ORG to MIDI converter, but the code is very sloppy (basically one big main function), and I never added all the features I wanted to. While making the ORG to MIDI converter, and at various times since then, I've been testing OrgMaker outputs by making test recordings, and recently by using OllyDbg to poke around.

    I'm not sure if I'm comfortable releasing my sloppy ORG to MIDI code, but I have been intermittently drafing up my tests and findings for a while, because I wanted to at least share what I've found so others can make various converters and things. Wedge of Cheese's recent posts have made me interested in writing up these findings again.