ojn字节集
.版本 2' note总数
歌曲音符16 = 删首空 (字节集到十六进制文本 (取字节集中间 (文件字节集, 49, 2)))
数据处理1 = 分割文本 (歌曲音符16, “ ”, 2)
音符最终 = 十六进制到十进制 (数据处理1 + 数据处理1 )
这些数字位置即ue打开后的每行16个第一行1~16
第二行17~32
以此类推
struct header {
int songid; 00-03
char signature;03-07
float encode_version;08-11
int genre;12-15
float bpm;16-19
short level;20-27
int event_count;28-39
int note_count;40-51
int measure_count;52-63
int package_count;64-75
short old_encode_version;76-77
short old_songid;78-79
char old_genre;80-99
int bmp_size;100-103
int old_file_version;104-107
char title;108-171
char artist;172-203
char noter;204-235
char ojm_file;236-267
int cover_size;268-271
int time;272-283
int note_offset;284-295
int cover_offset;296-299
};
共300字节
int songid; 歌曲内部编号
char signature; 幻数。用来标记当前文件是否是ojn文件 通常为"ojn"即ojn\0
float encode_version;OJN版本 通常为2.9 没什么卵用
int genre;歌曲类型没什么卵用
以下为具体枚举值
Ballad
1 Rock
2 Dance
3 Techno
4 Hip-hop
5 Soul/R&B
6 Jazz
7 Funk
8 Classical
9 Traditional
10 Etc
float bpm; 初始BPM
short level;歌曲的3个等级只使用前3个 范围0~65535
int event_count; 表示3个难度,包括bpm列 note列 BGM列 总共23列(待确认) 在内的所有音符的总数
int note_count;不包括bpm和bgm音乐列在内的note总数(选歌界面上显示的note数)即实际弹奏区的note
int measure_count; 3个难度各自有多少小节数
int package_count; 标记包的数量 3难度,package代表一个小节中的某一列
short old_encode_version;
short old_songid;
char old_genre;这三个貌似是以前版本的ojn格式用来存放编码版本 歌曲编号 以及歌曲 风格 3个参数值的变量 并没有什么卵用
int bmp_size; 选歌面板上歌曲的小缩略图的文件大小 bmp格式的
int old_file_version; 文件版本 没卵用
char title;歌名
char artist; 作曲家名
char noter; noter名
char ojm_file; 相关联的ojm文件名,一般o2maxxxx.ojm
int cover_size; 歌曲封面文件大小jpg格式就是载入歌曲时的那个图
int time; 3 难度歌曲时长 单位是秒
int note_offset; 3难度各note数据部分基址相对ojn文件开头的偏移,见下面解释
int cover_offset; 同上 封面偏移 见下面解释
整个ojn文件结构:
{
header300 byte
dataeasy
datanormal
datahard
jpg载入界面图片
bmpbmp小预览图
}
sizeof(header) :0-300字节是描述歌曲信息的部分
note_offset: 这个值通常就是300 表示easy难度的数据区首地址
note_offset: normal数据区首地址
note_offset:hard难度首地址
cover_offset: jpg首地址
cover_offset+cover_size: bmp首地址
ojn whole file size: bmp尾地址
因此
easy数据区大小可以用note_offset - note_offset计算
其余同理
下面针对note数据区做说明
每个难度的数据区由
包(package) +包 + 包 + 包...... 组成(上面提到过
其中每个包又由描述头部分(package_header) 加数据区(note_event数组) 2个部分组成
因此又可以描述为
包(package_header+note_event[])+包(package_header+note_event[])+包(package_header+note_event[])...
struct package_header {
int32 measure; //标明这个package是属于第几小节线
short16 channel; //标明这个package是第几列的具体取值见下面
short16 events; // 标明这个package 使用的是多少格线.最大是192线(我已经测过).比如是64线那么package_header后面就会紧跟上一个note_event数组;
};
channel的取值:
0 measure fraction //nt里有一列可以调 一个小节要显示百分之多少就是这列和ibmsc里的小节线比例等同
1 BPM change //bpm 值
2 note on 1st lane
3 note on 2nd lane
4 note on 3rd lane
5 note on 4th lane(middle button)
6 note on 5th lane
7 note on 6th lane
8 note on 7th lane
9~22 auto-play samples //bgm列
注意的是 当channel为 0或1的时候 note_event是个float类型(4字节)
当channel为2-22时note_event类型(也是4字节)也就是package_header后面跟的数组实际解释类型取决于channel列的取值 可以把note_event和float看成一个union类型
每个note_event有4个字节,
当channel==0 类型为float 表示当前小节按多少百分比显示
当channel==1 类型为float 表示bpm值
当channel 为2-22表示实际的note此时结构如下(9-22为bgm列 自动弹奏note)
struct note_event {
short16 value;
half-char volume;
half-char pan;
char note_type;
};
value 表示Key音列表的索引值 比如这个值为17 那么key表索引值=17的地方如果有放一个
"abc.ogg"的音频key 那么击打时就会自动播放
volume; 播放key的音量,1-15,0为最大基本用不到 置0即可
pan;左右声道权重,1-7 左,0或8表示中间,9-15右 用不到 置0
note_type ; 为0时表示米粒,2表示LN的开头,3表示LN的结尾,4 不确定 可能表示当wav和ogg同时存在时 辨别是不是ogg用的
OJM 部分//待完成
struct M30_header {
char signature; // "M30"
int file_format_version;
int encryption_flag;
int sample_count;
int samples_offset;
int payload_size;
int padding;
};
ojm 的格式是什么?
我在这里查到一个 https://github.com/djzmo/render-ojn/blob/master/src/Nx/O2Jam/OJM.hpp
求详解
本帖最后由 ZackLee 于 2016-12-11 02:40 编辑
我来说明下 ojm 格式。
参考链接:https://open2jam.wordpress.com/the-ojm-documentation/
ojm 的整体格式为:M30_header + (M30_sample_header + 音乐数据区)*sample_count。
ojm 首先是一个 header,28字节:
struct M30_header {
char signature; // 固定字符串 "M30"
int file_format_version;//internal version of the OJM format.
int encryption_flag;// encrypted format: 1 scramble1; 2 scramble2; 4 decode; 8 decrypt; 16 nami
int sample_count;// the number of sound files inside the OJM(we don’t rely on this much)
int samples_offset;//where the data actually starts, usually after the header(28)
int payload_size;// the size of the rest of the file, usually total_file_size – 28(header), in bytes
int padding;
};
这部分结束后,每个 sample 还有一个 header 元信息,52字节:
struct M30_sample_header {
char sample_name;//
int sample_size;
short codec_code;
short unk_fixed;
int unk_music_flag;
short ref;
short unk_zero;
int pcm_samples;
};
其中最重要的就是 sample_size,因为之后每个 sample_size 都是一个独立的音乐数据区,只要抓取出来就是一个 key 音。
需要注意的是,如果 M30_header 的 encryption_flag 为 16,则说明文件使用了 nami 加密,此时需要解密 sample_size 所代表的音乐数据区:
for(int i=0; i+3 < data.length; i=i+4) {
data = 'n' XOR data;
data = 'a' XOR data;
data = 'm' XOR data;
data = 'i' XOR data;
}
若剩余数据不满足4个字符,或说字节,则忽略异或操作。
本帖最后由 ZackLee 于 2016-12-31 19:55 编辑
如果 signature 是“OMC”或“OJM”,那么存放音乐数据的方式为:
OMC_header + wav_count*(OMC_WAV_header + wave 数据) + ogg_count*(OMC_OGG_header + ogg 数据).
下面简单说明:
struct OMC_header {
char signature;// "OMC" or "OJM"
short wav_count;
short ogg_count;
int wav_start;
int ogg_start;
int filesize;
};
此元信息共 20 字节,结构非常清晰。
wav 就是 nt 中的 W###;
ogg 就是 nt 中的 M###。
count 就是实际的数量,filesize 就是整个文件的大小。
之后的数据就是 wav 数据和 ogg 数据。和 M30 相同,之前都有一个结构体保存元信息:
struct OMC_WAV_header {
char sample_name;
short audio_format;
short num_channels;
int sample_rate;
int bit_rate;
short block_align;
short bits_per_sample;
int unk_data;
int chunk_size;
};
其中最重要的就是 chunk_size,其后面字节数的数据就是 wav 数据。
OMC_header.wav_count 个 wav 数据后,就是 ogg 数据。
struct OMC_OGG_header {
char sample_name;
int sample_size;
};
其中 sample_size 就是后面跟着的 ogg 数据。
===========================
此外,ojm 的 ref + 1,才等于 ojn 中 note 的 value。
===========================
最后强调下:
W###(NT)即wav(OMC)即codec_code == 0(M30)即note_type == 其他任何值(ojn)。
M###(NT)即ogg(OMC)即codec_code == 5(M30)即note_type == 4(ojn)。 本帖最后由 ZackLee 于 2017-1-6 15:05 编辑
补充一下, 从 ojm 中提取 wav 格式的音乐时:
如果是 M30 格式,那么其 pcm_samples 是一个关键属性,它就是比特率。
如果为 OMC 格式,wav 的头信息可以从 OMC_WAV_header 获取,当然也包括比特率。
可以参考如下代码 https://github.com/open2jamorg/o ... mper/OJMDumper.java
其中的 225~240 行就是 OMC 格式的 wav 导出代码。
页:
[1]