mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-29 05:40:25 -04:00
Added playmidi1...
This commit is contained in:
parent
fa2745a3c5
commit
53cbbeee14
5 changed files with 2356 additions and 2 deletions
|
|
@ -1,4 +1,4 @@
|
|||
check_PROGRAMS=control mixer switches pause pcm latency seq
|
||||
check_PROGRAMS=control mixer switches pause pcm latency seq playmidi1
|
||||
|
||||
control_LDADD=../src/libasound.la
|
||||
mixer_LDADD=../src/libasound.la
|
||||
|
|
@ -7,8 +7,10 @@ pause_LDADD=../src/libasound.la
|
|||
pcm_LDADD=../src/libasound.la
|
||||
latency_LDADD=../src/libasound.la
|
||||
seq_LDADD=../src/libasound.la
|
||||
playmidi1_LDADD=../src/libasound.la
|
||||
|
||||
INCLUDES=-I$(top_srcdir)/include
|
||||
CFLAGS=-static -Wall -pipe -g
|
||||
|
||||
EXTRA_DIST=seq-decoder.c seq-sender.c
|
||||
EXTRA_DIST=seq-decoder.c seq-sender.c midifile.h midifile.c midifile.3
|
||||
|
||||
|
|
|
|||
336
test/midifile.3
Normal file
336
test/midifile.3
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
.TH MIDIFILE 3
|
||||
.SH NAME
|
||||
mfread,mfwrite - read and write a standard MIDI file
|
||||
.SH SYNOPSIS
|
||||
\fC#include "mfread.h"
|
||||
|
||||
mfread ()
|
||||
|
||||
.nf
|
||||
int (*Mf_getc) ();
|
||||
int (*Mf_putc) ();
|
||||
int (*Mf_error) (char *msg);
|
||||
int (*Mf_header) (int format, int ntrks, int division);
|
||||
int (*Mf_trackstart) ();
|
||||
int (*Mf_trackend) ();
|
||||
int (*Mf_noteon) (int chan, int pitch, int vol);
|
||||
int (*Mf_noteoff) (int chan, int pitch, int vol);
|
||||
int (*Mf_pressure) (int chan, int pitch, int pressure);
|
||||
int (*Mf_parameter) (int chan, int control, int value);
|
||||
int (*Mf_pitchbend) (int chan, int msb, int lsb);
|
||||
int (*Mf_program) (int chan, int program);
|
||||
int (*Mf_chanpressure) (int chan, int pressure);
|
||||
int (*Mf_sysex) (int leng, char *msg);
|
||||
int (*Mf_metamisc) (int type, int leng, int msg);
|
||||
int (*Mf_seqspecific) (int type, int leng, int msg);
|
||||
int (*Mf_seqnum) (int num);
|
||||
int (*Mf_text) (int type, int leng, int msg);
|
||||
int (*Mf_eot) ();
|
||||
int (*Mf_timesig) (int numer, int denom, int clocks, int qnotes);
|
||||
int (*Mf_smpte) (int hour, int min, int sec, int frame, int fract);
|
||||
int (*Mf_tempo) (int microsecs);
|
||||
int (*Mf_keysig) (int sharpflat, int minor);
|
||||
int (*Mf_arbitrary) (int leng, int msg);
|
||||
int Mf_nomerge;
|
||||
long Mf_currtime;
|
||||
.fi
|
||||
.sp 1
|
||||
mfwrite(int format, int ntracks, int division, FILE *fp)
|
||||
.sp 1
|
||||
.nf
|
||||
int (*Mf_writetrack)(int track);
|
||||
int (*Mf_writetempotrack)();
|
||||
|
||||
void mf_write_midi_event(delta, type, chan, data, size)
|
||||
unsigned long delta;
|
||||
unsigned int type,chan,size;
|
||||
char *data;
|
||||
|
||||
void mf_write_meta_event(delta, type, data, size)
|
||||
unsigned long delta;
|
||||
unsigned int type,chan,size;
|
||||
char *data;
|
||||
|
||||
void mf_write_tempo(tempo)
|
||||
unsigned long tempo;
|
||||
|
||||
unsigned long mf_sec2ticks(float seconds, int division, int tempo)
|
||||
float seconds;
|
||||
int division;
|
||||
unsigned int tempo;
|
||||
|
||||
float mf_ticks2sec(ticks, division, tempo)
|
||||
unsigned long ticks;
|
||||
int division;
|
||||
unsigned int tempo;
|
||||
.fi
|
||||
|
||||
.SH DESCRIPTION
|
||||
The \fCmfread\fR function reads and inteprets a standard MIDI file.
|
||||
To use it you need to understand the general form of a
|
||||
MIDI file and the type of information it contains, but you don't
|
||||
need to know much, if anything, about the detailed format of the file
|
||||
and the mechanics of reading it reliably and portably.
|
||||
|
||||
The \fCmfwrite\fR function writes a standard MIDI file making
|
||||
use of user-defined functions that access the program's
|
||||
data structure. To use it you need to define your own Mf_writetrack
|
||||
routine and then make use of the write_* family of routines to
|
||||
write out the MIDI data. The \fCmfwrite\fR routine takes
|
||||
care of the file format and writing the file and track chunk headers.
|
||||
|
||||
.SH READING STANDARD MIDI FILES
|
||||
A single call to \fCmfread\fR will read an entire MIDI file.
|
||||
The interface to \fCmfread\fR is a set of external variables
|
||||
named \fCMf_*\fR, most of which are function pointers to be called
|
||||
from within \fCmfread\fR during the process of parsing the MIDI file.
|
||||
Before calling \fCmfread\fR, the only
|
||||
requirement is that you assign a value
|
||||
to \fCMf_getc\fR - a pointer to a function that will return
|
||||
characters from the MIDI file, using -1 to indicate EOF.
|
||||
All the rest of the function
|
||||
pointers are initialized to NULL, and the default action for each
|
||||
is to do nothing. The following is a complete program using \fCmfread\fR
|
||||
that could serve as a 'syntax checker' for MIDI files:
|
||||
|
||||
.in +1i
|
||||
.ft C
|
||||
.nf
|
||||
#include <stdio.h>
|
||||
#include "midifile.h"
|
||||
|
||||
mygetc()
|
||||
{
|
||||
/* use standard input */
|
||||
return(getchar());
|
||||
}
|
||||
|
||||
main()
|
||||
{
|
||||
Mf_getc = mygetc;
|
||||
mfread();
|
||||
exit(0);
|
||||
}
|
||||
.fi
|
||||
.ft R
|
||||
.in -1i
|
||||
|
||||
This takes advantage of the default action when an error is detected, which
|
||||
is to exit silently with a return code of 1. An error function of your
|
||||
own can be used by giving a value to \fCMf_error\fR; the function will be
|
||||
called with the error message as an argument.
|
||||
The other \fCMf_* variables can similarly be used to call arbitrary
|
||||
functions while parsing the MIDI file. The descriptions below
|
||||
of the information passed to these functions is sparse; refer to
|
||||
the MIDI file standard for the complete descriptions.
|
||||
|
||||
\fCMf_header\fR is the first function to be called, and its arguments
|
||||
contain information from the MIDI file's header; the format (0,1, or 2),
|
||||
the number of tracks, and the division of a quarter-note that defines
|
||||
the times units.
|
||||
\fCMf_trackstart\fR and
|
||||
\fCMf_trackend\fR are called at the beginning and end of each track.
|
||||
|
||||
Once inside a track, each separate message causes a function to be called.
|
||||
For example, each note-on message causes \fCMf_noteon\fR to be called
|
||||
with the channel, pitch, and volume as arguments. The time at which
|
||||
the message occurred is stored in \fCMf_currtime\fR - one of the few
|
||||
external variables that isn't a function pointer. The other channel messages
|
||||
are handled in a similar and obvious fashion -
|
||||
\fCMf_noteoff\fR,
|
||||
\fCMf_pressure\fR,
|
||||
\fCMf_parameter\fR,
|
||||
\fCMf_pitchbend\fR,
|
||||
\fCMf_program\fR,
|
||||
and \fCMf_chanpressure\fR. See the declarations above for the arguments
|
||||
that are passed to each.
|
||||
|
||||
System exclusive messages are handled by calling \fCMf_sysex\fR, passing
|
||||
as arguments the message length and a pointer to a static buffer containing
|
||||
the entire message.
|
||||
The buffer is expanded when necessary; memory availability is the only limit
|
||||
to its size. Normally, 'continued' system exclusives are automatically
|
||||
merged, and \fCMf_sysex\fR is only called once. It you want to disable this
|
||||
you can set \fCMf_nomerge\fR to 1, causing \fCMf_sysex\fR to be called
|
||||
once for each part of the message.
|
||||
|
||||
\fCMf_seqnum\fR is called by the \fImeta\fR message that provides
|
||||
a sequence number,
|
||||
which if present must appear at the beginning of a track.
|
||||
The tempo \fImeta\fR message causes \fCMf_tempo\fR to be called; its
|
||||
argument is the number of microseconds per MIDI quarter-note (24 MIDI clocks).
|
||||
The end-of-track \fImeta\fR message causes \fCMf_eot\fR to be called.
|
||||
The key signature \fImeta\fR message causes \fCMf_keysig\fR to be called;
|
||||
the first argument conveys the number of sharps or flats, the second
|
||||
argument is 1 if the key is minor.
|
||||
|
||||
The \fCMf_timesig\fR and \fCMf_smpte\fR functions are called when the
|
||||
corresponding \fImeta\fR messages are seen. See the MIDI file standard
|
||||
for a description of their arguments.
|
||||
|
||||
The \fItext\fR messages in the MIDI file standard are of the following
|
||||
types:
|
||||
|
||||
.in +1i
|
||||
.nf
|
||||
0x01 Text Event
|
||||
0x02 Copyright
|
||||
0x03 Sequence/Track Name
|
||||
0x04 Instrument
|
||||
0x05 Lyric
|
||||
0x06 Marker
|
||||
0x07 Cue Point
|
||||
0x08-0x0F Reserverd but Undefined
|
||||
.fi
|
||||
.in -1i
|
||||
|
||||
\fCMf_text\fR is called for each of these; the arguments are
|
||||
the type number, the message length, and a pointer to the message buffer.
|
||||
|
||||
Misceallaneous \fImeta\fR messages are handled by \fCMf_metamisc\fR,
|
||||
sequencer-specific messages are handled by \fCMf_seqspecific\fR, and
|
||||
arbitrary "escape" messages (started with 0xF7) are handled by
|
||||
\fCMf_arbitrary\fR.
|
||||
.SH READING EXAMPLE
|
||||
The following is a \fCstrings\fR-like program for MIDI files:
|
||||
|
||||
.in +1i
|
||||
.ft C
|
||||
.nf
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include "midifile.h"
|
||||
|
||||
FILE *F;
|
||||
|
||||
mygetc() { return(getc(F)); }
|
||||
|
||||
mytext(type,leng,msg)
|
||||
char *msg;
|
||||
{
|
||||
char *p;
|
||||
char *ep = msg + leng;
|
||||
|
||||
for ( p=msg; p<ep ; p++ )
|
||||
putchar( isprint(*p) ? *p : '?' );
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
main(argc,argv)
|
||||
char **argv;
|
||||
{
|
||||
if ( argc > 1 )
|
||||
F = fopen(argv[1],"r");
|
||||
else
|
||||
F = stdin;
|
||||
|
||||
Mf_getc = mygetc;
|
||||
Mf_text = mytext;
|
||||
|
||||
mfread();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
.fi
|
||||
.ft R
|
||||
.in -1i
|
||||
.sp
|
||||
.SH WRITING STANDARD MIDI FILES
|
||||
A single call to \fCmfwrite\fR will write an entire MIDI file. Before
|
||||
calling \fCmfwrite\fR, you must assign values to function pointers
|
||||
\fCMf_writetrack\fR and \fCMf_putc\fR. The first is a routine to
|
||||
access your MIDI data structure, which can make use of other library
|
||||
routines to write the actual MIDI data. The routine
|
||||
\fCMf_writetrack\fR will be passed a single parameter which is the
|
||||
number of the track to be written. The pointer \fCMf_putc\fR should be
|
||||
set to point to a routine that accepts a charcter as input, writes that
|
||||
character to a file, and returns the value that was written. In the
|
||||
case of a format 1 file, a routine has to be written to write a tempo
|
||||
map, and assigned to the function pointer \fCMf_writetempotrack\fR.
|
||||
This is because format 1 files assume the first track written is a
|
||||
tempo track.
|
||||
|
||||
\fCmf_write_midi_event\fR and \fCmf_write_meta_event\fR are routines
|
||||
that should be called from your \fCMf_writetrack\fR routine to write
|
||||
out MIDI events. The delta time param is the number of ticks since the
|
||||
last event. The int "type" is the type of MIDI message. The int "chan"
|
||||
is the MIDI channel, which can be between 1 and 16. The char pointer
|
||||
"data" points to an array containing the data bytes, if any exist. The
|
||||
int "size" is the number of data bytes.
|
||||
|
||||
\fCmf_sec2ticks\fR and \fCmf_ticks2sec\fR are utility routines
|
||||
to help you convert between the MIDI file parameter of ticks
|
||||
and the more standard seconds. The int "division" is the same
|
||||
division parameter from the file header, and tempo is expressed
|
||||
in microseconds per MIDI quarter-note, or "24ths of a microsecond
|
||||
per MIDI clock". The division has two meanings, depending on
|
||||
whether bit 15 is set or not. If bit 15 of division is zero,
|
||||
bits 14 through 0 represent the number of delta-time "ticks"
|
||||
which make up a quarter note. If bit 15 of division is a one,
|
||||
delta-times in a file correspond to subdivisions of a second
|
||||
similiar to SMPTE and MIDI time code. In this format bits
|
||||
14 through 8 contain one of four values - 24, -25, -29, or -30,
|
||||
corresponding to the four standard SMPTE and MIDI time code
|
||||
frame per second formats, where -29 represents 30 drop frame.
|
||||
The second byte consisting of bits 7 through 0 corresponds
|
||||
the the resolution within a frame. Refer the Standard MIDI Files
|
||||
1.0 spec for more details.
|
||||
|
||||
.SH WRITING EXAMPLE
|
||||
The following is a simple program to demonstrate writing MIDI files.
|
||||
The track would consist of a series of quarter notes from lowest to
|
||||
highest in pitch at constant velocity, each separted by a quarter-note
|
||||
rest.
|
||||
.sp
|
||||
.in +1i
|
||||
.ft C
|
||||
.nf
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include "midifile.h"
|
||||
|
||||
FILE *fp;
|
||||
myputc(c) { return(putc(c,fp));}
|
||||
|
||||
int mywritetrack(track)
|
||||
int track;
|
||||
{
|
||||
int i;
|
||||
char data[2];
|
||||
|
||||
/* 120 beats/per/second */
|
||||
mf_write_tempo((long)500000);
|
||||
|
||||
for(i = 1 ; i < 128; i++){
|
||||
data[0] = i; /* note number */
|
||||
data[1] = 64; /* velocity */
|
||||
if(!mf_write_midi_event(480,note_on,1,data,2))
|
||||
return(-1);
|
||||
if(!mf_write_midi_event(480,note_off,1,data,2))
|
||||
return(-1);
|
||||
}
|
||||
|
||||
return(1);
|
||||
} /* end of write_track() */
|
||||
|
||||
main(argc,argv)
|
||||
char **argv;
|
||||
{
|
||||
if((fp = fopen(argv[1],"w")) == 0L)
|
||||
exit(1);
|
||||
|
||||
Mf_putc = myputc;
|
||||
Mf_writetrack = mywritetrack;
|
||||
|
||||
/* write a single track */
|
||||
mfwrite(0,1,480,fp);
|
||||
}
|
||||
.sp
|
||||
.fi
|
||||
.ft R
|
||||
.in -1i
|
||||
.sp
|
||||
.SH AUTHOR
|
||||
Tim Thompson (att!twitch!glimmer!tjt)
|
||||
.SH CONTRIBUTORS
|
||||
Michael Czeiszperger (mike@pan.com)
|
||||
1173
test/midifile.c
Normal file
1173
test/midifile.c
Normal file
File diff suppressed because it is too large
Load diff
132
test/midifile.h
Normal file
132
test/midifile.h
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/* definitions for MIDI file parsing code */
|
||||
extern int (*Mf_getc)();
|
||||
extern void (*Mf_header)();
|
||||
extern void (*Mf_trackstart)();
|
||||
extern void (*Mf_trackend)();
|
||||
extern void (*Mf_noteon)();
|
||||
extern void (*Mf_noteoff)();
|
||||
extern void (*Mf_pressure)();
|
||||
extern void (*Mf_parameter)();
|
||||
extern void (*Mf_pitchbend)();
|
||||
extern void (*Mf_program)();
|
||||
extern void (*Mf_chanpressure)();
|
||||
extern void (*Mf_sysex)();
|
||||
extern void (*Mf_metamisc)();
|
||||
extern void (*Mf_seqspecific)();
|
||||
extern void (*Mf_seqnum)();
|
||||
extern void (*Mf_text)();
|
||||
extern void (*Mf_eot)();
|
||||
extern void (*Mf_timesig)();
|
||||
extern void (*Mf_smpte)();
|
||||
extern void (*Mf_tempo)();
|
||||
extern void (*Mf_keysig)();
|
||||
extern void (*Mf_arbitrary)();
|
||||
extern void (*Mf_error)();
|
||||
extern unsigned long Mf_currtime;
|
||||
extern unsigned long Mf_realtime;
|
||||
extern unsigned long Mf_currtempo;
|
||||
extern int Mf_division;
|
||||
extern int Mf_nomerge;
|
||||
#ifdef READ_MODS
|
||||
extern unsigned char *Mf_file_contents;
|
||||
extern int Mf_file_size;
|
||||
#endif
|
||||
|
||||
/* definitions for MIDI file writing code */
|
||||
extern int (*Mf_putc)();
|
||||
extern int (*Mf_writetrack)();
|
||||
extern int (*Mf_writetempotrack)();
|
||||
|
||||
extern void midifile();
|
||||
extern unsigned long mf_sec2ticks();
|
||||
extern void mfwrite();
|
||||
extern int mf_write_meta_event();
|
||||
extern int mf_write_midi_event(unsigned long delta_time, int type,
|
||||
int chan, char *data, unsigned long size);
|
||||
extern double mf_ticks2sec(unsigned long ticks,int division,unsigned long tempo);
|
||||
extern void mf_write_tempo();
|
||||
extern void mf_write_seqnum();
|
||||
extern void mfread();
|
||||
extern void mferror(char *s);
|
||||
|
||||
#ifndef NO_LC_DEFINES
|
||||
/* MIDI status commands most significant bit is 1 */
|
||||
#define note_off 0x80
|
||||
#define note_on 0x90
|
||||
#define poly_aftertouch 0xa0
|
||||
#define control_change 0xb0
|
||||
#define program_chng 0xc0
|
||||
#define channel_aftertouch 0xd0
|
||||
#define pitch_wheel 0xe0
|
||||
#define system_exclusive 0xf0
|
||||
#define delay_packet (1111)
|
||||
|
||||
/* 7 bit controllers */
|
||||
#define damper_pedal 0x40
|
||||
#define portamento 0x41
|
||||
#define sostenuto 0x42
|
||||
#define soft_pedal 0x43
|
||||
#define general_4 0x44
|
||||
#define hold_2 0x45
|
||||
#define general_5 0x50
|
||||
#define general_6 0x51
|
||||
#define general_7 0x52
|
||||
#define general_8 0x53
|
||||
#ifndef PLAYMIDI
|
||||
#define tremolo_depth 0x5c
|
||||
#define ctrl_chorus_depth 0x5d
|
||||
#define detune 0x5e
|
||||
#define phaser_depth 0x5f
|
||||
#endif
|
||||
|
||||
/* parameter values */
|
||||
#define data_inc 0x60
|
||||
#define data_dec 0x61
|
||||
|
||||
/* parameter selection */
|
||||
#define non_reg_lsb 0x62
|
||||
#define non_reg_msb 0x63
|
||||
#define reg_lsb 0x64
|
||||
#define reg_msb 0x65
|
||||
|
||||
/* Standard MIDI Files meta event definitions */
|
||||
#define meta_event 0xFF
|
||||
#define sequence_number 0x00
|
||||
#define text_event 0x01
|
||||
#define copyright_notice 0x02
|
||||
#define sequence_name 0x03
|
||||
#define instrument_name 0x04
|
||||
#define lyric 0x05
|
||||
#define marker 0x06
|
||||
#define cue_point 0x07
|
||||
#define channel_prefix 0x20
|
||||
#define end_of_track 0x2f
|
||||
#define set_tempo 0x51
|
||||
#define smpte_offset 0x54
|
||||
#define time_signature 0x58
|
||||
#define key_signature 0x59
|
||||
#define sequencer_specific 0x74
|
||||
|
||||
/* Manufacturer's ID number */
|
||||
#define Seq_Circuits (0x01) /* Sequential Circuits Inc. */
|
||||
#define Big_Briar (0x02) /* Big Briar Inc. */
|
||||
#define Octave (0x03) /* Octave/Plateau */
|
||||
#define Moog (0x04) /* Moog Music */
|
||||
#define Passport (0x05) /* Passport Designs */
|
||||
#define Lexicon (0x06) /* Lexicon */
|
||||
#define Tempi (0x20) /* Bon Tempi */
|
||||
#define Siel (0x21) /* S.I.E.L. */
|
||||
#define Kawai (0x41)
|
||||
#define Roland (0x42)
|
||||
#define Korg (0x42)
|
||||
#define Yamaha (0x43)
|
||||
#endif
|
||||
|
||||
/* miscellaneous definitions */
|
||||
#define MThd 0x4d546864
|
||||
#define MTrk 0x4d54726b
|
||||
|
||||
#ifndef NO_LC_DEFINES
|
||||
#define lowerbyte(x) ((unsigned char)(x & 0xff))
|
||||
#define upperbyte(x) ((unsigned char)((x & 0xff00)>>8))
|
||||
#endif
|
||||
711
test/playmidi1.c
Normal file
711
test/playmidi1.c
Normal file
|
|
@ -0,0 +1,711 @@
|
|||
/*
|
||||
* MIDI file player for ALSA sequencer
|
||||
* (type 0 only!, the library that is used doesn't support merging of tracks)
|
||||
*
|
||||
* Copyright (c) 1998 by Frank van de Pol <F.K.W.van.de.Pol@inter.nl.net>
|
||||
*
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/* define this if you want to send real-time time stamps instead of midi ticks to the ALSA sequencer */
|
||||
/*#define USE_REALTIME */
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "midifile.h" /* SMF library header */
|
||||
#include "midifile.c" /* SMF library code */
|
||||
|
||||
#include "sys/asoundlib.h"
|
||||
|
||||
//#define DEST_QUEUE_NUMBER 0
|
||||
#define DEST_QUEUE_NUMBER 7
|
||||
#define DEST_CLIENT_NUMBER 64
|
||||
//#define DEST_CLIENT_NUMBER 72
|
||||
//#define DEST_CLIENT_NUMBER 128
|
||||
//#define DEST_CLIENT_NUMBER 255
|
||||
//#define DEST_CLIENT_NUMBER SND_SEQ_ADDRESS_BROADCAST
|
||||
|
||||
FILE *F;
|
||||
void* seq_handle = NULL;
|
||||
int ppq = 96;
|
||||
|
||||
double local_secs = 0;
|
||||
int local_ticks = 0;
|
||||
int local_tempo = 500000;
|
||||
|
||||
static int dest_queue = DEST_QUEUE_NUMBER;
|
||||
static int dest_client = DEST_CLIENT_NUMBER;
|
||||
static int dest_port = 0;
|
||||
//static int dest_port = 1;
|
||||
static int source_channel = 0;
|
||||
static int source_port = 0;
|
||||
|
||||
|
||||
extern void alsa_start_timer(void);
|
||||
extern void alsa_stop_timer(void);
|
||||
|
||||
|
||||
static inline double tick2time_dbl(int tick)
|
||||
{
|
||||
return local_secs + ((double) (tick - local_ticks) * (double) local_tempo * 1.0E-9 / (double) ppq);
|
||||
}
|
||||
|
||||
#ifdef USE_REALTIME
|
||||
static void tick2time(snd_seq_real_time_t * tm, int tick)
|
||||
{
|
||||
double secs = tick2time_dbl(tick);
|
||||
|
||||
//double secs = ((double) tick * (double) local_tempo * 1.0E-6 / (double) ppq);
|
||||
|
||||
tm->tv_sec = secs;
|
||||
tm->tv_nsec = (secs - tm->tv_sec) * 1.0E9;
|
||||
|
||||
//printf("secs = %lf = %d.%09d\n", secs, tm->tv_sec, tm->tv_nsec);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* sleep until sequencer has reached specified timestamp, to guard that we play too much events ahead */
|
||||
void sleep_seq(int tick)
|
||||
{
|
||||
#if 0
|
||||
snd_seq_queue_info_t queue_info;
|
||||
const int COUNT_MAX = 500;
|
||||
const int COUNT_MIN = 50;
|
||||
static int count = 0;
|
||||
count++;
|
||||
if (count >= COUNT_MAX)
|
||||
{
|
||||
while (snd_seq_flush_output(seq_handle) > COUNT_MIN)
|
||||
sched_yield ();
|
||||
count = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* write event to ALSA sequencer */
|
||||
void write_ev_im(snd_seq_event_t * ev)
|
||||
{
|
||||
int written;
|
||||
|
||||
ev->flags &= ~SND_SEQ_EVENT_LENGTH_MASK;
|
||||
ev->flags |= SND_SEQ_EVENT_LENGTH_FIXED;
|
||||
|
||||
|
||||
written = -ENOMEM;
|
||||
while (written<0) {
|
||||
written = snd_seq_event_output (seq_handle, ev);
|
||||
if (written<0) {
|
||||
printf("written = %i (%s)\n", written, strerror(-written));
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* write event to ALSA sequencer */
|
||||
void write_ev(snd_seq_event_t * ev)
|
||||
{
|
||||
sleep_seq(ev->time.tick-ppq);
|
||||
write_ev_im(ev);
|
||||
}
|
||||
|
||||
/* write variable length event to ALSA sequencer */
|
||||
void write_ev_var(snd_seq_event_t * ev, int len, void *ptr)
|
||||
{
|
||||
int bytes;
|
||||
int written;
|
||||
unsigned char *buf;
|
||||
|
||||
sleep_seq(ev->time.tick+ppq);
|
||||
|
||||
ev->flags &= ~SND_SEQ_EVENT_LENGTH_MASK;
|
||||
ev->flags |= SND_SEQ_EVENT_LENGTH_VARIABLE;
|
||||
ev->data.ext.len = len;
|
||||
ev->data.ext.ptr = ptr;
|
||||
|
||||
written = 0;
|
||||
while (!written) {
|
||||
written = snd_seq_event_output (seq_handle, ev);
|
||||
if (!written)
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int mygetc(void)
|
||||
{
|
||||
return (getc(F));
|
||||
}
|
||||
|
||||
void mytext(int type, int leng, char *msg)
|
||||
{
|
||||
char *p;
|
||||
char *ep = msg + leng;
|
||||
|
||||
for (p = msg; p < ep; p++)
|
||||
putchar(isprint(*p) ? *p : '?');
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
void do_header(int format, int ntracks, int division)
|
||||
{
|
||||
printf("smf format %d, %d tracks, %d ppq\n", format, ntracks, division);
|
||||
ppq = division;
|
||||
|
||||
if ((format != 0) || (ntracks != 1)) {
|
||||
printf("This player does not support merging of tracks.\n");
|
||||
alsa_stop_timer();
|
||||
exit(1);
|
||||
}
|
||||
/* set ppq */
|
||||
{
|
||||
snd_seq_queue_info_t queue_info;
|
||||
|
||||
queue_info.queue = 1; /* queue we're using */
|
||||
queue_info.ppq = ppq;
|
||||
//queue_info.tempo = -1; /* don't change */
|
||||
queue_info.tempo = 500000; /* don't change */
|
||||
if (snd_seq_set_queue_info (seq_handle, dest_queue, &queue_info) < 0) {
|
||||
perror("ioctl");
|
||||
exit(1);
|
||||
}
|
||||
printf("ALSA Timer updated, PPQ = %d\n", queue_info.ppq);
|
||||
}
|
||||
|
||||
/* start playing... */
|
||||
alsa_start_timer();
|
||||
}
|
||||
|
||||
void do_tempo(int us)
|
||||
{
|
||||
double bpm;
|
||||
snd_seq_event_t ev;
|
||||
|
||||
bpm = 60.0E6 / (double) us;
|
||||
|
||||
printf("tempo = %d us/beat\n", us);
|
||||
printf("tempo = %.2f bpm\n", bpm);
|
||||
|
||||
/* store new tempo and timestamp of tempo change */
|
||||
local_secs = tick2time_dbl(Mf_currtime);
|
||||
local_ticks = Mf_currtime;
|
||||
local_tempo = us;
|
||||
|
||||
|
||||
/* and send tempo change event to the sequencer.... */
|
||||
ev.source.port = dest_port;
|
||||
ev.source.channel = source_channel;
|
||||
|
||||
ev.dest.queue = dest_queue;
|
||||
//ev.dest.client = dest_client; /* broadcast */
|
||||
ev.dest.client = 255; /* broadcast */
|
||||
ev.dest.port = 0;
|
||||
ev.dest.channel = 0; /* don't care */
|
||||
|
||||
#ifdef USE_REALTIME
|
||||
ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_ABS;
|
||||
tick2time(&ev.time.real, Mf_currtime);
|
||||
#else
|
||||
ev.flags = SND_SEQ_TIME_STAMP_TICK | SND_SEQ_TIME_MODE_ABS;
|
||||
ev.time.tick = Mf_currtime;
|
||||
#endif
|
||||
|
||||
ev.type = SND_SEQ_EVENT_TEMPO;
|
||||
ev.data.control.value = us;
|
||||
|
||||
write_ev_im(&ev);
|
||||
|
||||
}
|
||||
|
||||
void do_noteon(int chan, int pitch, int vol)
|
||||
{
|
||||
snd_seq_event_t ev;
|
||||
|
||||
ev.source.port = dest_port;
|
||||
ev.source.channel = source_channel;
|
||||
|
||||
ev.dest.queue = dest_queue;
|
||||
ev.dest.client = dest_client;
|
||||
ev.dest.port = dest_port;
|
||||
ev.dest.channel = chan;
|
||||
|
||||
#ifdef USE_REALTIME
|
||||
ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_ABS;
|
||||
tick2time(&ev.time.real, Mf_currtime);
|
||||
#else
|
||||
ev.flags = SND_SEQ_TIME_STAMP_TICK | SND_SEQ_TIME_MODE_ABS;
|
||||
ev.time.tick = Mf_currtime;
|
||||
#endif
|
||||
|
||||
ev.type = SND_SEQ_EVENT_NOTEON;
|
||||
ev.data.note.note = pitch;
|
||||
ev.data.note.velocity = vol;
|
||||
|
||||
write_ev(&ev);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void do_noteoff(int chan, int pitch, int vol)
|
||||
{
|
||||
snd_seq_event_t ev;
|
||||
|
||||
ev.source.port = dest_port;
|
||||
ev.source.channel = source_channel;
|
||||
|
||||
ev.dest.queue = dest_queue;
|
||||
ev.dest.client = dest_client;
|
||||
ev.dest.port = dest_port;
|
||||
ev.dest.channel = chan;
|
||||
|
||||
#ifdef USE_REALTIME
|
||||
ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_ABS;
|
||||
tick2time(&ev.time.real, Mf_currtime);
|
||||
#else
|
||||
ev.flags = SND_SEQ_TIME_STAMP_TICK | SND_SEQ_TIME_MODE_ABS;
|
||||
ev.time.tick = Mf_currtime;
|
||||
#endif
|
||||
|
||||
ev.type = SND_SEQ_EVENT_NOTEOFF;
|
||||
ev.data.note.note = pitch;
|
||||
ev.data.note.velocity = vol;
|
||||
|
||||
write_ev(&ev);
|
||||
}
|
||||
|
||||
|
||||
void do_program(int chan, int program)
|
||||
{
|
||||
snd_seq_event_t ev;
|
||||
|
||||
ev.source.port = dest_port;
|
||||
ev.source.channel = source_channel;
|
||||
|
||||
ev.dest.queue = dest_queue;
|
||||
ev.dest.client = dest_client;
|
||||
ev.dest.port = dest_port;
|
||||
ev.dest.channel = chan;
|
||||
|
||||
#ifdef USE_REALTIME
|
||||
ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_ABS;
|
||||
tick2time(&ev.time.real, Mf_currtime);
|
||||
#else
|
||||
ev.flags = SND_SEQ_TIME_STAMP_TICK | SND_SEQ_TIME_MODE_ABS;
|
||||
ev.time.tick = Mf_currtime;
|
||||
#endif
|
||||
|
||||
ev.type = SND_SEQ_EVENT_PGMCHANGE;
|
||||
ev.data.control.value = program;
|
||||
|
||||
write_ev_im(&ev);
|
||||
}
|
||||
|
||||
|
||||
void do_parameter(int chan, int control, int value)
|
||||
{
|
||||
snd_seq_event_t ev;
|
||||
|
||||
ev.source.port = dest_port;
|
||||
ev.source.channel = source_channel;
|
||||
|
||||
ev.dest.queue = dest_queue;
|
||||
ev.dest.client = dest_client;
|
||||
ev.dest.port = dest_port;
|
||||
ev.dest.channel = chan;
|
||||
|
||||
#ifdef USE_REALTIME
|
||||
ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_ABS;
|
||||
tick2time(&ev.time.real, Mf_currtime);
|
||||
#else
|
||||
ev.flags = SND_SEQ_TIME_STAMP_TICK | SND_SEQ_TIME_MODE_ABS;
|
||||
ev.time.tick = Mf_currtime;
|
||||
#endif
|
||||
|
||||
ev.type = SND_SEQ_EVENT_CONTROLLER;
|
||||
ev.data.control.param = control;
|
||||
ev.data.control.value = value;
|
||||
|
||||
write_ev(&ev);
|
||||
}
|
||||
|
||||
|
||||
void do_pitchbend(int chan, int lsb, int msb)
|
||||
{ /* !@#$% lsb & msb are in wrong order in docs */
|
||||
snd_seq_event_t ev;
|
||||
|
||||
ev.source.port = dest_port;
|
||||
ev.source.channel = source_channel;
|
||||
|
||||
ev.dest.queue = dest_queue;
|
||||
ev.dest.client = dest_client;
|
||||
ev.dest.port = dest_port;
|
||||
ev.dest.channel = chan;
|
||||
|
||||
#ifdef USE_REALTIME
|
||||
ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_ABS;
|
||||
tick2time(&ev.time.real, Mf_currtime);
|
||||
#else
|
||||
ev.flags = SND_SEQ_TIME_STAMP_TICK | SND_SEQ_TIME_MODE_ABS;
|
||||
ev.time.tick = Mf_currtime;
|
||||
#endif
|
||||
|
||||
ev.type = SND_SEQ_EVENT_PITCHBEND;
|
||||
ev.data.control.value = (lsb + (msb << 7)) - 8192;
|
||||
|
||||
write_ev(&ev);
|
||||
}
|
||||
|
||||
void do_pressure(int chan, int pitch, int pressure)
|
||||
{
|
||||
snd_seq_event_t ev;
|
||||
|
||||
ev.source.port = dest_port;
|
||||
ev.source.channel = source_channel;
|
||||
|
||||
ev.dest.queue = dest_queue;
|
||||
ev.dest.client = dest_client;
|
||||
ev.dest.port = dest_port;
|
||||
ev.dest.channel = chan;
|
||||
|
||||
#ifdef USE_REALTIME
|
||||
ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_ABS;
|
||||
tick2time(&ev.time.real, Mf_currtime);
|
||||
#else
|
||||
ev.flags = SND_SEQ_TIME_STAMP_TICK | SND_SEQ_TIME_MODE_ABS;
|
||||
ev.time.tick = Mf_currtime;
|
||||
#endif
|
||||
|
||||
ev.type = SND_SEQ_EVENT_KEYPRESS;
|
||||
ev.data.control.param = pitch;
|
||||
ev.data.control.value = pressure;
|
||||
|
||||
write_ev(&ev);
|
||||
}
|
||||
|
||||
void do_chanpressure(int chan, int pressure)
|
||||
{
|
||||
snd_seq_event_t ev;
|
||||
|
||||
ev.source.port = dest_port;
|
||||
ev.source.channel = source_channel;
|
||||
|
||||
ev.dest.queue = dest_queue;
|
||||
ev.dest.client = dest_client;
|
||||
ev.dest.port = dest_port;
|
||||
ev.dest.channel = chan;
|
||||
|
||||
#ifdef USE_REALTIME
|
||||
ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_ABS;
|
||||
tick2time(&ev.time.real, Mf_currtime);
|
||||
#else
|
||||
ev.flags = SND_SEQ_TIME_STAMP_TICK | SND_SEQ_TIME_MODE_ABS;
|
||||
ev.time.tick = Mf_currtime;
|
||||
#endif
|
||||
|
||||
ev.type = SND_SEQ_EVENT_CHANPRESS;
|
||||
ev.data.control.value = pressure;
|
||||
|
||||
write_ev(&ev);
|
||||
}
|
||||
|
||||
void do_sysex(int len, char *msg)
|
||||
{
|
||||
snd_seq_event_t ev;
|
||||
|
||||
#if 0
|
||||
int c;
|
||||
|
||||
printf("Sysex, len=%d\n", len);
|
||||
for (c = 0; c < len; c++) {
|
||||
printf(" %3d : %02x\n", c, (unsigned char) msg[c]);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
ev.source.port = dest_port;
|
||||
ev.source.channel = source_channel;
|
||||
|
||||
ev.dest.queue = dest_queue;
|
||||
ev.dest.client = dest_client;
|
||||
ev.dest.port = dest_port;
|
||||
ev.dest.channel = 0; /* don't care */
|
||||
|
||||
#ifdef USE_REALTIME
|
||||
ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_ABS;
|
||||
tick2time(&ev.time.real, Mf_currtime);
|
||||
#else
|
||||
ev.flags = SND_SEQ_TIME_STAMP_TICK | SND_SEQ_TIME_MODE_ABS;
|
||||
ev.time.tick = Mf_currtime;
|
||||
#endif
|
||||
|
||||
ev.type = SND_SEQ_EVENT_SYSEX;
|
||||
|
||||
write_ev_var(&ev, len, msg);
|
||||
}
|
||||
|
||||
/**/
|
||||
void alsa_sync ()
|
||||
{
|
||||
//dump buffer
|
||||
int left = snd_seq_flush_output (seq_handle);
|
||||
printf ("alsa_sync syncing\n");
|
||||
while (left > 0)
|
||||
{
|
||||
sched_yield ();
|
||||
left = snd_seq_flush_output (seq_handle);
|
||||
if (left < 0)
|
||||
{
|
||||
printf ("alsa_sync error!:%s\n", snd_strerror (left));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (;;)
|
||||
{
|
||||
snd_seq_queue_info_t queue_info;
|
||||
int tmp = snd_seq_get_queue_info (seq_handle, dest_queue, &queue_info);
|
||||
if (tmp < 0)
|
||||
{
|
||||
printf ("AlsaClient::sync snd_seq_get_queue_info:%s\n",
|
||||
snd_strerror (tmp));
|
||||
break;
|
||||
}
|
||||
|
||||
if (Mf_currtime < queue_info.tick)
|
||||
break;
|
||||
sched_yield ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* start timer */
|
||||
void alsa_start_timer(void)
|
||||
{
|
||||
snd_seq_event_t ev;
|
||||
|
||||
ev.source.port = SND_SEQ_PORT_SYSTEM_TIMER;
|
||||
ev.source.channel = 0;
|
||||
|
||||
ev.dest.queue = dest_queue;
|
||||
//ev.dest.client = 0; /* system */
|
||||
ev.dest.client = SND_SEQ_CLIENT_SYSTEM; /* system */
|
||||
//ev.dest.client = dest_client; /* broadcast */
|
||||
ev.dest.client = 255; /* broadcast */
|
||||
ev.dest.port = 0; /* timer */
|
||||
ev.dest.channel = 0; /* don't care */
|
||||
|
||||
ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_REL;
|
||||
//ev.flags = SND_SEQ_TIME_STAMP_TICK | SND_SEQ_TIME_MODE_REL;
|
||||
ev.time.real.tv_sec = 0;
|
||||
ev.time.real.tv_nsec = 0;
|
||||
|
||||
ev.type = SND_SEQ_EVENT_START;
|
||||
|
||||
write_ev_im(&ev);
|
||||
usleep(0.1E6);
|
||||
}
|
||||
|
||||
/* stop timer */
|
||||
void alsa_stop_timer(void)
|
||||
{
|
||||
|
||||
snd_seq_event_t ev;
|
||||
|
||||
ev.source.port = 0;
|
||||
ev.source.channel = 0;
|
||||
|
||||
ev.dest.queue = dest_queue;
|
||||
ev.dest.client = 0; /* system */
|
||||
ev.dest.client = dest_client; /* broadcast */
|
||||
ev.dest.port = 0; /* timer */
|
||||
ev.dest.channel = 0; /* don't care */
|
||||
|
||||
#ifdef USE_REALTIME
|
||||
ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_ABS;
|
||||
tick2time(&ev.time.real, Mf_currtime);
|
||||
#else
|
||||
ev.flags = SND_SEQ_TIME_STAMP_TICK | SND_SEQ_TIME_MODE_ABS;
|
||||
ev.time.tick = Mf_currtime;
|
||||
#endif
|
||||
|
||||
ev.type = SND_SEQ_EVENT_STOP;
|
||||
|
||||
write_ev_im(&ev);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *name;
|
||||
snd_seq_client_info_t inf;
|
||||
snd_seq_port_info_t src_port_info;
|
||||
snd_seq_queue_client_t queue_info;
|
||||
snd_seq_port_subscribe_t subscribe;
|
||||
int tmp;
|
||||
|
||||
#ifdef USE_REALTIME
|
||||
printf("ALSA MIDI Player, feeding events to real-time queue\n");
|
||||
#else
|
||||
printf("ALSA MIDI Player, feeding events to song queue\n");
|
||||
#endif
|
||||
|
||||
/* open sequencer device */
|
||||
//tmp = snd_seq_open (&seq_handle, SND_SEQ_OPEN_OUT);
|
||||
tmp = snd_seq_open (&seq_handle, SND_SEQ_OPEN);
|
||||
if (tmp < 0) {
|
||||
perror("open /dev/snd/seq");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
tmp = snd_seq_block_mode (seq_handle, 0);
|
||||
if (tmp < 0)
|
||||
{
|
||||
perror ("block_mode");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
|
||||
/* set name */
|
||||
memset(&inf, 0, sizeof(snd_seq_client_info_t));
|
||||
strcpy(inf.name, "MIDI file player");
|
||||
if (snd_seq_set_client_info (seq_handle, &inf) < 0) {
|
||||
perror("ioctl");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
//create port
|
||||
memset (&src_port_info, 0, sizeof (snd_seq_port_info_t));
|
||||
src_port_info.capability = SND_SEQ_PORT_CAP_OUT | SND_SEQ_PORT_CAP_SUBSCRIPTION;
|
||||
src_port_info.type = SND_SEQ_PORT_TYPE_MIDI_GENERIC;
|
||||
src_port_info.midi_channels = 16;
|
||||
src_port_info.synth_voices = 0;
|
||||
src_port_info.use = 0;
|
||||
src_port_info.kernel = NULL;
|
||||
tmp = snd_seq_create_port (seq_handle, &src_port_info);
|
||||
if (tmp < 0)
|
||||
{
|
||||
perror ("creat port");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
//setup queue
|
||||
queue_info.used = 1;
|
||||
//queue_info.low = 100;//???
|
||||
//queue_info.low = SND_SEQ_MAX_EVENTS-1;//???
|
||||
//queue_info.low = 500-1;//???
|
||||
//queue_info.low = 1;//???
|
||||
queue_info.low = 0;
|
||||
//queue_info.low = 0x7ffffff;//???
|
||||
//queue_info.high = 0x7ffffff;//???
|
||||
queue_info.high = 500-100;//???
|
||||
//queue_info.high = 50;//???
|
||||
//queue_info.high = 0;//???
|
||||
//queue_info.high = 1;//???
|
||||
tmp = snd_seq_set_queue_client (seq_handle, dest_queue,
|
||||
&queue_info);
|
||||
if (tmp < 0)
|
||||
{
|
||||
perror ("queue_client");
|
||||
exit (1);
|
||||
}
|
||||
#if (DEST_CLIENT_NUMBER == 64) || (DEST_CLIENT_NUMBER == 72)
|
||||
//setup subscriber
|
||||
printf ("debug subscribe src_port_info.client=%d\n",
|
||||
src_port_info.client);
|
||||
subscribe.sender.client = snd_seq_client_id (seq_handle);
|
||||
subscribe.sender.queue = dest_queue;
|
||||
subscribe.sender.port = src_port_info.port;
|
||||
subscribe.dest.client = dest_client;
|
||||
subscribe.dest.port = dest_port;
|
||||
subscribe.dest.queue = dest_queue;
|
||||
subscribe.realtime = 1;
|
||||
subscribe.exclusive = 0;
|
||||
tmp = snd_seq_subscribe_port (seq_handle, &subscribe);
|
||||
if (tmp < 0)
|
||||
{
|
||||
perror ("subscribe");
|
||||
exit (1);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (argc > 1)
|
||||
F = fopen(argv[1], "r");
|
||||
else
|
||||
F = stdin;
|
||||
|
||||
Mf_header = do_header;
|
||||
Mf_tempo = do_tempo;
|
||||
Mf_getc = mygetc;
|
||||
Mf_text = mytext;
|
||||
|
||||
Mf_noteon = do_noteon;
|
||||
Mf_noteoff = do_noteoff;
|
||||
Mf_program = do_program;
|
||||
Mf_parameter = do_parameter;
|
||||
Mf_pitchbend = do_pitchbend;
|
||||
Mf_pressure = do_pressure;
|
||||
Mf_chanpressure = do_chanpressure;
|
||||
Mf_sysex = do_sysex;
|
||||
|
||||
|
||||
/* stop timer in case it was left running by a previous client */
|
||||
{
|
||||
snd_seq_event_t ev;
|
||||
|
||||
ev.source.port = 0;
|
||||
ev.source.channel = 0;
|
||||
|
||||
ev.dest.queue = dest_queue;
|
||||
ev.dest.client = 0; /* system */
|
||||
//ev.dest.client = dest_client; /* broadcast */
|
||||
ev.dest.client = 255; /* broadcast */
|
||||
ev.dest.port = 0; /* timer */
|
||||
ev.dest.channel = 0; /* don't care */
|
||||
|
||||
ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_REL;
|
||||
//ev.flags = SND_SEQ_TIME_STAMP_TICK | SND_SEQ_TIME_MODE_REL;
|
||||
ev.time.real.tv_sec = 0;
|
||||
ev.time.real.tv_nsec = 0;
|
||||
|
||||
//ev.type = SND_SEQ_EVENT_STOP;
|
||||
ev.type = SND_SEQ_EVENT_START;
|
||||
|
||||
write_ev(&ev);
|
||||
}
|
||||
alsa_start_timer ();
|
||||
|
||||
/* go.. go.. go.. */
|
||||
mfread();
|
||||
|
||||
alsa_sync ();
|
||||
alsa_stop_timer();
|
||||
|
||||
snd_seq_close (seq_handle);
|
||||
|
||||
printf("Stopping at %lf s, tick %d\n", tick2time_dbl(Mf_currtime + 1), Mf_currtime + 1);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue