@@ -111,6 +111,11 @@ def unicode(s):
111
111
# sys.stderr.write(traceback.format_exc())
112
112
fluidsynth_available = False
113
113
114
+ #FAU:MIDIPLAY: On Mac, it is possible to interface directly to the Midi syntethiser of mac OS via mplay: https://github.com/jheinen/mplay
115
+ # An adaptation is done to integrate with EasyABC
116
+ if wx .Platform == "__WXMAC__" :
117
+ from mplaysmfplayer import *
118
+
114
119
from xml2abc_interface import xml_to_abc , abc_to_xml
115
120
from midi2abc import midi_to_abc , Note , duration2abc
116
121
from generalmidi import general_midi_instruments
@@ -1400,8 +1405,9 @@ def abc_to_midi(abc_code, settings, midi_file_name, add_follow_score_markers):
1400
1405
1401
1406
# 1.3.6 [SS] 2014-11-24
1402
1407
def add_abc2midi_options (cmd , settings , add_follow_score_markers ):
1403
- if str2bool (settings ['barfly' ]):
1404
- cmd .append ('-BF' )
1408
+ #Force BF option flag to be at the last position according to jwdj/EasyABC#86 and sshlien/abcmidi#8
1409
+ #if str2bool(settings['barfly']):
1410
+ # cmd.append('-BF')
1405
1411
if str2bool (settings ['nofermatas' ]):
1406
1412
cmd .append ('-NFER' )
1407
1413
if str2bool (settings ['nograce' ]):
@@ -1414,6 +1420,8 @@ def add_abc2midi_options(cmd, settings, add_follow_score_markers):
1414
1420
# 1.3.6.4 [JWDJ] 2016-06-22
1415
1421
if add_follow_score_markers :
1416
1422
cmd .append ('-EA' )
1423
+ if str2bool (settings ['barfly' ]):
1424
+ cmd .append ('-BF' )
1417
1425
return cmd
1418
1426
1419
1427
@@ -3980,14 +3988,24 @@ def __init__(self, parent, ID, app_dir, settings, options):
3980
3988
except Exception as e :
3981
3989
error_msg = traceback .format_exc ()
3982
3990
self .mc = None
3991
+
3992
+ #FAU:MIDIPLAY: on Mac add the ability to interface to System Midi Synth via mplay in case fluidsynth not available or not configured with soundfont
3993
+ if wx .Platform == "__WXMAC__" and self .mc is None :
3994
+ try :
3995
+ self .mc = MPlaySMFPlayer (self )
3996
+ except :
3997
+ error_msg = "Error on loading SMF Midi Player"
3998
+ self .mc = None
3983
3999
3984
4000
if self .mc is None :
3985
4001
try :
3986
4002
backend = None
3987
4003
from wxmediaplayer import WxMediaPlayer
3988
- if wx .Platform == "__WXMAC__" :
3989
- backend = wx .media .MEDIABACKEND_QUICKTIME
3990
- elif wx .Platform == "__WXMSW__" :
4004
+ #FAU:MIDIPLAY:The Quicktime interface do not manage MIDI File on latest version of Mac so keep only possibility on Windows
4005
+ #if wx.Platform == "__WXMAC__":
4006
+ # backend = wx.media.MEDIABACKEND_QUICKTIME
4007
+ #elif wx.Platform == "__WXMSW__":
4008
+ if wx .Platform == "__WXMSW__" :
3991
4009
if platform .release () == 'XP' :
3992
4010
backend = wx .media .MEDIABACKEND_DIRECTSHOW
3993
4011
else :
@@ -4292,13 +4310,15 @@ def GetAbcToPlay(self):
4292
4310
notes = get_notes_from_abc (text )
4293
4311
num_header_lines , first_note_line_index = self .get_num_extra_header_lines (tune )
4294
4312
4313
+ #FAU: Seems not needed and issue when selecting not on a second page as it is considering all lines from first page as header. jwdj/EasyABC#100
4314
+ #FAU: To be noted it was already removed from OnNoteSelectionChangedDesc
4295
4315
# workaround for the fact the abcm2ps returns incorrect row numbers
4296
4316
# check the row number of the first note and if it doesn't agree with the actual value
4297
4317
# then pretend that we have more or less extra header lines
4298
- if self .music_pane .current_page .notes : # 1.3.6.2 [JWdJ] 2015-02
4299
- actual_first_row = self .music_pane .current_page .notes [0 ][2 ]- 1
4300
- correction = (actual_first_row - first_note_line_index )
4301
- num_header_lines += correction
4318
+ # if self.music_pane.current_page.notes: # 1.3.6.2 [JWdJ] 2015-02
4319
+ # actual_first_row = self.music_pane.current_page.notes[0][2]-1
4320
+ # correction = (actual_first_row - first_note_line_index)
4321
+ # num_header_lines += correction
4302
4322
4303
4323
temp = text .replace ('\r \n ' , ' \n ' ).replace ('\r ' , '\n ' ) # re.sub(r'\r\n|\r', '\n', text)
4304
4324
line_start_offset = [m .start (0 ) for m in re .finditer (r'(?m)^' , temp )]
@@ -4474,12 +4494,17 @@ def OnMouseWheel(self, evt):
4474
4494
evt .Skip ()
4475
4495
4476
4496
def play (self ):
4497
+ self .play_timer .Start (50 )
4477
4498
if self .settings .get ('follow_score' , False ) and self .current_page_index != 0 :
4478
4499
self .select_page (0 )
4479
4500
wx .CallAfter (self .mc .Play )
4480
4501
4481
4502
def stop_playing (self ):
4482
4503
self .mc .Stop ()
4504
+ #FAU:remove highlighted notes
4505
+ self .music_pane .draw_notes_highlighted (None )
4506
+ #FAU:MIDIPLAY: play timer can be stopped no need to update progress slider
4507
+ self .play_timer .Stop ()
4483
4508
self .play_button .SetBitmap (self .play_bitmap )
4484
4509
self .play_button .Refresh ()
4485
4510
self .progress_slider .SetValue (0 )
@@ -4551,6 +4576,14 @@ def do_load_media_file(self, path):
4551
4576
if wx .Platform == "__WXMSW__" and platform .release () != 'XP' :
4552
4577
# 1.3.6.3 [JWDJ] 2015-3 It seems mc.Play() triggers the OnMediaLoaded event
4553
4578
self .mc .Play () # does not start playing but triggers OnMediaLoaded event
4579
+ #FAU:MIDIPLAY: added support for playback for Mac with SMF player For now kept apart from Windows
4580
+ #FAU:MIDIPLAY: %%TODO%% verify if can be merged with preceeding if
4581
+ #FAU:MIDIPLAY: 20250125 Not needed as correctly started based on OnMediaLoaded
4582
+ #elif wx.Platform == "__WXMAC__":
4583
+ # self.mc.Play()
4584
+ #FAU:MIDIPLAY: Start timer to be able to have progress bar updated
4585
+ # self.play_timer.Start(20)
4586
+ # self.play_button.SetBitmap(self.pause_bitmap)
4554
4587
else :
4555
4588
wx .MessageBox (_ ("Unable to load %s: Unsupported format?" ) % path ,
4556
4589
_ ("Error" ), wx .ICON_ERROR | wx .OK )
@@ -4561,21 +4594,28 @@ def play():
4561
4594
# time.sleep(0.3) # 1.3.6.4 [JWDJ] on Mac the first note is skipped the first time. hope this helps
4562
4595
# self.mc.Seek(self.play_start_offset, wx.FromStart)
4563
4596
self .play_button .SetBitmap (self .pause_bitmap )
4564
- self .progress_slider .SetRange (0 , self .mc .Length ())
4597
+ self .progress_slider .SetRange (0 , int ( self .mc .Length ())) #FAU:MIDIPLAY: mplay might return a float. thus forcing an int
4565
4598
self .progress_slider .SetValue (0 )
4566
4599
self .OnBpmSlider (None )
4567
4600
self .update_playback_rate ()
4568
- if wx .Platform == "__WXMAC__" :
4569
- self .mc .Seek (0 ) # When using wx.media.MEDIABACKEND_QUICKTIME the music starts playing too early (when loading a file)
4570
- time .sleep (0.5 ) # hopefully this fixes the first notes not being played
4601
+ #FAU:MIDIPLAY: The next 'if' was when using MediaCtrl which is not used anymore. Todo: remove the corresponding code if confirmed
4602
+ #if wx.Platform == "__WXMAC__":
4603
+ # self.mc.Seek(0) # When using wx.media.MEDIABACKEND_QUICKTIME the music starts playing too early (when loading a file)
4604
+ # time.sleep(0.5) # hopefully this fixes the first notes not being played
4571
4605
self .play ()
4572
4606
wx .CallAfter (play )
4573
4607
4574
4608
def OnAfterStop (self ):
4609
+ self .set_loop_midi_playback (False )
4575
4610
# 1.3.6.3 [SS] 2015-05-04
4576
4611
self .stop_playing ()
4577
- self .reset_BpmSlider ()
4578
- self .flip_tempobox (False )
4612
+ #FAU preserve latest bpm choice
4613
+ #self.reset_BpmSlider()
4614
+ #FAU20250125: Do not hide it if supported
4615
+ if self .settings ['midiplayer_path' ]:
4616
+ self .flip_tempobox (False )
4617
+ if wx .Platform != "__WXMSW__" :
4618
+ self .toolbar .Realize () # 1.3.6.4 [JWDJ] fixes toolbar repaint bug for Windows
4579
4619
4580
4620
def OnToolRecord (self , evt ):
4581
4621
if self .record_thread and self .record_thread .is_running :
@@ -4594,19 +4634,21 @@ def OnToolRecord(self, evt):
4594
4634
self .record_thread .start ()
4595
4635
4596
4636
def OnToolStop (self , evt ):
4597
- self .set_loop_midi_playback (False )
4598
- self .stop_playing ()
4637
+ #FAU 20250125: Cleaning, trying to centralised what is common to Stop avoiding of mon to Stop instead of multiple call
4638
+ self .OnAfterStop ()
4639
+ #self.set_loop_midi_playback(False)
4640
+ #self.stop_playing()
4599
4641
# 1.3.6.3 [SS] 2015-04-03
4600
4642
#self.play_panel.Show(False)
4601
- self .flip_tempobox (False )
4602
- self .progress_slider .SetValue (0 )
4643
+ # self.flip_tempobox(False)
4644
+ # self.progress_slider.SetValue(0)
4603
4645
# self.reset_BpmSlider() #[EPO] 2018-11-20 make sticky - this is new functionality
4604
- if wx .Platform != "__WXMSW__" :
4605
- self .toolbar .Realize () # 1.3.6.4 [JWDJ] fixes toolbar repaint bug for Windows
4646
+ # if wx.Platform != "__WXMSW__":
4647
+ # self.toolbar.Realize() # 1.3.6.4 [JWDJ] fixes toolbar repaint bug for Windows
4606
4648
if self .record_thread and self .record_thread .is_running :
4607
4649
self .OnToolRecord (None )
4608
- if self .uses_fluidsynth :
4609
- self .OnAfterStop ()
4650
+ # if self.uses_fluidsynth:
4651
+ # self.OnAfterStop()
4610
4652
4611
4653
def OnSeek (self , evt ):
4612
4654
self .mc .Seek (self .progress_slider .GetValue ())
@@ -4629,21 +4671,31 @@ def OnPlayTimer(self, evt):
4629
4671
if not self .is_closed :
4630
4672
if self .mc .is_playing :
4631
4673
self .started_playing = True
4632
-
4674
+
4675
+ if wx .Platform == "__WXMAC__" : #FAU:MIDIPLAY: Used to give the hand to MIDI player
4676
+ delta = self .mc .IdlePlay ()
4677
+ #print(self.mc.get_songinfo)
4678
+ if delta == 0 :
4679
+ if self .loop_midi_playback :
4680
+ self .mc .Seek (0 )
4681
+ else :
4682
+ self .mc .is_play_started = False
4683
+
4633
4684
offset = self .mc .Tell ()
4634
4685
if offset >= self .progress_slider .Max :
4635
4686
length = self .mc .Length ()
4636
- self .progress_slider .SetRange (0 , length )
4637
-
4687
+ self .progress_slider .SetRange (0 , int ( length )) #FAU:MIDIPLAY: mplay might return a float. thus forcing an int
4688
+
4638
4689
if self .settings .get ('follow_score' , False ):
4639
4690
self .queue_number_follow_score += 1
4640
4691
queue_number = self .queue_number_follow_score
4641
- wx .CallLater (1 , self .FollowScore , offset , queue_number ) #[EPO] 2018-11-20 first arg 0 causes exception
4642
-
4692
+ #wx.CallLater(1, self.FollowScore, offset, queue_number) #[EPO] 2018-11-20 first arg 0 causes exception
4693
+ self .FollowScore (offset , queue_number )
4694
+
4643
4695
self .progress_slider .SetValue (offset )
4644
- elif self .started_playing and self . uses_fluidsynth and not self .mc .is_paused :
4696
+ elif self .started_playing and not self .mc .is_paused : #and self.uses_fluidsynth
4645
4697
self .started_playing = False
4646
- self . OnToolStop ( None )
4698
+ wx . CallLater ( 500 , self . OnAfterStop )
4647
4699
4648
4700
def FollowScore (self , offset , queue_number ):
4649
4701
if self .queue_number_follow_score != queue_number :
@@ -7487,7 +7539,15 @@ def extract_note_timings(self, midi_tune, svg_tune):
7487
7539
midi_rows = [i + 1 for i in midi_rows ]
7488
7540
7489
7541
errors = defaultdict (lambda : defaultdict (int ))
7490
- pos_re = re .compile (r'^\s*(\d+\.\d+)\s+CntlParm\s+1\s+unknown\s+=\s+(\d+)' )
7542
+ #FAU: jwdj/EasyABC#99 Starting from commit sshlien/abcmidi@705d9e1f737a2db9fdc615b622bc75204b1bcbee of midi2abc, Follow_score not working
7543
+ #FAU: This commit of midi2abc changed the format of CntlParm.
7544
+ #FAU: it used to be printf("CntlParm %2d %s = %d\n",chan+1, ctype[control],value);
7545
+ #FAU: it is now printf("CntlParm %2d %s = %d %d\n",chan+1, ctype[control],control,value);
7546
+ #FAU: Following regex is expecting only one decimal however an extra one is now present
7547
+ #FAU: To have a regex working for both version, \s*\d* is added
7548
+ #FAU: might need some further check
7549
+ #pos_re = re.compile(r'^\s*(\d+\.\d+)\s+CntlParm\s+1\s+unknown\s+=\s+(\d+)')
7550
+ pos_re = re .compile (r'^\s*(\d+\.\d+)\s+CntlParm\s+1\s+unknown\s+=\s*\d*\s+(\d+)' )
7491
7551
note_re = re .compile (r'^\s*(\d+\.\d+)\s+Note (on|off)\s+(\d+)\s+(\d+)' )
7492
7552
tempo_re = re .compile (r'^\s*(\d+\.\d+)\s+Metatext\s+tempo\s+=\s+(\d+\.\d+)\s+bpm' )
7493
7553
new_track_re = re .compile (r'^Track \d+ contains' )
@@ -8575,7 +8635,8 @@ class AboutFrame(wx.Dialog):
8575
8635
</center>
8576
8636
<p><b>{0}</b><br/>
8577
8637
an open source ABC editor for Windows, OSX and Linux. It is published under the <a href="https://www.gnu.org/licenses/gpl-2.0.html">GNU Public License</a>. </p>
8578
- <p><center><a href="https://www.nilsliberg.se/ksp/easyabc/">https://www.nilsliberg.se/ksp/easyabc/</a></center></p>
8638
+ <p><center>initial repository was at <a href="https://www.nilsliberg.se/ksp/easyabc/">https://www.nilsliberg.se/ksp/easyabc/</a></center></p>
8639
+ <p><center>Now documentation available here<a href="https://easyabc.sourceforge.net">https://easyabc.sourceforge.net</a></center></p>
8579
8640
<p><u>Features</u>:</p>
8580
8641
<ul style="line-height: 150%; margin-top: 3px;">
8581
8642
<li> Good ABC standard coverage thanks to internal use of abcm2ps and abc2midi
@@ -8599,6 +8660,7 @@ class AboutFrame(wx.Dialog):
8599
8660
</ul>
8600
8661
8601
8662
<p><b>EasyABC</b> is brought to you by <b>Nils Liberg</b>, Copyright © 2010-2012.</p>
8663
+ <p><b>EasyABC</b> is maintained by <b>Jan Wybren de Jong</b>, <b>Seymour Shlien</b> and by <b>Frédéric Aupépin</b> for Mac adaptation</p>
8602
8664
<p><b>Credits</b> - software components used by EasyABC:</p>
8603
8665
<ul class="nicelist">
8604
8666
<li><a href="http://moinejf.free.fr/">abcm2ps</a> for converting ABC code to note images (developed/maintained by Jean-François Moine)</li>
@@ -8610,9 +8672,10 @@ class AboutFrame(wx.Dialog):
8610
8672
<li><a href="https://www.mxm.dk/products/public/pythonmidi">python midi package</a> for the initial parsing of midi files to be imported</li>
8611
8673
<li><a href="https://www.pygame.org/download.shtml">pygame</a> (which wraps <a href="https://sourceforge.net/apps/trac/portmedia/wiki/portmidi">portmidi</a>) for real-time midi input</li>
8612
8674
<li><a href="https://www.fluidsynth.org/">FluidSynth</a> for playing midi (and made fit for Python by <a href="https://wim.vree.org/svgParse/index.html">Willem Vree</a>)</li>
8675
+ <li><a href="https://github.com/jheinen/mplay">Python MIDI Player</a> for playing midi on Mac</li>
8613
8676
<li>Thanks to Guido Gonzato for providing the fields and command reference.
8614
8677
<li><br>Many thanks to the translators: Valerio Pelliccioni, Guido Gonzato (italian), Bendix Rødgaard (danish), Frédéric Aupépin (french), Bernard Weichel (german), Jan Wybren de Jong (dutch) and Wu Xiaotian (chinese).</li>
8615
- <li>Universal binaries of abcm2ps and abc2midi for OSX are available thanks to Chuck Boody. </li>
8678
+ <li>Universal binaries of <a href="https://abcplus.sourceforge.net/# abcm2ps">abcm2ps</a> and <a href="https://abcplus.sourceforge.net/#abcmidi"> abc2midi</a> for OSX are available thanks to Chuck Boody and Guido Gonzato </li>
8616
8679
</ul>
8617
8680
8618
8681
<p><b>Links</b></p>
@@ -8621,7 +8684,7 @@ class AboutFrame(wx.Dialog):
8621
8684
<li><a href="http://abcplus.sourceforge.net/">abcplus.sourceforge.net</a></li>
8622
8685
<li><a href="http://moinejf.free.fr/">Jef Moine's abcm2ps page</a></li>
8623
8686
<li><a href="https://abcmidi.sourceforge.io/">Seymour Shlien's abcMIDI page</a></li>
8624
- <li><a href="http://www.folkwiki.se/">folkwiki.se - Swedish folk music</a> (my involvement here is the reason why I implemented the program)</li>
8687
+ <li><a href="http://www.folkwiki.se/">folkwiki.se - Swedish folk music</a> (initial involvement of Nils here is the reason why he implemented the program)</li>
8625
8688
</ul>
8626
8689
</body>
8627
8690
</html>
0 commit comments