-
-
Save MervinPraison/3c0c67b56ebfaf9d284b3e1000757857 to your computer and use it in GitHub Desktop.
Revisions
-
mjaSanJose revised this gist
Aug 15, 2016 . 6 changed files with 697 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,370 @@ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>MidiCode</key> <array> <integer>21</integer> <integer>22</integer> <integer>23</integer> <integer>24</integer> <integer>25</integer> <integer>26</integer> <integer>27</integer> <integer>28</integer> <integer>29</integer> <integer>30</integer> <integer>31</integer> <integer>32</integer> <integer>33</integer> <integer>34</integer> <integer>35</integer> <integer>36</integer> <integer>37</integer> <integer>38</integer> <integer>39</integer> <integer>40</integer> <integer>41</integer> <integer>42</integer> <integer>43</integer> <integer>44</integer> <integer>45</integer> <integer>46</integer> <integer>47</integer> <integer>48</integer> <integer>49</integer> <integer>50</integer> <integer>51</integer> <integer>52</integer> <integer>53</integer> <integer>54</integer> <integer>55</integer> <integer>56</integer> <integer>57</integer> <integer>58</integer> <integer>59</integer> <integer>60</integer> <integer>61</integer> <integer>62</integer> <integer>63</integer> <integer>64</integer> <integer>65</integer> <integer>66</integer> <integer>67</integer> <integer>68</integer> <integer>69</integer> <integer>70</integer> <integer>71</integer> <integer>72</integer> <integer>73</integer> <integer>74</integer> <integer>75</integer> <integer>76</integer> <integer>77</integer> <integer>78</integer> <integer>79</integer> <integer>80</integer> <integer>81</integer> <integer>82</integer> <integer>83</integer> <integer>84</integer> <integer>85</integer> <integer>86</integer> <integer>87</integer> <integer>88</integer> <integer>89</integer> <integer>90</integer> <integer>91</integer> <integer>92</integer> <integer>93</integer> <integer>94</integer> <integer>95</integer> <integer>96</integer> <integer>97</integer> <integer>98</integer> <integer>99</integer> <integer>100</integer> <integer>101</integer> <integer>102</integer> <integer>103</integer> <integer>104</integer> <integer>105</integer> <integer>106</integer> <integer>107</integer> <integer>108</integer> </array> <key>NoteName</key> <array> <string>A0</string> <string>Black</string> <string>B0</string> <string>C0</string> <string>Black</string> <string>D1</string> <string>Black</string> <string>E1</string> <string>F1</string> <string>Black</string> <string>G1</string> <string>Black</string> <string>A1</string> <string>Black</string> <string>B1</string> <string>C2</string> <string>Black</string> <string>D2</string> <string>Black</string> <string>E2</string> <string>F2</string> <string>Black</string> <string>G2</string> <string>Black</string> <string>A2</string> <string>Black</string> <string>B2</string> <string>C3</string> <string>Black</string> <string>D3</string> <string>Black</string> <string>E3</string> <string>F3</string> <string>Black</string> <string>G3</string> <string>Black</string> <string>A3</string> <string>Black</string> <string>B3</string> <string>C4</string> <string>Black</string> <string>D4</string> <string>Black</string> <string>E4</string> <string>F4</string> <string>Black</string> <string>G4</string> <string>Black</string> <string>A4</string> <string>Black</string> <string>B4</string> <string>C5</string> <string>Black</string> <string>D5</string> <string>Black</string> <string>E5</string> <string>F5</string> <string>Black</string> <string>G5</string> <string>Black</string> <string>A5</string> <string>Black</string> <string>B5</string> <string>C6</string> <string>Black</string> <string>D6</string> <string>Black</string> <string>E6</string> <string>F6</string> <string>Black</string> <string>G6</string> <string>Black</string> <string>A6</string> <string>Black</string> <string>B6</string> <string>C7</string> <string>Black</string> <string>D7</string> <string>Black</string> <string>E7</string> <string>F7</string> <string>Black</string> <string>G7</string> <string>Black</string> <string>A7</string> <string>Black</string> <string>B7</string> <string>C8</string> </array> <key>Frequency</key> <array> <real>27.5</real> <real>29.135</real> <real>30.868</real> <real>32.703</real> <real>34.648</real> <real>36.708</real> <real>38.891</real> <real>41.203</real> <real>43.654</real> <real>46.249</real> <real>48.999</real> <real>51.913</real> <real>55</real> <real>58.27</real> <real>61.735</real> <real>65.40600000000001</real> <real>69.29600000000001</real> <real>73.416</real> <real>77.782</real> <real>82.407</real> <real>87.307</real> <real>92.499</real> <real>97.999</real> <real>103.83</real> <real>110</real> <real>116.54</real> <real>123.47</real> <real>130.81</real> <real>138.59</real> <real>146.83</real> <real>155.56</real> <real>164.81</real> <real>174.61</real> <real>185</real> <integer>196</integer> <real>207.65</real> <integer>220</integer> <real>233.08</real> <real>246.94</real> <real>261.63</real> <real>277.18</real> <real>293.67</real> <real>311.13</real> <real>329.63</real> <real>349.23</real> <real>369.99</real> <real>392</real> <real>415.3</real> <integer>440</integer> <real>466.16</real> <real>493.88</real> <real>523.25</real> <real>554.37</real> <real>587.33</real> <real>622.25</real> <real>659.26</real> <real>698.46</real> <real>739.99</real> <real>783.99</real> <real>830.61</real> <integer>880</integer> <real>932.33</real> <real>987.77</real> <real>1046.5</real> <real>1108.7</real> <real>1174.7</real> <real>1244.5</real> <real>1318.5</real> <real>1396.9</real> <real>1480</real> <real>1568</real> <real>1661.2</real> <integer>1760</integer> <real>1864.7</real> <real>1975.5</real> <integer>2093</integer> <real>2217.5</real> <real>2349.3</real> <real>2489</real> <integer>2637</integer> <real>2793</real> <integer>2960</integer> <real>3136</real> <real>3322.4</real> <integer>3520</integer> <real>3729.3</real> <real>3951.1</real> <real>4186</real> </array> <key>Period</key> <array> <real>36.36</real> <real>34.32</real> <real>32.4</real> <real>30.58</real> <real>28.86</real> <real>27.24</real> <real>25.71</real> <real>24.27</real> <real>22.91</real> <real>21.62</real> <real>20.41</real> <real>19.26</real> <real>18.18</real> <real>17.16</real> <real>16.2</real> <real>15.29</real> <real>14.29</real> <real>13.62</real> <real>12.86</real> <real>12.13</real> <real>11.45</real> <real>10.81</real> <real>10.2</real> <real>9.631</real> <real>9.090999999999999</real> <real>8.581</real> <real>8.099</real> <real>7.645</real> <real>7.216</real> <real>6.811</real> <real>6.428</real> <real>6.068</real> <real>5.727</real> <real>5.405</real> <real>5.102</real> <real>4.816</real> <real>4.545</real> <real>4.29</real> <real>4.05</real> <real>3.822</real> <real>3.608</real> <real>3.405</real> <real>3.214</real> <real>3.034</real> <real>2.863</real> <real>2.703</real> <real>2.551</real> <real>2.408</real> <real>2.273</real> <real>2.145</real> <real>2.025</real> <real>1.91</real> <real>1.804</real> <real>1.703</real> <real>1.607</real> <real>1.517</real> <real>1.432</real> <real>1.351</real> <real>1.276</real> <real>1.204</real> <real>1.136</real> <real>1.073</real> <real>1.012</real> <real>0.9556</real> <real>0.902</real> <real>0.8512999999999999</real> <real>0.8034</real> <real>0.7584</real> <real>0.7159</real> <real>0.6757</real> <real>0.6378</real> <real>0.602</real> <real>0.5682</real> <real>0.5363</real> <real>0.5062</real> <real>0.4778</real> <real>0.451</real> <real>0.4257</real> <real>0.4018</real> <real>0.3792</real> <real>0.358</real> <real>0.3378</real> <real>0.3189</real> <real>0.301</real> <real>0.2841</real> <real>0.2681</real> <real>0.2531</real> <real>0.2389</real> </array> </dict> </plist> This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,30 @@ // // MusicMidiNoteFrequenciesPianoTable.h // MusicTheory // // Created by Michael J Albanese on 4/16/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import <Foundation/Foundation.h> #import "MusicTheoryDefs.h" #define kMusicMidiCode_MiddleC 60 #define kMusicMidiCode_PitchA440 69 @class MusicMidiNoteFrequencyEntry; @interface MusicMidiNoteFrequenciesPianoTable : NSObject @property (nonatomic, readonly) BOOL tableBuilt; + (instancetype) midiNoteFrequenciesPianoTable; - (void) buildFrequenciesTable; - (MusicMidiNoteFrequencyEntry *) findEntryByMidiCode:(NSInteger)midiCode; - (MusicMidiNoteFrequencyEntry *) findMiddleCEntry; - (MusicMidiNoteFrequencyEntry *) findPitchA440Entry; - (MusicMidiNoteFrequencyEntry *) findEntryByNoteId:(kMusicNoteId)noteId andMidiOctave:(NSInteger)octave; // May need an auxiliary Octave --> frequency range mapping @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,226 @@ // // MusicMidiNoteFrequenciesPianoTable.m // MusicTheory // // Created by Michael J Albanese on 4/16/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import "MusicMidiNoteFrequencyEntry.h" #import "MusicMidiNoteFrequenciesPianoTable.h" NSString *kMidiCodeFrequenciesFileName = @"MidiCodeFrequencies"; @interface MusicMidiNoteFrequenciesPianoTable () @property (nonatomic, readwrite) BOOL tableBuilt; @property (strong, nonatomic) NSMutableDictionary *frequenciesTable; @property (strong, nonatomic) NSMutableArray *sortedEntries; @end @implementation MusicMidiNoteFrequenciesPianoTable + (instancetype) midiNoteFrequenciesPianoTable { return [[MusicMidiNoteFrequenciesPianoTable alloc] initFrequenciesTable]; } - (id) initFrequenciesTable { if (self = [super init]) { _frequenciesTable = [NSMutableDictionary dictionary]; _sortedEntries = [NSMutableArray array]; } return self; } - (void) dealloc { [_frequenciesTable removeAllObjects]; } - (MusicMidiNoteFrequencyEntry *) findEntryByMidiCode:(NSInteger)midiCode { if (_frequenciesTable == nil || _frequenciesTable.count < midiCode) { return nil; } MusicMidiNoteFrequencyEntry *entry = _frequenciesTable[ @(midiCode) ]; return entry; } - (MusicMidiNoteFrequencyEntry *) findEntryByNoteId:(kMusicNoteId)noteId andMidiOctave:(NSInteger)octave { MusicMidiNoteFrequencyEntry *foundEntry; // Brute force linear scane for now, still only 88 entries ...small for (MusicMidiNoteFrequencyEntry *entry in _sortedEntries) { if (entry.noteId == noteId && entry.octave == octave) { foundEntry = entry; break; } } return foundEntry; } - (MusicMidiNoteFrequencyEntry *) findMiddleCEntry { if (_frequenciesTable == nil || _frequenciesTable.count < kMusicMidiCode_MiddleC) { return nil; } MusicMidiNoteFrequencyEntry *entry = _frequenciesTable[ @(kMusicMidiCode_MiddleC) ]; return entry; } - (MusicMidiNoteFrequencyEntry *) findPitchA440Entry { if (_frequenciesTable == nil || _frequenciesTable.count < kMusicMidiCode_PitchA440) { return nil; } MusicMidiNoteFrequencyEntry *entry = _frequenciesTable[ @(kMusicMidiCode_PitchA440) ]; return entry; } #pragma mark - Internal Build Methods - (void) buildFrequenciesTable { NSURL *urlForMidiCodeFile = [self loadMidiCodeSourceListingFile]; if (urlForMidiCodeFile != nil) { [self loadKeysAndParseDictionaryFromUrl:urlForMidiCodeFile]; } } - (NSURL *) loadMidiCodeSourceListingFile { // This will always run in the context of an App, and therefore is dependent // upon the application to have copied the MusicTheoryBundle target into the // Copy Bundle Resources section of the applications Build Phases -> Copy Bundle Resources // Load the library's bundle object from Apps resources NSString *libraryBundlePath = [[NSBundle mainBundle] pathForResource:@"MusicTheoryBundle" ofType:@"bundle"]; NSBundle *staticLibraryBundle = [NSBundle bundleWithPath:libraryBundlePath]; NSAssert(staticLibraryBundle != nil, @"MusicTheoryBundle NOT loaded"); NSURL *midiUrl = [staticLibraryBundle URLForResource:kMidiCodeFrequenciesFileName withExtension:@"plist"]; return midiUrl; } - (BOOL) loadKeysAndParseDictionaryFromUrl:(NSURL *)midiCodeListingUrl { MusicMidiNoteFrequencyEntry *entry, *lastEntry; NSInteger totalCodeCount = 88; // Load the file content and read the data into arrays NSDictionary *tempDict = [[NSDictionary alloc] initWithContentsOfURL:midiCodeListingUrl]; if (tempDict == nil || tempDict.count != 4) { return NO; } // Grab the known / named 4 sub arrays NSArray *arMidiCodes = [tempDict objectForKey:@"MidiCode"]; NSArray *arNoteName = [tempDict objectForKey:@"NoteName"]; NSArray *arFrequencies = [tempDict objectForKey:@"Frequency"]; NSArray *arPeriods = [tempDict objectForKey:@"Period"]; if (arMidiCodes.count != totalCodeCount || arNoteName.count != totalCodeCount || arFrequencies.count != totalCodeCount || arPeriods.count != totalCodeCount) { return NO; } [_frequenciesTable removeAllObjects]; _tableBuilt = NO; NSString *singleLetter, *octaveLetter; NSNumber *previousMidiCode; // Loop over the set of 4 arrays and build frequency Dictionary for (NSInteger offset = 0; offset < totalCodeCount; offset++) { NSNumber *midiNumberObj = arMidiCodes[offset]; NSString *noteName = arNoteName[offset]; NSNumber *frequencyNumObj = arFrequencies[offset]; NSNumber *periodNumObj = arPeriods[offset]; entry = [MusicMidiNoteFrequencyEntry midiNoteFrequencyEntry:midiNumberObj.integerValue]; entry.frequency = frequencyNumObj.floatValue; entry.period = periodNumObj.floatValue; if ([noteName isEqualToString:@"Black"]) { entry.isBlackKey = YES; entry.noteId = kMusicNoteIdEnharmonic; entry.octave = lastEntry.octave; entry.previousKeyMidiCode = previousMidiCode.integerValue; } else { singleLetter = [noteName substringWithRange:NSMakeRange(0, 1)]; octaveLetter = [noteName substringWithRange:NSMakeRange(1, 1)]; entry.noteId = [self noteIdFromLetterString:singleLetter]; entry.octave = [octaveLetter integerValue]; entry.codedNoteName = noteName; previousMidiCode = arMidiCodes[offset]; } _frequenciesTable[ @(midiNumberObj.integerValue) ] = entry; lastEntry = entry; } if (_frequenciesTable.count == totalCodeCount) { [self sortDictionaryValues]; _tableBuilt = YES; } return _tableBuilt; } - (void) sortDictionaryValues { NSArray *allEntries = [_frequenciesTable allValues]; NSSortDescriptor *sd; NSString *kvSortPath = @"midiCode"; sd = [NSSortDescriptor sortDescriptorWithKey:kvSortPath ascending:YES]; NSArray *arSorted = [allEntries sortedArrayUsingDescriptors:@[sd]]; [_sortedEntries addObjectsFromArray:arSorted]; } - (kMusicNoteId) noteIdFromLetterString:(NSString *)singleLetter { kMusicNoteId aNoteId = kMusicNoteIdMax; if ([singleLetter isEqualToString:@"C"]) { aNoteId = kMusicNoteId_C; } else if ([singleLetter isEqualToString:@"D"]) { aNoteId = kMusicNoteId_D; } else if ([singleLetter isEqualToString:@"E"]) { aNoteId = kMusicNoteId_E; } else if ([singleLetter isEqualToString:@"F"]) { aNoteId = kMusicNoteId_F; } else if ([singleLetter isEqualToString:@"G"]) { aNoteId = kMusicNoteId_G; } else if ([singleLetter isEqualToString:@"A"]) { aNoteId = kMusicNoteId_A; } else if ([singleLetter isEqualToString:@"B"]) { aNoteId = kMusicNoteId_B; } return aNoteId; } @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,29 @@ // // MusicMidiNoteFrequencyEntry.h // MusicTheory // // Created by Michael J Albanese on 4/16/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import <Foundation/Foundation.h> #import "MusicTheoryDefs.h" /** * Small class represents basic entry in the MidiNoteFrequenciesTable */ @interface MusicMidiNoteFrequencyEntry : NSObject @property (nonatomic) NSInteger midiCode; @property (nonatomic) kMusicNoteId noteId; @property (nonatomic) NSInteger octave; @property (nonatomic) float frequency; @property (nonatomic) float period; @property (nonatomic) BOOL isBlackKey; @property (nonatomic) NSInteger previousKeyMidiCode; @property (nonatomic, copy) NSString *codedNoteName; // When the kMusicStaffOctave enums get reworked, start using this // @property (nonatomic) kMusicStaffOctave octave; + (instancetype) midiNoteFrequencyEntry:(NSInteger)code; @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,28 @@ // // MusicMidiNoteFrequencyEntry.m // MusicTheory // // Created by Michael J Albanese on 4/16/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import "MusicMidiNoteFrequencyEntry.h" @implementation MusicMidiNoteFrequencyEntry + (instancetype) midiNoteFrequencyEntry:(NSInteger)code { return [[MusicMidiNoteFrequencyEntry alloc] initWithMidiCode:code]; } - (id) initWithMidiCode:(NSInteger)code { if (self = [super init]) { _midiCode = code; _noteId = kMusicNoteIdMax; } return self; } @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -5,16 +5,23 @@ This gist has the source code to match the series of [Blog posts for mapping not Here is a brief rundown of the source files, and headers in this gist: ``` TestIteration.m - example code for using the Iterator MusicGuitarStringFretIterator.hm - Offers iteration over Tab staff / guitar fretboard MusicGuitarStringFretLocation.hm - basic string-fret location class MusicGuitarStringFretRange.hm - defines range for controlling iterators fretboard position MusicGuitarTabSixStaff.hm - actual TAB staff, has six (or four) tuneable strings MusicTabStaffStringFretMidiMaps.hm - acts as the 'string' on TAB staff and holds a series of SingleFretMap's MusicTabStaffSingleFretMap.hm - contains info for an individual guitar fret MidiNoteFrequenciesPianoTable.hm - collection class, opens, parses .plist, offers lookups MidiNoteFrequencyEntry.hm - one element in the midi lookup table collection MusicMidiNoteOctave.hm - simple class, part of public Api to Iterator find methods MidiCodeFrequencies.plist - plist file defines 4 arrays, notes, midi codes, etc. ``` Since this is a gist, these files will _not_ compile as is. They are part of a much larger project. -
mjaSanJose revised this gist
Aug 13, 2016 . 7 changed files with 2356 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,45 @@ // // MusicGuitarTabSixStaff.h // MusicRendering // // Created by Michael J Albanese on 4/9/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import "MusicStaff.h" #import "MusicStaffTraversable_p.h" @class MusicTabClefRenderable; @class MusicMidiNoteFrequenciesPianoTable; /** * Derived from the standard MusicStaff class. Important that the 'init' method * be defined publicly, AND express the _exact same_ prototype as the base class. * This is due to the nature of having a MusicStaffFactory object be the central * instantiater of staffs (although public may also create staffs stand alone). */ @interface MusicGuitarTabSixStaff : MusicStaff <MusicStaffTraversable_p> + (MusicGuitarTabSixStaff *) guitarTabStaffWithLayoutInfo:(MusicStaffLayoutInfo *)layoutInfo andMiddleXCoordinate:(float)middleX andLowestYCoordinate:(float)lowY andStaffMetrics:(MusicStaffRenderMetrics *)staffMetrics andPageRenderCriteria:(MusicPageRenderCriteria *)renderCriteria; - (id) initWithLayoutInfo:(MusicStaffLayoutInfo *)layoutInfo andMiddleXCoordinate:(float)middleX andLowestYCoordinate:(float)lowY andStaffMetrics:(MusicStaffRenderMetrics *)staffMetrics andPageRenderCriteria:(MusicPageRenderCriteria *)renderCriteria; - (void) tuneStringsUsingBaseMidiCodes:(NSArray *)arrayOfMidiCodes; - (MusicMidiNoteFrequenciesPianoTable *) getMidiCodeTable; - (void) clearMidiTable; - (void) calcClefDrawingRectsWithLeftMostX:(CGFloat)leftMostX topMostY:(CGFloat)topMostY areaSize:(CGSize)areaSize andFont:(UIFont *)theFont intoRenderable:(MusicTabClefRenderable *) tabClefRenderable; /** quasi 'overrides' of base/super methods */ - (void) mapStaffOctaveAndNoteCoordinates; - (void) calcDropZonesFromCombinedVectorMappings; @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,571 @@ // // MusicGuitarTabSixStaff.m // MusicRendering // // Created by Michael J Albanese on 4/9/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import <CoreText/CoreText.h> #import "SWFVector3.h" #import "NoteData+MoreFuncs.h" #import "MusicStaffNotifications_p.h" #import "MusicPageRenderCriteria.h" #import "MusicStaffRenderMetrics.h" #import "MusicTabStaffStringFretMidiMaps.h" #import "MusicMidiNoteFrequenciesPianoTable.h" #import "MusicMidiNoteFrequencyEntry.h" #import "MusicTabClefRenderable.h" #import "MusicStaffLineVectorMap.h" #import "MusicStaffDropZone.h" #import "MusicGuitarTabSixStaff.h" #pragma mark - TabSixStaff Class @interface MusicGuitarTabSixStaff () @property (strong, nonatomic) MusicMidiNoteFrequenciesPianoTable *midiTable; @property (strong, nonatomic) MusicTabClefRenderable *tabClefRenderable; @end #pragma mark - Main TabSixStaff Class Methods @implementation MusicGuitarTabSixStaff + (MusicGuitarTabSixStaff *) guitarTabStaffWithLayoutInfo:(MusicStaffLayoutInfo *)layoutInfo andMiddleXCoordinate:(float)middleX andLowestYCoordinate:(float)lowY andStaffMetrics:(MusicStaffRenderMetrics *)staffMetrics andPageRenderCriteria:(MusicPageRenderCriteria *)pageCriteria { return [[MusicGuitarTabSixStaff alloc] initWithLayoutInfo:layoutInfo andMiddleXCoordinate:middleX andLowestYCoordinate:lowY andStaffMetrics:staffMetrics andPageRenderCriteria:pageCriteria]; } - (id) initWithLayoutInfo:(MusicStaffLayoutInfo *)layoutInfo andMiddleXCoordinate:(float)middleX andLowestYCoordinate:(float)lowY andStaffMetrics:(MusicStaffRenderMetrics *)staffMetrics andPageRenderCriteria:(MusicPageRenderCriteria *)pageCriteria { if (self = [super initWithLayoutInfo:layoutInfo andMiddleXCoordinate:middleX andLowestYCoordinate:lowY andStaffMetrics:staffMetrics andPageRenderCriteria:pageCriteria]) { _tabClefRenderable = [[MusicTabClefRenderable alloc] init]; } return self; } - (kSongStaffId) staffId { return self.staffLayoutInfo.staffId; } // FIXME either make this Class static member (of GuitarTabSixStaff) // - OR - make the MidiTable itself a Singleton // // Currently a static member of the MusicKey hierarchy's base class... // - (MusicMidiNoteFrequenciesPianoTable *) getMidiCodeTable { if (_midiTable == nil) { _midiTable = [MusicMidiNoteFrequenciesPianoTable midiNoteFrequenciesPianoTable]; [_midiTable buildFrequenciesTable]; } return _midiTable; } - (void) clearMidiTable { _midiTable = nil; } #pragma mark - Mapping Staff Strings - (void) mapStaffOctaveAndNoteCoordinates { NSMutableArray *arMidiCodes = [NSMutableArray array]; // Note the use of the staff layout 'usedStaffLines' property to control // number of nut midi codes (may be 6 or 4). Implicitily this dictates // line count for either a six string guitar, or four string bass guitar. for (int i = 0; i < self.staffLayoutInfo.usedStaffLines; i++) { NSInteger nutMidiCode = self.staffLayoutInfo.lineNotes[i].midiCode; [arMidiCodes addObject:@(nutMidiCode)]; } if (arMidiCodes.count > 0) { [self tuneStringsUsingBaseMidiCodes:arMidiCodes]; } } // Consider making this a public method, or in some way callable // in order to retune a Tab staff - (void) tuneStringsUsingBaseMidiCodes:(NSArray *)arrayOfMidiCodes { [self.staffVectorMappings removeAllObjects]; [self.linesVectorMappingsIndex removeAllObjects]; [self.loToHiCombinedMappingsIndex removeAllObjects]; float x = self.middleXCoordinate; float y = self.lowestYCoordinate; float z = 0.; self.lowestLineVectorMapIndex = self.highestLineVectorMapIndex = kMaxStaffLines + 10; self.highestYCoordinate = 2000.0; float yincr = self.staffMetrics.yGapBetweenLines; MusicMidiNoteFrequenciesPianoTable *midiTable = [self getMidiCodeTable]; MusicTabStaffStringFretMidiMaps *oneStringMapping; // self.staffLayoutInfo.usedStaffLines; if (arrayOfMidiCodes.count != kMusicGuitarTotalStrings) { NSAssert1(arrayOfMidiCodes.count == kMusicGuitarTotalStrings, @"Illegal string count for TAB staff: %d", arrayOfMidiCodes.count); } // utilize stringNumber Enums which are zero based, 0 being 6th string NSInteger stringNumber = kMusicGuitarFirstString; // loop bottom up for Lines in staff while (stringNumber <= kMusicGuitarLastString) { NSNumber *codeNumber = arrayOfMidiCodes[stringNumber]; NSInteger nutMidiCode = codeNumber.integerValue; oneStringMapping = [MusicTabStaffStringFretMidiMaps stringFretMidiMapForStringNumber:stringNumber]; oneStringMapping.vec = [SWFVector3 vectorFromValues:x y:y z:z]; oneStringMapping.zoneType = kMusicLineZone; [oneStringMapping tuneAllFretsUsingNutMidiCode:nutMidiCode andFrequenciesTable:midiTable]; // Add the custom StringFretMidiMap object into the inherited vectorMappings array [self.staffVectorMappings addObject:oneStringMapping]; if (stringNumber == kMusicGuitarFirstString) { self.lowestLineVector = [SWFVector3 vectorFromVector:oneStringMapping.vec]; self.lowestLineVectorMapIndex = stringNumber; } if (y < self.highestYCoordinate) { self.highestYCoordinate = y; self.highestLineVectorMapIndex = stringNumber; } // update the lines index with offset of newly added line mapping [self.linesVectorMappingsIndex addObject:@(self.staffVectorMappings.count - 1)]; y -= yincr; stringNumber++; } // now populate the combined mappings array even though this staff only has lines [self.loToHiCombinedMappingsIndex removeAllObjects]; NSInteger totalMappings = self.staffVectorMappings.count; NSInteger lineOff = 0; for (NSInteger x = 0; x < totalMappings; x++) { if (lineOff < self.linesVectorMappingsIndex.count) { oneStringMapping = self.staffVectorMappings[lineOff]; if (oneStringMapping) { [self.loToHiCombinedMappingsIndex addObject:@(lineOff)]; } lineOff++; } } [self clearMidiTable]; } - (SWFVector3 *) findStaffVectorForNoteUsingItsGuitarString:(NoteData *)aNote { SWFVector3 *noteVec; MusicTabStaffStringFretMidiMaps *oneStringMapping; if (aNote.guitarStringNumber) { NSInteger zeroBasedStringOffset = aNote.guitarStringNumber.integerValue; if (kVALID_GUITAR_STRING(zeroBasedStringOffset) && zeroBasedStringOffset < self.staffVectorMappings.count) { oneStringMapping = self.staffVectorMappings[ zeroBasedStringOffset ]; noteVec = [oneStringMapping.vec mutableCopy]; } } return noteVec; } #pragma mark - MusicStaffDisplayable Protocol methods - (SWFVector3 *) findStaffVectorForNoteUsingItsOctave:(NoteData *)aNote { SWFVector3 *noteVec = [self findStaffVectorForNoteUsingItsGuitarString:aNote]; return noteVec; } - (NSArray *) measureBarCoordinatesForLine { return [super measureBarCoordinatesForLine]; } - (float) calcTopRealLineUsingMetrics { CGFloat fixedStaffHeight = (self.staffMetrics.fixedLineCount - 1) * self.staffMetrics.yGapBetweenLines; fixedStaffHeight += self.staffMetrics.fixedLineCount * self.staffMetrics.staffLineWidth; CGFloat firstY = self.staffYCenter - (fixedStaffHeight / 2); if (self.staffMetrics.staffLineWidth == 1) { firstY = [MusicStaffRenderMetrics roundToLeastHalfPoint:firstY]; } else if (self.staffMetrics.staffLineWidth == 2) { firstY = floorf(firstY); } return firstY; } - (float) calcBottomRealLineUsingMetrics { return self.lowestYCoordinate; } #pragma mark - Drop Zone Methods - (void) calcDropZonesFromCombinedVectorMappings { MusicTabStaffStringFretMidiMaps *staffStringFretMaps = nil; MusicStaffDropZone *last1back = nil; MusicStaffDropZone *dz; [self clearDropZones]; self.currZoneHilite = nil; // calc the uniform height of all drop zones float zoneHeight = [self calcUniformDropZoneHeightBasedOnYGap]; // update the members consulted for swipe up and boundary dimensions [self establishLeftAndRightXBoundaryFromMetrics]; // accessing the _staffVectorMappings thru the combined indexing array // delivers staff positions in sequence starting at the lowest Space of staff NSInteger dropZoneId = 1; for (NSNumber *ixOff in self.loToHiCombinedMappingsIndex) { staffStringFretMaps = [self.staffVectorMappings objectAtIndex:ixOff.integerValue]; if (staffStringFretMaps) { // calc a bounding rect based on 1/2 zoneHeight either side of the // 'Y' postion of the staffMapping for given string (e.g. line on staff) SWFVector3 *mappingVector = staffStringFretMaps.vec; float zoneTop = mappingVector.y - (zoneHeight / 2.); float zoneBottom = mappingVector.y + (zoneHeight / 2.); SWFBoundingBox boundingBox; boundingBox.llr_x = self.leftmostX; boundingBox.llr_y = zoneBottom; boundingBox.llr_z = 0; boundingBox.upr_x = self.rightmostX; boundingBox.upr_y = zoneTop; boundingBox.upr_z = 0; // Create a guitar staff oriented Drop zone dz = [[MusicStaffDropZone alloc] initWithBoundingBox:&boundingBox andType:staffStringFretMaps.zoneType guitarString:staffStringFretMaps.stringNumber andFretMaps:staffStringFretMaps.allFretMaps]; dz.staffId = self.staffLayoutInfo.staffId; dz.noteId = kMusicNoteIdMax; dz.octave = kOctaveNone; // do the linked list thing if (ixOff.integerValue == 0) { last1back = dz; } else { dz.previousZone = last1back; last1back.nextZone = dz; last1back = dz; } dz.zoneId = dropZoneId; [self addDropZone:dz]; dropZoneId++; } } } - (BOOL) doDropZoneSetsMatch:(NSArray *)zoneSet1 secondSet:(NSArray *)zoneSet2 { BOOL setsEqual = NO; if (zoneSet1.count != zoneSet2.count) { return NO; } MusicStaffDropZone *zoneA; MusicStaffDropZone *zoneB; setsEqual = YES; for (int i = 0; i < zoneSet1.count; i++) { zoneA = [zoneSet1 objectAtIndex:i]; zoneB = [zoneSet2 objectAtIndex:i]; if (!kVALID_GUITAR_STRING(zoneA.guitarString)) { setsEqual = NO; continue; } if (zoneA.guitarString == zoneB.guitarString) { setsEqual = YES; } else { setsEqual = NO; break; } } return setsEqual; } - (float) distanceBetweenStaffLines { return self.staffMetrics.yGapBetweenLines; } - (void) remapStaffCoordinates { // NSLog(@"Derived MusicGuitarTabSixStaff remapStaffCoords"); } #pragma mark - MusicStaffTraversable_p Protocol methods - (NSArray *) traversableLines { return self.staffVectorMappings; } - (NSArray *) traversableSpaces { return nil; } #pragma mark - Calc Rectangles - (void) calcClefDrawingRectsWithLeftMostX:(CGFloat)leftMostX topMostY:(CGFloat)topMostY areaSize:(CGSize)areaSize andFont:(UIFont *)theFont intoRenderable:(MusicTabClefRenderable *) tabClefRenderable { CGRect rectT = CGRectZero, rectA = CGRectZero, rectB = CGRectZero; // Divide the given area height into 3 'slots', where the rects are // initially placed. // Each rect though will be given the height of the font ascender // (even if this exceeds the slot Height), to ensure the full character is rendered. // Finally the 'Y' origin is yanked 'up' by the distance of the font's descender, // this ensures the rendered chars land in the designated 'slot' positions with no // top side gaps. //MusicTabStaffStringFretMidiMaps *hiStringMapping = self.staffVectorMappings[self.highestLineVectorMapIndex]; //CGFloat topMostY = hiStringMapping.vec.y + self.staffMetrics.staffLineWidth; //CGFloat leftMostX = self.staffMetrics.leftMostXOffset + self.staffMetrics.firstMeasureBarLineWidth + 1; CGFloat eachSlotMark = (areaSize.height / 3); eachSlotMark = [MusicStaffRenderMetrics roundToLeastHalfPoint:eachSlotMark]; topMostY = [MusicStaffRenderMetrics roundToLeastHalfPoint:topMostY]; leftMostX = [MusicStaffRenderMetrics roundToLeastHalfPoint:leftMostX]; areaSize.width = [MusicStaffRenderMetrics roundToLeastHalfPoint:areaSize.width]; // left side origin and widths are never altered rectT.origin.x = rectA.origin.x = rectB.origin.x = leftMostX; rectT.size.width = rectA.size.width = rectB.size.width = areaSize.width; // lay these down vertically along the pre calc'd slot positions rectT.origin.y = topMostY; topMostY += eachSlotMark; rectA.origin.y = topMostY; topMostY += eachSlotMark ; rectB.origin.y = topMostY; // ensure each rect is tall enough for Capital Letters (only) CGFloat fontAscender = ceilf(theFont.ascender); rectT.size.height = rectA.size.height = rectB.size.height = fontAscender; // sleight of hand here, pull the rects 'up' by descender value, to // snug-fit them to desired locations be aware the descender is // often (always ?) a negative value rectT.origin.y -= fabsf(theFont.descender); rectA.origin.y -= fabsf(theFont.descender); rectB.origin.y -= fabsf(theFont.descender); rectT.origin.y = [MusicStaffRenderMetrics roundToLeastHalfPoint:rectT.origin.y]; rectA.origin.y = [MusicStaffRenderMetrics roundToLeastHalfPoint:rectA.origin.y]; rectB.origin.y = [MusicStaffRenderMetrics roundToLeastHalfPoint:rectB.origin.y]; // place results in passed parameter [tabClefRenderable clearRects]; tabClefRenderable.rectForT = rectT; tabClefRenderable.rectForA = rectA; tabClefRenderable.rectForB = rectB; } - (CGRect) boundingRectForClef { NSDictionary *attrsDict = [super textAttributesDictionaryForClef]; if (attrsDict == nil) { return CGRectZero; } CGRect boundingClefRect = CGRectZero; CGSize clefArea = {0}; MusicTabStaffStringFretMidiMaps *lowStringMapping = self.staffVectorMappings[self.lowestLineVectorMapIndex]; MusicTabStaffStringFretMidiMaps *hiStringMapping = self.staffVectorMappings[self.highestLineVectorMapIndex]; clefArea.height = lowStringMapping.vec.y - hiStringMapping.vec.y; CGSize strSize = [_tabClefRenderable.letterA sizeWithAttributes:attrsDict]; clefArea.width = strSize.width; // calc bounding rects for each of the veritally aligned letters CGFloat topMostY = hiStringMapping.vec.y + self.staffMetrics.staffLineWidth; CGFloat leftMostX = self.staffMetrics.leftMostXOffset + self.staffMetrics.firstMeasureBarLineWidth + 1; [self calcClefDrawingRectsWithLeftMostX:leftMostX topMostY:topMostY areaSize:clefArea andFont:attrsDict[NSFontAttributeName] intoRenderable:_tabClefRenderable]; CGPoint clefOrigin = _tabClefRenderable.rectForT.origin; CGSize clefSize = _tabClefRenderable.rectForA.size; // assuming A is widest character CGFloat bottomOfB = _tabClefRenderable.rectForB.origin.y + _tabClefRenderable.rectForB.size.height; clefSize.height = bottomOfB - clefOrigin.y; boundingClefRect.origin = clefOrigin; boundingClefRect.size = clefSize; return boundingClefRect; } #pragma mark - Draw Rendering Methods - (void) drawClefOnStaffInContext:(CGContextRef)ctx { NSDictionary *attrsDict = [super textAttributesDictionaryForClef]; if (attrsDict == nil) { return; } // calling this method forces the internal calcs, for our drawing though we don't need the Rect [self boundingRectForClef]; UIGraphicsPushContext(ctx); CGContextSaveGState(ctx); CGContextSetAllowsAntialiasing(ctx, YES); CGContextSetShouldAntialias(ctx, YES); CGContextSetShouldSmoothFonts(ctx, YES); [_tabClefRenderable.letterT drawInRect:_tabClefRenderable.rectForT withAttributes:attrsDict]; [_tabClefRenderable.letterA drawInRect:_tabClefRenderable.rectForA withAttributes:attrsDict]; [_tabClefRenderable.letterB drawInRect:_tabClefRenderable.rectForB withAttributes:attrsDict]; CGContextRestoreGState(ctx); UIGraphicsPopContext(); } - (void) drawStaffInContext:(CGContextRef)ctx withMeasureCount:(int)measureCount { CGFloat leftX = self.staffMetrics.leftMostXOffset; CGFloat rightX = leftX + (measureCount * self.staffMetrics.lineLengthOneMeasure); CGFloat lineGap = self.staffMetrics.yGapBetweenLines; // calc top lines Y offset, and round to land on half pixel CGFloat firstY = self.staffYCenter - (self.staffMetrics.staffHeight / 2); if (self.staffMetrics.staffLineWidth == 1) { firstY = [MusicStaffRenderMetrics roundToLeastHalfPoint:firstY]; } else if (self.staffMetrics.staffLineWidth == 2) { firstY = floorf(firstY); } // these allow vertical bar lines to 'extend' part way into intersection horz line CGFloat barLineTopY = firstY - (self.staffMetrics.staffLineWidth / 2); CGFloat barLineBottomY = firstY + (self.staffMetrics.staffLineWidth / 2); UIGraphicsPushContext(ctx); CGContextSaveGState(ctx); CGContextSetStrokeColorWithColor(ctx, self.staffMetrics.lineColor.CGColor); CGContextSetLineWidth(ctx, self.staffMetrics.staffLineWidth); int numFixedLines = self.staffMetrics.fixedLineCount; for (int x=0; x < numFixedLines; x++) { CGContextMoveToPoint(ctx, leftX, firstY); CGContextAddLineToPoint(ctx, rightX, firstY); CGContextStrokePath(ctx); firstY += lineGap; if (x < (numFixedLines - 1)) { barLineBottomY += lineGap; } } CGContextRestoreGState(ctx); UIGraphicsPopContext(); // now draw the vertical bar lines, optionally skipping last for open bar [self.verticalBarCoordinates removeAllObjects]; SWFVector3 *barVec; int barCount = measureCount + 1; float barX = self.staffMetrics.leftMostXOffset; float measureLength = self.staffMetrics.lineLengthOneMeasure; UIGraphicsPushContext(ctx); CGContextSaveGState(ctx); CGContextSetStrokeColorWithColor(ctx, self.staffMetrics.lineColor.CGColor); for (int z = 0; z < barCount; z++) { if (z == 0) { CGContextSetLineWidth(ctx, self.staffMetrics.firstMeasureBarLineWidth); } else { CGContextSetLineWidth(ctx, self.staffMetrics.measureBarLineWidth); } // always draw vertical bars, ...but sometimes don't draw the last bar (guitar TAB) if (((self.staffMetrics.lastMeasureOpenBar == YES) && (z + 1 < barCount)) || (self.staffMetrics.lastMeasureOpenBar == NO)) { CGContextMoveToPoint(ctx, barX, barLineTopY); CGContextAddLineToPoint(ctx, barX, barLineBottomY); CGContextStrokePath(ctx); } // stash their locations for further use barVec = [SWFVector3 vectorFromValues:barX y:barLineTopY z:barLineBottomY]; [self.verticalBarCoordinates addObject:barVec]; barX += measureLength; } CGContextRestoreGState(ctx); UIGraphicsPopContext(); } - (void) drawStubLineInContext:(CGContextRef)ctx withNotePosition:(SWFVector3 *)noteVec andNoteGeometry:(SWFVector3 *)noteGeometry forNoteId:(kMusicNoteId)noteId inOctave:(kMusicStaffOctave)octave { // override to do nothing TAB has no stubLines } @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,143 @@ // // MusicStaff.h // SongData1 // // Created by Michael J Albanese on 10/9/12. // Copyright (c) 2013 WayFwonts.com. All rights reserved. // #import <UIKit/UIKit.h> #import <Foundation/Foundation.h> #import "MusicTheoryDefs.h" #import "SWFDroppable_p.h" #import "MusicStaffDisplayable_p.h" @class SWFVector3; @class NoteData; @class MusicStaffRenderMetrics; @class MusicPageRenderCriteria; @class MusicStaffLineVectorMap; @protocol MusicStaffNotifications_p; /** * Renderable displayable MusicStaff object. This class is a Drag n Drop target by * virture of adopting the SWFDroppable_p protocol. To accomodate such a protocol * this class defines and maintaines a set of internal 'Drop Zones', valid locations * which offer valid coordinates for Draggable entities to be dropped. * * Additionally this class is displayable and can render a MusicStaff in a given context. * Other than the veneer of a music staff, this class does _not_ maintain any residents. * Besides an internal mapping of note id's and octaves that serve as information given * within a Drag n Drop exchange, this class is agnostic to any music entities. It is the * MusicMeasure (and its set of MusicMeasureColumns) which worry about placement and * housing of music entity objects. * * This MusicStaff is like a boiler plate staff that can render, given a set of * MusicScoreStaffMetrics, and create an internal plotting given a MusicStaffLayoutInfo * structure. * * @seealso MusicScoreStaffMetrics * @seealso MusicStaffLayoutInfo * @seealso MusicPageRenderCriteria * @seealso MusicStaffDisplayable_p * @seealso SWFDroppable_p */ @interface MusicStaff : NSObject <MusicStaffDisplayable_p, SWFDroppable_p> @property (weak, nonatomic) id<MusicStaffNotifications_p> staffDelegate; @property (weak, nonatomic, readonly, getter = findLowestLineVectorMap) MusicStaffLineVectorMap *lowestLineVectorMap; @property (weak, nonatomic, readonly, getter = findHighestLineVectorMap) MusicStaffLineVectorMap *highestLineVectorMap; @property (nonatomic, readonly) MusicStaffLayoutInfo staffLayoutInfo; @property (nonatomic, readonly, getter = myStaffName) NSString *name; @property (nonatomic, getter = retrieveSwipeUpBounds, readonly) CGRect boundsForSwipeUp; @property (nonatomic) NSInteger lowestLineVectorMapIndex; @property (nonatomic) NSInteger highestLineVectorMapIndex; @property (nonatomic, readonly) CGFloat topLineY; @property (nonatomic, readonly) CGFloat bottomLineY; @property (strong, nonatomic, readonly) NSMutableArray *verticalBarCoordinates; /** Array of all spaces/lines in staff and their mapped notes, octaves and 'type' */ @property (strong, nonatomic) NSMutableArray *staffVectorMappings; @property (strong, nonatomic) NSMutableArray *linesVectorMappingsIndex; @property (strong, nonatomic) NSMutableArray *spacesVectorMappingsIndex; @property (strong, nonatomic) NSMutableArray *loToHiCombinedMappingsIndex; /** calc'd during DropZone calculations and used for swipe gesture bounds */ @property (nonatomic) float leftmostX; @property (nonatomic) float rightmostX; @property (nonatomic) float highestYCoordinate; /** measurements and properties describing the visual appearance of Music Staff */ @property (strong, nonatomic) MusicPageRenderCriteria *pageRenderCriteria; @property (strong, nonatomic) SWFVector3 *lowestLineVector; @property (strong, nonatomic) SWFVector3 *lowestSpaceVector; @property (strong, nonatomic) SWFVector3 *middleCVector; /** Following are needed by derived class, Obj C forces us to place in public header !*&% */ /** flag/object reference to currently hilighted (single) dropZone */ @property (strong, nonatomic) MusicStaffDropZone *currZoneHilite; /** Array of calculated DropZone objects used in Drag Drop hit testing */ // @property (strong, nonatomic) NSMutableArray *dropZones; + (MusicStaff *) musicStaffWithLayoutInfo:(MusicStaffLayoutInfo *)layoutInfo andMiddleXCoordinate:(float)middleX andLowestYCoordinate:(float)lowY andStaffMetrics:(MusicStaffRenderMetrics *)staffMetrics andPageRenderCriteria:(MusicPageRenderCriteria *)pageCriteria; - (id) initWithLayoutInfo:(MusicStaffLayoutInfo *)layoutInfo andMiddleXCoordinate:(float)middleX andLowestYCoordinate:(float)lowY andStaffMetrics:(MusicStaffRenderMetrics *)staffMetrics andPageRenderCriteria:(MusicPageRenderCriteria *)pageCriteria; - (void) drawStaffInContext:(CGContextRef)ctx withMeasureCount:(int)measureCount; - (void) drawStubLineInContext:(CGContextRef)ctx withNotePosition:(SWFVector3 *)noteVec forNoteId:(kMusicNoteId)noteId inOctave:(kMusicStaffOctave)octave; - (void) drawClefOnStaffInContext:(CGContextRef)ctx; - (SWFVector3 *) findStaffVectorForNoteUsingItsOctave:(NoteData *)aNote; - (CGPoint) findStaffCoordinateForNoteUsingItsOctave:(NoteData *)aNote; - (CGPoint) findStaffCoordinateForNoteId:(kMusicNoteId)noteId inOctave:(kMusicStaffOctave)oct; - (kMusicStaffZone) findStaffZoneTypeForNoteId:(kMusicNoteId)noteId inOctave:(kMusicStaffOctave)oct; - (MusicStaffLineVectorMap *) findStaffVectorMapForNoteId:(kMusicNoteId)noteId inOctave:(kMusicStaffOctave)oct; - (float) stubLineYCoordinateForNoteId:(kMusicNoteId)noteId inOctave:(kMusicStaffOctave)octave withNotePosition:(SWFVector3 *)noteVec; /** * @return combined array of both line and space vector mappings */ - (NSArray *) staffVectorMappingsBottomUp; /** mainly used by derived classes */ - (void) clearDropZones; /** called as part of building the Drop zones in any staff */ - (void) establishLeftAndRightXBoundaryFromMetrics; /** another utility called from Drop zone calculations in any staff */ - (float) calcUniformDropZoneHeightBasedOnYGap; /** Allows derived staffs to examine their specific drop zone criteria */ - (BOOL) doDropZoneSetsMatch:(NSArray *)zoneSet1 secondSet:(NSArray *)zoneSet2; /** TEMP Until FontAndNamesManager class created */ - (NSDictionary *) textAttributesDictionaryForClef; - (CGRect) boundingRectForClefString:(NSString *)clefString usingAttrs:(NSDictionary *)attrsDict atOrigin:(CGPoint)ptOrigin; @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,1424 @@ // // MusicStaff.m // // Created by Michael J Albanese on 10/9/12. // Copyright (c) 2014 WayFwonts.com. All rights reserved. // #import <CoreText/CoreText.h> #import "MusicStaffRenderMetrics.h" #import "NoteData+MoreFuncs.h" #import "SWFDraggable.h" #import "MusicStaffDropReply.h" #import "MusicStaffDropRequest.h" #import "MusicStaffDropZone.h" #import "MusicStaffNotifications_p.h" #import "MusicPageRenderCriteria.h" #import "MusicStaffLineVectorMap.h" #import "MusicNamesFormatter.h" #import "MusicStaff.h" #pragma mark - MusicStaff main Class begins @interface MusicStaff () @property (strong, nonatomic) NSMutableArray *dropZones; @property (strong, nonatomic) NSMutableArray *multiZoneHilite; @property (strong, nonatomic, readwrite) NSMutableArray *verticalBarCoordinates; @property (nonatomic, readwrite) CGFloat topLineY; @property (nonatomic, readwrite) CGFloat bottomLineY; /** attempt at quasi protected methods for subclasses to override */ - (void) mapStaffOctaveAndNoteCoordinates; - (void) calcDropZonesFromCombinedVectorMappings; @end @implementation MusicStaff /** Need to manually synthesize properties delared in the Protocol */ @synthesize staffId = _staffId; @synthesize clefId = _clefId; @synthesize staffMetrics = _staffMetrics; @synthesize isVisible = _isVisible; @synthesize isSelected = _isSelected; @synthesize lowestYCoordinate = _lowestYCoordinate; @synthesize centerYCoordinate = _centerYCoordinate; @synthesize staffYCenter = _staffYCenter; @synthesize middleXCoordinate = _middleXCoordinate; + (MusicStaff *) musicStaffWithLayoutInfo:(MusicStaffLayoutInfo *)layoutInfo andMiddleXCoordinate:(float)middleX andLowestYCoordinate:(float)lowY andStaffMetrics:(MusicStaffRenderMetrics *)staffMetrics andPageRenderCriteria:(MusicPageRenderCriteria *)renderCriteria { return [[self alloc] initWithLayoutInfo:layoutInfo andMiddleXCoordinate:middleX andLowestYCoordinate:lowY andStaffMetrics:staffMetrics andPageRenderCriteria:renderCriteria]; } - (id) initWithLayoutInfo:(MusicStaffLayoutInfo *)layoutInfo andMiddleXCoordinate:(float)middleX andLowestYCoordinate:(float)lowY andStaffMetrics:(MusicStaffRenderMetrics *)staffMetrics andPageRenderCriteria:(MusicPageRenderCriteria *)renderCriteria { if (self = [super init]) { _isVisible = YES; _staffLayoutInfo = *layoutInfo; _middleXCoordinate = middleX; _lowestYCoordinate = lowY; _staffMetrics = staffMetrics; _pageRenderCriteria = renderCriteria; _lowestLineVectorMapIndex = _highestLineVectorMapIndex = kMaxStaffLines + 10; _dropZones = [NSMutableArray array]; _staffVectorMappings = [NSMutableArray array]; _verticalBarCoordinates = [NSMutableArray array]; _linesVectorMappingsIndex = [NSMutableArray array]; _spacesVectorMappingsIndex = [NSMutableArray array]; _loToHiCombinedMappingsIndex = [NSMutableArray array]; [self mapStaffOctaveAndNoteCoordinates]; [self calcDropZonesFromCombinedVectorMappings]; } return self; } - (void) dealloc { [_staffVectorMappings removeAllObjects]; _staffVectorMappings = nil; [_dropZones removeAllObjects]; _dropZones = nil; [_multiZoneHilite removeAllObjects]; _multiZoneHilite = nil; [_staffVectorMappings removeAllObjects]; [_verticalBarCoordinates removeAllObjects]; [_linesVectorMappingsIndex removeAllObjects]; [_spacesVectorMappingsIndex removeAllObjects]; [_loToHiCombinedMappingsIndex removeAllObjects]; } - (kSongStaffId) staffId { return _staffLayoutInfo.staffId; } - (kSongClefId) clefId { return _staffLayoutInfo.clefId; } - (NSString *) myStaffName { NSString *myName = nil; if (_staffLayoutInfo.staffId == kStaffTreble) { myName = @"Treble Staff"; } else if (_staffLayoutInfo.staffId == kStaffBass) { myName = @"Bass Staff"; } else if (_staffLayoutInfo.staffId == kStaffTABSix) { myName = @"Guitar TAB"; } else if (_staffLayoutInfo.staffId == kStaffTABFour) { myName = @"Bass Guitar TAB"; } else if (_staffLayoutInfo.staffId == kStaffMelody) { myName = @"Melody Staff"; } return myName; } #pragma mark - Custom Bounds Getters - (CGRect) retrieveCalcdBounds { // Note: this is not a traditional CGRect as last element is not the height // but the bottom Y coordinate, e.g. these are 2 points TL and BR return CGRectMake(_leftmostX, _highestYCoordinate, _rightmostX, _lowestYCoordinate); } - (CGRect) retrieveSwipeUpBounds { // for chords residing at/near bottom of staff, the starting point of // a swipe up often begins in the staff below this staff's actual bounds. // In order for swipeUp detection to appear accurate to the user, and deal // with the fact that iOS reports only the beginning touch of a swipe, we // temporarily extend our bottom bounds. return CGRectMake(_leftmostX, _highestYCoordinate, _rightmostX, _lowestYCoordinate + (2. * _staffMetrics.yGapBetweenLines)); } #pragma mark - Mapping Staff Verticies - (void) remapStaffCoordinates { [self mapStaffOctaveAndNoteCoordinates]; [self calcDropZonesFromCombinedVectorMappings]; } - (void) mapStaffOctaveAndNoteCoordinates { // All mapping is constructed from the bottom of the staff 'Up' to top float x = _middleXCoordinate; float y = _lowestYCoordinate; float z = 0.; int mapOffset = 0; _lowestLineVectorMapIndex = _highestLineVectorMapIndex = kMaxStaffLines + 10; _highestYCoordinate = 2000.0; // because in 2D 0,0 is top left float yincr = _staffMetrics.yGapBetweenLines; if (!_staffVectorMappings) { _staffVectorMappings = [NSMutableArray array]; _linesVectorMappingsIndex = [NSMutableArray array]; _spacesVectorMappingsIndex = [NSMutableArray array]; _loToHiCombinedMappingsIndex = [NSMutableArray array]; } else { [_staffVectorMappings removeAllObjects]; [_linesVectorMappingsIndex removeAllObjects]; [_spacesVectorMappingsIndex removeAllObjects]; [_loToHiCombinedMappingsIndex removeAllObjects]; } MusicStaffLineVectorMap *staffMapping = nil; // first loop for lines, from middle C Up for (int i = 0; i < _staffLayoutInfo.usedStaffLines; i++, mapOffset++) { staffMapping = [[MusicStaffLineVectorMap alloc] init]; MusicNoteToOctaveMap noteOctMap; noteOctMap.lineSpaceIndicator = kMusicLineZone; noteOctMap.octave = _staffLayoutInfo.lineNotes[i].octave; noteOctMap.noteId = _staffLayoutInfo.lineNotes[i].noteId; staffMapping.octaveInfo = noteOctMap; staffMapping.vec = [SWFVector3 vectorFromValues:x y:y z:z]; staffMapping.zoneType = kMusicLineZone; [_staffVectorMappings addObject:staffMapping]; if (i == _staffLayoutInfo.lineOffsetMiddleC) { _middleCVector = staffMapping.vec; } if (i == 0) { _lowestLineVector = staffMapping.vec; _lowestLineVectorMapIndex = i; } if (y < _highestYCoordinate) { _highestYCoordinate = y; _highestLineVectorMapIndex = i; } // update the lines index with offset of newly added line mapping [_linesVectorMappingsIndex addObject:@(_staffVectorMappings.count - 1)]; y -= yincr; } int numStaffPositions = _staffLayoutInfo.usedStaffLines + _staffLayoutInfo.usedStaffSpaces; // next loop maps the spaces, from B below middle C Up y = _lowestYCoordinate + (_staffMetrics.yGapBetweenLines / 2); for (int i = 0; i < _staffLayoutInfo.usedStaffSpaces && mapOffset < numStaffPositions; i++, mapOffset++) { staffMapping = [[MusicStaffLineVectorMap alloc] init]; MusicNoteToOctaveMap noteOctMap; noteOctMap.lineSpaceIndicator = kMusicSpaceZone; noteOctMap.octave = _staffLayoutInfo.spaceNotes[i].octave; noteOctMap.noteId = _staffLayoutInfo.spaceNotes[i].noteId; staffMapping.octaveInfo = noteOctMap; staffMapping.vec = [SWFVector3 vectorFromValues:x y:y z:z]; staffMapping.zoneType = kMusicSpaceZone; [_staffVectorMappings addObject:staffMapping]; if (i == 0) { _lowestSpaceVector = staffMapping.vec; } if (y < _highestYCoordinate) { _highestYCoordinate = y; } // update the spaces index with offset of newly added space mapping [_spacesVectorMappingsIndex addObject:@(_staffVectorMappings.count - 1)]; y -= yincr; } // _highestYCoordinate -= _staffMetrics.yGapBetweenLines; NSAssert(_highestYCoordinate > 0., @"NEGATIVE mapping of _highestYCoordinate not allowed"); // build the combined from the just composed spaces and lines separate indexes [self buildCombinedVectorMappingsIndex]; } #pragma mark - VectorMaps - (void) buildCombinedVectorMappingsIndex { // Dependent upon both the _linesVectorMappingIndex and _spacesVectorMappingIndex // to already be populated with accurate indexes into _staffVectorMappings array. // This will build one array which when sequentially traverse the mappings by // encountering the standard alternating space-line staff mappings starting with the // lowest space mapping of this staff NSInteger totalMappings = _staffVectorMappings.count; if (_linesVectorMappingsIndex.count + _spacesVectorMappingsIndex.count > totalMappings) { return; } [_loToHiCombinedMappingsIndex removeAllObjects]; MusicStaffLineVectorMap *staffMapping = nil; NSInteger spaceOff = 0, lineOff = 0; BOOL doSpace = YES; for (NSInteger x = 0; x < totalMappings; x++) { if (doSpace && spaceOff < _spacesVectorMappingsIndex.count) { NSNumber *spaceIndex = [_spacesVectorMappingsIndex objectAtIndex:spaceOff++]; staffMapping = [_staffVectorMappings objectAtIndex:spaceIndex.integerValue]; if (staffMapping) { [_loToHiCombinedMappingsIndex addObject:@(spaceIndex.integerValue)]; } doSpace = NO; } else if (lineOff < _linesVectorMappingsIndex.count) { NSNumber *lineIndex = [_linesVectorMappingsIndex objectAtIndex:lineOff++]; staffMapping = [_staffVectorMappings objectAtIndex:lineIndex.integerValue]; if (staffMapping) { [_loToHiCombinedMappingsIndex addObject:@(lineIndex.integerValue)]; } doSpace = YES; } } } - (NSArray *) staffVectorMappingsBottomUp { NSMutableArray *combinedMappings = [NSMutableArray array]; MusicStaffLineVectorMap *aMap; // While _staffVectorMappings does hold all mappings, their order // is grouped as all lines, then all spaces. This method produces // a interweaved resulting array as dictated by the combined indexes. for (NSNumber *indexNum in _loToHiCombinedMappingsIndex) { aMap = [_staffVectorMappings objectAtIndex:indexNum.integerValue]; // FIXME implement NSCopying protocol on vectorMaps and hand out copies ! [combinedMappings addObject:aMap]; } return combinedMappings; } - (MusicStaffLineVectorMap *) findStaffVectorMapForNoteId:(kMusicNoteId)noteId inOctave:(kMusicStaffOctave)oct { MusicStaffLineVectorMap *staffMapping = nil; MusicStaffLineVectorMap *aMap; for (NSNumber *indexNum in _loToHiCombinedMappingsIndex) { aMap = [_staffVectorMappings objectAtIndex:indexNum.integerValue]; if (aMap.octaveInfo.octave == oct) { if (aMap.octaveInfo.noteId == noteId) { staffMapping = aMap; break; } } } return staffMapping; } - (MusicStaffLineVectorMap *) findStaffVectorMapForNoteId:(kMusicNoteId)noteId inOctave:(kMusicStaffOctave)oct ofZoneType:(kMusicStaffZone)zoneType { MusicStaffLineVectorMap *staffMapping = nil; MusicStaffLineVectorMap *aMap; for (NSNumber *indexNum in _loToHiCombinedMappingsIndex) { aMap = [_staffVectorMappings objectAtIndex:indexNum.integerValue]; if (aMap.octaveInfo.octave == oct) { if (aMap.octaveInfo.noteId == noteId) { if (aMap.zoneType == zoneType) { staffMapping = aMap; break; } } } } return staffMapping; } #pragma mark - Find Staff Coordinate methods - (CGPoint) findStaffCoordinateForNoteId:(kMusicNoteId)noteId inOctave:(kMusicStaffOctave)oct { MusicStaffLineVectorMap *staffMapping = nil; CGPoint nowhere = CGPointMake(0, 0); int numStaffPositions = _staffLayoutInfo.usedStaffLines + _staffLayoutInfo.usedStaffSpaces; // simple linear scan for now, very small array. for (int i = 0; i < numStaffPositions; i++) { staffMapping = [_staffVectorMappings objectAtIndex:i]; if (staffMapping.octaveInfo.octave == oct) { if (staffMapping.octaveInfo.noteId == noteId) { return staffMapping.coordinate; } } } return nowhere; } - (CGPoint) findStaffCoordinateForNoteUsingItsOctave:(NoteData *)aNote { SWFVector3 *staffVector = [self findStaffVectorForNoteUsingItsOctave:aNote]; return staffVector.pointFromVector; } - (kMusicStaffZone) findStaffZoneTypeForNoteId:(kMusicNoteId)noteId inOctave:(kMusicStaffOctave)oct { kMusicStaffZone zoneType = kMusicStaffNOZone; MusicStaffLineVectorMap *staffMapping = nil; int numStaffPositions = _staffLayoutInfo.usedStaffLines + _staffLayoutInfo.usedStaffSpaces; // simple linear scan for now, very small array. for (int i = 0; i < numStaffPositions; i++) { staffMapping = [_staffVectorMappings objectAtIndex:i]; if (staffMapping.octaveInfo.octave == oct) { if (staffMapping.octaveInfo.noteId == noteId) { zoneType = staffMapping.zoneType; break; } } } return zoneType; } - (MusicStaffLineVectorMap *) findLowestLineVectorMap { if (_lowestLineVectorMapIndex > kMaxStaffLines) { return nil; } MusicStaffLineVectorMap *staffMapping = [_staffVectorMappings objectAtIndex:_lowestLineVectorMapIndex]; return staffMapping; } - (MusicStaffLineVectorMap *) findHighestLineVectorMap { if (_highestLineVectorMapIndex > kMaxStaffLines) { return nil; } MusicStaffLineVectorMap *staffMapping = [_staffVectorMappings objectAtIndex:_highestLineVectorMapIndex]; return staffMapping; } - (float) calcTopRealLineUsingMetrics { // FIXME DANGER this assumes only 1 stub will be below and above CGFloat upperStub = _lowestYCoordinate - (6 * _staffMetrics.yGapBetweenLines); // calc top lines Y offset, and round to land on half pixel CGFloat topLine = upperStub + _staffMetrics.yGapBetweenLines; if (_staffMetrics.staffLineWidth == 1) { topLine = [MusicStaffRenderMetrics roundToLeastHalfPoint:topLine]; } return topLine; } - (float) calcBottomRealLineUsingMetrics { // FIXME DANGER return _lowestYCoordinate - _staffMetrics.yGapBetweenLines; } - (BOOL) isYCoordinateAboveTopStaffLine:(float)proposedY { if (proposedY < [self calcTopRealLineUsingMetrics]) { return YES; } return NO; } - (BOOL) isYCoordinateBelowBottomStaffLine:(float)proposedY { if (proposedY > [self calcBottomRealLineUsingMetrics]) { return YES; } return NO; } #pragma mark - MusicStaffDisplayable Protocol Methods - (SWFVector3 *) middleCVector { return _middleCVector; } - (SWFVector3 *) findStaffVertexForNoteId:(kMusicNoteId)noteId inOctave:(kMusicStaffOctave)oct { MusicStaffLineVectorMap *staffMapping = nil; int numStaffPositions = _staffLayoutInfo.usedStaffLines + _staffLayoutInfo.usedStaffSpaces; // simple linear scan for now, very small array. for (int i = 0; i < numStaffPositions; i++) { staffMapping = [_staffVectorMappings objectAtIndex:i]; if (staffMapping.octaveInfo.octave == oct) { if (staffMapping.octaveInfo.noteId == noteId) { return staffMapping.vec; } } } return nil; } - (SWFVector3 *) findStaffVectorForNoteUsingItsOctave:(NoteData *)aNote { MusicStaffLineVectorMap *staffMapping; SWFVector3 *staffVector = nil; int numOctaves; float noteGap = [self distanceBetweenStaffLines] / 2.0; for (NSNumber *indexNum in _loToHiCombinedMappingsIndex) { staffMapping = [_staffVectorMappings objectAtIndex:indexNum.integerValue]; if (aNote.noteId.integerValue == staffMapping.octaveInfo.noteId) { staffVector = [staffMapping.vec mutableCopy]; // simple case note matches found staff location if (aNote.octave.integerValue == staffMapping.octaveInfo.octave) { break; } // Adjust position factor diff of chordNotes octave and staff note's octave if (aNote.octave.integerValue > staffMapping.octaveInfo.octave) { numOctaves = aNote.octave.intValue - staffMapping.octaveInfo.octave; staffVector.y -= ((numOctaves * kNotesPerOctave) * noteGap); // NSLog(@"Staff adj Y coord for noteId: %d to >: %.2f NoteOct: %d", // aNote.noteId.intValue, staffVector.y, aNote.octave.integerValue); break; } else if (aNote.octave.integerValue < staffMapping.octaveInfo.octave) { numOctaves = staffMapping.octaveInfo.octave - aNote.octave.intValue; staffVector.y += ((numOctaves * kNotesPerOctave) * noteGap); NSLog(@"Staff adj Y coord for noteId: %d to <: %.2f NoteOct: %ld", aNote.noteId.intValue, staffVector.y, aNote.octave.longValue); break; } } } return staffVector; } - (kMusicStaffOctave) nearestOctaveForNote:(kMusicNoteId)noteId andVertex:(SWFVector3 *)vec { kMusicStaffOctave oct = kOctaveNone; MusicStaffDropZone *mdz; // start at bottom zone and work up unsigned count = 0; while (count < _dropZones.count) { mdz = [_dropZones objectAtIndex:count]; SWFBoundingBox zbb = mdz.boundingZone; if (vec.y <= zbb.upr_y && vec.y >= zbb.llr_y) { if (vec.x >= zbb.llr_x && vec.x <= zbb.upr_x) { // cheap hit OR go after 'Z' too if (vec.z >= zbb.llr_z && vec.z <= zbb.upr_z) { oct = mdz.octave; break; } } } count++; } if (oct == kOctaveNone) { NSLog(@"Music Staff returning OCTAVE NONE for noteId: %ld", (long)noteId); } return oct; } - (MusicStaffDropZone *) nearestDropZoneFromVector:(SWFVector3 *)vec usingGap:(kDropZoneGap)dzGap { MusicStaffDropZone *mdz = nil; kDropStatus hitStatus = kDropStatus_None; SWFVector3 *targetVec = vec; float gapFudge = _staffMetrics.yGapBetweenLines / (float)dzGap; // NSLog(@"MusicStaff nearestZone using GAP: %f", gapFudge); unsigned count = (unsigned)[_dropZones count]; while (count--) { mdz = [_dropZones objectAtIndex:count]; hitStatus = [mdz detectHitFromVector:targetVec gapAllowance:gapFudge]; if (hitStatus == kDropStatus_DirectHit || hitStatus == kDropStatus_NearZone) { break; } mdz = nil; } return mdz; } - (float) distanceBetweenStaffLines { return _staffMetrics.yGapBetweenLines; } - (SWFVector3 *) lowestStaffPositionVector { if (_lowestLineVector && _lowestSpaceVector) { if (_lowestLineVector.y < _lowestSpaceVector.y) { return _lowestLineVector; } else { return _lowestSpaceVector; } } return nil; } - (NSArray *) allDropZones { return self.dropZones; } - (NSArray *) measureBarCoordinatesForLine { return [NSArray arrayWithArray:_verticalBarCoordinates]; } #pragma mark - Drop Zone Methods - (void) calcDropZonesFromCombinedVectorMappings { MusicStaffLineVectorMap *staffMapping = nil; MusicStaffDropZone *last1back = nil; MusicStaffDropZone *dz; [_dropZones removeAllObjects]; _currZoneHilite = nil; // calc the uniform height of all drop zones float zoneHeight = [self calcUniformDropZoneHeightBasedOnYGap]; // update the members consulted for swipe up and boundary dimensions [self establishLeftAndRightXBoundaryFromMetrics]; // accessing the _staffVectorMappings thru the combined indexing array // delivers staff positions in sequence starting at the lowest Space of staff NSInteger dropZoneId = 1; for (NSNumber *indexOffset in _loToHiCombinedMappingsIndex) { staffMapping = [_staffVectorMappings objectAtIndex:indexOffset.integerValue]; if (staffMapping) { // calc a bounding rect based on 1/2 zoneHeight either side of the // 'Y' postion of staffMapping of given line or space SWFVector3 *mappingVector = staffMapping.vec; float zoneTop = mappingVector.y - (zoneHeight / 2.); float zoneBottom = mappingVector.y + (zoneHeight / 2.); SWFBoundingBox boundingBox; boundingBox.llr_x = _leftmostX; boundingBox.llr_y = zoneBottom; boundingBox.llr_z = 0; boundingBox.upr_x = _rightmostX; boundingBox.upr_y = zoneTop; boundingBox.upr_z = 0; dz = [[MusicStaffDropZone alloc] initWithBoundingBox:&boundingBox andType:staffMapping.zoneType]; dz.noteId = staffMapping.octaveInfo.noteId; dz.octave = staffMapping.octaveInfo.octave; dz.staffId = _staffLayoutInfo.staffId; // do the linked list thing if (indexOffset.integerValue == 0) { last1back = dz; } else { dz.previousZone = last1back; last1back.nextZone = dz; last1back = dz; } dz.zoneId = dropZoneId; [self addDropZone:dz]; dropZoneId++; } } // NSLog(@"MyStaff DropZones--> %@", [self dropZoneDescriptions]); } - (void) establishLeftAndRightXBoundaryFromMetrics { _leftmostX = _staffMetrics.leftMostXOffset; _rightmostX = _leftmostX; _rightmostX += (_staffMetrics.numberOfMeasures * _staffMetrics.lineLengthOneMeasure); } - (float) calcUniformDropZoneHeightBasedOnYGap { float spaceBetweenLines = _staffMetrics.yGapBetweenLines; float zoneHeight = (spaceBetweenLines * _staffMetrics.dropZonePercentage); zoneHeight = [MusicStaffRenderMetrics roundToLeastHalfPoint:zoneHeight]; return zoneHeight; } - (void) addDropZone:(id<SWFDropZone_p>)dropZone { [self.dropZones addObject:dropZone]; } - (void) clearDropZones { [self.dropZones removeAllObjects]; } - (BOOL) doDropZoneSetsMatch:(NSArray *)zoneSet1 secondSet:(NSArray *)zoneSet2 { BOOL setsEqual = NO; if (zoneSet1.count != zoneSet2.count) { return NO; } MusicStaffDropZone *zoneA; MusicStaffDropZone *zoneB; setsEqual = YES; for (int i = 0; i < zoneSet1.count; i++) { zoneA = [zoneSet1 objectAtIndex:i]; zoneB = [zoneSet2 objectAtIndex:i]; // currently just look at noteId's if (zoneA.noteId == zoneB.noteId) { setsEqual = YES; #ifdef MORE_DETAILED_MATCHING_NOT_YET_NEEDED if (zoneA.octave == zoneB.octave) { if (zoneA.center.y == zoneB.center.y) { continue; } else { setsEqual = NO; break; } } else { setsEqual = NO; break; } #endif } else { setsEqual = NO; break; } } return setsEqual; } #pragma mark - SWFDroppable Protocol Methods - (id<SWFDropZone_p>) queryDropZoneAt:(float)x y:(float)y z:(float)z { MusicStaffDropZone *retZone = nil; SWFVector3 *targetVec = [SWFVector3 vectorFromValues:x y:y z:z]; // Being a little forgiving here as point-perfect direct hits for // drag an drop are a little much to ask of users retZone = [self nearestDropZoneFromVector:targetVec usingGap:kDropZoneGap_ThirdSpace]; return retZone; } - (SWFDropReply *) dropRequest:(SWFDropRequest *)dr { MusicStaffDropRequest *dropRequest = (MusicStaffDropRequest *)dr; if ([dropRequest isGroupRequest]) { return [self handleGroupDropRequest:dr]; } else { return [self handleSoloDropRequest:dr]; } } - (SWFDropReply *) handleSoloDropRequest:(SWFDropRequest *)dr { MusicStaffDropReply *reply = nil; MusicStaffDropRequest *dropRequest = (MusicStaffDropRequest *)dr; MusicStaffDropZone *mdz; bool hitZone = NO; kDropStatus hitStatus = kDropStatus_None; SWFVector3 *requestPos = dropRequest.requestPos; mdz = (MusicStaffDropZone *)[self queryDropZoneAt:requestPos.x y:requestPos.y z:requestPos.z]; if (mdz != nil) { hitZone = YES; hitStatus = kDropStatus_DirectHit; } // unhilite prev zone if its not same as this (potential) hit if (_currZoneHilite != nil) { if (mdz == nil || ((mdz != nil) && (_currZoneHilite != mdz)) ) { [self unHiliteZone:_currZoneHilite]; _currZoneHilite = nil; } } // take care of no hit if (!hitZone) { // unhilite any previsouly lit zone if (_currZoneHilite != nil) { [self unHiliteZone:_currZoneHilite]; _currZoneHilite = nil; } // inform Draggable no hit detected on valid dropZone reply = [[MusicStaffDropReply alloc] initWith:kDropStatus_None andOrigRequest:dropRequest]; reply.staffId = self.staffId; } else { // Hilite the new hit zone if (_currZoneHilite == nil) { [self hiliteZone:mdz]; _currZoneHilite = mdz; } // simple Rules Passage, either direct or near hit a valid dropzone reply = [[MusicStaffDropReply alloc] initWith:hitStatus andOrigRequest:dropRequest andHitZone:mdz andNoteId:mdz.noteId]; reply.staffId = self.staffId; } return reply; } - (SWFDropReply *) handleGroupDropRequest:(SWFDropRequest *)dr { MusicStaffDropReply *reply = nil; MusicStaffDropRequest *dropRequest = (MusicStaffDropRequest *)dr; MusicStaffDropZone *mdz; kDropStatus hitStatus = kDropStatus_None; NSArray *acceptableHiliteZones = nil; NSMutableArray *hitZones = [[NSMutableArray alloc] init]; BOOL allZonesMatch = NO; NSArray *arrayOfPos = dropRequest.arrayPos; for (int i = 0; i < arrayOfPos.count; i++) { SWFVector3 *vec = [arrayOfPos objectAtIndex:i]; float stupidX = vec.x, stupidY = vec.y, stupidZ = vec.z; mdz = (MusicStaffDropZone *)[self queryDropZoneAt:stupidX y:stupidY z:stupidZ]; if (mdz != nil) { [hitZones addObject:mdz]; // FIXME Consider producing an array of hitZone status's, one for each vec hitStatus = kDropStatus_DirectHit; } } if (hitZones.count == arrayOfPos.count) { if (self.multiZoneHilite == nil) { // only do hiliting of multi zones if request supplied matching zoneIds acceptableHiliteZones = dropRequest.acceptableHiliteZones; if ([self doDropZoneSetsMatch:hitZones secondSet:acceptableHiliteZones]) { [self hiliteMultiZones:hitZones]; _multiZoneHilite = hitZones; allZonesMatch = YES; NSLog(@"MusicStaffId %d All Zones MATCH:\n %@", self.staffId, hitZones); } } else { // verify that existing hilites may stay that way acceptableHiliteZones = dropRequest.acceptableHiliteZones; if ([self doDropZoneSetsMatch:hitZones secondSet:acceptableHiliteZones]) { allZonesMatch = YES; } } // only if hits on all requested zones return a happy Reply if (allZonesMatch) { reply = [[MusicStaffDropReply alloc] initWith:hitStatus andOrigRequest:dropRequest andZonesArray:hitZones]; reply.staffId = self.staffId; return reply; } } else if (hitZones.count > 0) { NSLog(@"MusicStaffId %d PARTIAL Zone Match: %d", self.staffId, hitZones.count); } // else { // unHilite previous set of multiZones if (self.multiZoneHilite != nil) { [self unHiliteMultiZones:_multiZoneHilite]; [_multiZoneHilite removeAllObjects]; _multiZoneHilite = nil; } // inform Draggable no hit detected on any valid dropZone reply = [[MusicStaffDropReply alloc] initWith:kDropStatus_None andOrigRequest:dropRequest]; reply.staffId = self.staffId; } return reply; } - (void) dropComplete:(SWFDropReply *)dropReply { // MusicStaffDropReply *mdr = (MusicStaffDropReply *)dropReply; // unhilite previsouly lit zone if (_currZoneHilite != nil) { [self unHiliteZone:_currZoneHilite]; _currZoneHilite = nil; } } - (void) cancelAllRequests { // unhilite any previsouly lit zone if (_currZoneHilite != nil) { [self unHiliteZone:_currZoneHilite]; _currZoneHilite = nil; } if (self.multiZoneHilite != nil) { for (MusicStaffDropZone *mdz in self.multiZoneHilite) { [self unHiliteZone:mdz]; } [self.multiZoneHilite removeAllObjects]; self.multiZoneHilite = nil; } } - (NSString *) dropZoneDescriptions { NSMutableString *mutString = [NSMutableString string]; for (MusicStaffDropZone *z in _dropZones) { NSString *firstLine = [NSString stringWithFormat:@"ZoneId: %d staffId: %d noteId: %d", z.zoneId, z.staffId, z.noteId]; NSString *secondLine = [NSString stringWithFormat:@"ZoneCenter: %@ boundingBox: %@", z.center, NSStringFromCGRect(z.boundingRect)]; [mutString appendFormat:@"\n %@ %@", firstLine, secondLine]; } return mutString; } - (void) hilightOptions:(kDropHilite)clue { } #pragma mark - HiLight UnHilight workers call Delegate Notifcation Protocol - (void) hiliteMultiZones:(NSArray *)zonesArray { for (MusicStaffDropZone *mdz in zonesArray) { [self hiliteZone:mdz]; } } - (void) unHiliteMultiZones:(NSArray *)zonesArray { for (MusicStaffDropZone *mdz in zonesArray) { [self unHiliteZone:mdz]; } } - (void) hiliteZone:(MusicStaffDropZone *)dz { if (_staffDelegate) [_staffDelegate musicStaff:self willHiliteZone:dz]; #ifdef NO_HILIGHTING_IN_THIS_STAFF if (dz.type == kMusicSpaceZone) { [dz.swfMesh.isglMeshNode setMaterial:_hiliteSpaceMaterial]; dz.swfMesh.isglMeshNode.alpha = 0.6; } else if (dz.type == kMusicLineZone) { [dz.swfMesh.isglMeshNode setMaterial:_hiliteLineMaterial]; } // FIXME toggle x or -x depending on users finger // display hint note leter _hintLetterOrigPos = _hintLetters[dz.noteId].position; _hintLetters[dz.noteId].position = iv3(-2.0, dz.swfMesh.meshCenter.y, _hintLetterOrigPos.z); #endif } - (void) unHiliteZone:(MusicStaffDropZone *)dz { if (_staffDelegate) [_staffDelegate musicStaff:self willUnHiliteZone:dz]; #ifdef NO_HILIGHTING_IN_THIS_STAFF // call into the wrapped Isgl mesh if (dz.type == kMusicSpaceZone) { [dz.swfMesh.isglMeshNode setMaterial:_spaceMaterial]; dz.swfMesh.isglMeshNode.alpha = 0.03; } else if (dz.type == kMusicLineZone) { [dz.swfMesh.isglMeshNode setMaterial:_lineMaterial]; } // pull hint note letter out of view _hintLetters[dz.noteId].position = iv3(-20.0, _hintLetterOrigPos.y, _hintLetterOrigPos.z); #endif } - (NSDictionary *) textAttributesDictionaryForClef { NSMutableDictionary *attrsDict = [NSMutableDictionary dictionary]; if (_staffMetrics.musicSymbolsUnicodeFont != nil) { attrsDict[NSFontAttributeName] = _staffMetrics.musicSymbolsUnicodeFont; // ios defaults to horizontal, here we set to vertical for the music symbols attrsDict[NSVerticalGlyphFormAttributeName] = @(1); } else { // Exit if No Font supplied !!!! return nil; } if (_staffMetrics.clefAlphaComponent > 0. && _staffMetrics.clefAlphaComponent < 1.0) { attrsDict[NSForegroundColorAttributeName] = [_staffMetrics.lineColor colorWithAlphaComponent:_staffMetrics.clefAlphaComponent]; } else if (_staffMetrics.clefAlphaComponent == 1.0) { attrsDict[NSForegroundColorAttributeName] = _staffMetrics.lineColor; } return attrsDict; } - (void) pushContextIfEmpty:(CGContextRef)ctx { if (CGContextIsPathEmpty(ctx)) { UIGraphicsPushContext(ctx); } else { NSLog(@"Staff ContextPathNOT Empty"); } } - (void) popContextIfEmpty:(CGContextRef)ctx { if (CGContextIsPathEmpty(ctx)) { UIGraphicsPopContext(); } } #pragma mark - Draw Rendering Methods - (void) drawStaffInContext:(CGContextRef)ctx withMeasureCount:(int)measureCount { CGFloat leftX = _staffMetrics.leftMostXOffset; CGFloat rightX = leftX + (measureCount * _staffMetrics.lineLengthOneMeasure); CGFloat lineGap = _staffMetrics.yGapBetweenLines; // UIGraphicsPushContext(ctx); [self pushContextIfEmpty:ctx]; CGContextSetStrokeColorWithColor(ctx, _staffMetrics.lineColor.CGColor); CGContextSetLineWidth(ctx, _staffMetrics.staffLineWidth); // DANGER this assumes only 1 stub will be below and above CGFloat upperStubY = _lowestYCoordinate - (6 * lineGap); // calc top lines Y offset, and round to land on half pixel CGFloat firstY = upperStubY + lineGap; if (_staffMetrics.staffLineWidth == 1) { firstY = [MusicStaffRenderMetrics roundToLeastHalfPoint:firstY]; } // FIXME ? these may need conditional half pixel tweaks (or whole pixel) CGFloat barLineTopY = firstY - (_staffMetrics.staffLineWidth / 2); CGFloat barLineBottomY = firstY + (_staffMetrics.staffLineWidth / 2); int numFixedLines = _staffMetrics.fixedLineCount; // 5; for (int x=0; x < numFixedLines; x++) { CGContextMoveToPoint(ctx, leftX, firstY); CGContextAddLineToPoint(ctx, rightX, firstY); CGContextStrokePath(ctx); firstY += lineGap; if (x < (numFixedLines - 1)) { barLineBottomY += lineGap; } } // UIGraphicsPopContext(); [self popContextIfEmpty:ctx]; // now draw the vertical bar lines, optionally skipping last for open bar [_verticalBarCoordinates removeAllObjects]; SWFVector3 *barVec; BOOL skipBarLine = NO; int barCount = measureCount + 1; float barX = _staffMetrics.leftMostXOffset; float measureLength = _staffMetrics.lineLengthOneMeasure; // UIGraphicsPushContext(ctx); [self pushContextIfEmpty:ctx]; CGContextSetStrokeColorWithColor(ctx, _staffMetrics.lineColor.CGColor); for (int z = 0; z < barCount; z++) { if (z == 0) { CGContextSetLineWidth(ctx, _staffMetrics.firstMeasureBarLineWidth); skipBarLine = _staffMetrics.skipFirstMeasureBarLine; } else { CGContextSetLineWidth(ctx, _staffMetrics.measureBarLineWidth); } // draw vertical bars, ...but sometimes don't draw the last bar (guitar TAB) if (skipBarLine == NO) { if (((_staffMetrics.lastMeasureOpenBar == YES) && (z + 1 < barCount)) || (_staffMetrics.lastMeasureOpenBar == NO)) { CGContextMoveToPoint(ctx, barX, barLineTopY); CGContextAddLineToPoint(ctx, barX, barLineBottomY); CGContextStrokePath(ctx); } } // stash their locations for further use barVec = [SWFVector3 vectorFromValues:barX y:barLineTopY z:barLineBottomY]; [_verticalBarCoordinates addObject:barVec]; barX += measureLength; skipBarLine = NO; } // update properties for drawing down the call stack _topLineY = barLineTopY; _bottomLineY = barLineBottomY; CGContextSetLineWidth(ctx, _staffMetrics.staffLineWidth); // UIGraphicsPopContext(); [self popContextIfEmpty:ctx]; } #pragma mark - Clef Sign Calculations - (CGRect) boundingRectForClef { NSString *clefString = [[MusicNamesFormatter sharedInstance] clefNameForId:self.clefId]; NSDictionary *attrsDict = [self textAttributesDictionaryForClef]; if (clefString == nil || attrsDict == nil) { return CGRectZero; } CGPoint ptOrigin = CGPointMake(_staffMetrics.leftMostXOffset, _topLineY); CGRect textRect = [self boundingRectForClefString:clefString usingAttrs:attrsDict atOrigin:ptOrigin]; return textRect; } - (CGRect) boundingRectForClefString:(NSString *)clefString usingAttrs:(NSDictionary *)attrsDict atOrigin:(CGPoint)ptOrigin { if (clefString == nil || attrsDict == nil) { return CGRectZero; } CGRect textRect = CGRectZero; // obtain bounding box for first glyph in surrogate pair codepoints, // used for left/top/height CGRect bbBox = [self getBoundingRectForGlyphFromString:clefString withFont:attrsDict[NSFontAttributeName]]; CGFloat glyphLeftOffset = bbBox.origin.x; if (glyphLeftOffset < 0) { glyphLeftOffset = fabsf(glyphLeftOffset); } // If the glyph is positioned 'down' e.g. positive Y, then negate that CGFloat glyphTopOffset = 0; if (bbBox.origin.y > 0) { glyphTopOffset = bbBox.origin.y * -1; glyphTopOffset = [MusicStaffRenderMetrics roundToLeastHalfPoint:glyphTopOffset]; } // position top/left account for any left negative bearing, or top positive textRect.origin.x = (ptOrigin.x + glyphLeftOffset + _staffMetrics.measureBarLineWidth); textRect.origin.x = [MusicStaffRenderMetrics roundToLeastHalfPoint:textRect.origin.x]; textRect.origin.y = ptOrigin.y + glyphTopOffset + _staffMetrics.staffLineWidth; // can only rely on the .height being accurate, width is incorrect at 1 CGSize strSize = [clefString sizeWithAttributes:attrsDict]; textRect.size.height = ceilf(strSize.height); textRect.size.width = ceilf(bbBox.size.width); return textRect; } - (CGPoint) glyphOffsetFromString:(NSString *)glyphString withFont:(UIFont *)fnt { // obtain bounding box for first glyph in surrogate pair codepoints CGRect bbBox = [self getBoundingRectForGlyphFromString:glyphString withFont:fnt]; CGFloat glyphLeftOffset = bbBox.origin.x; if (glyphLeftOffset < 0) { glyphLeftOffset = fabsf((float)glyphLeftOffset); } glyphLeftOffset = [MusicStaffRenderMetrics roundToLeastHalfPoint:glyphLeftOffset]; // If the glyph is positioned 'down' e.g. positive Y, then negate that CGFloat glyphTopOffset = 0; if (bbBox.origin.y > 0) { glyphTopOffset = bbBox.origin.y * -1; glyphTopOffset = [MusicStaffRenderMetrics roundToLeastHalfPoint:glyphTopOffset]; } return CGPointMake(glyphLeftOffset, glyphTopOffset); } - (CGRect) getBoundingRectForGlyphFromString:(NSString *)string withFont:(UIFont *)fnt { CGRect bbRectFirstGlyph; // get characters from NSString NSUInteger len = [string length]; UniChar *characters = (UniChar *)malloc(sizeof(UniChar)*len); CFStringGetCharacters((__bridge CFStringRef)string, CFRangeMake(0, [string length]), characters); CTFontRef coreTextFont = CTFontCreateWithName((CFStringRef)fnt.fontName, fnt.pointSize, NULL); // allocate glyphs and bounding box arrays for holding the result // assuming that each character is only one glyph, which is wrong CGGlyph *glyphs = (CGGlyph *)malloc(sizeof(CGGlyph)*len); CTFontGetGlyphsForCharacters(coreTextFont, characters, glyphs, len); // get bounding boxes for glyphs CGRect *bb = (CGRect *)malloc(sizeof(CGRect)*len); CTFontGetBoundingRectsForGlyphs(coreTextFont, kCTFontDefaultOrientation, glyphs, bb, len); // only interested in first glyph of surrogate pair that identify Clefs bbRectFirstGlyph = bb[0]; CFRelease(coreTextFont); free(characters); free(glyphs); free(bb); return bbRectFirstGlyph; } #pragma mark - Draw Clef - (void) drawClefOnStaffInContext:(CGContextRef)ctx { NSString *clefString = [[MusicNamesFormatter sharedInstance] clefNameForId:self.clefId]; NSDictionary *attrsDict = [self textAttributesDictionaryForClef]; if (clefString == nil || attrsDict == nil) { return; } CGPoint ptOrigin = CGPointMake(_staffMetrics.leftMostXOffset, _topLineY); CGRect textRect = [self boundingRectForClefString:clefString usingAttrs:attrsDict atOrigin:ptOrigin]; CGRect bbBox = [self boundingRectForClef]; NSLog(@"Clef: %@ bbBox: %@\n\t\t\t\ttextRect: %@", clefString, NSStringFromCGRect(bbBox), NSStringFromCGRect(textRect)); UIGraphicsPushContext(ctx); [clefString drawInRect:textRect withAttributes:attrsDict]; UIGraphicsPopContext(); } - (CGRect) getBoundingRectsForGlyphUsingPathWithString:(NSString *)string andFont:(UIFont *)fnt { // get characters from NSString NSUInteger len = [string length]; UniChar *characters = (UniChar *)malloc(sizeof(UniChar)*len); CFStringGetCharacters((__bridge CFStringRef)string, CFRangeMake(0, [string length]), characters); CTFontRef coreTextFont = CTFontCreateWithName((CFStringRef)fnt.fontName, fnt.pointSize, NULL); // allocate glyphs and bounding box arrays for holding the result // assuming that each character is only one glyph, which is wrong CGGlyph *glyphs = (CGGlyph *)malloc(sizeof(CGGlyph)*len); CTFontGetGlyphsForCharacters(coreTextFont, characters, glyphs, len); CGPathRef glyphPath = CTFontCreatePathForGlyph(coreTextFont, glyphs[0], NULL); CGRect rect = CGPathGetBoundingBox(glyphPath); CFRelease(coreTextFont); CGPathRelease(glyphPath); free(characters); free(glyphs); return rect; } #pragma mark - Stub Lines - (BOOL) notePositionNeedsStubLine:(SWFVector3 *)noteVec { BOOL needsStub = YES; float proposedY = noteVec.pointFromVector.y; float topRealY = [self calcTopRealLineUsingMetrics]; float bottomRealY = [self calcBottomRealLineUsingMetrics]; if (proposedY >= topRealY && proposedY <= bottomRealY) { // nothing to do, proposed location is in staff main body needsStub = NO; } return needsStub; } - (float) stubLineYCoordinateForNoteId:(kMusicNoteId)noteId inOctave:(kMusicStaffOctave)octave withNotePosition:(SWFVector3 *)noteVec { float stubY = -1.; float proposedY = 0.; MusicStaffLineVectorMap *staffVecMap = nil; float spaceHalfLineCheck = [self distanceBetweenStaffLines] / 2.0; if ([self notePositionNeedsStubLine:noteVec] == NO) { // nothing to do, proposed location is in staff main body return -1.; } kMusicStaffZone zoneForNote = [self findStaffZoneTypeForNoteId:noteId inOctave:octave]; if (zoneForNote == kMusicLineZone) { // when note's center position matches lineZone, just use it stubY = noteVec.y; } else if (zoneForNote == kMusicSpaceZone) { // for spaces ensure the stub line has room _underneath_ the note staffVecMap = [self findStaffVectorMapForNoteId:noteId inOctave:octave ofZoneType:zoneForNote]; if (staffVecMap) { // first test the center of the space zone proposedY = staffVecMap.vec.y; if ([self isYCoordinateAboveTopStaffLine:proposedY] == YES) { // now ensure the proposed stub line Y coord is valid location above main staff body proposedY += spaceHalfLineCheck; if ([self isYCoordinateAboveTopStaffLine:proposedY] == NO) { // must be the first space above staff, no need for stub here return -1.; } // Attn: observe the Y coordinate assigned to the stub line is based // from the _notes Vector_ rather than the staffVectorMapping. While // in 'most' cases the two will be equal, in scenarios such as the // Selection Halo, the noteVectors are relative to the Halo View. As such // the stub line needs to be rendered relative to that notes local space. // Under 'normal' staff rendering on music page the noteVector and // staffVectorMap will be the same. This disjointed useage works // because the MusicStaffLineVectorMap is // also matched up with the given notes, noteId and octave. stubY = noteVec.y + spaceHalfLineCheck; // proposedY; } else if ([self isYCoordinateBelowBottomStaffLine:proposedY] == YES) { // under the main staff, ensure adjusted stub location is below the main staff body proposedY -= spaceHalfLineCheck; if ([self isYCoordinateBelowBottomStaffLine:proposedY] == NO) { // must be the first space below the staff, no need for stub here return -1.; } // Attn: read the observation written in the above if() condition, applies here. stubY = noteVec.y + spaceHalfLineCheck; // proposedY; } } } else if (zoneForNote == kMusicStaffNOZone) { // octave for proposed Note is off the staff's mapping vectors. Here a cheap stop-gap // is just draw a stub line at the 'last' mapped line coordinate farthest from main staff // body. Now determine if the unmapped octave is too far above or below the staff. // look hi staffVecMap = [self findHighestLineVectorMap]; if (octave >= staffVecMap.octaveInfo.octave) { stubY = staffVecMap.vec.y; } else { // look low staffVecMap = [self findLowestLineVectorMap]; if (octave <= staffVecMap.octaveInfo.octave) { stubY = staffVecMap.vec.y; } else { // get out of town (extremely rare as in never) return -1.; } } } return stubY; } - (void) drawStubLineInContext:(CGContextRef)ctx withNotePosition:(SWFVector3 *)noteVec andNoteGeometry:(SWFVector3 *)noteGeometry forNoteId:(kMusicNoteId)noteId inOctave:(kMusicStaffOctave)octave { // determine valid Y coordinate for stub line, a negative value means no stub possible float stubY = [self stubLineYCoordinateForNoteId:noteId inOctave:octave withNotePosition:noteVec]; if (stubY < 0.) { return; } CGPoint notePos = noteVec.pointFromVector; // craft line length to be slightly longer than width of note float stubLineLength = noteGeometry.x * 1.4; float stubLeftX = notePos.x - (stubLineLength / 2); // UIGraphicsPushContext(ctx); [self pushContextIfEmpty:ctx]; CGContextSetStrokeColorWithColor(ctx, _staffMetrics.lineColor.CGColor); CGContextSetLineWidth(ctx, _staffMetrics.staffLineWidth); CGContextMoveToPoint(ctx, stubLeftX, stubY); CGContextAddLineToPoint(ctx, stubLeftX + stubLineLength, stubY); CGContextStrokePath(ctx); // UIGraphicsPopContext(); [self popContextIfEmpty:ctx]; } @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,40 @@ // // MusicStaffFactory.h // MusicRendering // // Created by Michael J Albanese on 4/12/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import <Foundation/Foundation.h> #import "MusicTheory.h" @class MusicStaffRenderMetrics; @class MusicPageRenderCriteria; @protocol MusicStaffDisplayable_p; /** * Factory performs dynamic instantiation of all MusicStaff classes and derivatives. * This is a convenience factory and it not required to obtain a MusicStaff, one * could directly instantiate an object. However this factory is very useful by * the 'Builders' hierarchy, as they churn through all the tracks in a song and * are required to produce many different staff objects. For them this quasi-agnostic * factory approach proves more straightforward than direct individual class creation. */ @interface MusicStaffFactory : NSObject + (instancetype) sharedInstance; + (void) clearInstance; /** * FIXME ...trim down the params here, perhaps use Dictionary with known/Public Keys */ - (id<MusicStaffDisplayable_p>) staffFromStaffId:(kSongStaffId)staffId andLayout:(MusicStaffLayoutInfo *)layoutInfo andMiddleXCoordinate:(float)middleX andLowestYCoordinate:(float)lowY andStaffMetrics:(MusicStaffRenderMetrics *)staffMetrics andPageRenderCriteria:(MusicPageRenderCriteria *)renderCriteria; @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,112 @@ // // MusicStaffFactory.m // MusicRendering // // Created by Michael J Albanese on 4/12/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import "MusicDataModelPrimitiveDefinitions.h" #import "MusicStaffRenderMetrics.h" #import "MusicPageRenderCriteria.h" #import "MusicStaff.h" #import "MusicGuitarTabSixStaff.h" #import "MusicStaffFactory.h" static MusicStaffFactory *_instance; // Internal names of the MusicStaff classes (or derivatives) NSString *kStandardMusicStaffClass = @"MusicStaff"; NSString *kGuitarTabSixStaffClass = @"MusicGuitarTabSixStaff"; NSString *kGuitarTabSixFretStaffClass = @"MusicGuitarTabSixFretStaff"; @interface MusicStaffFactory () @property (strong, nonatomic) NSMutableDictionary *keysDictionary; @property (strong, nonatomic) NSMutableDictionary *classNamesDictionary; @end @implementation MusicStaffFactory + (MusicStaffFactory *) sharedInstance { @synchronized (self) { if (!_instance) { _instance = [[MusicStaffFactory alloc] initSingleton]; } } return _instance; } + (void) clearInstance { @synchronized (self) { if (_instance != nil) { [_instance->_keysDictionary removeAllObjects]; _instance->_keysDictionary = nil; [_instance->_classNamesDictionary removeAllObjects]; _instance->_classNamesDictionary = nil; _instance = nil; } } } #pragma mark - Instance Methods - (id) initSingleton { if ((self = [super init])) { [self buildClassNamesDictionary]; _keysDictionary = [[NSMutableDictionary alloc] init]; } return self; } - (void) buildClassNamesDictionary { _classNamesDictionary = [NSMutableDictionary dictionary]; _classNamesDictionary[ @(kStaffTreble) ] = kStandardMusicStaffClass; _classNamesDictionary[ @(kStaffBass) ] = kStandardMusicStaffClass; _classNamesDictionary[ @(kStaffTABSix) ] = kGuitarTabSixStaffClass; _classNamesDictionary[ @(kStaffTABSixFret) ] = kGuitarTabSixFretStaffClass; } - (NSString *) findMusicStaffClassFromId:(kSongStaffId)staffId { NSString *staffClassName = _classNamesDictionary[ @(staffId) ]; return staffClassName; } #pragma mark - Public Api // - (MusicStaff *) staffFromStaffId:(kSongStaffId)staffId - (id<MusicStaffDisplayable_p>) staffFromStaffId:(kSongStaffId)staffId andLayout:(MusicStaffLayoutInfo *)layoutInfo andMiddleXCoordinate:(float)middleX andLowestYCoordinate:(float)lowY andStaffMetrics:(MusicStaffRenderMetrics *)staffMetrics andPageRenderCriteria:(MusicPageRenderCriteria *)renderCriteria { if (!kVALID_STAFF_ID(staffId) || layoutInfo == nil) { // || staffMetrics == nil || renderCriteria == nil) { return nil; } MusicStaff *staffObject = nil; NSString *staffClassName = [self findMusicStaffClassFromId:staffId]; if (staffClassName != nil) { Class musicStaffClass = NSClassFromString (staffClassName); id anInstance = [[musicStaffClass alloc] initWithLayoutInfo:layoutInfo andMiddleXCoordinate:middleX andLowestYCoordinate:lowY andStaffMetrics:staffMetrics andPageRenderCriteria:renderCriteria]; staffObject = anInstance; } return staffObject; } @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -18,6 +18,27 @@ - (instancetype) int return self; } - (MusicGuitarTabSixStaff *) fabricateTABStaff { MusicGuitarTabSixStaff *tabSixStaff; MusicScoreTrackTemplateFactory *templFactory; MusicScoreGuitarTabSixTemplate *guitarTabTemplate; MusicStaffLayoutInfo tabStaffLayout = {0}; templFactory = [MusicScoreTrackTemplateFactory sharedInstance]; guitarTabTemplate = [templFactory guitarTabSixTemplateDefaultStaffLayouts]; tabStaffLayout = [guitarTabTemplate staffLayoutForStaffId:kStaffTABSix]; tabSixTaff = (MusicGuitarTabSixStaff *) [[MusicStaffFactory sharedInstance] staffFromStaffId:kStaffTABSix andLayout:&tabStaffLayout andMiddleXCoordinate:0 andLowestYCoordinate:0 andStaffMetrics:nil andPageRenderCriteria:nil]; return tabSixStaff; } - (void) testAminorChord { SongChordNoteType *chordNote1, *chordNote2, *chordNote3; -
mjaSanJose revised this gist
Aug 6, 2016 . 1 changed file with 138 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,138 @@ // // MusicTabStaffStringFretMidiMaps.m // MusicTheory // // Created by Michael J Albanese on 4/21/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import "SWFVector3.h" #import "MusicMidiNoteFrequenciesPianoTable.h" #import "MusicMidiNoteFrequencyEntry.h" #import "MusicTabStaffSingleFretMap.h" #import "MusicTabStaffStringFretMidiMaps.h" @interface MusicTabStaffStringFretMidiMaps () @property (strong, nonatomic) NSMutableArray *fretMaps; @property (nonatomic, readwrite) kMusicGuitarString stringNumber; @property (nonatomic, readwrite) BOOL isTuned; @end @implementation MusicTabStaffStringFretMidiMaps + (instancetype) stringFretMidiMapForStringNumber:(kMusicGuitarString)stringNumber { return [[MusicTabStaffStringFretMidiMaps alloc] initWithStringNumber:stringNumber]; } - (id) initWithStringNumber:(kMusicGuitarString)stringNumber { if (self = [super init]) { _stringNumber = stringNumber; } return self; } - (void) dealloc { [_fretMaps removeAllObjects]; } - (CGPoint) coordinate { return _vec.pointFromVector; } - (NSInteger) countOfFretMaps { if (_fretMaps != nil) { return _fretMaps.count; } return 0; } - (NSArray *) allFretMaps { if (!_isTuned) { return nil; } return (NSArray *)_fretMaps; } - (void) setupSingleFretMap:(MusicTabStaffSingleFretMap *)aFretMap fromMidiTableEntry:(MusicMidiNoteFrequencyEntry *)tableEntry { aFretMap.midiCode = tableEntry.midiCode; aFretMap.frequency = tableEntry.frequency; aFretMap.noteId = tableEntry.noteId; aFretMap.octave = tableEntry.octave; aFretMap.codedNoteName = tableEntry.codedNoteName; } - (void) tuneAllFretsUsingNutMidiCode:(NSInteger)nutMidi andFrequenciesTable:(MusicMidiNoteFrequenciesPianoTable *)midiTable { MusicTabStaffSingleFretMap *aFretMap; if (_fretMaps) { [_fretMaps removeAllObjects]; } else { _fretMaps = [NSMutableArray array]; } MusicMidiNoteFrequencyEntry *tableEntry = [midiTable findEntryByMidiCode:nutMidi]; if (tableEntry == nil) { NSAssert1(tableEntry != nil, @"Invalid Midi Code: %ld for nut given to tune String", (long)nutMidi); return; } // do the Nut for this string first, place at element Zero aFretMap = [MusicTabStaffSingleFretMap singleFretMapForOwningString:_stringNumber]; aFretMap.fretNumber = 0; [self setupSingleFretMap:aFretMap fromMidiTableEntry:tableEntry]; [_fretMaps addObject:aFretMap]; // loop for all frets on string and build internal array NSInteger nextMidiCode = nutMidi + 1; for (NSInteger fretNumber = 1; fretNumber <= kTotalGuitarFretCount; fretNumber++) { tableEntry = [midiTable findEntryByMidiCode:nextMidiCode]; if (tableEntry != nil) { aFretMap = [MusicTabStaffSingleFretMap singleFretMapForOwningString:_stringNumber]; aFretMap.fretNumber = fretNumber; [self setupSingleFretMap:aFretMap fromMidiTableEntry:tableEntry]; [_fretMaps addObject:aFretMap]; } nextMidiCode++; } _isTuned = YES; } - (MusicTabStaffSingleFretMap *) fretMapForNut { if (_fretMaps == nil || !_isTuned) { return nil; } MusicTabStaffSingleFretMap *oneFret = _fretMaps[0]; return oneFret; } - (MusicTabStaffSingleFretMap *) fretMapForFretNumber:(NSInteger)fretNumber { if (_fretMaps == nil || fretNumber > _fretMaps.count || fretNumber < 0 || !_isTuned) { return nil; } MusicTabStaffSingleFretMap *oneFret = _fretMaps[fretNumber]; return oneFret; } @end -
mjaSanJose revised this gist
Aug 5, 2016 . 2 changed files with 0 additions and 20 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,20 +0,0 @@ Binary file not shown. -
mjaSanJose revised this gist
Aug 5, 2016 . 12 changed files with 606 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,20 @@ // // MusicGuitarStringFretLocation.h // MusicTheory // // Created by Michael J Albanese on 4/24/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import <Foundation/Foundation.h> #import "MusicTheoryDefs.h" /** * Identifies a string and fret number on a guitar fretboard, nothing else ! */ @interface MusicGuitarStringFretLocation : NSObject @property (nonatomic) kMusicGuitarString stringNumber; @property (nonatomic) NSInteger fretNumber; + (instancetype) locationWithString:(kMusicGuitarString)string andFret:(NSInteger)fret; @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,33 @@ // // MusicGuitarStringFretLocation.m // MusicTheory // // Created by Michael J Albanese on 4/24/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import "MusicGuitarStringFretLocation.h" @implementation MusicGuitarStringFretLocation + (instancetype) locationWithString:(kMusicGuitarString)string andFret:(NSInteger)fret { return [[MusicGuitarStringFretLocation alloc] initWithString:string andFret:fret]; } - (id) initWithString:(kMusicGuitarString)string andFret:(NSInteger)fret { if (self = [super init]) { _stringNumber = kMusicGuitarString_None; if (kVALID_GUITAR_STRING(string)) { _stringNumber = string; } _fretNumber = fret; } return self; } @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,61 @@ // // MusicGuitarStringFretRange.h // MusicTheory // // Created by Michael J Albanese on 4/24/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import <Foundation/Foundation.h> #import "MusicGuitarStringFretLocation.h" /** * This range class can be handed to a MusicGuitarStringFretIterator supplying it the * parameters within which to conduct its searching/iterating. The concept of 'bottom' * string refers to the bottom most string on the instrument, that being * (6th on guitar, or 4th on bass). The consumer of this range object will cease to * operate reliably if the values for bottom represent a location on the fretboard * that is equal to or beyond what is given for the topStringFret. * * There is the concept of a 'middle string fret'. This would be the starting point * for an iterator to being searching for a midi-code or note on the fretboard. Loosely * analagous to middle C on a piano. If not supplied (is it optional) then the iterator * simply starts at the bottom String Fret in its search. * * Another concept is the 'interimUpperFretLimit'. This can be a different fret number than * given in the 'topStringFret', and would confine the iterator to that fret number when * searching on all strings between the bottom and top string. If unset (e.g. value == 0) * then the iterator will walk up to the fret number named in the topStringFret while * iterating over frets on iterim strings. * * Note: By default the interim Upper and Bottom limits get set to a value -1. When * the Iterator sees this it looks into the bottom and top Location properties * for guidance in setting the lower/upper bounds on fret searches on iterim strings. * 'Interim' strings are those strings between the bottom and top StringLocations. * * @seealso MusicGuitarStringFretIterator */ @interface MusicGuitarStringFretRange : NSObject @property (strong, nonatomic) MusicGuitarStringFretLocation *bottomStringLocation; @property (strong, nonatomic) MusicGuitarStringFretLocation *topStringLocation; @property (strong, nonatomic) MusicGuitarStringFretLocation *middleStringLocation; @property (nonatomic) NSInteger interimUpperFretLimit; @property (nonatomic) NSInteger interimBottomFretLimit; /** * Class creator builds a range object using the given 'bottom' 'top' locations which * define lower and upper fretboard limits, typically given to the Iterator to confine * the seach for notes on a guitar. The 'middleLocation' is optional. * * @param bottomLocation defines a lower bound on the guitar, limiting the iterator * @param topLocation defines upper bound on guitar, limiting the iterator * @param middleLocation (optional) defines some point which may be used to start a search */ + (instancetype) stringFretRangeUsingBottom:(MusicGuitarStringFretLocation *)bottomLocation andTopLocation:(MusicGuitarStringFretLocation *)topLocation andMiddleLocation:(MusicGuitarStringFretLocation *)middleLocation; @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,35 @@ // // MusicGuitarStringFretRange.m // MusicTheory // // Created by Michael J Albanese on 4/24/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import "MusicGuitarStringFretRange.h" @implementation MusicGuitarStringFretRange + (instancetype) stringFretRangeUsingBottom:(MusicGuitarStringFretLocation *)bottomLocation andTopLocation:(MusicGuitarStringFretLocation *)topLocation andMiddleLocation:(MusicGuitarStringFretLocation *)middleLocation { MusicGuitarStringFretRange *theRange = [[MusicGuitarStringFretRange alloc] init]; theRange.bottomStringLocation = bottomLocation; theRange.topStringLocation = topLocation; theRange.middleStringLocation = middleLocation; return theRange; } - (id) init { if (self = [super init]) { _interimBottomFretLimit = -1; _interimUpperFretLimit = -1; } return self; } @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,27 @@ // // MusicMidiNoteOctave.h // MusicTheory // // Created by Michael J Albanese on 4/25/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import <Foundation/Foundation.h> #import "MusicTheoryDefs.h" @interface MusicMidiNoteOctave : NSObject @property (nonatomic) kMusicNoteId noteId; @property (nonatomic) NSInteger octave; @property (nonatomic) NSInteger midiCode; @property (nonatomic, readonly) BOOL isValidMidiCode; @property (nonatomic, readonly) BOOL isValidOctave; @property (nonatomic, readonly) BOOL isValidNote; + (instancetype) noteOctaveWithNote:(kMusicNoteId)noteId octave:(NSInteger)octave midiCode:(NSInteger)midiCode; + (instancetype) noteOctaveWithNote:(kMusicNoteId)noteId andOctave:(NSInteger)octave; + (NSInteger) mapMusicStaffOctaveToMidiOctave:(kMusicStaffOctave)staffOctave; + (NSInteger) adjustOctaveForAltoStaff:(NSInteger)anOctave; @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,105 @@ // // MusicMidiNoteOctave.m // MusicTheory // // Created by Michael J Albanese on 4/25/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import "MusicMidiNoteOctave.h" @implementation MusicMidiNoteOctave + (instancetype) noteOctaveWithNote:(kMusicNoteId)noteId octave:(NSInteger)octave midiCode:(NSInteger)midiCode { return [[MusicMidiNoteOctave alloc] initWithNote:noteId andOctave:octave midiCode:midiCode]; } + (instancetype) noteOctaveWithNote:(kMusicNoteId)noteId andOctave:(NSInteger)octave { return [[MusicMidiNoteOctave alloc] initWithNote:noteId andOctave:octave midiCode:0]; } + (NSInteger) mapMusicStaffOctaveToMidiOctave:(kMusicStaffOctave)staffOctave { NSInteger actualMidiOctave = 1; if (staffOctave == kOctaveMinusTwo) { actualMidiOctave = 2; } else if (staffOctave == kOctaveMinusOne) { actualMidiOctave = 3; } else if (staffOctave == kOctaveHome) { actualMidiOctave = 4; } else if (staffOctave == kOctavePlusOne) { actualMidiOctave = 5; } else if (staffOctave == kOctavePlusTwo) { actualMidiOctave = 6; } else if (staffOctave == kOctavePlusThree) { actualMidiOctave = 7; } // these are TEMPorary until all kMusicStaffOctave enums are matched up with Midi 0 - 8 return actualMidiOctave; } + (NSInteger) adjustOctaveForAltoStaff:(NSInteger)anOctave { NSInteger altoOctave = anOctave; if (anOctave > 1) { altoOctave = anOctave - 2; } return altoOctave; } - (id) initWithNote:(kMusicNoteId)noteId andOctave:(NSInteger)octave midiCode:(NSInteger)midiCode { if (self = [super init]) { _noteId = noteId; _octave = octave; if (midiCode >= 21 && midiCode <= 108) { _midiCode = midiCode; } } return self; } - (BOOL) isValidMidiCode { if (_midiCode >= 21 && _midiCode <= 108) { return YES; } return NO; } - (BOOL) isValidOctave { if (_octave >= 0 && _octave <= 8) { return YES; } return NO; } - (BOOL) isValidNote { return kVALID_NOTE_ID(_noteId); } @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,33 @@ // // MusicStaffTraversable_p.h // MusicRendering // // Created by Michael J Albanese on 4/24/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import <Foundation/Foundation.h> /** * Protocol adopted by those MusicStaff classes which offer traversing of * their contents. There are other library classes which take advantage of this * offering, and they will provide the application layer a higher level functionality * such that the app would not need to access this protocol's offering. * * Some examples may be staff iterators, which provide searching and lookup * of notes and/or locations on a staff. * * Bear in mind the return arrays may be far different depending on the Staff supplying * them. A standard music staff holds very few notes, whereas a guitar Tab staff has * no spaces. However a guitar Tab staff's lines, each hold a sub array of up to 24 * elements, each of which may hold a note and represents a fret on the instrument fretboard. * * These intracacies are hidden by the Iterators, who offer a much smoother and less detailed * access for locating notes in a staff. * * @seealso MusicGuitarStringFretIterator */ @protocol MusicStaffTraversable_p <NSObject> - (NSArray *)traversableLines; - (NSArray *)traversableSpaces; @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,42 @@ // // MusicTabStaffSingleFretMap.h // MusicTheory // // Created by Michael J Albanese on 4/24/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import <Foundation/Foundation.h> #import "MusicTheoryDefs.h" @class MusicMidiNoteOctave; /** * Small class used to hold contents of a fret on a given string in * the Tab Staff. Is mainly used internally by the TabStaff for its mappings. It * is also possible to receive an instance of this class while using the * custom Iterator to walk over a guitar Tab staff for notes, midi codes, etc. * that live at a given fretboard fret. * * @seealso MusicGuitarStringFretIterator */ @interface MusicTabStaffSingleFretMap : NSObject <NSCopying> @property (nonatomic) NSInteger fretNumber; @property (nonatomic) kMusicGuitarString owningString; @property (nonatomic) NSInteger midiCode; @property (nonatomic) float frequency; @property (nonatomic) kMusicNoteId noteId; @property (nonatomic, copy) NSString *codedNoteName; @property (nonatomic) NSInteger octave; // @property (nonatomic) kMusicStaffOctave octave; <-- uncomment when coverted to kMusicStaffOctave @property (nonatomic) kMusicAccidental resolvedAccidental; @property (nonatomic) kNoteInterval resolvedInterval; @property (nonatomic) kMusicNoteId resolvedNoteId; @property (nonatomic) kScaleDegree resolvedScaleDegree; @property (nonatomic) BOOL isResolved; + (instancetype) singleFretMapForOwningString:(kMusicGuitarString)owningString; - (BOOL) matchesMidiNoteOctave:(MusicMidiNoteOctave *)midiNoteOctave; @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,88 @@ // // MusicTabStaffSingleFretMap.m // MusicTheory // // Created by Michael J Albanese on 4/24/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import "MusicMidiNoteOctave.h" #import "MusicTabStaffSingleFretMap.h" @implementation MusicTabStaffSingleFretMap + (instancetype) singleFretMapForOwningString:(kMusicGuitarString)owningString { return [[MusicTabStaffSingleFretMap alloc] initWithOwningString:owningString]; } - (id) initWithOwningString:(kMusicGuitarString)owningStringNumber { if (self = [super init]) { _owningString = owningStringNumber; } return self; } - (BOOL) matchesMidiNoteOctave:(MusicMidiNoteOctave *)midiNoteOctave { BOOL theyMatch = NO; if (midiNoteOctave.isValidMidiCode) { if (_midiCode == midiNoteOctave.midiCode) { theyMatch = YES; } } else if (midiNoteOctave.isValidNote && midiNoteOctave.isValidOctave) { if (midiNoteOctave.noteId == _noteId && midiNoteOctave.octave == _octave) { theyMatch = YES; } } return theyMatch; } #pragma mark - Copying - (instancetype) copyWithZone:(NSZone *)zone { MusicTabStaffSingleFretMap *copyEnt; copyEnt = [[MusicTabStaffSingleFretMap allocWithZone:zone] initWithOwningString:self.owningString]; if (copyEnt) { copyEnt.fretNumber = self.fretNumber; copyEnt.midiCode = self.midiCode; copyEnt.frequency = self.frequency; copyEnt.noteId = self.noteId; copyEnt.octave = self.octave; copyEnt.codedNoteName = self.codedNoteName; } return copyEnt; } - (instancetype) mutableCopyWithZone:(NSZone *)zone { MusicTabStaffSingleFretMap *copyEnt; copyEnt = [[MusicTabStaffSingleFretMap allocWithZone:zone] initWithOwningString:self.owningString]; if (copyEnt) { copyEnt.fretNumber = self.fretNumber; copyEnt.midiCode = self.midiCode; copyEnt.frequency = self.frequency; copyEnt.noteId = self.noteId; copyEnt.octave = self.octave; copyEnt.codedNoteName = self.codedNoteName; } return copyEnt; } @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,47 @@ // // MusicTabStaffStringFretMidiMaps.h // MusicTheory // // Created by Michael J Albanese on 4/21/14. // Copyright (c) 2014 Michael J Albanese. All rights reserved. // #import <UIKit/UIKit.h> #import <Foundation/Foundation.h> #import "MusicTheoryDefs.h" @class SWFVector3; @class MusicMidiNoteFrequenciesPianoTable; @class MusicTabStaffSingleFretMap; /** * Conceptually this is a String object which maps to one line on a Tab staff. * Internally it holds an array of TabStaffSingleFretMap objects, one for each * fret on the fretboard. A Tab staff generally holds an array of six (or four) of * these string objets, and may at any time call upon the string object to 're-tune' * all of its frets based on a given base midi code (e.g. the midi code for this string * if it were to be played as open). * * @seealso MusicTabStaffSingleFretMap */ @interface MusicTabStaffStringFretMidiMaps : NSObject @property (nonatomic) BOOL isSelected; @property (nonatomic, readonly) BOOL isTuned; @property (nonatomic) CGPoint coordinate; @property (strong, nonatomic) SWFVector3 *vec; @property (nonatomic) kMusicStaffZone zoneType; @property (nonatomic, readonly) NSArray *allFretMaps; @property (nonatomic, readonly) NSInteger countOfFretMaps; @property (nonatomic, readonly) kMusicGuitarString stringNumber; @property (nonatomic, readonly) MusicTabStaffSingleFretMap *fretMapForNut; + (instancetype) stringFretMidiMapForStringNumber:(kMusicGuitarString)stringNumber; - (void) tuneAllFretsUsingNutMidiCode:(NSInteger)nutMidi andFrequenciesTable:(MusicMidiNoteFrequenciesPianoTable *)midiTable; /** caller uses 1 based fret numbers, even though zero is valid and identifies nut */ - (MusicTabStaffSingleFretMap *) fretMapForFretNumber:(NSInteger)fretNumber; - (MusicTabStaffSingleFretMap *) fretMapForNut; @end This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,20 @@ ## Mapping Notes on a Guitar Fretboard This gist has the source code to match the series of [Blog posts for mapping notes on guitar](http://musicwritingcode.com/2016/07/31/mapping-notes-on-guitar/). Here is a brief rundown of the source files, and headers in this gist: ``` MusicTabStaffSingleFretMap.hm - contains info for an individual guitar fret MusicTabStaffStringFretMidiMaps.hm - acts as the 'string' on TAB staff and holds a series of SingleFretMap's MusicGuitarTabSixStaff.hm - actual TAB staff, has six (or four) tuneable strings MusicGuitarStringFretLocation.hm - basic string-fret location class MusicGuitarStringFretRange.hm - defines range for controlling iterators fretboard position MusicMidiNoteOctave.hm - simple class, part of public Api to Iterator find methods MusicGuitarStringFretIterator.hm - Offers iteration over Tab staff / guitar fretboard TestIteration.m - example code for using the Iterator ``` Being that this is a gist, these files will _not_ compile as is. They are part of a much larger project. This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,95 @@ #import "MusicMidiNoteOctave.h" #import "MusicGuitarStringFretRange.h" #import "MusicTabStaffSingleFretMap.h" #import "MusicTabStaffStringFretMidiMaps.h" #import "MusicGuitarStringFretIterator.h" @interface TestIterator : NSObject @end @implementation TestIterator - (instancetype) int { if (!(self = [super init])) { return nil; } return self; } - (void) testAminorChord { SongChordNoteType *chordNote1, *chordNote2, *chordNote3; // define array of chord notes chordNote1 = [SongChordNoteType chordNoteTypeWithId:kMusicNoteId_A]; chordNote1.octave = 3; chordNote2 = [SongChordNoteType chordNoteTypeWithId:kMusicNoteId_E]; chordNote2.octave = 3; chordNote3 = [SongChordNoteType chordNoteTypeWithId:kMusicNoteId_C]; chordNote3.octave = 4; NSArray *notesArray = @[ chordNote1, chordNote2, chordNote3 ]; // define the FretRange for iteration MusicGuitarStringFretRange *iterRange; MusicGuitarStringFretLocation *lowerLocation, *upperLocation, *middleLocation; upperLocation = [MusicGuitarStringFretLocation locationWithString:kMusicGuitarString_One andFret:3]; lowerLocation = [MusicGuitarStringFretLocation locationWithString:kMusicGuitarString_Four andFret:0]; middleLocation = [MusicGuitarStringFretLocation locationWithString:kMusicGuitarString_Three andFret:2]; iterRange = [MusicGuitarStringFretRange stringFretRangeUsingBottom:lowerLocation andTopLocation:upperLocation andMiddleLocation:middleLocation]; iterRange.interimUpperFretLimit = 3; // get a TAB staff, then acquire its 'lines' array MusicGuitarTabSixStaff *tabSixStaff = [self fabricateTABStaff]; NSArray *arTabLines = [tabSixStaff traversableLines]; [self detectFretsForNotes:notesArray withinRange:iterRange usingTABLines:arTabLines]; } - (void) detectFretsForNotes:(NSArray *)arrayOfNotes withinRange:(MusicGuitarStringFretRange *)iterRange usingTABLines:(NSArray *)tabLines { MusicTabStaffSingleFretMap *foundMap; MusicGuitarStringFretIterator *iter; MusicMidiNoteOctave *targetNote; iter = [MusicGuitarStringFretIterator iteratorWithLinesArray:tabLines andRange:iterRange]; BOOL firstFind = YES; for (SongChordNoteType *aNote in arrayOfNotes) { targetNote = [MusicMidiNoteOctave noteOctaveWithNote:aNote.noteId andOctave:aNote.octave]; if (firstFind) { foundMap = [iter findFirstMatchingNoteOctave:targetNote startingFromRange:kSearchRange_MiddleRange inDirection:kSearchDirection_Top wrap:YES]; } else { foundMap = [iter findUpMatchingNoteOctave:targetNote wrap:YES]; } if (foundMap) { aNote.guitarString = foundMap.owningString; aNote.guitarFret = foundMap.fretNumber; aNote.midiCode = foundMap.midiCode; } firstFind = NO; } } @end -
mjaSanJose revised this gist
Aug 5, 2016 . 1 changed file with 7 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -5,15 +5,15 @@ This gist has the source code to match the series of [Blog posts for mapping not Here is a brief rundown of the source files, and headers in this gist: ``` MusicTabStaffSingleFretMap.hm - contains info for an individual guitar fret MusicTabStaffStringFretMidiMaps.hm - acts as the 'string' on TAB staff and holds a series of SingleFretMap's MusicGuitarTabSixStaff.hm - actual TAB staff, has six (or four) tuneable strings MusicGuitarStringFretLocation.hm - basic string-fret location class MusicGuitarStringFretRange.hm - defines range for controlling iterators fretboard position MusicMidiNoteOctave.hm - simple class, part of public Api to Iterator find methods MusicGuitarStringFretIterator.hm - Offers iteration over Tab staff / guitar fretboard TestIteration.m - example code for using the Iterator ``` Being that this is a gist, these files will _not_ compile as is. They are part of a much larger project. -
mjaSanJose revised this gist
Aug 5, 2016 . 1 changed file with 2 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,6 +4,7 @@ This gist has the source code to match the series of [Blog posts for mapping not Here is a brief rundown of the source files, and headers in this gist: ``` MusicTabStaffSingleFretMap.hm - contains info for an individual guitar fret MusicTabStaffStringFretMidiMaps.hm - acts as the 'string' on TAB staff and holds a series of SingleFretMap's @@ -13,7 +14,7 @@ MusicGuitarStringFretRange.hm - defines range for controlling iterators fretboar MusicMidiNoteOctave.hm - simple class, part of public Api to Iterator find methods MusicGuitarStringFretIterator.hm - Offers iteration over Tab staff / guitar fretboard TestIteration.m - example code for using the Iterator ``` Being that this is a gist, these files will _not_ compile as is. They are part of a much larger project. -
mjaSanJose revised this gist
Aug 5, 2016 . 1 changed file with 0 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,7 +1,5 @@ ## Mapping Notes on a Guitar Fretboard This gist has the source code to match the series of [Blog posts for mapping notes on guitar](http://musicwritingcode.com/2016/07/31/mapping-notes-on-guitar/). Here is a brief rundown of the source files, and headers in this gist: -
mjaSanJose revised this gist
Aug 5, 2016 . 1 changed file with 13 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,14 +2,20 @@  This gist has the source code to match the series of [Blog posts for mapping notes on guitar](http://musicwritingcode.com/2016/07/31/mapping-notes-on-guitar/). Here is a brief rundown of the source files, and headers in this gist: MusicTabStaffSingleFretMap.hm - contains info for an individual guitar fret MusicTabStaffStringFretMidiMaps.hm - acts as the 'string' on TAB staff and holds a series of SingleFretMap's MusicGuitarTabSixStaff.hm - actual TAB staff, has six (or four) tuneable strings MusicGuitarStringFretLocation.hm - basic string-fret location class MusicGuitarStringFretRange.hm - defines range for controlling iterators fretboard position MusicMidiNoteOctave.hm - simple class, part of public Api to Iterator find methods MusicGuitarStringFretIterator.hm - Offers iteration over Tab staff / guitar fretboard TestIteration.m - example code for using the Iterator Being that this is a gist, these files will _not_ compile as is. They are part of a much larger project. -
mjaSanJose revised this gist
Jul 23, 2016 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,6 +1,6 @@ ## Mapping Notes on a Guitar Fretboard  Writing a music notation app has many challenges. Finding notes and formatting chords for Piano is somewhat straightforward. Doing so for a Guitar fretboard presents many difficult challenges. -
mjaSanJose revised this gist
Jul 23, 2016 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,6 +1,6 @@ ## Mapping Notes on a Guitar Fretboard  Writing a music notation app has many challenges. Finding notes and formatting chords for Piano is somewhat straightforward. Doing so for a Guitar fretboard presents many difficult challenges. -
mjaSanJose revised this gist
Jul 23, 2016 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
Binary file not shown. -
mjaSanJose revised this gist
Jul 23, 2016 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
LoadingSorry, something went wrong. Reload?Sorry, we cannot display this file.Sorry, this file is invalid so it cannot be displayed. -
mjaSanJose revised this gist
Jul 23, 2016 . 2 changed files with 2 additions and 0 deletions.There are no files selected for viewing
LoadingSorry, something went wrong. Reload?Sorry, we cannot display this file.Sorry, this file is invalid so it cannot be displayed.This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,7 @@ ## Mapping Notes on a Guitar Fretboard  Writing a music notation app has many challenges. Finding notes and formatting chords for Piano is somewhat straightforward. Doing so for a Guitar fretboard presents many difficult challenges. One such challenge is given a set of notes from a known music chord, write some code which will take those notes as input and iterate over the guitar fretboard to locate a reasonable set of guitar string/fret locations for each note. The string/fret locations may then be used to display a guitar 'Fret Diagram' of the given chord. -
mjaSanJose created this gist
Jul 23, 2016 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,13 @@ ## Mapping Notes on a Guitar Fretboard Writing a music notation app has many challenges. Finding notes and formatting chords for Piano is somewhat straightforward. Doing so for a Guitar fretboard presents many difficult challenges. One such challenge is given a set of notes from a known music chord, write some code which will take those notes as input and iterate over the guitar fretboard to locate a reasonable set of guitar string/fret locations for each note. The string/fret locations may then be used to display a guitar 'Fret Diagram' of the given chord. Since this is code we would like it to be flexible, ideally taking in some additional information beyond the music chord. * Given a music chord find all the string/fret locations for each note * Utilize 'Range' information to limit the Iterator to a fretboard section * Rely upon a listing that maps all 88 piano midi codes to their notes/octaves * (this will be needed to 'tune' the six strings on a guitar)