Added MIDI container processor and metadata reader
parent
870357a385
commit
711da5fe24
|
@ -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<uint8_t> & p_out )
|
||||
{
|
||||
p_out = m_bitmap;
|
||||
return p_out.size() != 0;
|
||||
}
|
||||
|
||||
void midi_meta_data::assign_bitmap( std::vector<uint8_t>::const_iterator const& begin, std::vector<uint8_t>::const_iterator const& end )
|
||||
{
|
||||
m_bitmap.assign( begin, end );
|
||||
}
|
||||
|
||||
std::size_t midi_meta_data::get_count() const
|
||||
{
|
||||
return m_data.size();
|
||||
|
|
|
@ -119,7 +119,8 @@ struct midi_meta_data_item
|
|||
class midi_meta_data
|
||||
{
|
||||
std::vector<midi_meta_data_item> m_data;
|
||||
|
||||
std::vector<uint8_t> 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<uint8_t> & p_out );
|
||||
|
||||
void assign_bitmap( std::vector<uint8_t>::const_iterator const& begin, std::vector<uint8_t>::const_iterator const& end );
|
||||
|
||||
std::size_t get_count() const;
|
||||
|
||||
const midi_meta_data_item & operator [] ( std::size_t p_index ) const;
|
||||
|
|
|
@ -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<uint8_t> 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<uint8_t> 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;
|
||||
}
|
||||
|
|
|
@ -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 = "<group>"; };
|
||||
83B06723180D714F008E3612 /* Plugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Plugin.h; path = ../../../Audio/Plugin.h; sourceTree = "<group>"; };
|
||||
83B06724180D792E008E3612 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../../Utils/Logging.h; sourceTree = "<group>"; };
|
||||
83C35700180EDB74007E9DF0 /* MIDIContainer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MIDIContainer.mm; sourceTree = "<group>"; };
|
||||
83C35701180EDB74007E9DF0 /* MIDIContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIContainer.h; sourceTree = "<group>"; };
|
||||
83C35703180EDD1C007E9DF0 /* MIDIMetadataReader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MIDIMetadataReader.mm; sourceTree = "<group>"; };
|
||||
83C35704180EDD1C007E9DF0 /* MIDIMetadataReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIMetadataReader.h; sourceTree = "<group>"; };
|
||||
/* 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;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// MIDIContainer.h
|
||||
// MIDI
|
||||
//
|
||||
// Created by Christopher Snowhill on 10/16/13.
|
||||
// Copyright 2013 __NoWork, Inc__. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "Plugin.h"
|
||||
|
||||
@interface MIDIContainer : NSObject <CogContainer> {
|
||||
|
||||
}
|
||||
|
||||
@end
|
|
@ -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 <midi_processing/midi_processor.h>
|
||||
|
||||
@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<CogSource> 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<uint8_t> 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
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// MIDIMetadataReader.h
|
||||
// MIDI
|
||||
//
|
||||
// Created by Christopher Snowhill on 10/16/13.
|
||||
// Copyright 2013 __NoWork, Inc__. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "Plugin.h"
|
||||
|
||||
@interface MIDIMetadataReader : NSObject <CogMetadataReader> {
|
||||
|
||||
}
|
||||
|
||||
@end
|
|
@ -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 <midi_processing/midi_processor.h>
|
||||
|
||||
@implementation MIDIMetadataReader
|
||||
|
||||
+ (NSArray *)fileTypes
|
||||
{
|
||||
return [MIDIDecoder fileTypes];
|
||||
}
|
||||
|
||||
+ (NSArray *)mimeTypes
|
||||
{
|
||||
return [MIDIDecoder mimeTypes];
|
||||
}
|
||||
|
||||
+ (NSDictionary *)metadataForURL:(NSURL *)url
|
||||
{
|
||||
id audioSourceClass = NSClassFromString(@"AudioSource");
|
||||
id<CogSource> 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<uint8_t> 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<uint8_t> albumArt;
|
||||
|
||||
if ( metadata.get_bitmap( albumArt ) )
|
||||
{
|
||||
[dict setObject:[NSData dataWithBytes:&albumArt[0] length:albumArt.size()] forKey:@"albumArt"];
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in New Issue