Added MMC support by Rui Nuno Capela <rncbc@rncbc.org>

MMC support has been improved and transport LEDs is getting almost
functional. Tascam control protocol sysex decoding is also complete (LEDs
control mainly).
This commit is contained in:
Takashi Iwai 2004-07-28 15:56:45 +00:00
parent 9615f45528
commit 5313c94163
7 changed files with 541 additions and 35 deletions

View file

@ -79,3 +79,173 @@ char Cus428Midi::KnobParam[] = {
0x36,
0x37,
};
extern int verbose;
// Parse and dispatch input MIDI events.
void Cus428Midi::ProcessMidiEvents()
{
snd_seq_event_t *ev;
unsigned char *data;
do {
ev = NULL;
snd_seq_event_input(Seq, &ev);
if (ev == NULL)
break;
switch (ev->type) {
case SND_SEQ_EVENT_SYSEX:
data = (unsigned char *) ev->data.ext.ptr;
if (data[1] == 0x7f && data[3] == 0x06) {
// MMC Command code is in data[4]...
if (verbose > 1)
fprintf(stderr, "MMC Command 0x%02x: ", data[4]);
switch (data[4]) {
case MMC_CMD_STOP:
if (verbose > 1)
fprintf(stderr, "STOP.\n");
OneState->TransportSet(T_STOP, true);
break;
case MMC_CMD_PLAY:
case MMC_CMD_DEFERRED_PLAY:
if (verbose > 1)
fprintf(stderr, "PLAY.\n");
OneState->TransportSet(T_PLAY, true);
break;
case MMC_CMD_FAST_FORWARD:
if (verbose > 1)
fprintf(stderr, "FFWD.\n");
OneState->TransportSet(T_F_FWD, true);
break;
case MMC_CMD_REWIND:
if (verbose > 1)
fprintf(stderr, "REW.\n");
OneState->TransportSet(T_REW, true);
break;
case MMC_CMD_RECORD_STROBE:
case MMC_CMD_RECORD_PAUSE:
if (verbose > 1)
fprintf(stderr, "RECORD ON.\n");
OneState->TransportSet(T_RECORD, true);
break;
case MMC_CMD_RECORD_EXIT:
if (verbose > 1)
fprintf(stderr, "RECORD OFF.\n");
OneState->TransportSet(T_RECORD, false);
break;
case MMC_CMD_LOCATE:
if (verbose > 1)
fprintf(stderr, "LOCATE WHEEL.\n");
OneState->LocateWheel(&data[7]);
break;
case MMC_CMD_MMC_RESET:
if (verbose > 1)
fprintf(stderr, "MMC RESET.\n");
OneState->MmcReset();
break;
default:
if (verbose > 1)
fprintf(stderr, "Not implemented.\n");
break;
}
} // Look for Tascam US-224/US-428(?) specific LED codes.
else if (data[1] == 0x4e && data[3] == 0x12) {
if (verbose > 1)
fprintf(stderr, "TASCAM Command 0x%02x.\n", data[4]);
// Transport LEDs.
switch (data[4]) {
case 0x01: // Transport LEDs...
switch(data[5]) {
case 0x13: // REWIND.
OneState->LightSet(Cus428State::eL_Rew, data[6]);
OneState->LightSend();
break;
case 0x14: // FFWD.
OneState->LightSet(Cus428State::eL_FFwd, data[6]);
OneState->LightSend();
break;
case 0x16: // PLAY.
OneState->LightSet(Cus428State::eL_Play, data[6]);
OneState->LightSend();
break;
case 0x17: // REC.
OneState->LightSet(Cus428State::eL_Record, data[6]);
OneState->LightSend();
break;
default:
break;
}
break;
case 0x02: // Mute LEDs
OneState->LightSet(Cus428State::eL_Mute0 + data[5], data[6]);
OneState->LightSend();
break;
case 0x03: // Select LEDs
OneState->LightSet(Cus428State::eL_Select0 + data[5], data[6]);
OneState->LightSend();
break;
case 0x04: // Record LEDs
OneState->LightSet(Cus428State::eL_Rec0 + data[5], data[6]);
OneState->LightSend();
break;
case 0x05: // Null LED
OneState->LightSet(Cus428State::eL_Null, data[5]);
OneState->LightSend();
break;
case 0x06: // Solo LED
OneState->LightSet(Cus428State::eL_Solo, data[5]);
OneState->LightSend();
break;
case 0x07: // Bank L LED
OneState->LightSet(Cus428State::eL_BankL, data[5]);
OneState->LightSend();
break;
case 0x08: // Bank R LED
OneState->LightSet(Cus428State::eL_BankR, data[5]);
OneState->LightSend();
break;
case 0x10: // Dump fader position.
OneState->SliderSend(data[5]);
break;
}
}
break;
default:
break;
}
snd_seq_free_event(ev);
}
while (snd_seq_event_input_pending(Seq, 0) > 0);
}
// Send MMC command.
void Cus428Midi::SendMmcCommand(unsigned char MmcCmd, unsigned char *MmcData, unsigned char MmcLen)
{
unsigned char SysexSize;
unsigned char SysexLen;
unsigned char *SysexData;
SysexSize = 6;
if (MmcLen > 0)
SysexSize += 1 + MmcLen;
SysexData = (unsigned char *) alloca(SysexSize);
SysexLen = 0;
SysexData[SysexLen++] = 0xf0; // Sysex header.
SysexData[SysexLen++] = 0x7f; // Realtime sysex.
SysexData[SysexLen++] = 0x7f; // All-caller-id.
SysexData[SysexLen++] = 0x06; // MMC command mode.
SysexData[SysexLen++] = MmcCmd; // MMC command code.
if (MmcData && MmcLen > 0) {
SysexData[SysexLen++] = MmcLen;
memcpy(&SysexData[SysexLen], MmcData, MmcLen);
SysexLen += MmcLen;
}
SysexData[SysexLen++] = 0xf7; // Sysex trailer.
snd_seq_ev_set_sysex(&Ev, SysexLen, SysexData);
SubMitEvent();
}

View file

@ -19,6 +19,48 @@
#include "Cus428State.h"
// MMC Command Codes.
#define MMC_CMD_STOP 0x01
#define MMC_CMD_PLAY 0x02
#define MMC_CMD_DEFERRED_PLAY 0x03
#define MMC_CMD_FAST_FORWARD 0x04
#define MMC_CMD_REWIND 0x05
#define MMC_CMD_RECORD_STROBE 0x06
#define MMC_CMD_RECORD_EXIT 0x07
#define MMC_CMD_RECORD_PAUSE 0x08
#define MMC_CMD_PAUSE 0x09
#define MMC_CMD_EJECT 0x0a
#define MMC_CMD_CHASE 0x0b
#define MMC_CMD_COMMAND_ERROR_RESET 0x0c
#define MMC_CMD_MMC_RESET 0x0d
#define MMC_CMD_JOG_START 0x20
#define MMC_CMD_JOG_STOP 0x21
#define MMC_CMD_WRITE 0x40
#define MMC_CMD_MASKED_WRITE 0x41
#define MMC_CMD_READ 0x42
#define MMC_CMD_UPDATE 0x43
#define MMC_CMD_LOCATE 0x44
#define MMC_CMD_VARIABLE_PLAY 0x45
#define MMC_CMD_SEARCH 0x46
#define MMC_CMD_SHUTTLE 0x47
#define MMC_CMD_STEP 0x48
#define MMC_CMD_ASSIGN_SYSTEM_MASTER 0x49
#define MMC_CMD_GENERATOR_COMMAND 0x4a
#define MMC_CMD_MTC_COMMAND 0x4b
#define MMC_CMD_MOVE 0x4c
#define MMC_CMD_ADD 0x4d
#define MMC_CMD_SUBTRACT 0x4e
#define MMC_CMD_DROP_FRAME_ADJUST 0x4f
#define MMC_CMD_PROCEDURE 0x50
#define MMC_CMD_EVENT 0x51
#define MMC_CMD_GROUP 0x52
#define MMC_CMD_COMMAND_SEGMENT 0x53
#define MMC_CMD_DEFERRED_VARIABLE_PLAY 0x54
#define MMC_CMD_RECORD_STROBE_VARIABLE 0x55
#define MMC_CMD_WAIT 0x7c
#define MMC_CMD_RESUME 0x7f
class Cus428Midi {
public:
Cus428Midi():
@ -30,9 +72,9 @@ class Cus428Midi {
snd_seq_set_client_name(Seq, "US-428");
Err = snd_seq_create_simple_port(Seq, "Controls",
SND_SEQ_PORT_CAP_READ
//|SND_SEQ_PORT_CAP_WRITE FIXME: Next Step is to make Lights switchable
|SND_SEQ_PORT_CAP_WRITE
|SND_SEQ_PORT_CAP_SUBS_READ
/*|SND_SEQ_PORT_CAP_SUBS_WRITE*/,
|SND_SEQ_PORT_CAP_SUBS_WRITE,
SND_SEQ_PORT_TYPE_MIDI_GENERIC);
if (Err >= 0) {
Port = Err;
@ -55,8 +97,16 @@ class Cus428Midi {
return SendMidiControl(KnobParam[K - Cus428State::eK_RECORD], Down ? 0x7F : 0);
}
private:
// To parse and dispatch input MIDI events.
void ProcessMidiEvents();
// Send MMC command.
void SendMmcCommand(unsigned char MmcCmd, unsigned char *MmcData = 0, unsigned char MmcLen = 0);
// Made public for friendliness.
snd_seq_t *Seq;
private:
int Port;
snd_seq_event_t Ev;
int SubMitEvent(){

View file

@ -25,7 +25,9 @@
extern int verbose;
// Differential wheel tracking constants.
#define W_DELTA_MAX 0xff
#define W_DELTA_MIN (W_DELTA_MAX >> 1)
void us428_lights::init_us428_lights()
{
@ -71,11 +73,14 @@ void Cus428State::SliderChangedTo(int S, unsigned char New)
V.SetTo(S, New);
if (S == eFaderM || !LightIs(eL_Mute0 + S))
SendVolume(V);
} else
Midi.SendMidiControl(0x40 + S, ((unsigned char*)us428_ctls)[S] / 2);
}
else SliderSend(S);
}
void Cus428State::SliderSend(int S)
{
Midi.SendMidiControl(0x40 + S, ((unsigned char*)us428_ctls)[S] / 2);
}
void Cus428State::KnobChangedTo(eKnobs K, bool V)
{
@ -103,6 +108,73 @@ void Cus428State::KnobChangedTo(eKnobs K, bool V)
break;
default:
switch (K) {
case eK_STOP:
if (verbose > 1)
printf("Knob STOP now %i\n", V);
if (V) TransportToggle(T_STOP);
Midi.SendMidiControl(K, V);
break;
case eK_PLAY:
if (verbose > 1)
printf("Knob PLAY now %i", V);
if (V) TransportToggle(T_PLAY);
if (verbose > 1)
printf(" Light is %i\n", LightIs(eL_Play));
Midi.SendMidiControl(K, V);
break;
case eK_REW:
if (verbose > 1)
printf("Knob REW now %i", V);
if (V) TransportToggle(T_REW);
if (verbose > 1)
printf(" Light is %i\n", LightIs(eL_Rew));
Midi.SendMidiControl(K, V);
break;
case eK_FFWD:
if (verbose > 1)
printf("Knob FFWD now %i", V);
if (V) TransportToggle(T_F_FWD);
if (verbose > 1)
printf(" Light is %i\n", LightIs(eL_FFwd));
Midi.SendMidiControl(K, V);
break;
case eK_RECORD:
if (verbose > 1)
printf("Knob RECORD now %i", V);
if (V) TransportToggle(T_RECORD);
if (verbose > 1)
printf(" Light is %i\n", LightIs(eL_Record));
Midi.SendMidiControl(K, V);
break;
case eK_SET:
if (verbose > 1)
printf("Knob SET now %i", V);
bSetLocate = V;
break;
case eK_LOCATE_L:
if (verbose > 1)
printf("Knob LOCATE_L now %i", V);
if (V) {
if (bSetLocate)
aWheel_L = aWheel;
else {
aWheel = aWheel_L;
LocateSend();
}
}
break;
case eK_LOCATE_R:
if (verbose > 1)
printf("Knob LOCATE_R now %i", V);
if (V) {
if (bSetLocate)
aWheel_R = aWheel;
else {
aWheel = aWheel_R;
LocateSend();
}
}
break;
case eK_InputMonitor:
if (verbose > 1)
printf("Knob InputMonitor now %i", V);
@ -163,7 +235,166 @@ void Cus428State::WheelChangedTo(E_In84 W, char Diff)
break;
case eWheel:
Param = 0x60;
// Update the absolute wheel position.
WheelDelta((int) ((unsigned char*) us428_ctls)[W]);
LocateSend();
break;
}
Midi.SendMidiControl(Param, ((unsigned char*)us428_ctls)[W]);
}
// Convert time-code (hh:mm:ss:ff:fr) into absolute wheel position.
void Cus428State::LocateWheel ( unsigned char *tc )
{
#if 0
aWheel = (60 * 60 * 30) * (int) tc[0] // hh - hours [0..23]
+ ( 60 * 30) * (int) tc[1] // mm - minutes [0..59]
+ ( 30) * (int) tc[2] // ss - seconds [0..59]
+ (int) tc[3]; // ff - frames [0..29]
#else
aWheel = (60 * 60 * 3) * (int) tc[0] // hh - hours [0..23]
+ ( 60 * 3) * (int) tc[1] // mm - minutes [0..59]
+ ( 3) * (int) tc[2] // ss - seconds [0..59]
+ (int) tc[3] / 10; // ff - frames [0..29]
#endif
}
// Convert absolute wheel position into time-code (hh:mm:ss:ff:fr)
void Cus428State::WheelTimecode ( unsigned char *tc )
{
int W = aWheel;
#if 0
tc[0] = W / (60 * 60 * 30); W -= (60 * 60 * 30) * (int) tc[0];
tc[1] = W / ( 60 * 30); W -= ( 60 * 30) * (int) tc[1];
tc[2] = W / ( 30); W -= ( 30) * (int) tc[2];
tc[3] = W;
tc[4] = 0;
#else
tc[0] = W / (60 * 60 * 3); W -= (60 * 60 * 3) * (int) tc[0];
tc[1] = W / ( 60 * 3); W -= ( 60 * 3) * (int) tc[1];
tc[2] = W / ( 3); W -= ( 3) * (int) tc[2];
tc[3] = W * 10;
tc[4] = 0;
#endif
}
// Get the wheel differential.
void Cus428State::WheelDelta ( int W )
{
// Compute the wheel differential.
int dW = W - W0;
if (dW > 0 && dW > +W_DELTA_MIN)
dW -= W_DELTA_MAX;
else
if (dW < 0 && dW < -W_DELTA_MIN)
dW += W_DELTA_MAX;
W0 = W;
aWheel += dW;
// Can't be less than zero.
if (aWheel < 0)
aWheel = 0;
}
// Send the MMC wheel locate command...
void Cus428State::LocateSend ()
{
unsigned char MmcData[6];
// Timecode's embedded on MMC command.
MmcData[0] = 0x01;
WheelTimecode(&MmcData[1]);
// Send the MMC locate command...
Midi.SendMmcCommand(MMC_CMD_LOCATE, MmcData, sizeof(MmcData));
}
// Toggle application transport state.
void Cus428State::TransportToggle ( unsigned char T )
{
switch (T) {
case T_PLAY:
if (uTransport & T_PLAY) {
uTransport = T_STOP;
Midi.SendMmcCommand(MMC_CMD_STOP);
} else {
uTransport &= T_RECORD;
uTransport |= T_PLAY;
Midi.SendMmcCommand(MMC_CMD_PLAY);
}
break;
case T_RECORD:
if (uTransport & T_RECORD) {
uTransport &= ~T_RECORD;
Midi.SendMmcCommand(MMC_CMD_RECORD_EXIT);
} else {
uTransport &= T_PLAY;
uTransport |= T_RECORD;
Midi.SendMmcCommand(uTransport & T_PLAY ? MMC_CMD_RECORD_STROBE : MMC_CMD_RECORD_PAUSE);
}
break;
default:
if (uTransport & T) {
uTransport = T_STOP;
} else {
uTransport = T;
}
if (uTransport & T_STOP)
Midi.SendMmcCommand(MMC_CMD_STOP);
if (uTransport & T_REW)
Midi.SendMmcCommand(MMC_CMD_REWIND);
if (uTransport & T_F_FWD)
Midi.SendMmcCommand(MMC_CMD_FAST_FORWARD);
break;
}
TransportSend();
}
// Set application transport state.
void Cus428State::TransportSet ( unsigned char T, bool V )
{
if (V) {
if (T == T_RECORD) {
uTransport |= T_RECORD;
} else {
uTransport = T;
}
} else {
if (T == T_RECORD) {
uTransport &= ~T_RECORD;
} else {
uTransport = T_STOP;
}
}
TransportSend();
}
// Update transport lights.
void Cus428State::TransportSend()
{
LightSet(eL_Play, (uTransport & T_PLAY));
LightSet(eL_Record, (uTransport & T_RECORD));
LightSet(eL_Rew, (uTransport & T_REW));
LightSet(eL_FFwd, (uTransport & T_F_FWD));
LightSend();
}
// Reset MMC state.
void Cus428State::MmcReset()
{
W0 = 0;
aWheel = aWheel_L = aWheel_R = 0;
bSetLocate = false;
uTransport = 0;
TransportSend();
LocateSend();
}

View file

@ -32,6 +32,12 @@ class Cus428State: public us428_lights{
,SelectInputMonitor(0)
,Select(0)
,us428_ctls(0)
,W0(0)
,aWheel(0)
,aWheel_L(0)
,aWheel_R(0)
,bSetLocate(false)
,uTransport(0)
{
init_us428_lights();
for (int v = 0; v < 5; ++v) {
@ -79,25 +85,52 @@ class Cus428State: public us428_lights{
void InitDevice(void);
void KnobChangedTo(eKnobs K, bool V);
void SliderChangedTo(int S, unsigned char New);
void SliderSend(int S);
void WheelChangedTo(E_In84 W, char Diff);
Cus428_ctls *Set_us428_ctls(Cus428_ctls *New) {
Cus428_ctls *Old = us428_ctls;
us428_ctls = New;
return Old;
}
private:
// Update the LED lights state.
int LightSend();
// Time-code (hh:mm:ss:ff:fr) to/from absolute wheel position converters.
void LocateWheel(unsigned char *tc);
void LocateSend();
// Set basic application transport state.
void TransportToggle(unsigned char T);
void TransportSet(unsigned char T, bool V);
void TransportSend();
// Reset internal MMC state.
void MmcReset();
private:
void SendVolume(usX2Y_volume &V);
struct us428ctls_sharedmem* us428ctls_sharedmem;
bool StateInputMonitor() {
return LightIs(eL_InputMonitor);
}
// Set the wheel differential.
void WheelDelta(int W);
// Get the curent wheel timecode.
void WheelTimecode(unsigned char *tc);
usX2Y_volume_t Volume[5];
char MuteInputMonitor,
Mute,
SelectInputMonitor,
Select;
Cus428_ctls *us428_ctls;
// Differential wheel tracking.
int W0;
// Some way to convert wheel (absolute) position into hh:mm:ss:ff:fr
int aWheel;
// SET L/R points.
int aWheel_L;
int aWheel_R;
// SET knob state.
bool bSetLocate;
// Last/current transport state.
unsigned char uTransport;
};
extern Cus428State* OneState;

View file

@ -1,5 +1,5 @@
AC_INIT(us428control.cc)
AM_INIT_AUTOMAKE(us428control, 0.4.1)
AM_INIT_AUTOMAKE(us428control, 0.4.2)
AC_PROG_CXX
AC_PROG_INSTALL
AC_HEADER_STDC

View file

@ -89,7 +89,8 @@ int US428Control(const char* DevName)
int err;
unsigned int idx, dsps, loaded;
us428ctls_sharedmem_t *us428ctls_sharedmem;
struct pollfd pfds;
struct pollfd *pfds;
int npfd, pollrc;
if ((err = snd_hwdep_open(&hw, DevName, O_RDWR)) < 0) {
error("cannot open hwdep %s\n", DevName);
@ -101,39 +102,51 @@ int US428Control(const char* DevName)
snd_hwdep_close(hw);
return -ENODEV;
}
snd_hwdep_poll_descriptors(hw, &pfds, 1);
us428ctls_sharedmem = (us428ctls_sharedmem_t*)mmap(NULL, sizeof(us428ctls_sharedmem_t), PROT_READ|PROT_WRITE, MAP_SHARED, pfds.fd, 0);
Midi.CreatePorts();
npfd = snd_seq_poll_descriptors_count(Midi.Seq, POLLIN) + 1;
pfds = (struct pollfd *) alloca(npfd * sizeof(struct pollfd));
snd_hwdep_poll_descriptors(hw, &pfds[0], 1);
snd_seq_poll_descriptors(Midi.Seq, &pfds[1], npfd - 1, POLLIN);
us428ctls_sharedmem = (us428ctls_sharedmem_t *) mmap(NULL, sizeof(us428ctls_sharedmem_t), PROT_READ|PROT_WRITE, MAP_SHARED, pfds[0].fd, 0);
if (us428ctls_sharedmem == MAP_FAILED) {
perror("mmap failed:");
snd_hwdep_close(hw);
return -ENOMEM;
}
Midi.CreatePorts();
us428ctls_sharedmem->CtlSnapShotRed = us428ctls_sharedmem->CtlSnapShotLast;
OneState = new Cus428State(us428ctls_sharedmem);
OneState->InitDevice();
while (1) {
int x = poll(&pfds,1,-1);
if (verbose > 1 || pfds.revents & (POLLERR|POLLHUP))
printf("poll returned 0x%X\n", pfds.revents);
if (pfds.revents & (POLLERR|POLLHUP))
return -ENXIO;
int Last = us428ctls_sharedmem->CtlSnapShotLast;
if (verbose > 1)
printf("Last is %i\n", Last);
while (us428ctls_sharedmem->CtlSnapShotRed != Last) {
static Cus428_ctls *Red = 0;
int Read = us428ctls_sharedmem->CtlSnapShotRed + 1;
if (Read >= N_us428_ctl_BUFS || Read < 0)
Read = 0;
Cus428_ctls* PCtlSnapShot = ((Cus428_ctls*)(us428ctls_sharedmem->CtlSnapShot)) + Read;
int DiffAt = us428ctls_sharedmem->CtlSnapShotDiffersAt[Read];
while ((pollrc = poll(pfds, npfd, 60000)) >= 0) {
if (pfds[0].revents) {
if (verbose > 1 || pfds[0].revents & (POLLERR|POLLHUP))
printf("poll returned 0x%X\n", pfds[0].revents);
if (pfds[0].revents & (POLLERR|POLLHUP))
return -ENXIO;
int Last = us428ctls_sharedmem->CtlSnapShotLast;
if (verbose > 1)
PCtlSnapShot->dump(DiffAt);
PCtlSnapShot->analyse(Red, DiffAt);
Red = PCtlSnapShot;
us428ctls_sharedmem->CtlSnapShotRed = Read;
printf("Last is %i\n", Last);
while (us428ctls_sharedmem->CtlSnapShotRed != Last) {
static Cus428_ctls *Red = 0;
int Read = us428ctls_sharedmem->CtlSnapShotRed + 1;
if (Read >= N_us428_ctl_BUFS || Read < 0)
Read = 0;
Cus428_ctls* PCtlSnapShot = ((Cus428_ctls*)(us428ctls_sharedmem->CtlSnapShot)) + Read;
int DiffAt = us428ctls_sharedmem->CtlSnapShotDiffersAt[Read];
if (verbose > 1)
PCtlSnapShot->dump(DiffAt);
PCtlSnapShot->analyse(Red, DiffAt);
Red = PCtlSnapShot;
us428ctls_sharedmem->CtlSnapShotRed = Read;
}
}
else if (pollrc > 0) Midi.ProcessMidiEvents();
}
return pollrc;
}
int main (int argc, char *argv[])
@ -186,7 +199,7 @@ int main (int argc, char *argv[])
/* probe the all cards */
for (c = 0; c < SND_CARDS; c++) {
verbose--;
// verbose--;
sprintf(name, "hw:%d", c);
if (! US428Control(name))
card = c;

View file

@ -148,8 +148,17 @@ struct us428_lights{
public:
enum eLight{
eL_Select0 = 0,
eL_Rec0 = 8,
eL_Mute0 = 16,
eL_InputMonitor = 25
eL_Solo = 24,
eL_InputMonitor = 25,
eL_BankL = 26,
eL_BankR = 27,
eL_Rew = 28,
eL_FFwd = 29,
eL_Play = 30,
eL_Record = 31,
eL_Null
};
bool LightIs(int L){
return Light[L / 8].Value & (1 << (L % 8));