Import upstream version 0.99.2
[fmit.git] / libs / Music / omidistream.cpp
1 // Copyright 2004 "Gilles Degottex"
2
3 // This file is part of "Music"
4
5 // "Music" is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU Lesser General Public License as published by
7 // the Free Software Foundation; either version 2.1 of the License, or
8 // (at your option) any later version.
9 // 
10 // "Music" is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU Lesser General Public License for more details.
14 // 
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
19
20 #include "omidistream.h"
21
22 #include <iostream>
23 using namespace std;
24
25 namespace Music
26 {
27         queue<unsigned char> note::s_free_tags;
28         map<unsigned char, unsigned char> note::s_tags;
29
30         unsigned char note::getFreeTag()
31         {
32                 if(s_free_tags.empty())
33                         for(unsigned char c=1; c<255; c++)
34                                 s_free_tags.push(c);
35
36                 unsigned char tag = s_free_tags.front();
37                 s_free_tags.pop();
38
39                 return tag;
40         }
41         note::note(unsigned char n, double t, double duration)
42         {
43                 snd_seq_ev_clear(&m_ev);
44
45                 m_ev.type = SND_SEQ_EVENT_NOTE;
46                 m_ev.flags = SND_SEQ_TIME_STAMP_REAL|SND_SEQ_TIME_MODE_ABS|SND_SEQ_EVENT_LENGTH_FIXED|SND_SEQ_PRIORITY_NORMAL;
47                 m_ev.time.time.tv_sec = (unsigned int)t;
48                 m_ev.time.time.tv_nsec = (unsigned int)((t-(unsigned int)t)*1000000000);
49                 m_ev.queue = ~SND_SEQ_QUEUE_DIRECT;
50                 m_ev.tag = getFreeTag();
51                 m_ev.data.note.note = n;
52                 m_ev.data.note.velocity = 64;
53                 m_ev.data.note.off_velocity = 64;
54                 m_ev.data.note.duration = (unsigned int)(duration*1000);
55         }
56
57         note_on::note_on()
58         {
59                 snd_seq_ev_clear(&m_ev);
60         }
61         note_on::note_on(unsigned char n, double t)
62         {
63                 snd_seq_ev_clear(&m_ev);
64
65                 m_ev.type = SND_SEQ_EVENT_NOTEON;
66                 m_ev.flags = SND_SEQ_EVENT_LENGTH_FIXED|SND_SEQ_PRIORITY_NORMAL;
67                 if(t==MIDI_TIME_DIRECT) m_ev.queue = SND_SEQ_QUEUE_DIRECT;
68                 else
69                 {
70                         m_ev.flags |= SND_SEQ_TIME_STAMP_REAL|SND_SEQ_TIME_MODE_ABS;
71                         m_ev.queue = ~SND_SEQ_QUEUE_DIRECT;
72                         m_ev.time.time.tv_sec = (unsigned int)t;
73                         m_ev.time.time.tv_nsec = (unsigned int)((t-(unsigned int)t)*1000000000);
74                 }
75                 m_ev.tag = getFreeTag();
76                 m_ev.data.note.note = n;
77                 m_ev.data.note.velocity = 64;
78
79                 addNote(m_ev.tag, n);
80
81 //              cerr << "note_off::note_on tag=" << int(m_ev.tag) << " note=" << int(m_ev.data.note.note) << " time=" << t << endl;
82         }
83         note_off::note_off(const note_on& n_on, double t)
84         {
85                 snd_seq_ev_clear(&m_ev);
86
87                 m_ev.type = SND_SEQ_EVENT_NOTEOFF;
88                 m_ev.flags = SND_SEQ_EVENT_LENGTH_FIXED|SND_SEQ_PRIORITY_NORMAL;
89                 if(t==MIDI_TIME_DIRECT) m_ev.queue = SND_SEQ_QUEUE_DIRECT;
90                 else
91                 {
92                         m_ev.flags |= SND_SEQ_TIME_STAMP_REAL|SND_SEQ_TIME_MODE_ABS;
93                         m_ev.queue = ~SND_SEQ_QUEUE_DIRECT;
94                         m_ev.time.time.tv_sec = (unsigned int)t;
95                         m_ev.time.time.tv_nsec = (unsigned int)((t-(unsigned int)t)*1000000000);
96                 }
97                 m_ev.tag = n_on.getTag();
98                 m_ev.data.note.note = n_on.m_ev.data.note.note;
99                 m_ev.data.note.velocity = 64;
100         }
101
102         omidistream::omidistream(string name, double tempo, unsigned char channel)
103         : m_seq(NULL)
104         , m_queue(0)
105         , m_port(0)
106         , m_channel(channel)
107         , m_A3Index(69)
108         {
109                 int err = snd_seq_open(&m_seq, "default", SND_SEQ_OPEN_OUTPUT, 0);
110                 if (err < 0)    throw string("snd_seq_open: ")+snd_strerror(err);
111
112                 snd_seq_set_client_name(m_seq, name.c_str());
113
114                 m_port = snd_seq_create_simple_port(m_seq, (name+" port").c_str(),
115                                 SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC);
116
117                 //              cout << "client built: " << snd_seq_client_id(m_seq) << ":" << m_port << endl;
118
119                 m_queue = snd_seq_alloc_named_queue(m_seq, (name+" queue").c_str());
120                 snd_seq_queue_tempo_t* queue_tempo;
121                 snd_seq_queue_tempo_alloca(&queue_tempo);
122                 snd_seq_queue_tempo_set_tempo(queue_tempo, (unsigned int)(60*1000000/tempo)); // 60 BPM
123                 snd_seq_queue_tempo_set_ppq(queue_tempo, 48); // 48 PPQ
124                 snd_seq_set_queue_tempo(m_seq, m_queue, queue_tempo);
125                 //              cout << "Queue built and temperized: " << m_queue << endl;
126                 snd_seq_start_queue(m_seq, m_queue, NULL);
127         }
128
129         omidistream::~omidistream()
130         {
131                 snd_seq_stop_queue(m_seq, m_queue, NULL);
132         }
133
134         omidistream& omidistream::operator<<(note n)
135         {
136                 //              n.ev.source.client = snd_seq_client_id(m_seq);
137                 snd_seq_ev_set_source(&(n.m_ev), m_port);
138                 snd_seq_ev_set_subs(&(n.m_ev));
139                 n.m_ev.data.note.channel = m_channel;
140                 if(n.m_ev.queue!=SND_SEQ_QUEUE_DIRECT)
141                         n.m_ev.queue = m_queue;
142
143                 if(n.m_ev.type!=SND_SEQ_EVENT_NOTEON)
144                 {
145                         note::s_free_tags.push(n.m_ev.tag);
146                         note::s_tags.erase(n.m_ev.tag);
147                 }
148
149                 int err = snd_seq_event_output(m_seq, &n.m_ev);
150                 if(err<0)       throw string("operator<<(note) snd_seq_event_output: ")+string(snd_strerror(err));
151
152                 return *this;
153         }
154
155         omidistream& omidistream::drain()
156         {
157                 int err = snd_seq_drain_output(m_seq);
158                 if(err<0)       throw string("::omidistream::drain() snd_seq_drain_output: ")+string(snd_strerror(err));
159
160                 return *this;
161         }
162
163         omidistream& drain(omidistream& str)
164         {
165                 return str.drain();
166         }
167 };
168