91 lines
6.5 KiB
C
91 lines
6.5 KiB
C
#define _POSIX_C_SOURCE 200809L
|
|
#include "xtc/xtc_midi_file.h"
|
|
#include "xtc/xtc_mixer.h"
|
|
#include "../../src/midi/output.h"
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
static unsigned int read_vlq(const unsigned char *data,size_t size,size_t *at)
|
|
{
|
|
unsigned int value=0;unsigned char byte;int n=0;
|
|
do{assert(*at<size && ++n<=4);byte=data[(*at)++];value=(value<<7)|(byte&127);}while(byte&128);
|
|
return value;
|
|
}
|
|
static void output_tests(void)
|
|
{
|
|
xtc_project *p=NULL;xtc_midi_output_plan *plan=NULL;uint64_t t,c,n;xtc_midi_event events[XTC_MIDI_MAX_EVENTS];size_t count=999;
|
|
assert(xtc_project_create(&p)==XTC_OK && xtc_project_add_track(p,"Output",&t)==XTC_OK);
|
|
assert(xtc_project_add_midi_clip(p,t,0,1920,&c)==XTC_OK);
|
|
for(size_t i=0;i<129;++i)assert(xtc_project_add_note(p,c,i*2,1,60,100,0,&n)==XTC_OK);
|
|
assert(xtc_midi_output_create(p,8000,&plan)==XTC_OK);
|
|
assert(xtc_midi_output_events(plan,0,4000,events,&count)==XTC_ERR_CAPACITY && count==999);
|
|
assert(xtc_midi_output_events(plan,0,1,events,&count)==XTC_OK && count==17 && events[16].type==XTC_MIDI_NOTE_ON);
|
|
assert(xtc_midi_output_events(plan,1,4,events,&count)==XTC_OK && count==1 && events[0].type==XTC_MIDI_NOTE_OFF && events[0].offset==3);
|
|
xtc_midi_output_destroy(plan);xtc_project_destroy(p);
|
|
}
|
|
static void write_bytes(const char *path,const unsigned char *data,size_t size)
|
|
{
|
|
FILE *f=fopen(path,"wb");assert(f && fwrite(data,1,size,f)==size && fclose(f)==0);
|
|
}
|
|
static void import_tests(void)
|
|
{
|
|
static const unsigned char song[]={
|
|
'M','T','h','d',0,0,0,6,0,1,0,2,1,0xe0,
|
|
'M','T','r','k',0,0,0,12,0,0xff,0x51,3,7,0xa1,0x20,0x87,0x40,0xff,0x2f,0,
|
|
'M','T','r','k',0,0,0,27,
|
|
0,0xc1,10,0,0xf0,2,1,0xf7,
|
|
0,0x91,60,90,0,64,80,0x81,0x70,60,0,0,64,0,0x81,0x70,0xff,0x2f,0
|
|
};
|
|
unsigned char bad[sizeof(song)];char path[]="/tmp/xtc-midi-import-XXXXXX";int fd=mkstemp(path);
|
|
xtc_project *p=NULL;uint64_t t,c=999;xtc_clip_info clip;xtc_note_info note;size_t tracks,clips,notes;
|
|
assert(fd>=0);close(fd);assert(xtc_project_create(&p)==XTC_OK && xtc_project_add_track(p,"Import",&t)==XTC_OK);
|
|
assert(xtc_project_set_tempo(p,135000)==XTC_OK);write_bytes(path,song,sizeof(song));
|
|
assert(xtc_project_import_midi(p,t,3840,path,&c)==XTC_OK && xtc_project_tempo(p)==135000);
|
|
assert(xtc_project_find_clip(p,c,&clip)==XTC_OK && clip.start_tick==3840 && clip.length_tick==1920);
|
|
assert(xtc_project_note_count(p,¬es)==XTC_OK && notes==2);
|
|
assert(xtc_project_get_note(p,0,¬e)==XTC_OK && note.start_tick==0 && note.length_tick==480 && note.channel==1 && note.pitch==60 && note.velocity==90);
|
|
assert(xtc_project_get_note(p,1,¬e)==XTC_OK && note.pitch==64 && note.length_tick==480 && note.velocity==80);
|
|
for(size_t size=0;size<sizeof(song);++size){
|
|
c=999;write_bytes(path,song,size);assert(xtc_project_import_midi(p,t,0,path,&c)!=XTC_OK && c==999);
|
|
assert(xtc_project_get_counts(p,&tracks,&clips)==XTC_OK && tracks==1 && clips==1 && xtc_project_note_count(p,¬es)==XTC_OK && notes==2);
|
|
}
|
|
memcpy(bad,song,sizeof(song));bad[12]=0xe7;write_bytes(path,bad,sizeof(bad));assert(xtc_project_import_midi(p,t,0,path,&c)==XTC_ERR_UNSUPPORTED);
|
|
memcpy(bad,song,sizeof(song));bad[9]=2;write_bytes(path,bad,sizeof(bad));assert(xtc_project_import_midi(p,t,0,path,&c)==XTC_ERR_UNSUPPORTED);
|
|
memcpy(bad,song,sizeof(song));bad[sizeof(bad)-6]=0x90;write_bytes(path,bad,sizeof(bad));assert(xtc_project_import_midi(p,t,0,path,&c)!=XTC_OK);
|
|
write_bytes(path,song,sizeof(song));assert(xtc_project_import_midi(p,t,XTC_PROJECT_MAX_TICK-10,path,&c)==XTC_ERR_CAPACITY && c==999);
|
|
{uint64_t bus;assert(xtc_project_add_bus(p,"Bus",&bus)==XTC_OK && xtc_project_import_midi(p,bus,0,path,&c)==XTC_ERR_INVALID_STATE);}
|
|
xtc_project_destroy(p);unlink(path);
|
|
}
|
|
int main(void)
|
|
{
|
|
xtc_project *p=NULL;uint64_t t,c,n,audio;char path[]="/tmp/xtc-midi-file-XXXXXX";int fd=mkstemp(path);FILE *f;unsigned char bytes[1024];size_t size,at=22,notes=0;unsigned int tick=0;
|
|
assert(fd>=0);close(fd);assert(xtc_project_create(&p)==XTC_OK && xtc_project_add_track(p,"Keys",&t)==XTC_OK);
|
|
assert(xtc_project_set_tempo(p,150000)==XTC_OK && xtc_project_add_midi_clip(p,t,1000,1920,&c)==XTC_OK);
|
|
assert(xtc_project_add_note(p,c,0,480,60,80,2,&n)==XTC_OK && xtc_project_add_note(p,c,480,960,64,100,3,&n)==XTC_OK);
|
|
assert(xtc_project_add_note(p,c,1600,100,72,70,0,&n)==XTC_OK);
|
|
assert(xtc_project_edit_clip(p,c,t,1000,960,240)==XTC_OK);
|
|
assert(xtc_project_export_midi_clip(p,c,path)==XTC_OK);
|
|
f=fopen(path,"rb");assert(f);size=fread(bytes,1,sizeof(bytes),f);assert(fclose(f)==0);
|
|
assert(size>22 && !memcmp(bytes,"MThd\0\0\0\6\0\0\0\1\3\300MTrk",18));
|
|
while(at<size){unsigned int status,len;tick+=read_vlq(bytes,size,&at);assert(at<size);status=bytes[at++];
|
|
if(status==0xff){unsigned int type=bytes[at++];len=read_vlq(bytes,size,&at);assert(len<=size-at);
|
|
if(type==0x51)assert(len==3 && bytes[at]==6 && bytes[at+1]==0x1a && bytes[at+2]==0x80);
|
|
if(type==0x2f)assert(!len && tick==960 && at==size);
|
|
at+=len;
|
|
}else{unsigned int expected_tick[]={0,240,240,960},statuses[]={0x92,0x82,0x93,0x83},pitches[]={60,60,64,64},velocities[]={80,0,100,0};
|
|
assert(notes<4 && tick==expected_tick[notes] && status==statuses[notes] && at+2<=size && bytes[at]==pitches[notes] && bytes[at+1]==velocities[notes]);at+=2;++notes;
|
|
}
|
|
}
|
|
assert(notes==4);
|
|
assert(xtc_project_add_clip(p,t,"Audio",3840,960,&audio)==XTC_OK && xtc_project_export_midi_clip(p,audio,path)==XTC_ERR_INVALID_STATE);
|
|
{unsigned char preserved[1024];f=fopen(path,"rb");assert(f && fread(preserved,1,sizeof(preserved),f)==size && !memcmp(bytes,preserved,size));fclose(f);}
|
|
assert(xtc_project_export_midi_clip(p,c,"/no-such-xtc-directory/test.mid")==XTC_ERR_IO);
|
|
{uint64_t imported;xtc_note_info note;xtc_clip_info clip;
|
|
assert(xtc_project_import_midi(p,t,7680,path,&imported)==XTC_OK && xtc_project_tempo(p)==150000);
|
|
assert(xtc_project_find_clip(p,imported,&clip)==XTC_OK && clip.length_tick==960 && !clip.offset_tick);
|
|
assert(xtc_project_get_note(p,3,¬e)==XTC_OK && note.start_tick==0 && note.length_tick==240 && note.pitch==60 && note.channel==2);
|
|
assert(xtc_project_get_note(p,4,¬e)==XTC_OK && note.start_tick==240 && note.length_tick==720 && note.pitch==64 && note.channel==3);}
|
|
xtc_project_destroy(p);unlink(path);output_tests();import_tests();puts("MIDI file and bounded external scheduling checks passed.");return 0;
|
|
}
|