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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3476 - (show annotations) (download)
Wed Feb 20 19:12:49 2019 UTC (5 years, 1 month ago) by schoenebeck
File size: 56626 byte(s)
* Added MSVC build support
  (anonymous patch from mailing list).
* Introduced CMake build support (yet constrained for building with MSVC)
  (anonymous patch from mailing list).
* Bumped version (4.1.0.svn12).

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

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC