/[svn]/libgig/trunk/src/Akai.cpp
ViewVC logotype

Annotation of /libgig/trunk/src/Akai.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2573 - (hide annotations) (download)
Thu May 22 15:17:09 2014 UTC (10 years ago) by schoenebeck
File size: 56278 byte(s)
* Akai: Fixed Mac OSX support so that the Akai lib files and tools
  compile without any exotic third party libraries.

1 schoenebeck 2572 /*
2     libakai - C++ cross-platform akai sample disk reader
3     Copyright (C) 2002-2003 Sébastien Métrot
4    
5     Linux port by Christian Schoenebeck <cuse@users.sourceforge.net> 2003-2014
6    
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Lesser General Public
9     License as published by the Free Software Foundation; either
10     version 2.1 of the License, or (at your option) any later version.
11    
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15     Lesser General Public License for more details.
16    
17     You should have received a copy of the GNU Lesser General Public
18     License along with this library; if not, write to the Free Software
19     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20     */
21     // Akai.cpp
22    
23     #include "Akai.h"
24    
25     #include <stdio.h>
26     #include <string.h>
27     #include <unistd.h>
28     #include <fcntl.h>
29     #include <sys/ioctl.h>
30 schoenebeck 2573 #if defined (__GNUC__) && (__GNUC__ >= 4)
31     #include <sys/disk.h>
32     #else
33     #include <dev/disk.h>
34     #endif
35 schoenebeck 2572 #include <sys/types.h>
36     #include <sys/stat.h>
37     #include <errno.h>
38     #include <paths.h>
39 schoenebeck 2573 #if defined(_CARBON_) || defined(__APPLE__)
40 schoenebeck 2572 #include <sys/param.h>
41     #include <IOKit/IOKitLib.h>
42     #include <IOKit/IOBSD.h>
43     #include <IOKit/storage/IOMediaBSDClient.h>
44     #include <IOKit/storage/IOMedia.h>
45     #include <IOKit/storage/IOCDMedia.h>
46     #include <IOKit/storage/IOCDTypes.h>
47     #include <CoreFoundation/CoreFoundation.h>
48     #endif
49    
50 schoenebeck 2573 #if defined(_CARBON_) || defined(__APPLE__)
51    
52     // These definitions were taken from mount_cd9660.c
53     // There are some similar definitions in IOCDTypes.h
54     // however there seems to be some dissagreement in
55     // the definition of CDTOC.length
56     struct _CDMSF {
57     u_char minute;
58     u_char second;
59     u_char frame;
60     };
61    
62     #define MSF_TO_LBA(msf) \
63     (((((msf).minute * 60UL) + (msf).second) * 75UL) + (msf).frame - 150)
64    
65     struct _CDTOC_Desc {
66     u_char session;
67     u_char ctrl_adr; /* typed to be machine and compiler independent */
68     u_char tno;
69     u_char point;
70     struct _CDMSF address;
71     u_char zero;
72     struct _CDMSF p;
73     };
74    
75     struct _CDTOC {
76     u_short length; /* in native cpu endian */
77     u_char first_session;
78     u_char last_session;
79     struct _CDTOC_Desc trackdesc[1];
80     };
81    
82     // Most of the following Mac CDROM IO functions were taken from Apple's IOKit
83     // examples (BSD style license). ReadTOC() function was taken from the Bochs x86
84     // Emulator (LGPL). Most probably they have taken it however also from some
85     // other BSD style licensed example code as well ...
86    
87     // Returns an iterator across all CD media (class IOCDMedia). Caller is responsible for releasing
88     // the iterator when iteration is complete.
89     static kern_return_t FindEjectableCDMedia(io_iterator_t *mediaIterator) {
90     kern_return_t kernResult;
91     CFMutableDictionaryRef classesToMatch;
92    
93     // CD media are instances of class kIOCDMediaClass
94     classesToMatch = IOServiceMatching(kIOCDMediaClass);
95     if (classesToMatch == NULL) {
96     printf("IOServiceMatching returned a NULL dictionary.\n");
97     } else {
98     CFDictionarySetValue(classesToMatch, CFSTR(kIOMediaEjectableKey), kCFBooleanTrue);
99     // Each IOMedia object has a property with key kIOMediaEjectableKey which is true if the
100     // media is indeed ejectable. So add this property to the CFDictionary we're matching on.
101     }
102    
103     kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, mediaIterator);
104    
105     return kernResult;
106     }
107    
108     // Given an iterator across a set of CD media, return the BSD path to the
109     // next one. If no CD media was found the path name is set to an empty string.
110     static kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize) {
111     io_object_t nextMedia;
112     kern_return_t kernResult = KERN_FAILURE;
113    
114     *bsdPath = '\0';
115    
116     nextMedia = IOIteratorNext(mediaIterator);
117     if (nextMedia) {
118     CFTypeRef bsdPathAsCFString;
119    
120     bsdPathAsCFString = IORegistryEntryCreateCFProperty(nextMedia,
121     CFSTR(kIOBSDNameKey),
122     kCFAllocatorDefault,
123     0);
124     if (bsdPathAsCFString) {
125     strlcpy(bsdPath, _PATH_DEV, maxPathSize);
126    
127     // Add "r" before the BSD node name from the I/O Registry to specify the raw disk
128     // node. The raw disk nodes receive I/O requests directly and do not go through
129     // the buffer cache.
130    
131     strlcat(bsdPath, "r", maxPathSize);
132    
133     size_t devPathLength = strlen(bsdPath);
134    
135     if (CFStringGetCString((CFStringRef)bsdPathAsCFString,
136     bsdPath + devPathLength,
137     maxPathSize - devPathLength,
138     kCFStringEncodingUTF8)) {
139     printf("BSD path: %s\n", bsdPath);
140     kernResult = KERN_SUCCESS;
141     }
142    
143     CFRelease(bsdPathAsCFString);
144     }
145    
146     IOObjectRelease(nextMedia);
147     }
148    
149     return kernResult;
150     }
151    
152     // Given the path to a CD drive, open the drive.
153     // Return the file descriptor associated with the device.
154     static int OpenDrive(const char *bsdPath) {
155     int fileDescriptor;
156    
157     // This open() call will fail with a permissions error if the sample has been changed to
158     // look for non-removable media. This is because device nodes for fixed-media devices are
159     // owned by root instead of the current console user.
160    
161     fileDescriptor = open(bsdPath, O_RDONLY);
162    
163     if (fileDescriptor == -1) {
164     printf("Error opening device %s: ", bsdPath);
165     perror(NULL);
166     }
167    
168     return fileDescriptor;
169     }
170    
171     // Given the file descriptor for a whole-media CD device, read a sector from the drive.
172     // Return true if successful, otherwise false.
173     static Boolean ReadSector(int fileDescriptor) {
174     char *buffer;
175     ssize_t numBytes;
176     u_int32_t blockSize;
177    
178     // This ioctl call retrieves the preferred block size for the media. It is functionally
179     // equivalent to getting the value of the whole media object's "Preferred Block Size"
180     // property from the IORegistry.
181     if (ioctl(fileDescriptor, DKIOCGETBLOCKSIZE, &blockSize)) {
182     perror("Error getting preferred block size");
183    
184     // Set a reasonable default if we can't get the actual preferred block size. A real
185     // app would probably want to bail at this point.
186     blockSize = kCDSectorSizeCDDA;
187     }
188    
189     printf("Media has block size of %d bytes.\n", blockSize);
190    
191     // Allocate a buffer of the preferred block size. In a real application, performance
192     // can be improved by reading as many blocks at once as you can.
193     buffer = (char*) malloc(blockSize);
194    
195     // Do the read. Note that we use read() here, not fread(), since this is a raw device
196     // node.
197     numBytes = read(fileDescriptor, buffer, blockSize);
198    
199     // Free our buffer. Of course, a real app would do something useful with the data first.
200     free(buffer);
201    
202     return numBytes == blockSize ? true : false;
203     }
204    
205     // Given the file descriptor for a device, close that device.
206     static void CloseDrive(int fileDescriptor) {
207     close(fileDescriptor);
208     }
209    
210     // path is the BSD path to a raw device such as /dev/rdisk1
211     static struct _CDTOC * ReadTOC(const char *devpath) {
212     struct _CDTOC * toc_p = NULL;
213     io_iterator_t iterator = 0;
214     io_registry_entry_t service = 0;
215     CFDictionaryRef properties = 0;
216     CFDataRef data = 0;
217     mach_port_t port = 0;
218     char *devname;
219    
220     if ((devname = strrchr(devpath, '/')) != NULL) {
221     ++devname;
222     } else {
223     devname = (char *) devpath;
224     }
225    
226     if (IOMasterPort(bootstrap_port, &port) != KERN_SUCCESS) {
227     fprintf(stderr, "IOMasterPort failed\n");
228     goto Exit;
229     }
230    
231     if (IOServiceGetMatchingServices(port, IOBSDNameMatching(port, 0, devname),
232     &iterator) != KERN_SUCCESS) {
233     fprintf(stderr, "IOServiceGetMatchingServices failed\n");
234     goto Exit;
235     }
236    
237     service = IOIteratorNext(iterator);
238    
239     IOObjectRelease(iterator);
240    
241     iterator = 0;
242    
243     while (service && !IOObjectConformsTo(service, "IOCDMedia")) {
244     if (IORegistryEntryGetParentIterator(service, kIOServicePlane,
245     &iterator) != KERN_SUCCESS)
246     {
247     fprintf(stderr, "IORegistryEntryGetParentIterator failed\n");
248     goto Exit;
249     }
250    
251     IOObjectRelease(service);
252     service = IOIteratorNext(iterator);
253     IOObjectRelease(iterator);
254     }
255    
256     if (!service) {
257     fprintf(stderr, "CD media not found\n");
258     goto Exit;
259     }
260    
261     if (IORegistryEntryCreateCFProperties(service, (__CFDictionary **) &properties,
262     kCFAllocatorDefault,
263     kNilOptions) != KERN_SUCCESS)
264     {
265     fprintf(stderr, "IORegistryEntryGetParentIterator failed\n");
266     goto Exit;
267     }
268    
269     data = (CFDataRef) CFDictionaryGetValue(properties, CFSTR(kIOCDMediaTOCKey));
270     if (data == NULL) {
271     fprintf(stderr, "CFDictionaryGetValue failed\n");
272     goto Exit;
273     } else {
274     CFRange range;
275     CFIndex buflen;
276    
277     buflen = CFDataGetLength(data) + 1;
278     range = CFRangeMake(0, buflen);
279     toc_p = (struct _CDTOC *) malloc(buflen);
280     if (toc_p == NULL) {
281     fprintf(stderr, "Out of memory\n");
282     goto Exit;
283     } else {
284     CFDataGetBytes(data, range, (unsigned char *) toc_p);
285     }
286    
287     /*
288     fprintf(stderr, "Table of contents\n length %d first %d last %d\n",
289     toc_p->length, toc_p->first_session, toc_p->last_session);
290     */
291    
292     CFRelease(properties);
293     }
294    
295     Exit:
296    
297     if (service) {
298     IOObjectRelease(service);
299     }
300    
301     return toc_p;
302     }
303    
304     #endif // defined(_CARBON_) || defined(__APPLE__)
305    
306 schoenebeck 2572 //////////////////////////////////
307     // AkaiSample:
308     AkaiSample::AkaiSample(DiskImage* pDisk, AkaiVolume* pParent, const AkaiDirEntry& DirEntry)
309     : AkaiDiskElement(pDisk->GetPos())
310     {
311     mpParent = pParent;
312     mpDisk = pDisk;
313     mDirEntry = DirEntry;
314     mpSamples = NULL;
315     mHeaderOK = false;
316     mPos = 0;
317    
318     //
319     LoadHeader();
320     }
321    
322     AkaiSample::~AkaiSample()
323     {
324     if (mpSamples)
325     free(mpSamples);
326     }
327    
328     AkaiDirEntry AkaiSample::GetDirEntry()
329     {
330     return mDirEntry;
331     }
332    
333     bool AkaiSample::LoadSampleData()
334     {
335     if (!LoadHeader())
336     return false;
337     if (mpSamples)
338     return true;
339    
340     mpDisk->SetPos(mImageOffset);
341     mpSamples = (int16_t*) malloc(mNumberOfSamples * sizeof(int16_t));
342     if (!mpSamples)
343     return false;
344    
345     mpDisk->ReadInt16((uint16_t*)mpSamples, mNumberOfSamples);
346     return true;
347     }
348    
349     void AkaiSample::ReleaseSampleData()
350     {
351     if (!mpSamples)
352     return;
353     free(mpSamples);
354     mpSamples = NULL;
355     }
356    
357     int AkaiSample::SetPos(int Where, akai_stream_whence_t Whence)
358     {
359     if (!mHeaderOK) return -1;
360     int w = Where;
361     switch (Whence)
362     {
363     case akai_stream_start:
364     mPos = w;
365     break;
366     /*case eStreamRewind:
367     w = -w;*/
368     case akai_stream_curpos:
369     mPos += w;
370     break;
371     case akai_stream_end:
372     mPos = mNumberOfSamples - w;
373     break;
374     }
375     if (mPos > mNumberOfSamples) mPos = mNumberOfSamples;
376     if (mPos < 0) mPos = 0;
377     return mPos;
378     }
379    
380     int AkaiSample::Read(void* pBuffer, uint SampleCount)
381     {
382     if (!mHeaderOK) return 0;
383    
384     if (mPos + SampleCount > mNumberOfSamples) SampleCount = mNumberOfSamples - mPos;
385    
386     mpDisk->SetPos(mImageOffset + mPos * 2); // FIXME: assumes 16 bit sample depth
387     mpDisk->ReadInt16((uint16_t*)pBuffer, SampleCount);
388     return SampleCount;
389     }
390    
391     bool AkaiSample::LoadHeader()
392     {
393     if (mHeaderOK)
394     return true;
395    
396     mpDisk->SetPos(mpParent->GetParent()->GetOffset() + mDirEntry.mStart * AKAI_BLOCK_SIZE );
397    
398     // Length Format Description
399     // --------------------------------------------------------------
400     // 1 3
401     if (mpDisk->ReadInt8() != AKAI_SAMPLE_ID)
402     return false;
403     // 1 Not important: 0 for 22050Hz, 1 for 44100Hz
404     mpDisk->ReadInt8();
405     // 1 unsigned MIDI root note (C3=60)
406     mMidiRootNote = mpDisk->ReadInt8();
407     // 12 AKAII Filename
408     char buffer[13];
409     mpDisk->Read(buffer, 12, 1);
410     AkaiToAscii(buffer, 12);
411     mName = buffer;
412    
413     // 1 128
414     mpDisk->ReadInt8();
415     // 1 unsigned Number of active loops
416     mActiveLoops = mpDisk->ReadInt8();
417     // 1 unsigned char First active loop (0 for none)
418     mFirstActiveLoop = mpDisk->ReadInt8();
419     // 1 0
420     mpDisk->ReadInt8();
421     // 1 unsigned Loop mode: 0=in release 1=until release
422     // 2=none 3=play to end
423     mLoopMode = mpDisk->ReadInt8();
424     // 1 signed Cents tune -50...+50
425     mTuneCents = mpDisk->ReadInt8();
426     // 1 signed Semi tune -50...+50
427     mTuneSemitones = mpDisk->ReadInt8();
428     // 4 0,8,2,0
429     mpDisk->ReadInt8();
430     mpDisk->ReadInt8();
431     mpDisk->ReadInt8();
432     mpDisk->ReadInt8();
433     //
434     // 4 unsigned Number of sample words
435     mNumberOfSamples = mpDisk->ReadInt32();
436     // 4 unsigned Start marker
437     mStartMarker = mpDisk->ReadInt32();
438     // 4 unsigned End marker
439     mEndMarker = mpDisk->ReadInt32();
440     //
441     // 4 unsigned Loop 1 marker
442     // 2 unsigned Loop 1 fine length (65536ths)
443     // 4 unsigned Loop 1 coarse length (words)
444     // 2 unsigned Loop 1 time (msec. or 9999=infinite)
445     //
446     // 84 [as above] Loops 2 to 8
447     //
448     int i;
449     for (i=0; i<8; i++)
450     mLoops[i].Load(mpDisk);
451     // 4 0,0,255,255
452     mpDisk->ReadInt32();
453     // 2 unsigned Sampling frequency
454     mSamplingFrequency = mpDisk->ReadInt16();
455     // 1 signed char Loop tune offset -50...+50
456     mLoopTuneOffset = mpDisk->ReadInt8();
457    
458     mImageOffset = mpParent->GetParent()->GetOffset() + mDirEntry.mStart * AKAI_BLOCK_SIZE + 150; // 150 is the size of the header
459    
460     //FIXME: no header validation yet implemented
461     return (mHeaderOK = true);
462     }
463    
464     bool AkaiSampleLoop::Load(DiskImage* pDisk)
465     {
466     // 4 unsigned Loop 1 marker
467     mMarker = pDisk->ReadInt32();
468     // 2 unsigned Loop 1 fine length (65536ths)
469     mFineLength = pDisk->ReadInt16();
470     // 4 unsigned Loop 1 coarse length (words)
471     mCoarseLength = pDisk->ReadInt32();
472     // 2 unsigned Loop 1 time (msec. or 9999=infinite)
473     mTime = pDisk->ReadInt16();
474     return true;
475     }
476    
477     //////////////////////////////////
478     // AkaiProgram:
479     AkaiProgram::AkaiProgram(DiskImage* pDisk, AkaiVolume* pParent, const AkaiDirEntry& DirEntry)
480     : AkaiDiskElement(pDisk->GetPos())
481     {
482     mpParent = pParent;
483     mpDisk = pDisk;
484     mDirEntry = DirEntry;
485     mpKeygroups = NULL;
486     Load();
487     }
488    
489     AkaiProgram::~AkaiProgram()
490     {
491     if (mpKeygroups)
492     delete[] mpKeygroups;
493     }
494    
495     AkaiDirEntry AkaiProgram::GetDirEntry()
496     {
497     return mDirEntry;
498     }
499    
500     bool AkaiProgram::Load()
501     {
502     // Read each of the program parameters
503     uint temppos = mpDisk->GetPos();
504     mpDisk->SetPos(mpParent->GetParent()->GetOffset() + mDirEntry.mStart * AKAI_BLOCK_SIZE );
505     // byte description default range/comments
506     // ---------------------------------------------------------------------------
507     // 1 program ID 1
508     uint8_t ProgID = mpDisk->ReadInt8();
509     if (ProgID != AKAI_PROGRAM_ID)
510     {
511     mpDisk->SetPos(temppos);
512     return false;
513     }
514     // 2-3 first keygroup address 150,0
515     uint16_t KeygroupAddress = mpDisk->ReadInt16();
516     // 4-15 program name 10,10,10... AKAII character set
517     char buffer[13];
518     mpDisk->Read(buffer, 12, 1);
519     AkaiToAscii(buffer, 12);
520     mName = buffer;
521     // 16 MIDI program number 0 0..127
522     mMidiProgramNumber = mpDisk->ReadInt8();
523     // 17 MIDI channel 0 0..15, 255=OMNI
524     mMidiChannel = mpDisk->ReadInt8();
525     // 18 polyphony 15 1..16
526     mPolyphony = mpDisk->ReadInt8();
527     // 19 priority 1 0=LOW 1=NORM 2=HIGH 3=HOLD
528     mPriority = mpDisk->ReadInt8();
529     // 20 low key 24 24..127
530     mLowKey = mpDisk->ReadInt8();
531     // 21 high key 127 24..127
532     mHighKey = mpDisk->ReadInt8();
533     // 22 octave shift 0 -2..2
534     mOctaveShift = mpDisk->ReadInt8();
535     // 23 aux output select 255 0..7, 255=OFF
536     mAuxOutputSelect = mpDisk->ReadInt8();
537     // 24 mix output level 99 0..99
538     mMixOutputSelect = mpDisk->ReadInt8();
539     // 25 mix output pan 0 -50..50
540     mMixPan = mpDisk->ReadInt8();
541     // 26 volume 80 0..99
542     mVolume = mpDisk->ReadInt8();
543     // 27 vel>volume 20 -50..50
544     mVelocityToVolume = mpDisk->ReadInt8();
545     // 28 key>volume 0 -50..50
546     mKeyToVolume = mpDisk->ReadInt8();
547     // 29 pres>volume 0 -50..50
548     mPressureToVolume = mpDisk->ReadInt8();
549     // 30 pan lfo rate 50 0..99
550     mPanLFORate = mpDisk->ReadInt8();
551     // 31 pan lfo depth 0 0..99
552     mPanLFODepth = mpDisk->ReadInt8();
553     // 32 pan lfo delay 0 0..99
554     mPanLFODelay = mpDisk->ReadInt8();
555     // 33 key>pan 0 -50..50
556     mKeyToPan = mpDisk->ReadInt8();
557     // 34 lfo rate 50 0..99
558     mLFORate = mpDisk->ReadInt8();
559     // 35 lfo depth 0 0..99
560     mLFODepth = mpDisk->ReadInt8();
561     // 36 lfo delay 0 0..99
562     mLFODelay = mpDisk->ReadInt8();
563     // 37 mod>lfo depth 30 0..99
564     mModulationToLFODepth = mpDisk->ReadInt8();
565     // 38 pres>lfo depth 0 0..99
566     mPressureToLFODepth = mpDisk->ReadInt8();
567     // 39 vel>lfo depth 0 0..99
568     mVelocityToLFODepth = mpDisk->ReadInt8();
569     // 40 bend>pitch 2 0..12 semitones
570     mBendToPitch = mpDisk->ReadInt8();
571     // 41 pres>pitch 0 -12..12 semitones
572     mPressureToPitch = mpDisk->ReadInt8();
573     // 42 keygroup crossfade 0 0=OFF 1=ON
574     mKeygroupCrossfade = mpDisk->ReadInt8()?true:false;
575     // 43 number of keygroups 1 1..99
576     mNumberOfKeygroups = mpDisk->ReadInt8();
577     // 44 (internal use) 0 program number
578     mpDisk->ReadInt8();
579     // 45-56 key temperament C,C#,D... 0 -25..25 cents
580     uint i;
581     for (i = 0; i<11; i++)
582     mKeyTemperament[i] = mpDisk->ReadInt8();
583     // 57 fx output 0 0=OFF 1=ON
584     mFXOutput = mpDisk->ReadInt8()?true:false;
585     // 58 mod>pan 0 -50..50
586     mModulationToPan = mpDisk->ReadInt8();
587     // 59 stereo coherence 0 0=OFF 1=ON
588     mStereoCoherence = mpDisk->ReadInt8()?true:false;
589     // 60 lfo desync 1 0=OFF 1=ON
590     mLFODesync = mpDisk->ReadInt8()?true:false;
591     // 61 pitch law 0 0=LINEAR
592     mPitchLaw = mpDisk->ReadInt8();
593     // 62 voice re-assign 0 0=OLDEST 1=QUIETEST
594     mVoiceReassign = mpDisk->ReadInt8();
595     // 63 softped>volume 10 0..99
596     mSoftpedToVolume = mpDisk->ReadInt8();
597     // 64 softped>attack 10 0..99
598     mSoftpedToAttack = mpDisk->ReadInt8();
599     // 65 softped>filt 10 0..99
600     mSoftpedToFilter = mpDisk->ReadInt8();
601     // 66 tune cents 0 -128..127 (-50..50 cents)
602     mSoftpedToTuneCents = mpDisk->ReadInt8();
603     // 67 tune semitones 0 -50..50
604     mSoftpedToTuneSemitones = mpDisk->ReadInt8();
605     // 68 key>lfo rate 0 -50..50
606     mKeyToLFORate = mpDisk->ReadInt8();
607     // 69 key>lfo depth 0 -50..50
608     mKeyToLFODepth = mpDisk->ReadInt8();
609     // 70 key>lfo delay 0 -50..50
610     mKeyToLFODelay = mpDisk->ReadInt8();
611     // 71 voice output scale 1 0=-6dB 1=0dB 2=+12dB
612     mVoiceOutputScale = mpDisk->ReadInt8();
613     // 72 stereo output scale 0 0=0dB 1=+6dB
614     mStereoOutputScale = mpDisk->ReadInt8();
615     // 73-150 (not used)
616    
617     // Read the key groups:
618     if (mpKeygroups)
619     delete[] mpKeygroups;
620     mpKeygroups = new AkaiKeygroup[mNumberOfKeygroups];
621     for (i = 0; i < mNumberOfKeygroups; i++)
622     {
623     // Go where the key group is on the disk:
624     mpDisk->SetPos(mpParent->GetParent()->GetOffset() + mDirEntry.mStart * AKAI_BLOCK_SIZE + 150 * (i+1));
625     if (!mpKeygroups[i].Load(mpDisk))
626     {
627     mpDisk->SetPos(temppos);
628     return false;
629     }
630     }
631    
632     mpDisk->SetPos(temppos);
633     return true;
634     }
635    
636     uint AkaiProgram::ListSamples(std::list<String>& rSamples)
637     {
638     return 0;
639     }
640    
641     AkaiSample* AkaiProgram::GetSample(uint Index)
642     {
643     return NULL;
644     }
645    
646     AkaiSample* AkaiProgram::GetSample(const String& rName)
647     {
648     return NULL;
649     }
650    
651     // AkaiKeygroup:
652     bool AkaiKeygroup::Load(DiskImage* pDisk)
653     {
654     uint i;
655     // byte description default range/comments
656     // ---------------------------------------------------------------------------
657     // 1 keygroup ID 2
658     if (pDisk->ReadInt8() != AKAI_KEYGROUP_ID)
659     return false;
660     // 2-3 next keygroup address 44,1 300,450,600,750.. (16-bit)
661     uint16_t NextKeygroupAddress = pDisk->ReadInt16();
662     // 4 low key 24 24..127
663     mLowKey = pDisk->ReadInt8();
664     // 5 high key 127 24..127
665     mHighKey = pDisk->ReadInt8();
666     // 6 tune cents 0 -128..127 (-50..50 cents)
667     mTuneCents = pDisk->ReadInt8();
668     // 7 tune semitones 0 -50..50
669     mTuneSemitones = pDisk->ReadInt8();
670     // 8 filter 99 0..99
671     mFilter = pDisk->ReadInt8();
672     // 9 key>filter 12 0..24 semitone/oct
673     mKeyToFilter = pDisk->ReadInt8();
674     // 10 vel>filt 0 -50..50
675     mVelocityToFilter = pDisk->ReadInt8();
676     // 11 pres>filt 0 -50..50
677     mPressureToFilter = pDisk->ReadInt8();
678     // 12 env2>filt 0 -50..50
679     mEnveloppe2ToFilter = pDisk->ReadInt8();
680    
681     // 13 env1 attack 0 0..99
682     // 14 env1 decay 30 0..99
683     // 15 env1 sustain 99 0..99
684     // 16 env1 release 45 0..99
685     // 17 env1 vel>attack 0 -50..50
686     // 18 env1 vel>release 0 -50..50
687     // 19 env1 offvel>release 0 -50..50
688     // 20 env1 key>dec&rel 0 -50..50
689     // 21 env2 attack 0 0..99
690     // 22 env2 decay 50 0..99
691     // 23 env2 sustain 99 0..99
692     // 24 env2 release 45 0..99
693     // 25 env2 vel>attack 0 -50..50
694     // 26 env2 vel>release 0 -50..50
695     // 27 env2 offvel>release 0 -50..50
696     // 28 env2 key>dec&rel 0 -50..50
697     for (i=0; i<2; i++)
698     mEnveloppes[i].Load(pDisk);
699    
700     // 29 vel>env2>filter 0 -50..50
701     mVelocityToEnveloppe2ToFilter = pDisk->ReadInt8();
702     // 30 env2>pitch 0 -50..50
703     mEnveloppe2ToPitch = pDisk->ReadInt8();
704     // 31 vel zone crossfade 1 0=OFF 1=ON
705     mVelocityZoneCrossfade = pDisk->ReadInt8()?true:false;
706     // 32 vel zones used 4
707     mVelocityZoneUsed = pDisk->ReadInt8();
708     // 33 (internal use) 255
709     pDisk->ReadInt8();
710     // 34 (internal use) 255
711     pDisk->ReadInt8();
712     //
713    
714     // 35-46 sample 1 name 10,10,10... AKAII character set
715     // 47 low vel 0 0..127
716     // 48 high vel 127 0..127
717     // 49 tune cents 0 -128..127 (-50..50 cents)
718     // 50 tune semitones 0 -50..50
719     // 51 loudness 0 -50..+50
720     // 52 filter 0 -50..+50
721     // 53 pan 0 -50..+50
722     // 54 loop mode 0 0=AS_SAMPLE 1=LOOP_IN_REL
723     // 2=LOOP_UNTIL_REL 3=NO_LOOP
724     // 4=PLAY_TO_END
725     // 55 (internal use) 255
726     // 56 (internal use) 255
727     // 57-58 (internal use) 44,1
728     //
729     // 59-82 [repeat 35-58 for sample 2]
730     //
731     // 83-106 [repeat 35-58 for sample 3]
732     //
733     // 107-130 [repeat 35-58 for sample 4]
734     //
735     for (i=0; i<4; i++)
736     mSamples[i].Load(pDisk);
737    
738     // 131 beat detune 0 -50..50
739     mBeatDetune = pDisk->ReadInt8();
740     // 132 hold attack until loop 0 0=OFF 1=ON
741     mHoldAttackUntilLoop = pDisk->ReadInt8()?true:false;
742     // 133-136 sample 1-4 key tracking 0 0=TRACK 1=FIXED
743     for (i=0; i<4; i++)
744     mSampleKeyTracking[i] = pDisk->ReadInt8()?true:false;
745     // 137-140 sample 1-4 aux out offset 0 0..7
746     for (i=0; i<4; i++)
747     mSampleAuxOutOffset[i] = pDisk->ReadInt8();
748     // 141-148 vel>sample start 0 -9999..9999 (16-bit signed)
749     for (i=0; i<4; i++)
750     mVelocityToSampleStart[i] = pDisk->ReadInt8();
751     // 149 vel>volume offset 0 -50..50
752     for (i=0; i<4; i++)
753     mVelocityToVolumeOffset[i] = pDisk->ReadInt8();
754     // 150 (not used)
755    
756     return true;
757     }
758    
759     bool AkaiEnveloppe::Load(DiskImage* pDisk)
760     {
761     // 13 env1 attack 0 0..99
762     mAttack = pDisk->ReadInt8();
763     // 14 env1 decay 30 0..99
764     mDecay = pDisk->ReadInt8();
765     // 15 env1 sustain 99 0..99
766     mSustain = pDisk->ReadInt8();
767     // 16 env1 release 45 0..99
768     mRelease = pDisk->ReadInt8();
769     // 17 env1 vel>attack 0 -50..50
770     mVelocityToAttack = pDisk->ReadInt8();
771     // 18 env1 vel>release 0 -50..50
772     mVelocityToRelease = pDisk->ReadInt8();
773     // 19 env1 offvel>release 0 -50..50
774     mOffVelocityToRelease = pDisk->ReadInt8();
775     // 20 env1 key>dec&rel 0 -50..50
776     mKeyToDecayAndRelease = pDisk->ReadInt8();
777     return true;
778     }
779    
780     bool AkaiKeygroupSample::Load(DiskImage* pDisk)
781     {
782     // 35-46 sample 1 name 10,10,10... AKAII character set
783     char buffer[13];
784     pDisk->Read(buffer, 12, 1);
785     AkaiToAscii(buffer, 12);
786     mName = buffer;
787    
788     // 47 low vel 0 0..127
789     mLowLevel = pDisk->ReadInt8();
790     // 48 high vel 127 0..127
791     uint8_t mHighLevel = pDisk->ReadInt8();
792     // 49 tune cents 0 -128..127 (-50..50 cents)
793     int8_t mTuneCents = pDisk->ReadInt8();
794     // 50 tune semitones 0 -50..50
795     int8_t mTuneSemitones = pDisk->ReadInt8();
796     // 51 loudness 0 -50..+50
797     int8_t mLoudness = pDisk->ReadInt8();
798     // 52 filter 0 -50..+50
799     int8_t mFilter = pDisk->ReadInt8();
800     // 53 pan 0 -50..+50
801     int8_t mPan = pDisk->ReadInt8();
802     // 54 loop mode 0 0=AS_SAMPLE 1=LOOP_IN_REL
803     // 2=LOOP_UNTIL_REL 3=NO_LOOP
804     // 4=PLAY_TO_END
805     uint8_t mLoopMode = pDisk->ReadInt8();
806     // 55 (internal use) 255
807     pDisk->ReadInt8();
808     // 56 (internal use) 255
809     pDisk->ReadInt8();
810     // 57-58 (internal use) 44,1
811     pDisk->ReadInt16();
812     //
813    
814     return true;
815     }
816    
817     //////////////////////////////////
818     // AkaiVolume:
819     AkaiVolume::AkaiVolume(DiskImage* pDisk, AkaiPartition* pParent, const AkaiDirEntry& DirEntry)
820     : AkaiDiskElement()
821     {
822     mpDisk = pDisk;
823     mpParent = pParent;
824     mDirEntry = DirEntry;
825    
826     if (mDirEntry.mType != AKAI_TYPE_DIR_S1000 && mDirEntry.mType != AKAI_TYPE_DIR_S3000)
827     {
828     printf("Creating Unknown Volume type! %d\n",mDirEntry.mType);
829 schoenebeck 2573 #ifdef WIN32
830 schoenebeck 2572 OutputDebugString("Creating Unknown Volume type!\n");
831     #endif
832     }
833     }
834    
835     AkaiVolume::~AkaiVolume()
836     {
837     {
838     std::list<AkaiProgram*>::iterator it;
839     std::list<AkaiProgram*>::iterator end = mpPrograms.end();
840     for (it = mpPrograms.begin(); it != end; it++)
841     if (*it)
842     (*it)->Release();
843     }
844    
845     {
846     std::list<AkaiSample*>::iterator it;
847     std::list<AkaiSample*>::iterator end = mpSamples.end();
848     for (it = mpSamples.begin(); it != end; it++)
849     if (*it)
850     (*it)->Release();
851     }
852     }
853    
854     uint AkaiVolume::ReadDir()
855     {
856     uint i;
857     if (mpPrograms.empty())
858     {
859     uint maxfiles = ReadFAT(mpDisk, mpParent,mDirEntry.mStart) ? AKAI_MAX_FILE_ENTRIES_S1000 : AKAI_MAX_FILE_ENTRIES_S3000;
860     for (i = 0; i < maxfiles; i++)
861     {
862     AkaiDirEntry DirEntry;
863     ReadDirEntry(mpDisk, mpParent, DirEntry, mDirEntry.mStart, i);
864     DirEntry.mIndex = i;
865     if (DirEntry.mType == 'p')
866     {
867     AkaiProgram* pProgram = new AkaiProgram(mpDisk, this, DirEntry);
868     pProgram->Acquire();
869     mpPrograms.push_back(pProgram);
870     }
871     else if (DirEntry.mType == 's')
872     {
873     AkaiSample* pSample = new AkaiSample(mpDisk, this, DirEntry);
874     pSample->Acquire();
875     mpSamples.push_back(pSample);
876     }
877     }
878     }
879     return (uint)(mpPrograms.size() + mpSamples.size());
880     }
881    
882     uint AkaiVolume::ListPrograms(std::list<AkaiDirEntry>& rPrograms)
883     {
884     rPrograms.clear();
885     ReadDir();
886    
887     std::list<AkaiProgram*>::iterator it;
888     std::list<AkaiProgram*>::iterator end = mpPrograms.end();
889     for (it = mpPrograms.begin(); it != end; it++)
890     if (*it)
891     rPrograms.push_back((*it)->GetDirEntry());
892     return (uint)rPrograms.size();
893     }
894    
895     AkaiProgram* AkaiVolume::GetProgram(uint Index)
896     {
897     uint i = 0;
898    
899     if (mpPrograms.empty())
900     {
901     std::list<AkaiDirEntry> dummy;
902     ListPrograms(dummy);
903     }
904    
905     std::list<AkaiProgram*>::iterator it;
906     std::list<AkaiProgram*>::iterator end = mpPrograms.end();
907     for (it = mpPrograms.begin(); it != end; it++)
908     {
909     if (*it && i == Index)
910     {
911     (*it)->Acquire();
912     return *it;
913     }
914     i++;
915     }
916     return NULL;
917     }
918    
919     AkaiProgram* AkaiVolume::GetProgram(const String& rName)
920     {
921     if (mpPrograms.empty())
922     {
923     std::list<AkaiDirEntry> dummy;
924     ListPrograms(dummy);
925     }
926    
927     std::list<AkaiProgram*>::iterator it;
928     std::list<AkaiProgram*>::iterator end = mpPrograms.end();
929     for (it = mpPrograms.begin(); it != end; it++)
930     {
931     if (*it && rName == (*it)->GetDirEntry().mName)
932     {
933     (*it)->Acquire();
934     return *it;
935     }
936     }
937     return NULL;
938     }
939    
940     uint AkaiVolume::ListSamples(std::list<AkaiDirEntry>& rSamples)
941     {
942     rSamples.clear();
943     ReadDir();
944    
945     std::list<AkaiSample*>::iterator it;
946     std::list<AkaiSample*>::iterator end = mpSamples.end();
947     for (it = mpSamples.begin(); it != end; it++)
948     if (*it)
949     rSamples.push_back((*it)->GetDirEntry());
950     return (uint)rSamples.size();
951     }
952    
953     AkaiSample* AkaiVolume::GetSample(uint Index)
954     {
955     uint i = 0;
956    
957     if (mpSamples.empty())
958     {
959     std::list<AkaiDirEntry> dummy;
960     ListSamples(dummy);
961     }
962    
963     std::list<AkaiSample*>::iterator it;
964     std::list<AkaiSample*>::iterator end = mpSamples.end();
965     for (it = mpSamples.begin(); it != end; it++)
966     {
967     if (*it && i == Index)
968     {
969     (*it)->Acquire();
970     return *it;
971     }
972     i++;
973     }
974     return NULL;
975     }
976    
977     AkaiSample* AkaiVolume::GetSample(const String& rName)
978     {
979     if (mpSamples.empty())
980     {
981     std::list<AkaiDirEntry> dummy;
982     ListSamples(dummy);
983     }
984    
985     std::list<AkaiSample*>::iterator it;
986     std::list<AkaiSample*>::iterator end = mpSamples.end();
987     for (it = mpSamples.begin(); it != end; it++)
988     {
989     if (*it && rName == (*it)->GetDirEntry().mName)
990     {
991     (*it)->Acquire();
992     return *it;
993     }
994     }
995     return NULL;
996     }
997    
998     AkaiDirEntry AkaiVolume::GetDirEntry()
999     {
1000     return mDirEntry;
1001     }
1002    
1003     bool AkaiVolume::IsEmpty()
1004     {
1005     return ReadDir() == 0;
1006     }
1007    
1008    
1009     //////////////////////////////////
1010     // AkaiPartition:
1011     AkaiPartition::AkaiPartition(DiskImage* pDisk, AkaiDisk* pParent)
1012     {
1013     mpDisk = pDisk;
1014     mpParent = pParent;
1015     }
1016    
1017     AkaiPartition::~AkaiPartition()
1018     {
1019     std::list<AkaiVolume*>::iterator it;
1020     std::list<AkaiVolume*>::iterator end = mpVolumes.end();
1021     for (it = mpVolumes.begin(); it != end; it++)
1022     if (*it)
1023     (*it)->Release();
1024     }
1025    
1026     uint AkaiPartition::ListVolumes(std::list<AkaiDirEntry>& rVolumes)
1027     {
1028     rVolumes.clear();
1029     uint i;
1030     if (mpVolumes.empty())
1031     {
1032     for (i = 0; i < AKAI_MAX_DIR_ENTRIES; i++)
1033     {
1034     AkaiDirEntry DirEntry;
1035     ReadDirEntry(mpDisk, this, DirEntry, AKAI_ROOT_ENTRY_OFFSET, i);
1036     DirEntry.mIndex = i;
1037     if (DirEntry.mType == AKAI_TYPE_DIR_S1000 || DirEntry.mType == AKAI_TYPE_DIR_S3000)
1038     {
1039     AkaiVolume* pVolume = new AkaiVolume(mpDisk, this, DirEntry);
1040     pVolume->Acquire();
1041     if (!pVolume->IsEmpty())
1042     {
1043     mpVolumes.push_back(pVolume);
1044     rVolumes.push_back(DirEntry);
1045     }
1046     else
1047     pVolume->Release();
1048     }
1049     }
1050     }
1051     else
1052     {
1053     std::list<AkaiVolume*>::iterator it;
1054     std::list<AkaiVolume*>::iterator end = mpVolumes.end();
1055     for (it = mpVolumes.begin(); it != end; it++)
1056     if (*it)
1057     rVolumes.push_back((*it)->GetDirEntry());
1058     }
1059     return (uint)rVolumes.size();
1060     }
1061    
1062     AkaiVolume* AkaiPartition::GetVolume(uint Index)
1063     {
1064     uint i = 0;
1065    
1066     if (mpVolumes.empty())
1067     {
1068     std::list<AkaiDirEntry> dummy;
1069     ListVolumes(dummy);
1070     }
1071    
1072     std::list<AkaiVolume*>::iterator it;
1073     std::list<AkaiVolume*>::iterator end = mpVolumes.end();
1074     for (it = mpVolumes.begin(); it != end; it++)
1075     {
1076     if (*it && i == Index)
1077     {
1078     (*it)->Acquire();
1079     return *it;
1080     }
1081     i++;
1082     }
1083     return NULL;
1084     }
1085    
1086     AkaiVolume* AkaiPartition::GetVolume(const String& rName)
1087     {
1088     if (mpVolumes.empty())
1089     {
1090     std::list<AkaiDirEntry> dummy;
1091     ListVolumes(dummy);
1092     }
1093    
1094     std::list<AkaiVolume*>::iterator it;
1095     std::list<AkaiVolume*>::iterator end = mpVolumes.end();
1096     for (it = mpVolumes.begin(); it != end; it++)
1097     {
1098     if (*it && rName == (*it)->GetDirEntry().mName)
1099     {
1100     (*it)->Acquire();
1101     return *it;
1102     }
1103     }
1104     return NULL;
1105     }
1106    
1107     bool AkaiPartition::IsEmpty()
1108     {
1109     std::list<AkaiDirEntry> Volumes;
1110     return ListVolumes(Volumes) == 0;
1111     }
1112    
1113    
1114     //////////////////////////////////
1115     // AkaiDisk:
1116     AkaiDisk::AkaiDisk(DiskImage* pDisk)
1117     {
1118     mpDisk = pDisk;
1119     }
1120    
1121     AkaiDisk::~AkaiDisk()
1122     {
1123     std::list<AkaiPartition*>::iterator it;
1124     std::list<AkaiPartition*>::iterator end = mpPartitions.end();
1125     for (it = mpPartitions.begin(); it != end ; it++)
1126     if (*it)
1127     (*it)->Release();
1128     }
1129    
1130     uint AkaiDisk::GetPartitionCount()
1131     {
1132     if (!mpPartitions.empty())
1133     return (uint)mpPartitions.size();
1134    
1135     uint offset = 0;
1136     uint16_t size = 0;
1137     while (size != AKAI_PARTITION_END_MARK && size != 0x0fff && size != 0xffff
1138     && size<30720 && mpPartitions.size()<9)
1139     {
1140     // printf("size: %x\t",size);
1141     AkaiPartition* pPartition = new AkaiPartition(mpDisk,this);
1142     pPartition->Acquire();
1143     pPartition->SetOffset(offset);
1144    
1145     if (!pPartition->IsEmpty())
1146     {
1147     mpPartitions.push_back(pPartition); // Add this partitions' offset to the list.
1148     }
1149    
1150     mpDisk->SetPos(offset);
1151     if (!mpDisk->ReadInt16(&size))
1152     return (uint)mpPartitions.size();
1153     uint t = size;
1154     offset += AKAI_BLOCK_SIZE * t;
1155     // printf("new offset %d / size %d\n",offset,size);
1156     }
1157    
1158     return (uint)mpPartitions.size();
1159     }
1160    
1161     AkaiPartition* AkaiDisk::GetPartition(uint count)
1162     {
1163     std::list<AkaiPartition*>::iterator it;
1164     std::list<AkaiPartition*>::iterator end = mpPartitions.end();
1165     uint i = 0;
1166     for (i = 0, it = mpPartitions.begin(); it != end && i != count; i++) it++;
1167    
1168     if (i != count || it == end)
1169     return NULL;
1170    
1171     (*it)->Acquire();
1172     return *it;
1173     }
1174    
1175     /////////////////////////
1176     // AkaiDiskElement
1177    
1178     int AkaiDiskElement::ReadFAT(DiskImage* pDisk, AkaiPartition* pPartition, int block)
1179     {
1180     int16_t value = 0;
1181     pDisk->SetPos(pPartition->GetOffset()+AKAI_FAT_OFFSET + block*2);
1182     pDisk->Read(&value, 2,1);
1183     return value;
1184     }
1185    
1186    
1187     bool AkaiDiskElement::ReadDirEntry(DiskImage* pDisk, AkaiPartition* pPartition, AkaiDirEntry& rEntry, int block, int pos)
1188     {
1189     char buffer[13];
1190    
1191     if (block == AKAI_ROOT_ENTRY_OFFSET)
1192     {
1193     pDisk->SetPos(pPartition->GetOffset()+AKAI_DIR_ENTRY_OFFSET + pos * AKAI_DIR_ENTRY_SIZE);
1194     pDisk->Read(buffer, 12, 1);
1195     AkaiToAscii(buffer, 12);
1196     rEntry.mName = buffer;
1197    
1198     pDisk->ReadInt16(&rEntry.mType);
1199     pDisk->ReadInt16(&rEntry.mStart);
1200     rEntry.mSize = 0;
1201     return true;
1202     }
1203     else
1204     {
1205     if (pos < 341)
1206     {
1207     pDisk->SetPos(block * AKAI_BLOCK_SIZE + pos * AKAI_FILE_ENTRY_SIZE + pPartition->GetOffset());
1208     }
1209     else
1210     {
1211     int temp;
1212     temp = ReadFAT(pDisk, pPartition, block);
1213     pDisk->SetPos(pPartition->GetOffset()+temp * AKAI_BLOCK_SIZE + (pos - 341) * AKAI_FILE_ENTRY_SIZE);
1214     }
1215    
1216     pDisk->Read(buffer, 12, 1);
1217     AkaiToAscii(buffer, 12);
1218     rEntry.mName = buffer;
1219    
1220     uint8_t t1,t2,t3;
1221     pDisk->SetPos(4,akai_stream_curpos);
1222     pDisk->Read(&t1, 1,1);
1223     rEntry.mType = t1;
1224    
1225     pDisk->Read(&t1, 1,1);
1226     pDisk->Read(&t2, 1,1);
1227     pDisk->Read(&t3, 1,1);
1228     rEntry.mSize = (unsigned char)t1 | ((unsigned char)t2 <<8) | ((unsigned char)t3<<16);
1229    
1230     pDisk->ReadInt16(&rEntry.mStart,1);
1231     return true;
1232     } // not root block
1233     }
1234    
1235     void AkaiDiskElement::AkaiToAscii(char * buffer, int length)
1236     {
1237     int i;
1238    
1239     for (i = 0; i < length; i++)
1240     {
1241     if (buffer[i]>=0 && buffer[i]<=9)
1242     buffer[i] +=48;
1243     else if (buffer[i]==10)
1244     buffer[i] = 32;
1245     else if (buffer[i]>=11 && buffer[i]<=36)
1246     buffer[i] = 64+(buffer[i]-10);
1247     else
1248     buffer[i] = 32;
1249     }
1250     buffer[length] = '\0';
1251     while (length-- > 0 && buffer[length] == 32)
1252     {
1253     // This block intentionaly left blank :)
1254     }
1255     buffer[length+1] = '\0';
1256     }
1257    
1258    
1259     //////////////////////////////////
1260     // DiskImage:
1261    
1262     DiskImage::DiskImage(const char* path)
1263     {
1264     Init();
1265     OpenStream(path);
1266     }
1267    
1268     #ifdef _CARBON_
1269     kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator )
1270     {
1271     kern_return_t kernResult;
1272     mach_port_t masterPort;
1273     CFMutableDictionaryRef classesToMatch;
1274    
1275     kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort );
1276     if ( KERN_SUCCESS != kernResult )
1277     {
1278     printf( "IOMasterPort returned %d\n", kernResult );
1279     }
1280    
1281     classesToMatch = IOServiceMatching( kIOCDMediaClass );
1282     if ( classesToMatch == NULL )
1283     {
1284     printf( "IOServiceMatching returned a NULL dictionary.\n" );
1285     }
1286     else
1287     {
1288     CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue );
1289     }
1290    
1291     kernResult = IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator );
1292     if ( KERN_SUCCESS != kernResult )
1293     {
1294     printf( "IOServiceGetMatchingServices returned %d\n", kernResult );
1295     }
1296    
1297     return kernResult;
1298     }
1299    
1300     kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize )
1301     {
1302     io_object_t nextMedia;
1303     kern_return_t kernResult = KERN_FAILURE;
1304    
1305     *bsdPath = '\0';
1306    
1307     nextMedia = IOIteratorNext( mediaIterator );
1308     if ( nextMedia )
1309     {
1310     CFTypeRef bsdPathAsCFString;
1311    
1312     bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia,
1313     CFSTR( kIOBSDNameKey ),
1314     kCFAllocatorDefault,
1315     0 );
1316     if ( bsdPathAsCFString )
1317     {
1318     size_t devPathLength;
1319    
1320     strcpy( bsdPath, _PATH_DEV );
1321     strcat( bsdPath, "r" );
1322    
1323     devPathLength = strlen( bsdPath );
1324    
1325     if ( CFStringGetCString( (__CFString*)bsdPathAsCFString,
1326     bsdPath + devPathLength,
1327     maxPathSize - devPathLength,
1328     kCFStringEncodingASCII ) )
1329     {
1330     printf( "BSD path: %s\n", bsdPath );
1331     kernResult = KERN_SUCCESS;
1332     }
1333    
1334     CFRelease( bsdPathAsCFString );
1335     }
1336     IOObjectRelease( nextMedia );
1337     }
1338    
1339     return kernResult;
1340     }
1341    
1342     struct _CDMSF
1343     {
1344     u_char minute;
1345     u_char second;
1346     u_char frame;
1347     };
1348    
1349     /* converting from minute-second to logical block entity */
1350     #define MSF_TO_LBA(msf) (((((msf).minute * 60UL) + (msf).second) * 75UL) + (msf).frame - 150)
1351    
1352     struct _CDTOC_Desc
1353     {
1354     u_char session;
1355     u_char ctrl_adr; /* typed to be machine and compiler independent */
1356     u_char tno;
1357     u_char point;
1358     struct _CDMSF address;
1359     u_char zero;
1360     struct _CDMSF p;
1361     };
1362    
1363     struct _CDTOC
1364     {
1365     u_short length; /* in native cpu endian */
1366     u_char first_session;
1367     u_char last_session;
1368     struct _CDTOC_Desc trackdesc[1];
1369     };
1370    
1371     static struct _CDTOC * ReadTOC (const char * devpath )
1372     {
1373     struct _CDTOC * toc_p = NULL;
1374     io_iterator_t iterator = 0;
1375     io_registry_entry_t service = 0;
1376     CFDictionaryRef properties = 0;
1377     CFDataRef data = 0;
1378     mach_port_t port = 0;
1379     char * devname;
1380     if (( devname = strrchr( devpath, '/' )) != NULL )
1381     {
1382     ++devname;
1383     }
1384     else
1385     {
1386     devname = ( char *) devpath;
1387     }
1388    
1389     if ( IOMasterPort(bootstrap_port, &port ) != KERN_SUCCESS )
1390     {
1391     printf( "IOMasterPort failed\n" ); goto Exit ;
1392     }
1393     if ( IOServiceGetMatchingServices( port, IOBSDNameMatching( port, 0, devname ),
1394     &iterator ) != KERN_SUCCESS )
1395     {
1396     printf( "IOServiceGetMatchingServices failed\n" ); goto Exit ;
1397     }
1398    
1399     char buffer[1024];
1400     IOObjectGetClass(iterator,buffer);
1401     printf("Service: %s\n",buffer);
1402    
1403    
1404     IOIteratorReset (iterator);
1405     service = IOIteratorNext( iterator );
1406    
1407     IOObjectRelease( iterator );
1408    
1409     iterator = 0;
1410     while ( service && !IOObjectConformsTo( service, "IOCDMedia" ))
1411     {
1412     char buffer[1024];
1413     IOObjectGetClass(service,buffer);
1414     printf("Service: %s\n",buffer);
1415    
1416     if ( IORegistryEntryGetParentIterator( service, kIOServicePlane, &iterator ) != KERN_SUCCESS )
1417     {
1418     printf( "IORegistryEntryGetParentIterator failed\n" );
1419     goto Exit ;
1420     }
1421    
1422     IOObjectRelease( service );
1423     service = IOIteratorNext( iterator );
1424     IOObjectRelease( iterator );
1425    
1426     }
1427     if ( service == NULL )
1428     {
1429     printf( "CD media not found\n" ); goto Exit ;
1430     }
1431     if ( IORegistryEntryCreateCFProperties( service, (__CFDictionary **) &properties,
1432     kCFAllocatorDefault,
1433     kNilOptions ) != KERN_SUCCESS )
1434     {
1435     printf( "IORegistryEntryGetParentIterator failed\n" ); goto Exit ;
1436     }
1437    
1438     data = (CFDataRef) CFDictionaryGetValue( properties, CFSTR( "TOC" ) );
1439     if ( data == NULL )
1440     {
1441     printf( "CFDictionaryGetValue failed\n" ); goto Exit ;
1442     }
1443     else
1444     {
1445    
1446     CFRange range;
1447     CFIndex buflen;
1448    
1449     buflen = CFDataGetLength( data ) + 1;
1450     range = CFRangeMake( 0, buflen );
1451     toc_p = ( struct _CDTOC *) malloc( buflen );
1452     if ( toc_p == NULL )
1453     {
1454     printf( "Out of memory\n" ); goto Exit ;
1455     }
1456     else
1457     {
1458     CFDataGetBytes( data, range, ( unsigned char *) toc_p );
1459     }
1460    
1461     /*
1462     fprintf( stderr, "Table of contents\n length %d first %d last %d\n",
1463     toc_p->length, toc_p->first_session, toc_p->last_session );
1464     */
1465    
1466     CFRelease( properties );
1467    
1468     }
1469     Exit :
1470     if ( service )
1471     {
1472     IOObjectRelease( service );
1473     }
1474    
1475     return toc_p;
1476     }
1477     #endif // _CARBON_
1478    
1479     DiskImage::DiskImage(int disk)
1480     {
1481     Init();
1482     #ifdef _WIN32_
1483     char buffer[1024];
1484     sprintf(buffer,"%c:\\",'A'+disk);
1485     mSize = GetFileSize(buffer,NULL);
1486    
1487     sprintf(buffer,"\\\\.\\%c:",'a'+disk);
1488     mFile = CreateFile(buffer, GENERIC_READ,0,NULL,OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS,NULL);
1489    
1490     DWORD junk;
1491     DISK_GEOMETRY dg;
1492     DISK_GEOMETRY* pdg = &dg;
1493     BOOL res = DeviceIoControl(mFile,
1494     IOCTL_DISK_GET_DRIVE_GEOMETRY,
1495     NULL, 0,
1496     pdg, sizeof(*pdg),
1497     &junk,
1498     NULL);
1499    
1500     if (res)
1501     {
1502     mSize = dg.BytesPerSector * dg.SectorsPerTrack * dg.TracksPerCylinder * dg.Cylinders.LowPart;
1503     mClusterSize = dg.BytesPerSector;
1504     }
1505 schoenebeck 2573 #elif defined(_CARBON_) || defined(__APPLE__)
1506 schoenebeck 2572 kern_return_t kernResult;
1507     io_iterator_t mediaIterator;
1508     char bsdPath[ MAXPATHLEN ];
1509     kernResult = FindEjectableCDMedia( &mediaIterator );
1510     kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) );
1511     if ( bsdPath[ 0 ] != '\0' )
1512     {
1513     strcpy(bsdPath,"/dev/rdisk1s0");
1514    
1515     struct _CDTOC * toc = ReadTOC( bsdPath );
1516     if ( toc )
1517     {
1518     size_t toc_entries = ( toc->length - 2 ) / sizeof (struct _CDTOC_Desc );
1519    
1520     int start_sector = -1;
1521     int data_track = -1;
1522     // Iterate through the list backward. Pick the first data track and
1523     // get the address of the immediately previous (or following depending
1524     // on how you look at it). The difference in the sector numbers
1525     // is returned as the sized of the data track.
1526     for (int i=toc_entries - 1; i>=0; i-- )
1527     {
1528     if ( start_sector != -1 )
1529     {
1530     start_sector = MSF_TO_LBA(toc->trackdesc[i].p) - start_sector;
1531     break ;
1532    
1533     }
1534     if (( toc->trackdesc[i].ctrl_adr >> 4) != 1 )
1535     continue ;
1536     if ( toc->trackdesc[i].ctrl_adr & 0x04 )
1537     {
1538     data_track = toc->trackdesc[i].point;
1539     start_sector = MSF_TO_LBA(toc->trackdesc[i].p);
1540     }
1541     }
1542    
1543     free( toc );
1544     if ( start_sector == -1 )
1545     {
1546     start_sector = 0;
1547     }
1548     }
1549     // mSize = start_sector;
1550     // printf("size %d\n",mSize);
1551    
1552    
1553     mFile = open(bsdPath,O_RDONLY);
1554     if (!mFile)
1555     printf("Error while opening file: %s\n",bsdPath);
1556     else
1557     {
1558     printf("opened file: %s\n",bsdPath);
1559    
1560     mSize = lseek(mFile,1000000000,SEEK_SET);
1561     printf("size %d\n",mSize);
1562     lseek(mFile,0,SEEK_SET);
1563    
1564     mSize = 700 * 1024 * 1024;
1565    
1566     }
1567     }
1568     if ( mediaIterator )
1569     {
1570     IOObjectRelease( mediaIterator );
1571     }
1572     #elif LINUX
1573     OpenStream("/dev/cdrom");
1574     #endif
1575     }
1576    
1577     void DiskImage::Init()
1578     {
1579     mFile = 0;
1580     mPos = 0;
1581     mCluster = (uint)-1;
1582     mStartFrame = -1;
1583     mEndFrame = -1;
1584 schoenebeck 2573 #ifdef WIN32
1585 schoenebeck 2572 mpCache = (char*) VirtualAlloc(NULL,mClusterSize,MEM_COMMIT,PAGE_READWRITE);
1586     #else
1587     mpCache = NULL; // we allocate the cache later when we know what type of media we access
1588     #endif
1589     }
1590    
1591     DiskImage::~DiskImage()
1592     {
1593 schoenebeck 2573 #ifdef WIN32
1594 schoenebeck 2572 if (mFile != INVALID_HANDLE_VALUE)
1595     {
1596     CloseHandle(mFile);
1597     }
1598 schoenebeck 2573 #elif defined _CARBON_ || defined(__APPLE__) || LINUX
1599 schoenebeck 2572 if (mFile)
1600     {
1601     close(mFile);
1602     }
1603     #endif
1604     if (mpCache)
1605     {
1606 schoenebeck 2573 #ifdef WIN32
1607 schoenebeck 2572 VirtualFree(mpCache, 0, MEM_RELEASE);
1608 schoenebeck 2573 #elif defined(_CARBON_) || defined(__APPLE__) || LINUX
1609 schoenebeck 2572 free(mpCache);
1610     #endif
1611     }
1612     }
1613    
1614     akai_stream_state_t DiskImage::GetState() const
1615     {
1616     if (!mFile) return akai_stream_closed;
1617     if (mPos > mSize) return akai_stream_end_reached;
1618     return akai_stream_ready;
1619     }
1620    
1621     int DiskImage::GetPos() const
1622     {
1623     return mPos;
1624     }
1625    
1626     int DiskImage::SetPos(int Where, akai_stream_whence_t Whence)
1627     {
1628     // printf("setpos %d\n",Where);
1629     int w = Where;
1630     switch (Whence)
1631     {
1632     case akai_stream_start:
1633     mPos = w;
1634     break;
1635     /*case eStreamRewind:
1636     w = -w;*/
1637     case akai_stream_curpos:
1638     mPos += w;
1639     break;
1640     case akai_stream_end:
1641     mPos = mSize - w;
1642     break;
1643     }
1644     // if (mPos > mSize)
1645     // mPos = mSize;
1646     if (mPos < 0)
1647     mPos = 0;
1648     return mPos;
1649     }
1650    
1651     int DiskImage::Available (uint WordSize)
1652     {
1653     return (mSize-mPos)/WordSize;
1654     }
1655    
1656     int DiskImage::Read(void* pData, uint WordCount, uint WordSize)
1657     {
1658     int readbytes = 0;
1659     int sizetoread = WordCount * WordSize;
1660    
1661     while (sizetoread > 0) {
1662     if (mSize <= mPos) return readbytes / WordSize;
1663     int requestedCluster = (mRegularFile) ? mPos / mClusterSize
1664     : mPos / mClusterSize + mStartFrame;
1665     if (mCluster != requestedCluster) { // read the requested cluster into cache
1666     mCluster = requestedCluster;
1667 schoenebeck 2573 #ifdef WIN32
1668 schoenebeck 2572 if (mCluster * mClusterSize != SetFilePointer(mFile, mCluster * mClusterSize, NULL, FILE_BEGIN)) {
1669     printf("ERROR: couldn't seek device!\n");
1670     if ((readbytes > 0) && (mEndian != eEndianNative)) {
1671     switch (WordSize) {
1672     case 2: bswap_16_s ((uint16*)pData, readbytes); break;
1673     case 4: bswap_32_s ((uint32*)pData, readbytes); break;
1674     case 8: bswap_64_s ((uint64*)pData, readbytes); break;
1675     }
1676     }
1677     return readbytes / WordSize;
1678     }
1679     DWORD size;
1680     ReadFile(mFile, mpCache, mClusterSize, &size, NULL);
1681 schoenebeck 2573 #elif defined(_CARBON_) || defined(__APPLE__) || LINUX
1682 schoenebeck 2572 if (mCluster * mClusterSize != lseek(mFile, mCluster * mClusterSize, SEEK_SET))
1683     return readbytes / WordSize;
1684     // printf("trying to read %d bytes from device!\n",mClusterSize);
1685     int size = read(mFile, mpCache, mClusterSize);
1686     // printf("read %d bytes from device!\n",size);
1687     #endif
1688     }
1689     // printf("read %d bytes at pos %d\n",WordCount*WordSize,mPos);
1690    
1691     int currentReadSize = sizetoread;
1692     int posInCluster = mPos % mClusterSize;
1693     if (currentReadSize > mClusterSize - posInCluster) // restrict to this current cached cluster.
1694     currentReadSize = mClusterSize - posInCluster;
1695    
1696     memcpy((uint8_t*)pData + readbytes, mpCache + posInCluster, currentReadSize);
1697    
1698     mPos += currentReadSize;
1699     readbytes += currentReadSize;
1700     sizetoread -= currentReadSize;
1701     // printf("new pos %d\n",mPos);
1702     }
1703    
1704     #if 0
1705     if ((readbytes > 0) && (mEndian != eEndianNative))
1706     switch (WordSize)
1707     {
1708     case 2: bswap_16_s ((uint16_t*)pData, readbytes); break;
1709     case 4: bswap_32_s ((uint32_t*)pData, readbytes); break;
1710     case 8: bswap_64_s ((uint64_t*)pData, readbytes); break;
1711     }
1712     #endif
1713    
1714     return readbytes / WordSize;
1715     }
1716    
1717     void DiskImage::ReadInt8(uint8_t* pData, uint WordCount) {
1718     Read(pData, WordCount, 1);
1719     }
1720    
1721     void DiskImage::ReadInt16(uint16_t* pData, uint WordCount) {
1722     int i;
1723     for (i = 0; i < WordCount; i++) {
1724     *(pData + i) = ReadInt16();
1725     }
1726     }
1727    
1728     void DiskImage::ReadInt32(uint32_t* pData, uint WordCount) {
1729     int i;
1730     for (i = 0; i < WordCount; i++) {
1731     *(pData + i) = ReadInt32();
1732     }
1733     }
1734    
1735     int DiskImage::ReadInt8(uint8_t* pData) {
1736     return Read(pData, 1, 1);
1737     }
1738    
1739     int DiskImage::ReadInt16(uint16_t* pData) {
1740     int result = Read(pData, 1, 2);
1741     #if WORDS_BIGENDIAN
1742     swapBytes_16(pData);
1743     #endif
1744     return result;
1745     }
1746    
1747     int DiskImage::ReadInt32(uint32_t* pData) {
1748     int result = Read(pData, 1, 4);
1749     #if WORDS_BIGENDIAN
1750     swapBytes_32(pData);
1751     #endif
1752     return result;
1753     }
1754    
1755     uint8_t DiskImage::ReadInt8()
1756     {
1757     uint8_t word;
1758     Read(&word,1,1);
1759     return word;
1760     }
1761    
1762     uint16_t DiskImage::ReadInt16()
1763     {
1764     uint16_t word;
1765     Read(&word,1,2);
1766     #if WORDS_BIGENDIAN
1767     swapBytes_16(&word);
1768     #endif
1769     return word;
1770     }
1771    
1772     uint32_t DiskImage::ReadInt32()
1773     {
1774     uint32_t word;
1775     Read(&word,1,4);
1776     #if WORDS_BIGENDIAN
1777     swapBytes_32(&word);
1778     #endif
1779     return word;
1780     }
1781    
1782     void DiskImage::OpenStream(const char* path) {
1783 schoenebeck 2573 #ifdef WIN32
1784 schoenebeck 2572 mFile = CreateFile(path, GENERIC_READ,0,NULL,OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS,NULL);
1785     BY_HANDLE_FILE_INFORMATION FileInfo;
1786     GetFileInformationByHandle(mFile,&FileInfo);
1787     mSize = FileInfo.nFileSizeLow;
1788 schoenebeck 2573 #elif defined(_CARBON_) || defined(__APPLE__) || LINUX
1789 schoenebeck 2572 struct stat filestat;
1790     stat(path,&filestat);
1791     mFile = open(path, O_RDONLY | O_NONBLOCK);
1792     if (mFile <= 0) {
1793     printf("Can't open %s\n", path);
1794     mFile = 0;
1795     return;
1796     }
1797     // TODO: we should also check here if 'path' is a link or something
1798     if (S_ISREG(filestat.st_mode)) { // regular file
1799     printf("Using regular Akai image file.\n");
1800     mRegularFile = true;
1801     mSize = filestat.st_size;
1802     mClusterSize = DISK_CLUSTER_SIZE;
1803     mpCache = (char*) malloc(mClusterSize);
1804 schoenebeck 2573 } else { // CDROM
1805     #if defined(_CARBON_) || defined(__APPLE__)
1806     printf("Can't open %s: not a regular file\n", path);
1807     #else // Linux ...
1808 schoenebeck 2572 mRegularFile = false;
1809     mClusterSize = CD_FRAMESIZE;
1810     mpCache = (char*) malloc(mClusterSize);
1811    
1812     struct cdrom_tochdr tochdr;
1813     struct cdrom_tocentry tocentry;
1814     int prev_addr = 0;
1815     if (ioctl(mFile, CDROMREADTOCHDR, (unsigned long)&tochdr) < 0) {
1816     printf("Trying to read TOC of %s failed\n", path);
1817     close(mFile);
1818     mFile = 0;
1819     return;
1820     }
1821     printf("Total tracks: %d\n", tochdr.cdth_trk1);
1822    
1823     /* we access the CD with logical blocks as entity */
1824     tocentry.cdte_format = CDROM_LBA;
1825    
1826     int firstDataTrack = -1;
1827     int start, end, length;
1828    
1829     /* we pick up the lowest data track by iterating backwards, starting with Lead Out */
1830     for (int t = tochdr.cdth_trk1; t >= 0; t--) {
1831     tocentry.cdte_track = (t == tochdr.cdth_trk1) ? CDROM_LEADOUT : t + 1;
1832     if (ioctl(mFile, CDROMREADTOCENTRY, (unsigned long)&tocentry) < 0){
1833     printf("Failed to read TOC entry for track %d\n", tocentry.cdte_track);
1834     close(mFile);
1835     mFile = 0;
1836     return;
1837     }
1838     if (tocentry.cdte_track == CDROM_LEADOUT) {
1839     printf("Lead Out: Start(LBA)=%d\n", tocentry.cdte_addr.lba);
1840     }
1841     else {
1842     printf("Track %d: Start(LBA)=%d End(LBA)=%d Length(Blocks)=%d ",
1843     tocentry.cdte_track, tocentry.cdte_addr.lba, prev_addr - 1, prev_addr - tocentry.cdte_addr.lba);
1844     if (tocentry.cdte_ctrl & CDROM_DATA_TRACK) {
1845     printf("Type: Data\n");
1846     firstDataTrack = tocentry.cdte_track;
1847     start = tocentry.cdte_addr.lba;
1848     end = prev_addr - 1;
1849     length = prev_addr - tocentry.cdte_addr.lba;
1850     }
1851     else printf("Type: Audio\n");
1852     }
1853     prev_addr = tocentry.cdte_addr.lba;
1854     }
1855    
1856     if (firstDataTrack == -1) {
1857     printf("Sorry, no data track found on %s\n", path);
1858     close(mFile);
1859     mFile = 0;
1860     return;
1861     }
1862    
1863     printf("Ok, I'll pick track %d\n", firstDataTrack);
1864     mStartFrame = start;
1865     mEndFrame = end;
1866     mSize = length * CD_FRAMESIZE;
1867 schoenebeck 2573 #endif
1868 schoenebeck 2572 } // CDROM
1869     #endif
1870     }
1871    
1872     bool DiskImage::WriteImage(const char* path)
1873     {
1874 schoenebeck 2573 #if defined(_CARBON_) || defined(__APPLE__) || LINUX
1875 schoenebeck 2572 const uint bufferSize = 524288; // 512 kB
1876     int fOut = open(path, O_WRONLY | O_NONBLOCK | O_CREAT | O_TRUNC);
1877     if (mFile <= 0) {
1878     printf("Can't open output file %s\n", path);
1879     return false;
1880     }
1881     uint8_t* pBuffer = new uint8_t[bufferSize];
1882     SetPos(0);
1883     while (Available() > 0) {
1884     int readBytes = Read(pBuffer,bufferSize,1);
1885     if (readBytes > 0) write(fOut,pBuffer,readBytes);
1886     }
1887     delete[] pBuffer;
1888     close(fOut);
1889     return true;
1890     #endif // _CARBON_ || LINUX
1891     }
1892    
1893     inline void DiskImage::swapBytes_16(void* Word) {
1894     uint8_t byteCache;
1895     byteCache = *((uint8_t*) Word);
1896     *((uint8_t*) Word) = *((uint8_t*) Word + 1);
1897     *((uint8_t*) Word + 1) = byteCache;
1898     }
1899    
1900     inline void DiskImage::swapBytes_32(void* Word) {
1901     uint8_t byteCache;
1902     byteCache = *((uint8_t*) Word);
1903     *((uint8_t*) Word) = *((uint8_t*) Word + 3);
1904     *((uint8_t*) Word + 3) = byteCache;
1905     byteCache = *((uint8_t*) Word + 1);
1906     *((uint8_t*) Word + 1) = *((uint8_t*) Word + 2);
1907     *((uint8_t*) Word + 2) = byteCache;
1908     }

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC