Files
haiku-beta6/src/kits/midi/MidiStore.cpp
T

1098 lines
23 KiB
C++
Raw Normal View History

2003-03-17 22:30:19 +00:00
/*
2004-05-13 09:44:22 +00:00
* Copyright (c) 2002-2004 Matthijs Hollemans
2003-03-17 22:30:19 +00:00
* Copyright (c) 2002 Jerome Leveque
* Copyright (c) 2002 Paul Stadler
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
2002-11-21 19:23:22 +00:00
#include <File.h>
#include <List.h>
2003-03-17 22:30:19 +00:00
#include <stdlib.h>
#include <string.h>
2003-03-17 22:30:19 +00:00
#include "MidiDefs.h"
#include "MidiStore.h"
#include "debug.h"
2002-07-09 12:24:59 +00:00
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
struct BMidiEvent
2002-11-21 19:23:22 +00:00
{
2003-03-17 22:30:19 +00:00
BMidiEvent()
{
byte1 = 0;
byte2 = 0;
byte3 = 0;
data = NULL;
length = 0;
}
~BMidiEvent()
{
free(data);
}
uint32 time; // either ticks or milliseconds
bool ticks; // event is from MIDI file
uchar byte1;
uchar byte2;
uchar byte3;
void* data; // sysex data
size_t length; // sysex data size
int32 tempo; // beats per minute
};
//------------------------------------------------------------------------------
static int compare_events(const void* event1, const void* event2)
{
BMidiEvent* e1 = *((BMidiEvent**) event1);
BMidiEvent* e2 = *((BMidiEvent**) event2);
return (e1->time - e2->time);
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
BMidiStore::BMidiStore()
{
2003-03-17 22:30:19 +00:00
events = new BList;
currentEvent = 0;
startTime = 0;
needsSorting = false;
beatsPerMinute = 60;
ticksPerBeat = 240;
file = NULL;
2004-05-13 19:54:00 +00:00
hookFunc = NULL;
looping = false;
paused = false;
finished = false;
instruments = new bool[128];
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
BMidiStore::~BMidiStore()
{
2003-03-17 22:30:19 +00:00
for (int32 t = 0; t < events->CountItems(); ++t)
{
2003-03-17 22:30:19 +00:00
delete EventAt(t);
2002-07-09 12:24:59 +00:00
}
delete events;
delete[] instruments;
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
void BMidiStore::NoteOff(
uchar channel, uchar note, uchar velocity, uint32 time)
{
2003-03-17 22:30:19 +00:00
BMidiEvent* event = new BMidiEvent;
event->time = time;
event->ticks = false;
event->byte1 = B_NOTE_OFF | (channel - 1);
event->byte2 = note;
event->byte3 = velocity;
AddEvent(event);
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
void BMidiStore::NoteOn(
uchar channel, uchar note, uchar velocity, uint32 time)
{
2003-03-17 22:30:19 +00:00
BMidiEvent* event = new BMidiEvent;
event->time = time;
event->ticks = false;
event->byte1 = B_NOTE_ON | (channel - 1);
event->byte2 = note;
event->byte3 = velocity;
AddEvent(event);
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
void BMidiStore::KeyPressure(
uchar channel, uchar note, uchar pressure, uint32 time)
{
2003-03-17 22:30:19 +00:00
BMidiEvent* event = new BMidiEvent;
event->time = time;
event->ticks = false;
event->byte1 = B_KEY_PRESSURE | (channel - 1);
event->byte2 = note;
event->byte3 = pressure;
AddEvent(event);
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-07-09 12:24:59 +00:00
2003-03-17 22:30:19 +00:00
void BMidiStore::ControlChange(
uchar channel, uchar controlNumber, uchar controlValue, uint32 time)
{
2003-03-17 22:30:19 +00:00
BMidiEvent* event = new BMidiEvent;
event->time = time;
event->ticks = false;
event->byte1 = B_CONTROL_CHANGE | (channel - 1);
event->byte2 = controlNumber;
event->byte3 = controlValue;
AddEvent(event);
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-07-09 12:24:59 +00:00
2003-03-17 22:30:19 +00:00
void BMidiStore::ProgramChange(
uchar channel, uchar programNumber, uint32 time)
{
2003-03-17 22:30:19 +00:00
BMidiEvent* event = new BMidiEvent;
event->time = time;
event->ticks = false;
event->byte1 = B_PROGRAM_CHANGE | (channel - 1);
event->byte2 = programNumber;
AddEvent(event);
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
void BMidiStore::ChannelPressure(uchar channel, uchar pressure, uint32 time)
{
2003-03-17 22:30:19 +00:00
BMidiEvent* event = new BMidiEvent;
event->time = time;
event->ticks = false;
event->byte1 = B_CHANNEL_PRESSURE | (channel - 1);
event->byte2 = pressure;
AddEvent(event);
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
void BMidiStore::PitchBend(uchar channel, uchar lsb, uchar msb, uint32 time)
{
2003-03-17 22:30:19 +00:00
BMidiEvent* event = new BMidiEvent;
event->time = time;
event->ticks = false;
event->byte1 = B_PITCH_BEND | (channel - 1);
event->byte2 = lsb;
event->byte3 = msb;
AddEvent(event);
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
void BMidiStore::SystemExclusive(void* data, size_t length, uint32 time)
{
2003-03-17 22:30:19 +00:00
BMidiEvent* event = new BMidiEvent;
event->time = time;
event->ticks = false;
event->byte1 = B_SYS_EX_START;
event->data = malloc(length);
event->length = length;
memcpy(event->data, data, length);
AddEvent(event);
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
void BMidiStore::SystemCommon(
uchar status, uchar data1, uchar data2, uint32 time)
{
2003-03-17 22:30:19 +00:00
BMidiEvent* event = new BMidiEvent;
event->time = time;
event->ticks = false;
event->byte1 = status;
event->byte2 = data1;
event->byte3 = data2;
AddEvent(event);
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
void BMidiStore::SystemRealTime(uchar status, uint32 time)
{
2003-03-17 22:30:19 +00:00
BMidiEvent* event = new BMidiEvent;
event->time = time;
event->ticks = false;
event->byte1 = status;
AddEvent(event);
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
void BMidiStore::TempoChange(int32 beatsPerMinute, uint32 time)
{
2003-03-17 22:30:19 +00:00
BMidiEvent* event = new BMidiEvent;
event->time = time;
event->ticks = false;
event->byte1 = 0xFF;
event->byte2 = 0x51;
event->byte3 = 0x03;
event->tempo = beatsPerMinute;
AddEvent(event);
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
status_t BMidiStore::Import(const entry_ref* ref)
{
memset(instruments, 0, 128 * sizeof(bool));
2003-03-17 22:30:19 +00:00
try
2002-11-21 19:23:22 +00:00
{
2003-03-17 22:30:19 +00:00
file = new BFile(ref, B_READ_ONLY);
if (file->InitCheck() != B_OK)
2002-11-21 19:23:22 +00:00
{
2003-03-17 22:30:19 +00:00
throw file->InitCheck();
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
char fourcc[4];
ReadFourCC(fourcc);
if (strncmp(fourcc, "MThd", 4) != 0)
2002-11-21 19:23:22 +00:00
{
2003-03-17 22:30:19 +00:00
throw (status_t) B_BAD_MIDI_DATA;
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
if (Read32Bit() != 6)
{
2003-03-17 22:30:19 +00:00
throw (status_t) B_BAD_MIDI_DATA;
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
format = Read16Bit();
numTracks = Read16Bit();
ticksPerBeat = Read16Bit();
2003-03-17 22:30:19 +00:00
if (ticksPerBeat & 0x8000) // we don't support SMPTE
{ // time codes, only ticks
ticksPerBeat = 240; // per quarter note
}
2003-03-17 22:30:19 +00:00
currTrack = 0;
while (currTrack < numTracks)
{
ReadChunk();
}
}
catch (status_t e)
{
2003-03-17 22:30:19 +00:00
delete file;
file = NULL;
return e;
}
2002-11-21 19:23:22 +00:00
SortEvents(true);
2002-07-09 12:24:59 +00:00
2003-03-17 22:30:19 +00:00
delete file;
file = NULL;
return B_OK;
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
status_t BMidiStore::Export(const entry_ref* ref, int32 format)
{
try
{
file = new BFile(ref, B_READ_WRITE);
if (file->InitCheck() != B_OK)
2002-11-21 19:23:22 +00:00
{
2003-03-17 22:30:19 +00:00
throw file->InitCheck();
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
SortEvents(true);
WriteFourCC('M', 'T', 'h', 'd');
Write32Bit(6);
Write16Bit(0); // we do only format 0
Write16Bit(1);
Write16Bit(ticksPerBeat);
WriteTrack();
}
catch (status_t e)
{
delete file;
file = NULL;
return e;
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
delete file;
file = NULL;
return B_OK;
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-07-09 12:24:59 +00:00
void BMidiStore::SortEvents(bool force)
{
2003-03-17 22:30:19 +00:00
if (force || needsSorting)
{
events->SortItems(compare_events);
needsSorting = false;
}
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-07-09 12:24:59 +00:00
uint32 BMidiStore::CountEvents() const
2003-03-17 22:30:19 +00:00
{
return events->CountItems();
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-07-09 12:24:59 +00:00
uint32 BMidiStore::CurrentEvent() const
2003-03-17 22:30:19 +00:00
{
return currentEvent;
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-07-09 12:24:59 +00:00
2003-03-17 22:30:19 +00:00
void BMidiStore::SetCurrentEvent(uint32 eventNumber)
{
currentEvent = eventNumber;
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
uint32 BMidiStore::DeltaOfEvent(uint32 eventNumber) const
{
// Even though the BeBook says that the delta is the time span between
// an event and the first event in the list, this doesn't appear to be
// true for events that were captured from other BMidi objects such as
// BMidiPort. For those events, we return the absolute timestamp. The
// BeBook is correct for events from MIDI files, though.
2003-03-17 22:30:19 +00:00
BMidiEvent* event = EventAt(eventNumber);
if (event != NULL)
{
return GetEventTime(event);
}
return 0;
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-11-21 19:23:22 +00:00
uint32 BMidiStore::EventAtDelta(uint32 time) const
2003-03-17 22:30:19 +00:00
{
for (int32 t = 0; t < events->CountItems(); ++t)
{
if (GetEventTime(EventAt(t)) >= time) { return t; }
}
return 0;
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-11-21 19:23:22 +00:00
uint32 BMidiStore::BeginTime() const
2003-03-17 22:30:19 +00:00
{
return startTime;
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
void BMidiStore::SetTempo(int32 beatsPerMinute_)
{
beatsPerMinute = beatsPerMinute_;
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-11-21 19:23:22 +00:00
int32 BMidiStore::Tempo() const
2003-03-17 22:30:19 +00:00
{
return beatsPerMinute;
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
void BMidiStore::_ReservedMidiStore1() { }
void BMidiStore::_ReservedMidiStore2() { }
void BMidiStore::_ReservedMidiStore3() { }
//------------------------------------------------------------------------------
2002-11-21 19:23:22 +00:00
void BMidiStore::Run()
{
2004-05-13 19:54:00 +00:00
// This rather compilicated Run() loop is not only used by BMidiStore
// but also by BMidiSynthFile. The "paused", "finished", and "looping"
// flags, and the "stop hook" are especially provided for the latter.
paused = false;
finished = false;
2004-06-25 08:30:38 +00:00
int32 timeAdjust = 0;
uint32 baseTime = 0;
2003-03-17 22:30:19 +00:00
bool firstEvent = true;
2004-05-13 19:54:00 +00:00
bool resetTime = false;
2003-03-17 22:30:19 +00:00
2002-11-21 19:23:22 +00:00
while (KeepRunning())
{
2004-05-13 19:54:00 +00:00
if (paused)
{
resetTime = true;
snooze(100000);
continue;
}
2003-03-17 22:30:19 +00:00
BMidiEvent* event = EventAt(currentEvent);
2004-05-13 19:54:00 +00:00
if (event == NULL) // no more events
{
if (looping)
{
resetTime = true;
currentEvent = 0;
}
else
{
break;
}
}
2003-03-17 22:30:19 +00:00
if (firstEvent)
{
startTime = B_NOW;
2004-05-13 19:54:00 +00:00
baseTime = startTime;
}
else if (resetTime)
{
baseTime = B_NOW;
}
if (firstEvent || resetTime)
{
timeAdjust = baseTime - GetEventTime(event);
SprayEvent(event, baseTime);
2003-03-17 22:30:19 +00:00
firstEvent = false;
2004-05-13 19:54:00 +00:00
resetTime = false;
2003-03-17 22:30:19 +00:00
}
else
{
SprayEvent(event, GetEventTime(event) + timeAdjust);
}
++currentEvent;
2002-11-21 19:23:22 +00:00
}
2004-05-13 19:54:00 +00:00
finished = true;
paused = false;
if (hookFunc != NULL)
{
(*hookFunc)(hookArg);
}
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
void BMidiStore::AddEvent(BMidiEvent* event)
{
events->AddItem(event);
needsSorting = true;
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
void BMidiStore::SprayEvent(const BMidiEvent* event, uint32 time)
{
uchar byte1 = event->byte1;
uchar byte2 = event->byte2;
uchar byte3 = event->byte3;
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
switch (byte1 & 0xF0)
{
2003-03-17 22:30:19 +00:00
case B_NOTE_OFF:
SprayNoteOff((byte1 & 0x0F) + 1, byte2, byte3, time);
return;
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
case B_NOTE_ON:
SprayNoteOn((byte1 & 0x0F) + 1, byte2, byte3, time);
return;
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
case B_KEY_PRESSURE:
SprayKeyPressure((byte1 & 0x0F) + 1, byte2, byte3, time);
return;
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
case B_CONTROL_CHANGE:
SprayControlChange((byte1 & 0x0F) + 1, byte2, byte3, time);
return;
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
case B_PROGRAM_CHANGE:
SprayProgramChange((byte1 & 0x0F) + 1, byte2, time);
return;
case B_CHANNEL_PRESSURE:
SprayChannelPressure((byte1 & 0x0F) + 1, byte2, time);
return;
case B_PITCH_BEND:
SprayPitchBend((byte1 & 0x0F) + 1, byte2, byte3, time);
return;
case 0xF0:
switch (byte1)
{
case B_SYS_EX_START:
SpraySystemExclusive(event->data, event->length, time);
return;
case B_MIDI_TIME_CODE:
case B_SONG_POSITION:
case B_SONG_SELECT:
case B_CABLE_MESSAGE:
case B_TUNE_REQUEST:
case B_SYS_EX_END:
SpraySystemCommon(byte1, byte2, byte3, time);
return;
case B_TIMING_CLOCK:
case B_START:
case B_CONTINUE:
case B_STOP:
case B_ACTIVE_SENSING:
SpraySystemRealTime(byte1, time);
return;
case B_SYSTEM_RESET:
if ((byte2 == 0x51) && (byte3 == 0x03))
{
SprayTempoChange(event->tempo, time);
beatsPerMinute = event->tempo;
}
else
{
SpraySystemRealTime(byte1, time);
}
return;
}
return;
}
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
BMidiEvent* BMidiStore::EventAt(int32 index) const
{
return (BMidiEvent*) events->ItemAt(index);
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
uint32 BMidiStore::GetEventTime(const BMidiEvent* event) const
{
if (event->ticks)
{
return TicksToMilliseconds(event->time);
}
else
{
return event->time;
}
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
uint32 BMidiStore::TicksToMilliseconds(uint32 ticks) const
{
return ((uint64) ticks * 60000) / (beatsPerMinute * ticksPerBeat);
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
uint32 BMidiStore::MillisecondsToTicks(uint32 ms) const
{
return ((uint64) ms * beatsPerMinute * ticksPerBeat) / 60000;
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
void BMidiStore::ReadFourCC(char* fourcc)
{
if (file->Read(fourcc, 4) != 4)
2002-11-21 19:23:22 +00:00
{
2003-03-17 22:30:19 +00:00
throw (status_t) B_BAD_MIDI_DATA;
2002-07-09 12:24:59 +00:00
}
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
void BMidiStore::WriteFourCC(char a, char b, char c, char d)
{
char fourcc[4] = { a, b, c, d };
if (file->Write(fourcc, 4) != 4)
2002-11-21 19:23:22 +00:00
{
2003-03-17 22:30:19 +00:00
throw (status_t) B_ERROR;
2002-11-21 19:23:22 +00:00
}
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
uint32 BMidiStore::Read32Bit()
{
uint8 buf[4];
if (file->Read(buf, 4) != 4)
{
throw (status_t) B_BAD_MIDI_DATA;
}
2002-07-09 12:24:59 +00:00
2003-03-17 22:30:19 +00:00
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
void BMidiStore::Write32Bit(uint32 val)
{
uint8 buf[4];
buf[0] = (val >> 24) & 0xFF;
buf[1] = (val >> 16) & 0xFF;
buf[2] = (val >> 8) & 0xFF;
buf[3] = val & 0xFF;
2002-07-09 12:24:59 +00:00
2003-03-17 22:30:19 +00:00
if (file->Write(buf, 4) != 4)
{
throw (status_t) B_ERROR;
}
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-07-09 12:24:59 +00:00
2003-03-17 22:30:19 +00:00
uint16 BMidiStore::Read16Bit()
{
uint8 buf[2];
if (file->Read(buf, 2) != 2)
{
throw (status_t) B_BAD_MIDI_DATA;
}
2002-07-09 12:24:59 +00:00
2003-03-17 22:30:19 +00:00
return (buf[0] << 8) | buf[1];
2002-11-21 19:23:22 +00:00
}
2002-07-09 12:24:59 +00:00
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-07-09 12:24:59 +00:00
2003-03-17 22:30:19 +00:00
void BMidiStore::Write16Bit(uint16 val)
{
uint8 buf[2];
buf[0] = (val >> 8) & 0xFF;
buf[1] = val & 0xFF;
2003-03-17 22:30:19 +00:00
if (file->Write(buf, 2) != 2)
{
throw (status_t) B_ERROR;
}
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
uint8 BMidiStore::PeekByte()
{
uint8 buf;
if (file->Read(&buf, 1) != 1)
{
throw (status_t) B_BAD_MIDI_DATA;
}
2002-07-09 12:24:59 +00:00
2003-03-17 22:30:19 +00:00
if (file->Seek(-1, SEEK_CUR) < 0)
{
throw (status_t) B_ERROR;
}
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
return buf;
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
uint8 BMidiStore::NextByte()
{
uint8 buf;
if (file->Read(&buf, 1) != 1)
2002-11-21 19:23:22 +00:00
{
2003-03-17 22:30:19 +00:00
throw (status_t) B_BAD_MIDI_DATA;
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
--byteCount;
return buf;
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
void BMidiStore::WriteByte(uint8 val)
{
if (file->Write(&val, 1) != 1)
{
throw (status_t) B_ERROR;
}
2003-03-17 22:30:19 +00:00
++byteCount;
2002-07-09 12:24:59 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
void BMidiStore::SkipBytes(uint32 length)
{
if (file->Seek(length, SEEK_CUR) < 0)
{
throw (status_t) B_BAD_MIDI_DATA;
}
2003-03-17 22:30:19 +00:00
byteCount -= length;
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
uint32 BMidiStore::ReadVarLength()
{
uint32 val;
uint8 byte;
if ((val = NextByte()) & 0x80)
{
2003-03-17 22:30:19 +00:00
val &= 0x7F;
do
2002-11-21 19:23:22 +00:00
{
2003-03-17 22:30:19 +00:00
val = (val << 7) + ((byte = NextByte()) & 0x7F);
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
while (byte & 0x80);
}
2003-03-17 22:30:19 +00:00
return val;
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
void BMidiStore::WriteVarLength(uint32 val)
{
uint32 buffer = val & 0x7F;
2003-03-17 22:30:19 +00:00
while ((val >>= 7))
{
buffer <<= 8;
buffer |= ((val & 0x7F) | 0x80);
}
2003-03-17 22:30:19 +00:00
while (true)
{
WriteByte(buffer);
if (buffer & 0x80)
buffer >>= 8;
else
break;
}
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
void BMidiStore::ReadChunk()
{
char fourcc[4];
ReadFourCC(fourcc);
2003-03-17 22:30:19 +00:00
byteCount = Read32Bit();
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
if (strncmp(fourcc, "MTrk", 4) == 0)
{
2003-03-17 22:30:19 +00:00
ReadTrack();
}
2003-03-17 22:30:19 +00:00
else
2002-11-21 19:23:22 +00:00
{
2003-03-17 22:30:19 +00:00
TRACE(("Skipping '%c%c%c%c' chunk (%lu bytes)",
fourcc[0], fourcc[1], fourcc[2], fourcc[3], byteCount))
SkipBytes(byteCount);
2002-11-21 19:23:22 +00:00
}
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
void BMidiStore::ReadTrack()
{
uint8 status = 0;
uint8 data1;
uint8 data2;
BMidiEvent* event;
totalTicks = 0;
while (byteCount > 0)
2002-11-21 19:23:22 +00:00
{
2003-03-17 22:30:19 +00:00
uint32 ticks = ReadVarLength();
totalTicks += ticks;
if (PeekByte() & 0x80)
{
status = NextByte();
}
switch (status & 0xF0)
{
case B_NOTE_OFF:
case B_NOTE_ON:
case B_KEY_PRESSURE:
case B_CONTROL_CHANGE:
case B_PITCH_BEND:
data1 = NextByte();
data2 = NextByte();
event = new BMidiEvent;
event->time = totalTicks;
event->ticks = true;
event->byte1 = status;
event->byte2 = data1;
event->byte3 = data2;
AddEvent(event);
break;
case B_PROGRAM_CHANGE:
case B_CHANNEL_PRESSURE:
data1 = NextByte();
event = new BMidiEvent;
event->time = totalTicks;
event->ticks = true;
event->byte1 = status;
event->byte2 = data1;
AddEvent(event);
if ((status & 0xF0) == B_PROGRAM_CHANGE)
{
instruments[data1] = true;
}
2003-03-17 22:30:19 +00:00
break;
case 0xF0:
switch (status)
{
case B_SYS_EX_START:
ReadSystemExclusive();
break;
case B_TUNE_REQUEST:
case B_SYS_EX_END:
case B_TIMING_CLOCK:
case B_START:
case B_CONTINUE:
case B_STOP:
case B_ACTIVE_SENSING:
event = new BMidiEvent;
event->time = totalTicks;
event->ticks = true;
event->byte1 = status;
AddEvent(event);
break;
case B_MIDI_TIME_CODE:
case B_SONG_SELECT:
case B_CABLE_MESSAGE:
data1 = NextByte();
event = new BMidiEvent;
event->time = totalTicks;
event->ticks = true;
event->byte1 = status;
event->byte2 = data1;
AddEvent(event);
break;
case B_SONG_POSITION:
data1 = NextByte();
data2 = NextByte();
event = new BMidiEvent;
event->time = totalTicks;
event->ticks = true;
event->byte1 = status;
event->byte2 = data1;
event->byte3 = data2;
AddEvent(event);
break;
case B_SYSTEM_RESET:
ReadMetaEvent();
break;
}
break;
}
event = NULL;
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
++currTrack;
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
void BMidiStore::ReadSystemExclusive()
{
// We do not import sysex's from MIDI files.
SkipBytes(ReadVarLength());
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
void BMidiStore::ReadMetaEvent()
{
// We only import the Tempo Change meta event.
uint8 type = NextByte();
uint32 length = ReadVarLength();
2003-03-17 22:30:19 +00:00
if ((type == 0x51) && (length == 3))
2002-11-21 19:23:22 +00:00
{
2003-03-17 22:30:19 +00:00
uchar data[3];
data[0] = NextByte();
data[1] = NextByte();
data[2] = NextByte();
uint32 val = (data[0] << 16) | (data[1] << 8) | data[2];
BMidiEvent* event = new BMidiEvent;
event->time = totalTicks;
event->ticks = true;
event->byte1 = 0xFF;
event->byte2 = 0x51;
event->byte3 = 0x03;
event->tempo = 60000000 / val;
AddEvent(event);
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
else
2002-11-21 19:23:22 +00:00
{
2003-03-17 22:30:19 +00:00
SkipBytes(length);
2002-11-21 19:23:22 +00:00
}
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2003-03-17 22:30:19 +00:00
void BMidiStore::WriteTrack()
{
WriteFourCC('M', 'T', 'r', 'k');
off_t lengthPos = file->Position();
Write32Bit(0);
2003-03-17 22:30:19 +00:00
byteCount = 0;
2004-06-25 08:30:38 +00:00
uint32 oldTime = 0;
uint32 newTime = 0;
2002-11-21 19:23:22 +00:00
2004-05-13 09:44:22 +00:00
for (uint32 t = 0; t < CountEvents(); ++t)
2003-03-17 22:30:19 +00:00
{
BMidiEvent* event = EventAt(t);
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
if (event->ticks)
{
newTime = event->time;
}
else
{
newTime = MillisecondsToTicks(event->time);
}
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
if (t == 0)
{
WriteVarLength(0);
}
else
{
WriteVarLength(newTime - oldTime);
}
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
oldTime = newTime;
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
switch (event->byte1 & 0xF0)
{
case B_NOTE_OFF:
case B_NOTE_ON:
case B_KEY_PRESSURE:
case B_CONTROL_CHANGE:
case B_PITCH_BEND:
WriteByte(event->byte1);
WriteByte(event->byte2);
WriteByte(event->byte3);
break;
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
case B_PROGRAM_CHANGE:
case B_CHANNEL_PRESSURE:
WriteByte(event->byte1);
WriteByte(event->byte2);
break;
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
case 0xF0:
switch (event->byte1)
{
case B_SYS_EX_START:
// We do not export sysex's.
break;
case B_TUNE_REQUEST:
case B_SYS_EX_END:
case B_TIMING_CLOCK:
case B_START:
case B_CONTINUE:
case B_STOP:
case B_ACTIVE_SENSING:
WriteByte(event->byte1);
break;
case B_MIDI_TIME_CODE:
case B_SONG_SELECT:
case B_CABLE_MESSAGE:
WriteByte(event->byte1);
WriteByte(event->byte2);
break;
case B_SONG_POSITION:
WriteByte(event->byte1);
WriteByte(event->byte2);
WriteByte(event->byte3);
break;
case B_SYSTEM_RESET:
WriteMetaEvent(event);
break;
}
break;
}
}
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
WriteVarLength(0);
WriteByte(0xFF); // the end-of-track
WriteByte(0x2F); // marker is required
WriteByte(0x00);
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
file->Seek(lengthPos, SEEK_SET);
Write32Bit(byteCount);
file->Seek(0, SEEK_END);
2002-11-21 19:23:22 +00:00
}
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------
2002-11-21 19:23:22 +00:00
2003-03-17 22:30:19 +00:00
void BMidiStore::WriteMetaEvent(BMidiEvent* event)
2002-11-21 19:23:22 +00:00
{
2003-03-17 22:30:19 +00:00
// We only export the Tempo Change meta event.
2003-03-17 22:30:19 +00:00
if ((event->byte2 == 0x51) && (event->byte3 == 0x03))
{
uint32 val = 60000000 / event->tempo;
WriteByte(0xFF);
WriteByte(0x51);
WriteByte(0x03);
WriteByte(val >> 16);
WriteByte(val >> 8);
WriteByte(val);
}
2002-11-21 19:23:22 +00:00
}
2004-05-13 19:54:00 +00:00
2003-03-17 22:30:19 +00:00
//------------------------------------------------------------------------------