Files
commandergenius/project/jni/application/fheroes2/src/engine/midi_mtrk.cpp
2010-11-12 17:37:35 +02:00

349 lines
8.1 KiB
C++

/***************************************************************************
* Copyright (C) 2009 by Andrey Afletdinov <fheroes2@gmail.com> *
* *
* Part of the Free Heroes2 Engine: *
* http://sourceforge.net/projects/fheroes2 *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <iomanip>
#include <functional>
#include <algorithm>
#include <iostream>
#include <string.h>
#include "midi_mtrk.h"
using namespace MIDI;
struct meta_t
{
meta_t() : command(0), quantity(0), duration(0){}
meta_t(u8 c, u8 q, u32 d) : command(c), quantity(q), duration(d){}
bool operator< (const meta_t & m) const{ return duration < m.duration; }
void decrease_duration(u32 delta) { duration -= delta; }
u8 command;
u8 quantity;
u32 duration;
};
MTrk::MTrk(const u8 *p, const u32 s)
{
const u8 *ptr = p;
bool end = false;
while(ptr && !end && ptr < (p + s))
{
u32 delta = 0;
const u8 s = MIDI::UnpackDelta(ptr, delta);
ptr += s;
const u8 status = *ptr;
ptr += 1;
switch(status >> 4)
{
// meta
case 0x0F:
{
u32 size = 0;
const u8 s = MIDI::UnpackDelta(ptr + 1, size);
if(0xFF == status && 0x2F == *ptr)
{
end = true;
events.push_back(new Event(delta, status, 1 + s + size, ptr));
}
ptr += 1 + s + size;
}
break;
// note off
case 0x08:
// note on
case 0x09:
// key pressure
case 0x0A:
// control change
case 0x0B:
// pitch bend
case 0x0E:
{
events.push_back(new Event(delta, status, 2, ptr));
ptr += 2;
}
break;
// program change
case 0x0C:
// chanel pressure
case 0x0D:
{
events.push_back(new Event(delta, status, 1, ptr));
ptr += 1;
}
break;
// unused command
default:
end = true;
CloseEvents();
std::cerr << "unknown st: 0x" << std::setw(2) << std::setfill('0') << std::hex << static_cast<int>(status) << ", ln: " << static_cast<int>(p + s - ptr) << std::endl;
break;
}
}
}
MTrk::MTrk(const MTrk & t)
{
std::list<Event *>::const_iterator it1 = t.events.begin();
std::list<Event *>::const_iterator it2 = t.events.end();
for(; it1 != it2; ++it1) if(*it1) events.push_back(new Event(**it1));
}
MTrk::~MTrk()
{
if(events.size())
{
std::list<Event *>::const_iterator it1 = events.begin();
std::list<Event *>::const_iterator it2 = events.end();
for(; it1 != it2; ++it1) if(*it1) delete *it1;
}
}
u32 MTrk::Size(void) const
{
u32 result = 8; // id + size
std::list<Event *>::const_iterator it1 = events.begin();
std::list<Event *>::const_iterator it2 = events.end();
for(; it1 != it2; ++it1) if(*it1) result += (*it1)->Size();
return result;
}
bool MTrk::Write(std::ostream & o) const
{
if(o.fail()) return false;
o.write(ID_MTRK, 4);
u32 size = 0;
std::list<Event *>::const_iterator it1 = events.begin();
std::list<Event *>::const_iterator it2 = events.end();
for(; it1 != it2; ++it1) if(*it1) size += (*it1)->Size();
u32 x = size;
SwapBE32(x);
o.write(reinterpret_cast<char *>(&x), 4);
if(events.size())
{
it1 = events.begin();
it2 = events.end();
for(; it1 != it2; ++it1) if(*it1) (*it1)->Write(o);
}
return true;
}
bool MTrk::Write(u8 *p) const
{
if(NULL == p) return false;
memcpy(p, ID_MTRK, 4);
p+= 4;
u32 size = 0;
std::list<Event *>::const_iterator it1 = events.begin();
std::list<Event *>::const_iterator it2 = events.end();
for(; it1 != it2; ++it1) if(*it1) size += (*it1)->Size();
u32 x = size;
WriteBE32(p, x);
p+= 4;
if(events.size())
{
it1 = events.begin();
it2 = events.end();
for(; it1 != it2; ++it1) if(*it1){ (*it1)->Write(p); p += (*it1)->Size(); }
}
return true;
}
void MTrk::AddEvent(const Event & e)
{
events.push_back(new Event(e));
}
void MTrk::CloseEvents(void)
{
events.push_back(new Event(0, 0xFF, 2, reinterpret_cast<const u8*>("\057\000")));
}
void MTrk::Dump(void) const
{
std::cerr << "[MTrk]\n";
if(events.size())
{
std::list<Event *>::const_iterator it1 = events.begin();
std::list<Event *>::const_iterator it2 = events.end();
for(; it1 != it2; ++it1) if(*it1) (*it1)->Dump();
}
std::cerr << std::endl;
}
void MTrk::ImportXmiEVNT(const Chunk & evnt)
{
const u8 *ptr = evnt.data;
u8 buf[2];
u32 delta = 0;
u32 delta2 = 0;
std::list<meta_t> notesoff;
std::list<meta_t>::iterator it1, it2;
while(ptr && ptr < (evnt.data + evnt.size))
{
// insert event: note off
if(delta)
{
// sort duration
notesoff.sort();
it1 = notesoff.begin();
it2 = notesoff.end();
delta2 = 0;
// apply delta
for(; it1 != it2; ++it1)
{
if((*it1).duration <= delta)
{
buf[0] = (*it1).quantity;
buf[1] = 0x7F;
// note off
events.push_back(new Event((*it1).duration - delta2, (*it1).command, 2, buf));
delta2 += ((*it1).duration - delta2);
}
}
// remove end notes
while(notesoff.size() && notesoff.front().duration <= delta)
notesoff.pop_front();
// fixed delta
if(delta2) delta -= delta2;
// decrease duration
std::for_each(notesoff.begin(), notesoff.end(), std::bind2nd(std::mem_fun_ref(&meta_t::decrease_duration), delta));
}
// interval
if(*ptr < 128)
{
delta += *ptr;
++ptr;
}
else
// command
{
// end
if(0xFF == *ptr && 0x2F == *(ptr + 1))
{
events.push_back(new Event(delta, *ptr, 2, ptr + 1));
break;
}
else
switch(*ptr >> 4)
{
// meta
case 0x0F:
{
u32 size = 0;
size += 1 + MIDI::UnpackDelta(ptr + 2, size);
ptr += size + 1;
delta = 0;
}
break;
// key pressure
case 0x0A:
// control change
case 0x0B:
// pitch bend
case 0x0E:
{
events.push_back(new Event(delta, *ptr, 2, ptr + 1));
ptr += 3;
delta = 0;
}
break;
// note off
case 0x08:
{
events.push_back(new Event(delta, *ptr, 2, ptr + 1));
u32 duration = 0;
const u8 s = MIDI::UnpackDelta(ptr + 3, duration);
notesoff.push_back(meta_t(*ptr - 0x10, *(ptr + 1), duration));
ptr += 3 + s;
delta = 0;
}
break;
// note on
case 0x09:
{
events.push_back(new Event(delta, *ptr, 2, ptr + 1));
u32 duration = 0;
const u8 s = MIDI::UnpackDelta(ptr + 3, duration);
notesoff.push_back(meta_t(*ptr - 0x10, *(ptr + 1), duration));
ptr += 3 + s;
delta = 0;
}
break;
// program change
case 0x0C:
// chanel pressure
case 0x0D:
{
events.push_back(new Event(delta, *ptr, 1, ptr + 1));
ptr += 2;
delta = 0;
}
break;
// unused command
default:
CloseEvents();
std::cerr << "unknown st: 0x" << std::setw(2) << std::setfill('0') << std::hex << static_cast<int>(*ptr) << ", ln: " << static_cast<int>(evnt.data + evnt.size - ptr) << std::endl;
break;
}
}
}
}