~ubuntu-branches/ubuntu/precise/fofix-dfsg/precise

« back to all changes in this revision

Viewing changes to src/midi/MidiOutFile.py

  • Committer: Bazaar Package Importer
  • Author(s): Christian Hammers
  • Date: 2010-02-21 12:09:32 UTC
  • Revision ID: james.westby@ubuntu.com-20100221120932-6bh992d2u8dtj9gr
Tags: upstream-3.121
ImportĀ upstreamĀ versionĀ 3.121

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: ISO-8859-1 -*-
 
2
 
 
3
from MidiOutStream import MidiOutStream
 
4
from RawOutstreamFile import RawOutstreamFile
 
5
 
 
6
from constants import *
 
7
from DataTypeConverters import fromBytes, writeVar
 
8
 
 
9
class MidiOutFile(MidiOutStream):
 
10
 
 
11
 
 
12
    """
 
13
    MidiOutFile is an eventhandler that subclasses MidiOutStream.
 
14
    """
 
15
 
 
16
 
 
17
    def __init__(self, raw_out=''):
 
18
 
 
19
        self.raw_out = RawOutstreamFile(raw_out)
 
20
        MidiOutStream.__init__(self)
 
21
        
 
22
    
 
23
    def write(self):
 
24
        self.raw_out.write()
 
25
 
 
26
 
 
27
    def event_slice(self, slc):
 
28
        """
 
29
        Writes the slice of an event to the current track. Correctly 
 
30
        inserting a varlen timestamp too.
 
31
        """
 
32
        trk = self._current_track_buffer
 
33
        trk.writeVarLen(self.rel_time())
 
34
        trk.writeSlice(slc)
 
35
        
 
36
    
 
37
    #####################
 
38
    ## Midi events
 
39
 
 
40
 
 
41
    def note_on(self, channel=0, note=0x40, velocity=0x40):
 
42
 
 
43
        """
 
44
        channel: 0-15
 
45
        note, velocity: 0-127
 
46
        """
 
47
        slc = fromBytes([NOTE_ON + channel, note, velocity])
 
48
        self.event_slice(slc)
 
49
 
 
50
 
 
51
    def note_off(self, channel=0, note=0x40, velocity=0x40):
 
52
 
 
53
        """
 
54
        channel: 0-15
 
55
        note, velocity: 0-127
 
56
        """
 
57
        slc = fromBytes([NOTE_OFF + channel, note, velocity])
 
58
        self.event_slice(slc)
 
59
 
 
60
 
 
61
    def aftertouch(self, channel=0, note=0x40, velocity=0x40):
 
62
 
 
63
        """
 
64
        channel: 0-15
 
65
        note, velocity: 0-127
 
66
        """
 
67
        slc = fromBytes([AFTERTOUCH + channel, note, velocity])
 
68
        self.event_slice(slc)
 
69
 
 
70
 
 
71
    def continuous_controller(self, channel, controller, value):
 
72
 
 
73
        """
 
74
        channel: 0-15
 
75
        controller, value: 0-127
 
76
        """
 
77
        slc = fromBytes([CONTINUOUS_CONTROLLER + channel, controller, value])
 
78
        self.event_slice(slc)
 
79
        # These should probably be implemented
 
80
        # http://users.argonet.co.uk/users/lenny/midi/tech/spec.html#ctrlnums
 
81
 
 
82
 
 
83
    def patch_change(self, channel, patch):
 
84
 
 
85
        """
 
86
        channel: 0-15
 
87
        patch: 0-127
 
88
        """
 
89
        slc = fromBytes([PATCH_CHANGE + channel, patch])
 
90
        self.event_slice(slc)
 
91
 
 
92
 
 
93
    def channel_pressure(self, channel, pressure):
 
94
 
 
95
        """
 
96
        channel: 0-15
 
97
        pressure: 0-127
 
98
        """
 
99
        slc = fromBytes([CHANNEL_PRESSURE + channel, pressure])
 
100
        self.event_slice(slc)
 
101
 
 
102
 
 
103
    def pitch_bend(self, channel, value):
 
104
 
 
105
        """
 
106
        channel: 0-15
 
107
        value: 0-16383
 
108
        """
 
109
        msb = (value>>7) & 0xFF
 
110
        lsb = value & 0xFF
 
111
        slc = fromBytes([PITCH_BEND + channel, msb, lsb])
 
112
        self.event_slice(slc)
 
113
 
 
114
 
 
115
 
 
116
 
 
117
    #####################
 
118
    ## System Exclusive
 
119
 
 
120
#    def sysex_slice(sysex_type, data):
 
121
#        ""
 
122
#        sysex_len = writeVar(len(data)+1)
 
123
#        self.event_slice(SYSTEM_EXCLUSIVE + sysex_len + data + END_OFF_EXCLUSIVE)
 
124
#
 
125
    def system_exclusive(self, data):
 
126
 
 
127
        """
 
128
        data: list of values in range(128)
 
129
        """
 
130
        sysex_len = writeVar(len(data)+1)
 
131
        self.event_slice(chr(SYSTEM_EXCLUSIVE) + sysex_len + data + chr(END_OFF_EXCLUSIVE))
 
132
 
 
133
 
 
134
    #####################
 
135
    ## Common events
 
136
 
 
137
    def midi_time_code(self, msg_type, values):
 
138
        """
 
139
        msg_type: 0-7
 
140
        values: 0-15
 
141
        """
 
142
        value = (msg_type<<4) + values
 
143
        self.event_slice(fromBytes([MIDI_TIME_CODE, value]))
 
144
 
 
145
 
 
146
    def song_position_pointer(self, value):
 
147
 
 
148
        """
 
149
        value: 0-16383
 
150
        """
 
151
        lsb = (value & 0x7F)
 
152
        msb = (value >> 7) & 0x7F
 
153
        self.event_slice(fromBytes([SONG_POSITION_POINTER, lsb, msb]))
 
154
 
 
155
 
 
156
    def song_select(self, songNumber):
 
157
 
 
158
        """
 
159
        songNumber: 0-127
 
160
        """
 
161
        self.event_slice(fromBytes([SONG_SELECT, songNumber]))
 
162
 
 
163
 
 
164
    def tuning_request(self):
 
165
 
 
166
        """
 
167
        No values passed
 
168
        """
 
169
        self.event_slice(chr(TUNING_REQUEST))
 
170
 
 
171
            
 
172
    #########################
 
173
    # header does not really belong here. But anyhoo!!!
 
174
    
 
175
    def header(self, format=0, nTracks=1, division=96):
 
176
 
 
177
        """
 
178
        format: type of midi file in [0,1,2]
 
179
        nTracks: number of tracks. 1 track for type 0 file
 
180
        division: timing division ie. 96 ppq.
 
181
        
 
182
        """        
 
183
        raw = self.raw_out
 
184
        raw.writeSlice('MThd')
 
185
        bew = raw.writeBew
 
186
        bew(6, 4) # header size
 
187
        bew(format, 2)
 
188
        bew(nTracks, 2)
 
189
        bew(division, 2)
 
190
 
 
191
 
 
192
    def eof(self):
 
193
 
 
194
        """
 
195
        End of file. No more events to be processed.
 
196
        """
 
197
        # just write the file then.
 
198
        self.write()
 
199
 
 
200
 
 
201
    #####################
 
202
    ## meta events
 
203
 
 
204
 
 
205
    def meta_slice(self, meta_type, data_slice):
 
206
        "Writes a meta event"
 
207
        slc = fromBytes([META_EVENT, meta_type]) + \
 
208
                         writeVar(len(data_slice)) +  data_slice
 
209
        self.event_slice(slc)
 
210
 
 
211
 
 
212
    def meta_event(self, meta_type, data):
 
213
        """
 
214
        Handles any undefined meta events
 
215
        """
 
216
        self.meta_slice(meta_type, fromBytes(data))
 
217
 
 
218
 
 
219
    def start_of_track(self, n_track=0):
 
220
        """
 
221
        n_track: number of track
 
222
        """
 
223
        self._current_track_buffer = RawOutstreamFile()
 
224
        self.reset_time()
 
225
        self._current_track += 1
 
226
 
 
227
 
 
228
    def end_of_track(self):
 
229
        """
 
230
        Writes the track to the buffer.
 
231
        """
 
232
        raw = self.raw_out
 
233
        raw.writeSlice(TRACK_HEADER)
 
234
        track_data = self._current_track_buffer.getvalue()
 
235
        # wee need to know size of track data.
 
236
        eot_slice = writeVar(self.rel_time()) + fromBytes([META_EVENT, END_OF_TRACK, 0])
 
237
        raw.writeBew(len(track_data)+len(eot_slice), 4)
 
238
        # then write
 
239
        raw.writeSlice(track_data)
 
240
        raw.writeSlice(eot_slice)
 
241
        
 
242
 
 
243
 
 
244
    def sequence_number(self, value):
 
245
 
 
246
        """
 
247
        value: 0-65535
 
248
        """
 
249
        self.meta_slice(meta_type, writeBew(value, 2))
 
250
 
 
251
 
 
252
    def text(self, text):
 
253
        """
 
254
        Text event
 
255
        text: string
 
256
        """
 
257
        self.meta_slice(TEXT, text)
 
258
 
 
259
 
 
260
    def copyright(self, text):
 
261
 
 
262
        """
 
263
        Copyright notice
 
264
        text: string
 
265
        """
 
266
        self.meta_slice(COPYRIGHT, text)
 
267
 
 
268
 
 
269
    def sequence_name(self, text):
 
270
        """
 
271
        Sequence/track name
 
272
        text: string
 
273
        """
 
274
        self.meta_slice(SEQUENCE_NAME, text)
 
275
 
 
276
 
 
277
    def instrument_name(self, text):
 
278
 
 
279
        """
 
280
        text: string
 
281
        """
 
282
        self.meta_slice(INSTRUMENT_NAME, text)
 
283
 
 
284
 
 
285
    def lyric(self, text):
 
286
 
 
287
        """
 
288
        text: string
 
289
        """
 
290
        self.meta_slice(LYRIC, text)
 
291
 
 
292
 
 
293
    def marker(self, text):
 
294
 
 
295
        """
 
296
        text: string
 
297
        """
 
298
        self.meta_slice(MARKER, text)
 
299
 
 
300
 
 
301
    def cuepoint(self, text):
 
302
 
 
303
        """
 
304
        text: string
 
305
        """
 
306
        self.meta_slice(CUEPOINT, text)
 
307
 
 
308
 
 
309
    def midi_ch_prefix(self, channel):
 
310
 
 
311
        """
 
312
        channel: midi channel for subsequent data
 
313
        (deprecated in the spec)
 
314
        """
 
315
        self.meta_slice(MIDI_CH_PREFIX, chr(channel))
 
316
 
 
317
 
 
318
    def midi_port(self, value):
 
319
 
 
320
        """
 
321
        value: Midi port (deprecated in the spec)
 
322
        """
 
323
        self.meta_slice(MIDI_CH_PREFIX, chr(value))
 
324
 
 
325
 
 
326
    def tempo(self, value):
 
327
 
 
328
        """
 
329
        value: 0-2097151
 
330
        tempo in us/quarternote
 
331
        (to calculate value from bpm: int(60,000,000.00 / BPM))
 
332
        """
 
333
        hb, mb, lb = (value>>16 & 0xff), (value>>8 & 0xff), (value & 0xff)
 
334
        self.meta_slice(TEMPO, fromBytes([hb, mb, lb]))
 
335
 
 
336
 
 
337
    def smtp_offset(self, hour, minute, second, frame, framePart):
 
338
 
 
339
        """
 
340
        hour,
 
341
        minute,
 
342
        second: 3 bytes specifying the hour (0-23), minutes (0-59) and 
 
343
                seconds (0-59), respectively. The hour should be 
 
344
                encoded with the SMPTE format, just as it is in MIDI 
 
345
                Time Code.
 
346
        frame: A byte specifying the number of frames per second (one 
 
347
               of : 24, 25, 29, 30).
 
348
        framePart: A byte specifying the number of fractional frames, 
 
349
                   in 100ths of a frame (even in SMPTE-based tracks 
 
350
                   using a different frame subdivision, defined in the 
 
351
                   MThd chunk).
 
352
        """
 
353
        self.meta_slice(SMTP_OFFSET, fromBytes([hour, minute, second, frame, framePart]))
 
354
 
 
355
 
 
356
 
 
357
    def time_signature(self, nn, dd, cc, bb):
 
358
 
 
359
        """
 
360
        nn: Numerator of the signature as notated on sheet music
 
361
        dd: Denominator of the signature as notated on sheet music
 
362
            The denominator is a negative power of 2: 2 = quarter 
 
363
            note, 3 = eighth, etc.
 
364
        cc: The number of MIDI clocks in a metronome click
 
365
        bb: The number of notated 32nd notes in a MIDI quarter note 
 
366
            (24 MIDI clocks)        
 
367
        """
 
368
        self.meta_slice(TIME_SIGNATURE, fromBytes([nn, dd, cc, bb]))
 
369
 
 
370
 
 
371
 
 
372
 
 
373
    def key_signature(self, sf, mi):
 
374
 
 
375
        """
 
376
        sf: is a byte specifying the number of flats (-ve) or sharps 
 
377
            (+ve) that identifies the key signature (-7 = 7 flats, -1 
 
378
            = 1 flat, 0 = key of C, 1 = 1 sharp, etc).
 
379
        mi: is a byte specifying a major (0) or minor (1) key.
 
380
        """
 
381
        self.meta_slice(KEY_SIGNATURE, fromBytes([sf, mi]))
 
382
 
 
383
 
 
384
 
 
385
    def sequencer_specific(self, data):
 
386
 
 
387
        """
 
388
        data: The data as byte values
 
389
        """
 
390
        self.meta_slice(SEQUENCER_SPECIFIC, data)
 
391
 
 
392
 
 
393
 
 
394
 
 
395
 
 
396
#    #####################
 
397
#    ## realtime events
 
398
 
 
399
#    These are of no use in a midi file, so they are ignored!!!
 
400
 
 
401
#    def timing_clock(self):
 
402
#    def song_start(self):
 
403
#    def song_stop(self):
 
404
#    def song_continue(self):
 
405
#    def active_sensing(self):
 
406
#    def system_reset(self):
 
407
 
 
408
 
 
409
 
 
410
if __name__ == '__main__':
 
411
 
 
412
    out_file = 'test/midifiles/midiout.mid'
 
413
    midi = MidiOutFile(out_file)
 
414
 
 
415
#format: 0, nTracks: 1, division: 480
 
416
#----------------------------------
 
417
#
 
418
#Start - track #0
 
419
#sequence_name: Type 0
 
420
#tempo: 500000
 
421
#time_signature: 4 2 24 8
 
422
#note_on  - ch:00,  note:48,  vel:64 time:0
 
423
#note_off - ch:00,  note:48,  vel:40 time:480
 
424
#End of track
 
425
#
 
426
#End of file
 
427
 
 
428
 
 
429
    midi.header(0, 1, 480)
 
430
    
 
431
    midi.start_of_track()
 
432
    midi.sequence_name('Type 0')
 
433
    midi.tempo(750000)
 
434
    midi.time_signature(4, 2, 24, 8)
 
435
    ch = 0
 
436
    for i in range(127):
 
437
        midi.note_on(ch, i, 0x64)
 
438
        midi.update_time(96)
 
439
        midi.note_off(ch, i, 0x40)
 
440
        midi.update_time(0)
 
441
    
 
442
    midi.update_time(0)
 
443
    midi.end_of_track()
 
444
    
 
445
    midi.eof() # currently optional, should it do the write instead of write??
 
446
 
 
447
 
 
448
    midi.write()
 
 
b'\\ No newline at end of file'