File: seq2midi.cpp

package info (click to toggle)
audacity 2.2.2-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 129,312 kB
  • sloc: ansic: 373,350; cpp: 276,880; sh: 56,060; python: 18,922; makefile: 10,309; lisp: 8,365; xml: 1,888; perl: 1,798; java: 1,551; asm: 545; pascal: 395; sed: 58; awk: 35
file content (166 lines) | stat: -rw-r--r-- 5,460 bytes parent folder | download | duplicates (9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// seq2midi.cpp -- simple sequence player, intended to help test/demo
// the allegro code

#include <fstream>
#include "allegro.h"
#include "porttime.h"
#include "portmidi.h"
#include "midicode.h"

using namespace std;

#define ROUND(x) (int) ((x)+0.5)


double time_elapsed()
{
    return Pt_Time() * 0.001;
}


void wait_until(double time)
{
    // print "." to stdout while waiting
    static double last_time = 0.0;
    double now = time_elapsed();
    if (now < last_time) last_time = now;
    while (now < time) {
        Pt_Sleep(1);
        now = time_elapsed();
        long now_sec = (long) now;
        long last_sec = (long) last_time;
        if (now_sec > last_sec) {
            fprintf(stdout, ".");
            fflush(stdout);
            last_time = now;
        }
    }
}


#define never 1000000 // represents infinite time

void midi_note_on(PortMidiStream *midi, double when, int chan, int key, int loud)
{
    unsigned long timestamp = (unsigned long) (when * 1000);
    chan = chan & 15;
    if (key > 127) key = 127;
    if (key < 0) key = 0;
    if (loud > 127) loud = 127;
    if (loud < 0) loud = 0;
    unsigned long data = Pm_Message(0x90 + chan, key, loud);
    Pm_WriteShort(midi, timestamp, data);
}


static void midi_channel_message_2(PortMidiStream *midi, double when, 
                                   int status, int chan, int data)
{
    unsigned long timestamp = (unsigned long) (when * 1000);
    chan = chan & 15;
    if (data > 127) data = 127;
    if (data < 0) data = 0;
    unsigned long msg = Pm_Message(status + chan, data, 0);
    Pm_WriteShort(midi, timestamp, msg);
}


static void midi_channel_message(PortMidiStream *midi, double when, 
                                 int status, int chan, int data, int data2)
{
    unsigned long timestamp = (unsigned long) (when * 1000);
    chan = chan & 15;
    if (data > 127) data = 127;
    if (data < 0) data = 0;
    if (data2 > 127) data2 = 127;
    if (data2 < 0) data2 = 0;
    unsigned long msg = Pm_Message(status + chan, data, data2);
    Pm_WriteShort(midi, timestamp, msg);
}


static const char *pressure_attr;
static const char *bend_attr;
static const char *program_attr;


void send_midi_update(Alg_update_ptr u, PortMidiStream *midi)
{
    if (u->get_attribute() == pressure_attr) {
        if (u->get_identifier() < 0) {
            midi_channel_message_2(midi, u->time, MIDI_TOUCH, u->chan,
                                   (int) (u->get_real_value() * 127));
        } else {
            midi_channel_message(midi, u->time, MIDI_POLY_TOUCH, u->chan, 
                                 u->get_identifier(), 
                                 (int) (u->get_real_value() * 127));
        }
    } else if (u->get_attribute() == bend_attr) {
        int bend = ROUND((u->get_real_value() + 1) * 8192);
        if (bend > 8191) bend = 8191;
        if (bend < 0) bend = 0;
        midi_channel_message(midi, u->time, MIDI_BEND, u->chan, 
                             bend >> 7, bend & 0x7F);
    } else if (u->get_attribute() == program_attr) {
        midi_channel_message_2(midi, u->time, MIDI_CH_PROGRAM, 
                               u->chan, u->get_integer_value());
    } else if (strncmp("control", u->get_attribute(), 7) == 0 &&
               u->get_update_type() == 'r') {
        int control = atoi(u->get_attribute() + 7);
        int val = ROUND(u->get_real_value() * 127);
        midi_channel_message(midi, u->time, MIDI_CTRL, u->chan, control, val);
    }
}


void seq2midi(Alg_seq &seq, PortMidiStream *midi)
{
    // prepare by doing lookup of important symbols
    pressure_attr = symbol_table.insert_string("pressurer") + 1;
    bend_attr = symbol_table.insert_string("bendr") + 1;
    program_attr = symbol_table.insert_string("programi") + 1;

    Alg_iterator iterator(&seq, true);
    iterator.begin();
    bool note_on;
    Alg_event_ptr e = iterator.next(&note_on);
    Pt_Start(1, NULL, NULL); // initialize time
    while (e) {
        double next_time = (note_on ? e->time : e->get_end_time());
        wait_until(next_time);
        if (e->is_note() && note_on) { // process notes here
            // printf("Note at %g: chan %d key %d loud %d\n",
            //        next_time, e->chan, e->key, (int) e->loud);
            midi_note_on(midi, next_time, e->chan, e->get_identifier(),
                         (int) e->get_loud());
        } else if (e->is_note()) { // must be a note off
            midi_note_on(midi, next_time, e->chan, e->get_identifier(), 0);
        } else if (e->is_update()) { // process updates here
            Alg_update_ptr u = (Alg_update_ptr) e; // coerce to proper type
            send_midi_update(u, midi);
        } 
        // add next note
        e = iterator.next(&note_on);
    }
    iterator.end();
}


void seq_play(Alg_seq &seq)
{
    PortMidiStream *mo;
    Pm_Initialize();
    PmDeviceID dev = Pm_GetDefaultOutputDeviceID();
    // note that the Pt_Time type cast is required because Pt_Time does 
    // not take an input parameter, whereas for generality, PortMidi
    // passes in a void * so the time function can get some context.
    // It is safe to call Pt_Time with a parameter -- it will just be ignored.
    if (Pm_OpenOutput(&mo, dev, NULL, 256, 
                      (PmTimestamp (*)(void *))&Pt_Time, NULL, 100) == pmNoError) {
        seq2midi(seq, mo);
        wait_until(time_elapsed() + 1);
        Pm_Close(mo);
    }
    Pm_Terminate();
    return;
}