From 711da5fe245dd54ce9b3fcfa8cc5fb1d675cbf75 Mon Sep 17 00:00:00 2001 From: Chris Moeller Date: Wed, 16 Oct 2013 08:59:44 -0700 Subject: [PATCH] Added MIDI container processor and metadata reader --- .../midi_processing/midi_container.cpp | 12 +++ .../midi_processing/midi_container.h | 7 +- .../midi_processor_riff_midi.cpp | 39 +++++++- Plugins/MIDI/MIDI.xcodeproj/project.pbxproj | 12 +++ Plugins/MIDI/MIDI/MIDIContainer.h | 17 ++++ Plugins/MIDI/MIDI/MIDIContainer.mm | 69 ++++++++++++++ Plugins/MIDI/MIDI/MIDIMetadataReader.h | 17 ++++ Plugins/MIDI/MIDI/MIDIMetadataReader.mm | 91 +++++++++++++++++++ 8 files changed, 262 insertions(+), 2 deletions(-) create mode 100755 Plugins/MIDI/MIDI/MIDIContainer.h create mode 100755 Plugins/MIDI/MIDI/MIDIContainer.mm create mode 100644 Plugins/MIDI/MIDI/MIDIMetadataReader.h create mode 100644 Plugins/MIDI/MIDI/MIDIMetadataReader.mm diff --git a/Frameworks/midi_processing/midi_processing/midi_container.cpp b/Frameworks/midi_processing/midi_processing/midi_container.cpp index 42674cb9c..fa8d75c7c 100644 --- a/Frameworks/midi_processing/midi_processing/midi_container.cpp +++ b/Frameworks/midi_processing/midi_processing/midi_container.cpp @@ -225,6 +225,7 @@ void midi_meta_data::add_item( const midi_meta_data_item & p_item ) void midi_meta_data::append( const midi_meta_data & p_data ) { m_data.insert( m_data.end(), p_data.m_data.begin(), p_data.m_data.end() ); + m_bitmap = p_data.m_bitmap; } bool midi_meta_data::get_item( const char * p_name, midi_meta_data_item & p_out ) const @@ -241,6 +242,17 @@ bool midi_meta_data::get_item( const char * p_name, midi_meta_data_item & p_out return false; } +bool midi_meta_data::get_bitmap( std::vector & p_out ) +{ + p_out = m_bitmap; + return p_out.size() != 0; +} + +void midi_meta_data::assign_bitmap( std::vector::const_iterator const& begin, std::vector::const_iterator const& end ) +{ + m_bitmap.assign( begin, end ); +} + std::size_t midi_meta_data::get_count() const { return m_data.size(); diff --git a/Frameworks/midi_processing/midi_processing/midi_container.h b/Frameworks/midi_processing/midi_processing/midi_container.h index 2385f45e9..95ac0da20 100644 --- a/Frameworks/midi_processing/midi_processing/midi_container.h +++ b/Frameworks/midi_processing/midi_processing/midi_container.h @@ -119,7 +119,8 @@ struct midi_meta_data_item class midi_meta_data { std::vector m_data; - + std::vector m_bitmap; + public: midi_meta_data() { } @@ -129,6 +130,10 @@ public: bool get_item( const char * p_name, midi_meta_data_item & p_out ) const; + bool get_bitmap( std::vector & p_out ); + + void assign_bitmap( std::vector::const_iterator const& begin, std::vector::const_iterator const& end ); + std::size_t get_count() const; const midi_meta_data_item & operator [] ( std::size_t p_index ) const; diff --git a/Frameworks/midi_processing/midi_processing/midi_processor_riff_midi.cpp b/Frameworks/midi_processing/midi_processing/midi_processor_riff_midi.cpp index 1670512b9..8ec05e56e 100644 --- a/Frameworks/midi_processing/midi_processing/midi_processor_riff_midi.cpp +++ b/Frameworks/midi_processing/midi_processing/midi_processor_riff_midi.cpp @@ -41,6 +41,10 @@ static const char * riff_tag_mappings[][2] = { "ITCH", "technician" } }; +#define CF_TEXT 1 +#define CF_TIFF 6 +#define CF_DIB 8 + bool midi_processor::process_riff_midi( std::vector const& p_file, midi_container & p_out ) { uint32_t file_size = p_file[ 4 ] | ( p_file[ 5 ] << 8 ) | ( p_file[ 6 ] << 16 ) | ( p_file[ 7 ] << 24 ); @@ -75,12 +79,45 @@ bool midi_processor::process_riff_midi( std::vector const& p_file, midi else if ( it[ 0 ] == 'D' && it[ 1 ] == 'I' && it[ 2 ] == 'S' && it[ 3 ] == 'P' ) { uint32_t type = it[ 8 ] | ( it[ 9 ] << 8 ) | ( it[ 10 ] << 16 ) | ( it[ 11 ] << 24 ); - if ( type == 1 ) + if ( type == CF_TEXT ) { extra_buffer.resize( chunk_size - 4 ); std::copy( it + 12, it + 8 + chunk_size, extra_buffer.begin() ); meta_data.add_item( midi_meta_data_item( 0, "display_name", (const char *) &extra_buffer[0] ) ); } + else if ( type == CF_TIFF ) + { + meta_data.assign_bitmap( it + 12, it + 8 + chunk_size ); + } + else if ( type == CF_DIB ) + { + if ( chunk_size >= 8 ) + { + uint32_t dib_header_size = it[ 12 ] | ( it[13] << 8 ) | ( it[14] << 16 ) | ( it[15] << 24 ); + if ( chunk_size >= 4 + dib_header_size ) + { + uint32_t dib_image_offset = 14 + dib_header_size; + uint32_t dib_size = chunk_size + 10; + extra_buffer.resize( dib_size ); + extra_buffer[ 0 ] = 'B'; + extra_buffer[ 1 ] = 'M'; + extra_buffer[ 2 ] = dib_size; + extra_buffer[ 3 ] = dib_size >> 8; + extra_buffer[ 4 ] = dib_size >> 16; + extra_buffer[ 5 ] = dib_size >> 24; + extra_buffer[ 6 ] = 0; + extra_buffer[ 7 ] = 0; + extra_buffer[ 8 ] = 0; + extra_buffer[ 9 ] = 0; + extra_buffer[ 10 ] = dib_image_offset; + extra_buffer[ 11 ] = dib_image_offset >> 8; + extra_buffer[ 12 ] = dib_image_offset >> 16; + extra_buffer[ 13 ] = dib_image_offset >> 24; + std::copy(it + 12, it + 8 + chunk_size, extra_buffer.begin() + 14); + meta_data.assign_bitmap(extra_buffer.begin(), extra_buffer.end()); + } + } + } it += 8 + chunk_size; if ( chunk_size & 1 && it < body_end ) ++it; } diff --git a/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj b/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj index 93f17ff0f..665543f6b 100644 --- a/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj +++ b/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj @@ -22,6 +22,8 @@ 83B0671E180D6FD8008E3612 /* libbassopus.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83B06716180D6FC8008E3612 /* libbassopus.dylib */; }; 83B0671F180D6FD8008E3612 /* libbasswv.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83B06717180D6FC8008E3612 /* libbasswv.dylib */; }; 83B06722180D70FE008E3612 /* MIDIDecoder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83B06721180D70FE008E3612 /* MIDIDecoder.mm */; }; + 83C35702180EDB74007E9DF0 /* MIDIContainer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83C35700180EDB74007E9DF0 /* MIDIContainer.mm */; }; + 83C35705180EDD1C007E9DF0 /* MIDIMetadataReader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83C35703180EDD1C007E9DF0 /* MIDIMetadataReader.mm */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -93,6 +95,10 @@ 83B06721180D70FE008E3612 /* MIDIDecoder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MIDIDecoder.mm; sourceTree = ""; }; 83B06723180D714F008E3612 /* Plugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Plugin.h; path = ../../../Audio/Plugin.h; sourceTree = ""; }; 83B06724180D792E008E3612 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../../Utils/Logging.h; sourceTree = ""; }; + 83C35700180EDB74007E9DF0 /* MIDIContainer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MIDIContainer.mm; sourceTree = ""; }; + 83C35701180EDB74007E9DF0 /* MIDIContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIContainer.h; sourceTree = ""; }; + 83C35703180EDD1C007E9DF0 /* MIDIMetadataReader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MIDIMetadataReader.mm; sourceTree = ""; }; + 83C35704180EDD1C007E9DF0 /* MIDIMetadataReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIMetadataReader.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -156,6 +162,10 @@ 83B06690180D5668008E3612 /* MIDI */ = { isa = PBXGroup; children = ( + 83C35703180EDD1C007E9DF0 /* MIDIMetadataReader.mm */, + 83C35704180EDD1C007E9DF0 /* MIDIMetadataReader.h */, + 83C35700180EDB74007E9DF0 /* MIDIContainer.mm */, + 83C35701180EDB74007E9DF0 /* MIDIContainer.h */, 83B06724180D792E008E3612 /* Logging.h */, 83B06723180D714F008E3612 /* Plugin.h */, 83B06720180D70FE008E3612 /* MIDIDecoder.h */, @@ -270,7 +280,9 @@ files = ( 83B06709180D64DA008E3612 /* MIDIPlayer.cpp in Sources */, 83B06722180D70FE008E3612 /* MIDIDecoder.mm in Sources */, + 83C35702180EDB74007E9DF0 /* MIDIContainer.mm in Sources */, 83B0670C180D6665008E3612 /* BMPlayer.cpp in Sources */, + 83C35705180EDD1C007E9DF0 /* MIDIMetadataReader.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Plugins/MIDI/MIDI/MIDIContainer.h b/Plugins/MIDI/MIDI/MIDIContainer.h new file mode 100755 index 000000000..83083fbdf --- /dev/null +++ b/Plugins/MIDI/MIDI/MIDIContainer.h @@ -0,0 +1,17 @@ +// +// MIDIContainer.h +// MIDI +// +// Created by Christopher Snowhill on 10/16/13. +// Copyright 2013 __NoWork, Inc__. All rights reserved. +// + +#import + +#import "Plugin.h" + +@interface MIDIContainer : NSObject { + +} + +@end diff --git a/Plugins/MIDI/MIDI/MIDIContainer.mm b/Plugins/MIDI/MIDI/MIDIContainer.mm new file mode 100755 index 000000000..8fb13f473 --- /dev/null +++ b/Plugins/MIDI/MIDI/MIDIContainer.mm @@ -0,0 +1,69 @@ +// +// MIDIContainer.mm +// MIDI +// +// Created by Christopher Snowhill on 10/16/13. +// Copyright 2013 __NoWork, Inc__. All rights reserved. +// + +#import "MIDIContainer.h" +#import "MIDIDecoder.h" + +#import + +@implementation MIDIContainer + ++ (NSArray *)fileTypes +{ + return [MIDIDecoder fileTypes]; +} + ++ (NSArray *)mimeTypes +{ + return [MIDIDecoder mimeTypes]; +} + +//This really should be source... ++ (NSArray *)urlsForContainerURL:(NSURL *)url +{ + if ([url fragment]) { + // input url already has fragment defined - no need to expand further + return [NSMutableArray arrayWithObject:url]; + } + + id audioSourceClass = NSClassFromString(@"AudioSource"); + id source = [audioSourceClass audioSourceForURL:url]; + + if (![source open:url]) + return 0; + + if (![source seekable]) + return 0; + + [source seek:0 whence:SEEK_END]; + long size = [source tell]; + [source seek:0 whence:SEEK_SET]; + + std::vector data; + data.resize( size ); + [source read:&data[0] amount:size]; + + midi_container midi_file; + + if ( !midi_processor::process_file( data, [[[url absoluteString] pathExtension] UTF8String], midi_file) ) + return 0; + + long track_count = midi_file.get_subsong_count(); + + NSMutableArray *tracks = [NSMutableArray array]; + + long i; + for (i = 0; i < track_count; i++) { + [tracks addObject:[NSURL URLWithString:[[url absoluteString] stringByAppendingFormat:@"#%li", midi_file.get_subsong( i )]]]; + } + + return tracks; +} + + +@end diff --git a/Plugins/MIDI/MIDI/MIDIMetadataReader.h b/Plugins/MIDI/MIDI/MIDIMetadataReader.h new file mode 100644 index 000000000..425eb3e9b --- /dev/null +++ b/Plugins/MIDI/MIDI/MIDIMetadataReader.h @@ -0,0 +1,17 @@ +// +// MIDIMetadataReader.h +// MIDI +// +// Created by Christopher Snowhill on 10/16/13. +// Copyright 2013 __NoWork, Inc__. All rights reserved. +// + +#import + +#import "Plugin.h" + +@interface MIDIMetadataReader : NSObject { + +} + +@end diff --git a/Plugins/MIDI/MIDI/MIDIMetadataReader.mm b/Plugins/MIDI/MIDI/MIDIMetadataReader.mm new file mode 100644 index 000000000..627cd95c8 --- /dev/null +++ b/Plugins/MIDI/MIDI/MIDIMetadataReader.mm @@ -0,0 +1,91 @@ +// +// MIDIMetadataReader.mm +// MIDI +// +// Created by Christopher Snowhill on 10/16/13. +// Copyright 2013 __NoWork, Inc__. All rights reserved. +// + +#import "MIDIMetadataReader.h" + +#import "MIDIDecoder.h" + +#import + +@implementation MIDIMetadataReader + ++ (NSArray *)fileTypes +{ + return [MIDIDecoder fileTypes]; +} + ++ (NSArray *)mimeTypes +{ + return [MIDIDecoder mimeTypes]; +} + ++ (NSDictionary *)metadataForURL:(NSURL *)url +{ + id audioSourceClass = NSClassFromString(@"AudioSource"); + id source = [audioSourceClass audioSourceForURL:url]; + + if (![source open:url]) + return 0; + + if (![source seekable]) + return 0; + + [source seek:0 whence:SEEK_END]; + long size = [source tell]; + [source seek:0 whence:SEEK_SET]; + + std::vector data; + data.resize( size ); + [source read:&data[0] amount:size]; + + midi_container midi_file; + + if ( !midi_processor::process_file( data, [[[url absoluteString] pathExtension] UTF8String], midi_file) ) + return 0; + + int track_num; + if ([[url fragment] length] == 0) + track_num = 0; + else + track_num = [[url fragment] intValue]; + + midi_meta_data metadata; + + midi_file.get_meta_data( track_num, metadata ); + + midi_meta_data_item item; + bool remap_display_name = !metadata.get_item( "title", item ); + + NSArray * allowedKeys = [NSArray arrayWithObjects:@"title", @"artist", @"album", @"year", nil]; + + NSMutableDictionary * dict = [NSMutableDictionary dictionaryWithCapacity:10]; + + for ( size_t i = 0; i < metadata.get_count(); ++i ) + { + const midi_meta_data_item & item = metadata[ i ]; + NSString * name = [[NSString stringWithUTF8String:item.m_name.c_str()] lowercaseString]; + if ( ![name isEqualToString:@"type"] ) + { + if ( remap_display_name && [name isEqualToString:@"display_name"] ) + name = @"title"; + if ( [allowedKeys containsObject:name] ) + [dict setObject:[NSString stringWithUTF8String:item.m_value.c_str()] forKey:name]; + } + } + + std::vector albumArt; + + if ( metadata.get_bitmap( albumArt ) ) + { + [dict setObject:[NSData dataWithBytes:&albumArt[0] length:albumArt.size()] forKey:@"albumArt"]; + } + + return dict; +} + +@end