Added MIDI container processor and metadata reader

CQTexperiment
Chris Moeller 2013-10-16 08:59:44 -07:00
parent 870357a385
commit 711da5fe24
8 changed files with 262 additions and 2 deletions

View File

@ -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();

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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