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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2573 - (show annotations) (download)
Thu May 22 15:17:09 2014 UTC (9 years, 11 months 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 /*
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 #if defined (__GNUC__) && (__GNUC__ >= 4)
31 #include <sys/disk.h>
32 #else
33 #include <dev/disk.h>
34 #endif
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <errno.h>
38 #include <paths.h>
39 #if defined(_CARBON_) || defined(__APPLE__)
40 #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 #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 //////////////////////////////////
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 #ifdef WIN32
830 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 #elif defined(_CARBON_) || defined(__APPLE__)
1506 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 #ifdef WIN32
1585 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 #ifdef WIN32
1594 if (mFile != INVALID_HANDLE_VALUE)
1595 {
1596 CloseHandle(mFile);
1597 }
1598 #elif defined _CARBON_ || defined(__APPLE__) || LINUX
1599 if (mFile)
1600 {
1601 close(mFile);
1602 }
1603 #endif
1604 if (mpCache)
1605 {
1606 #ifdef WIN32
1607 VirtualFree(mpCache, 0, MEM_RELEASE);
1608 #elif defined(_CARBON_) || defined(__APPLE__) || LINUX
1609 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 #ifdef WIN32
1668 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 #elif defined(_CARBON_) || defined(__APPLE__) || LINUX
1682 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 #ifdef WIN32
1784 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 #elif defined(_CARBON_) || defined(__APPLE__) || LINUX
1789 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 } else { // CDROM
1805 #if defined(_CARBON_) || defined(__APPLE__)
1806 printf("Can't open %s: not a regular file\n", path);
1807 #else // Linux ...
1808 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 #endif
1868 } // CDROM
1869 #endif
1870 }
1871
1872 bool DiskImage::WriteImage(const char* path)
1873 {
1874 #if defined(_CARBON_) || defined(__APPLE__) || LINUX
1875 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