CLAM-Development  1.4.0
main.cxx
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2004 MUSIC TECHNOLOGY GROUP (MTG)
3  * UNIVERSITAT POMPEU FABRA
4  *
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  */
21 
22 #include "MIDIEvent.hxx"
23 #include "MIDITrack.hxx"
24 #include "MIDISong.hxx"
25 #include "MIDIReader.hxx"
26 #include "MIDISongPlayer.hxx"
27 
28 
29 using namespace MIDI;
30 
31 #include "MIDITempo.hxx"
32 
33 #include <math.h>
34 #include <stdio.h>
35 
36 class SimpleSynth
37 {
38 /* a very very simple synthesizer */
39 private:
40  struct note
41  {
42  int chn;
43  int key;
44  int vel;
45  float phase;
46  float amp;
47  float env;
48  };
49 
50  note notes[256]; /* max 256 notes simultanious */
51  int nnotes;
52 
53  float dphase[128]; /* table to convert midi key to phase increment */
54 
55  FILE* file;
56 
57  int findnote(int chn,int key)
58  /* find an running (vel!=0) note with chn and key */
59  {
60  int i = 0;
61  while (i<nnotes)
62  {
63  if (notes[i].chn==chn && notes[i].key==key && notes[i].vel!=0) return i;
64  i++;
65  }
66  return i;
67  }
68 
69 public:
70  SimpleSynth()
71  {
72  nnotes = 0;
73  for (int i=0;i<128;i++)
74  {
75  float f = pow( 2. , ( float(i) - 69. ) / 12. ) * 440.;
76  dphase[i] = (M_PI*2.)*f / 44100.;
77  }
78  file = fopen("output.raw","wb");
79  }
80 
81  ~SimpleSynth()
82  {
83  fclose(file);
84  }
85 
86  void setnote(int chn,int key,int vel) /* turn not on/off */
87  {
88  if (vel==0)
89  {
90  /* turn off */
91  int i = findnote(chn,key);
92  if (i!=nnotes) {
93  notes[i].vel = 0;
94  }
95  return;
96  }
97  /* turn on: add a new note */
98  notes[nnotes].chn = chn;
99  notes[nnotes].key = key;
100  notes[nnotes].vel = vel;
101  notes[nnotes].phase = 0;
102  notes[nnotes].env = 0;
103  notes[nnotes].amp = float(vel)/128.;
104  nnotes++;
105  }
106 
107  void synthesize(void)
108  {
109  short buf[44];
110  for (int j=0;j<44;j++)
111  {
112  buf[j] = 0;
113  float out = 0;
114  int i;
115  for (i=0;i<nnotes;i++)
116  {
117  if (notes[i].vel)
118  {
119  if (notes[i].env<1.) notes[i].env+=0.002; /* attack */
120  }else{
121  if (notes[i].env>0.) notes[i].env-=0.001; /* release */
122  }
123 
124  /* a simple FM synthesizer */
125  out += sin(notes[i].phase+sin(notes[i].phase)*2.)
126  *notes[i].env*notes[i].amp;
127 
128  notes[i].phase += dphase[notes[i].key];
129  if (notes[i].phase>2.*M_PI) notes[i].phase -= 2.*M_PI;
130  }
131 
132  /* remove "dead" notes: notes that have finished the release */
133  for (i=0;i<nnotes;)
134  {
135  if (notes[i].vel==0 && notes[i].env<0.001)
136  {
137  nnotes--;
138  notes[i] = notes[nnotes];
139  }else{
140  i++;
141  }
142  }
143 
144  buf[j] = (short)(out*2000.);
145  }
146  fwrite(buf,2,44,file);
147  }
148 };
149 
150 int main(int argc,char** argv)
151 {
152  /* an example of reading a midi file */
153  Reader r(argc>1 ? argv[1] : "test.mid");
154  Song s;
155  r.Read(s);
156 
157  Tempo t(&s);
158 
159 #ifdef DOPRINT
160  fprintf(stderr,"song has %d tracks\n",s.Tracks());
161 #endif
162 
163  int trackId;
164  Event ev;
165 
166  SongPlayer sp(&s); /* to traverse the song's events */
167 
168  int last = 0;
169 
170  SimpleSynth synth; /* a simple synthesizer */
171 
172  while (sp.GetEvent(ev,trackId))
173  {
174 #ifdef DOPRINT
175  fprintf(stderr,"%d %d %d %02x %02x %02x\n",
176  ev.mTicks,
177  t.TicksToTime(ev.mTicks),
178  trackId,
179  ev.mMessage[0],
180  ev.mMessage[1],
181  ev.mMessage[2]);
182 #endif
183  if ((ev[0] & 0xF0)==0x90 || (ev[0] & 0xF0)==0x80) // note on / note off
184  {
185  int now = t.TicksToTime(ev.GetTicks());
186  while (last<now)
187  {
188  synth.synthesize();
189  last++;
190  }
191  {
192  synth.setnote(ev[0]&0x0F, ev[1], (ev[0] & 0xF0)==0x90 ? ev[2] : 0);
193  }
194  }
195  }
196 
197  for (int k=0;k<1000;k++) synth.synthesize();
198 
199  return 0;
200 }
201